diff --git a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala index 63bccaa8f50e..3ae561895b4a 100644 --- a/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala +++ b/compiler/src/dotty/tools/dotc/config/ScalaSettings.scala @@ -127,6 +127,7 @@ trait CommonScalaSettings: val encoding: Setting[String] = StringSetting(RootSetting, "encoding", "encoding", "Specify character encoding used by source files.", Properties.sourceEncoding, aliases = List("--encoding")) val scalajs: Setting[Boolean] = BooleanSetting(RootSetting, "scalajs", "Compile in Scala.js mode (requires scalajs-library.jar on the classpath).", aliases = List("--scalajs")) val replInitScript: Setting[String] = StringSetting(RootSetting, "repl-init-script", "code", "The code will be run on REPL startup.", "", aliases = List("--repl-init-script")) + val replInitPackage: Setting[String] = StringSetting(RootSetting, "repl-init-package", "package", "The package in which REPL-entered code is wrapped. Defaults to the empty package.", "", aliases = List("--repl-init-package")) val replQuitAfterInit: Setting[Boolean] = BooleanSetting(RootSetting, "repl-quit-after-init", "Quit REPL after evaluating the init script.", aliases = List("--repl-quit-after-init")) /* YSettings shared with scaladoc */ diff --git a/repl/src/dotty/tools/repl/ReplCompiler.scala b/repl/src/dotty/tools/repl/ReplCompiler.scala index 438f1e2ee1ef..53b380be86f2 100644 --- a/repl/src/dotty/tools/repl/ReplCompiler.scala +++ b/repl/src/dotty/tools/repl/ReplCompiler.scala @@ -50,9 +50,12 @@ class ReplCompiler extends Compiler: def importContext(imp: tpd.Import)(using Context) = ctx.importContext(imp, imp.symbol) + val pkgName = initCtx.settings.replInitPackage.value(using initCtx).trim + val pkgPrefix = if pkgName.isEmpty then nme.EMPTY_PACKAGE.toString else pkgName + def importPreviousRun(id: Int)(using Context) = { // we first import the wrapper object id - val path = nme.EMPTY_PACKAGE ++ "." ++ ReplCompiler.objectNames(id) + val path = s"$pkgPrefix.${ReplCompiler.objectNames(id)}" val ctx0 = ctx.fresh .setNewScope .withRootImports(RootRef(() => requiredModuleRef(path)) :: Nil) @@ -64,6 +67,9 @@ class ReplCompiler extends Compiler: importContext(imp)(using ctx)) } + // The owner stays the empty package; the PackageDef wrapping each + // input is what places definitions into the configured package + // during typechecking. val rootCtx = super.rootContext.fresh .withRootImports .fresh.setOwner(defn.EmptyPackageClass): Context @@ -77,7 +83,7 @@ class ReplCompiler extends Compiler: private def packaged(stats: List[untpd.Tree])(using Context): untpd.PackageDef = import untpd.* - PackageDef(Ident(nme.EMPTY_PACKAGE), stats) + PackageDef(ReplCompiler.packageId, stats) final def compile(parsed: Parsed)(using state: State): Either[(List[Diagnostic], State), (CompilationUnit, State)] = assert(!parsed.trees.isEmpty) @@ -166,7 +172,7 @@ class ReplCompiler extends Compiler: val wrapper = TypeDef("$wrapper".toTypeName, tmpl) .withMods(Modifiers(Final)) .withSpan(Span(0, expr.length)) - PackageDef(Ident(nme.EMPTY_PACKAGE), List(wrapper)) + PackageDef(ReplCompiler.packageId, List(wrapper)) } ParseResult(sourceFile) match { @@ -230,9 +236,19 @@ class ReplCompiler extends Compiler: } } } + object ReplCompiler: val ReplState: Property.StickyKey[State] = Property.StickyKey() val objectNames = mutable.Map.empty[Int, TermName] + + def packageId(using Context): untpd.RefTree = + import untpd.* + val name = ctx.settings.replInitPackage.value.trim + if name.isEmpty then Ident(nme.EMPTY_PACKAGE) + else + val parts = name.split('.').toList + parts.tail.foldLeft[RefTree](Ident(parts.head.toTermName))((acc, p) => + Select(acc, p.toTermName)) end ReplCompiler class ReplCompilationUnit(source: SourceFile) extends CompilationUnit(source, CompilationUnitInfo(source.file)): @@ -346,5 +362,5 @@ class ReplPhase extends Phase: val tmpl = Template(emptyConstructor, Nil, Nil, EmptyValDef, defs.stats) val module = ModuleDef(objectTermName, tmpl).withSpan(span) - PackageDef(Ident(nme.EMPTY_PACKAGE), List(module)) + PackageDef(ReplCompiler.packageId, List(module)) end ReplPhase diff --git a/repl/test-resources/repl/replInitPackage b/repl/test-resources/repl/replInitPackage new file mode 100644 index 000000000000..14eed9ba4261 --- /dev/null +++ b/repl/test-resources/repl/replInitPackage @@ -0,0 +1,11 @@ +//> using options --repl-init-package:my.app +scala> class Foo +// defined class Foo +scala> classOf[Foo].getName +val res0: String = "my.app.rs$line$1$Foo" +scala> object Bar { val x = 1 } +// defined object Bar +scala> Bar.x + 1 +val res1: Int = 2 +scala> Bar.getClass.getName +val res2: String = "my.app.rs$line$3$Bar$"