From de1508103e893e994dba44c6c10141b6eacd8682 Mon Sep 17 00:00:00 2001 From: Li Haoyi Date: Sun, 18 Jun 2017 17:03:44 +0800 Subject: [PATCH] - `--predef` is now `--predef-code` and `--predef-file` is now `--predef`, to reflect the more common use case of specifying a predef file rather than some predef code snippet - `ammonite.Main`'s `predef` argument has also become `predefCode`, and it has grown a new `predefFile` arg - `--predef` now always is *in addition to* the predef in `~/.ammonite/predef.sc`. You can use `--no-home-predef` to disable `~/.ammonite/predef.sc` instead. - `~/.ammonite/predefShared.sc` is now gone; if you want to share code between `predef.sc` and `predefScript.sc`, you can `import $exec` a third script from both of those --- .../scala/ammonite/interp/Interpreter.scala | 7 +- .../interp/PredefInitialization.scala | 11 +- .../src/main/scala/ammonite/repl/Repl.scala | 11 +- .../main/scala/ammonite/runtime/Storage.scala | 8 - amm/src/main/scala/ammonite/Main.scala | 213 +++++++++++------- amm/src/main/scala/ammonite/main/Cli.scala | 35 ++- amm/src/test/scala/ammonite/TestRepl.scala | 3 +- amm/src/test/scala/ammonite/TestUtils.scala | 1 + .../ammonite/integration/BasicTests.scala | 3 +- .../scala/ammonite/integration/TestMain.scala | 2 +- internals-docs/predef.md | 2 +- readme/Footer.scalatex | 38 ++++ readme/Repl.scalatex | 22 +- readme/Scripts.scalatex | 34 +++ .../test/scala/ammonite/shell/TestMain.scala | 3 +- .../main/scala/ammonite/sshd/SshdRepl.scala | 13 +- 16 files changed, 272 insertions(+), 134 deletions(-) diff --git a/amm/interp/src/main/scala/ammonite/interp/Interpreter.scala b/amm/interp/src/main/scala/ammonite/interp/Interpreter.scala index 06b55e853..c5e5b6c5a 100644 --- a/amm/interp/src/main/scala/ammonite/interp/Interpreter.scala +++ b/amm/interp/src/main/scala/ammonite/interp/Interpreter.scala @@ -20,6 +20,7 @@ import ammonite.util._ */ class Interpreter(val printer: Printer, val storage: Storage, + basePredefs: Seq[PredefInfo], customPredefs: Seq[PredefInfo], // Allows you to set up additional "bridges" between the REPL // world and the outside world, by passing in the full name @@ -93,6 +94,7 @@ class Interpreter(val printer: Printer, interpApi, evalClassloader, storage, + basePredefs, customPredefs, // AutoImport is false, because we do not want the predef imports to get // bundled into the main `eval.imports`: instead we pass `predefImports` @@ -112,10 +114,7 @@ class Interpreter(val printer: Printer, // The ReplAPI requires some special post-Interpreter-initialization // code to run, so let it pass it in a callback and we'll run it here - def watch(p: Path) = { - new Exception().printStackTrace() - watchedFiles.append(p -> Interpreter.pathSignature(p)) - } + def watch(p: Path) = watchedFiles.append(p -> Interpreter.pathSignature(p)) def resolveSingleImportHook(source: CodeSource, tree: ImportTree) = synchronized{ val strippedPrefix = tree.prefix.takeWhile(_(0) == '$').map(_.stripPrefix("$")) diff --git a/amm/interp/src/main/scala/ammonite/interp/PredefInitialization.scala b/amm/interp/src/main/scala/ammonite/interp/PredefInitialization.scala index 9b069c235..b62c93272 100644 --- a/amm/interp/src/main/scala/ammonite/interp/PredefInitialization.scala +++ b/amm/interp/src/main/scala/ammonite/interp/PredefInitialization.scala @@ -16,6 +16,7 @@ object PredefInitialization { interpApi: InterpAPI, evalClassloader: SpecialClassLoader, storage: Storage, + basePredefs: Seq[PredefInfo], customPredefs: Seq[PredefInfo], processModule: (String, CodeSource, Boolean) => Res[Metadata], addImports: Imports => Unit, @@ -37,19 +38,19 @@ object PredefInitialization { ) val predefs = { - bridgePredefs ++ customPredefs ++ - (storage.loadSharedPredef ++ storage.loadPredef).map{ + bridgePredefs ++ + basePredefs ++ + storage.loadPredef.map{ case (code, path) => PredefInfo(Name(path.last.stripSuffix(".sc")), code, false, Some(path)) - } + } ++ + customPredefs } predefs.filter(_.code.nonEmpty) Res.fold((), predefs){(_, predefInfo) => - pprint.log(predefInfo.name) - pprint.log(predefInfo.path) predefInfo.path.foreach(watch) if (predefInfo.code.isEmpty) Res.Success(()) else { diff --git a/amm/repl/src/main/scala/ammonite/repl/Repl.scala b/amm/repl/src/main/scala/ammonite/repl/Repl.scala index 66702bbfd..afeb648a1 100644 --- a/amm/repl/src/main/scala/ammonite/repl/Repl.scala +++ b/amm/repl/src/main/scala/ammonite/repl/Repl.scala @@ -15,8 +15,8 @@ class Repl(input: InputStream, output: OutputStream, error: OutputStream, storage: Storage, - defaultPredef: String, - mainPredef: String, + basePredefs: Seq[PredefInfo], + customPredefs: Seq[PredefInfo], wd: ammonite.ops.Path, welcomeBanner: Option[String], replArgs: IndexedSeq[Bind[_]] = Vector.empty, @@ -58,11 +58,8 @@ class Repl(input: InputStream, val interp: Interpreter = new Interpreter( printer, storage, - Seq( - PredefInfo(Name("DefaultPredef"), defaultPredef, true, None), - PredefInfo(Name("ArgsPredef"), argString, false, None), - PredefInfo(Name("MainPredef"), mainPredef, false, None) - ), + basePredefs, + customPredefs, Seq(( "ammonite.repl.ReplBridge", "repl", diff --git a/amm/runtime/src/main/scala/ammonite/runtime/Storage.scala b/amm/runtime/src/main/scala/ammonite/runtime/Storage.scala index ddc86422e..0eba72a58 100644 --- a/amm/runtime/src/main/scala/ammonite/runtime/Storage.scala +++ b/amm/runtime/src/main/scala/ammonite/runtime/Storage.scala @@ -20,7 +20,6 @@ import scala.reflect.NameTransformer.encode */ trait Storage{ def loadPredef: Option[(String, Path)] - def loadSharedPredef: Option[(String, Path)] val fullHistory: StableRef[History] val ivyCache: StableRef[Storage.IvyMap] def compileCacheSave(path: String, tag: Tag, data: Storage.CompileCache): Unit @@ -55,7 +54,6 @@ object Storage{ var predef = "" var sharedPredef = "" def loadPredef = None - def loadSharedPredef = None def getSessionId = 0L var _history = new History(Vector()) val fullHistory = new StableRef[History]{ @@ -105,7 +103,6 @@ object Storage{ class Folder(val dir: Path, isRepl: Boolean = true) extends Storage{ def predef = if (isRepl) dir/"predef.sc" else dir/"predefScript.sc" - def predefShared = dir/"predefShared.sc" // Each version puts its cache in a separate folder, to bust caches // on every version bump; otherwise binary-incompatible changes to // ReplAPI/Preprocessor/ammonite-ops will cause scripts to fail after @@ -247,11 +244,6 @@ object Storage{ try Some((read(predef), predef)) catch { case e: java.nio.file.NoSuchFileException => None } } - - def loadSharedPredef = { - try Some((read(predefShared), predefShared)) - catch {case e: java.nio.file.NoSuchFileException => None } - } } } diff --git a/amm/src/main/scala/ammonite/Main.scala b/amm/src/main/scala/ammonite/Main.scala index 892bc7095..c9602b5c8 100644 --- a/amm/src/main/scala/ammonite/Main.scala +++ b/amm/src/main/scala/ammonite/Main.scala @@ -1,12 +1,14 @@ package ammonite import java.io.{InputStream, OutputStream, PrintStream} +import java.nio.file.NoSuchFileException import ammonite.interp.Interpreter import ammonite.ops._ import ammonite.runtime.{Frame, Storage} import ammonite.main._ import ammonite.repl.{RemoteLogger, Repl} +import ammonite.util.Util.newLine import ammonite.util._ import scala.annotation.tailrec @@ -28,7 +30,7 @@ import scala.annotation.tailrec * Note that the [[instantiateRepl]] function generates a new [[Repl]] * every time it is called! * - * @param predef Any additional code you want to run before the REPL session + * @param predefCode Any additional code you want to run before the REPL session * starts. Can contain multiple blocks separated by `@`s * @param defaultPredef Do you want to include the "standard" predef imports * provided by Ammonite? These include tools like `time`, @@ -52,7 +54,8 @@ import scala.annotation.tailrec * gets sent miscellaneous info messages that aren't strictly * part of the REPL or script's output */ -case class Main(predef: String = "", +case class Main(predefCode: String = "", + predefFile: Option[Path] = None, defaultPredef: Boolean = true, storageBackend: Storage = new Storage.Folder(Defaults.ammoniteHome), wd: Path = ammonite.ops.pwd, @@ -64,57 +67,93 @@ case class Main(predef: String = "", remoteLogging: Boolean = true, colors: Colors = Colors.Default){ + def loadedPredefFile = predefFile match{ + case Some(path) => + try Right(Some(PredefInfo(Name("FilePredef"), read(path), false, Some(path)))) + catch{case e: NoSuchFileException => + Left((Res.Failure("Unable to load predef file " + path), Seq(path -> 0L))) + } + case None => Right(None) + } /** * Instantiates an ammonite.Repl using the configuration */ def instantiateRepl(replArgs: IndexedSeq[Bind[_]] = Vector.empty, - remoteLogger: Option[RemoteLogger]) = { - val augmentedPredef = Main.maybeDefaultPredef( - defaultPredef, - Defaults.replPredef + Defaults.predefString - ) + remoteLogger: Option[RemoteLogger]) = { + + + loadedPredefFile.right.map{ predefFileInfoOpt => + val augmentedPredef = Main.maybeDefaultPredef( + defaultPredef, + Defaults.replPredef + Defaults.predefString + ) + + val argString = replArgs.zipWithIndex.map{ case (b, idx) => + s""" + val ${b.name} = ammonite + .repl + .ReplBridge + .value + .Internal + .replArgs($idx) + .value + .asInstanceOf[${b.typeTag.tpe}] + """ + }.mkString(newLine) + + new Repl( + inputStream, outputStream, errorStream, + storage = storageBackend, + basePredefs = Seq( + PredefInfo(Name("DefaultPredef"), augmentedPredef, true, None), + PredefInfo(Name("ArgsPredef"), argString, false, None) + ), + customPredefs = predefFileInfoOpt.toSeq ++ Seq( + PredefInfo(Name("CodePredef"), predefCode, false, None) + ), + wd = wd, + welcomeBanner = welcomeBanner, + replArgs = replArgs, + remoteLogger = remoteLogger, + initialColors = colors + ) + } - new Repl( - inputStream, outputStream, errorStream, - storage = storageBackend, - defaultPredef = augmentedPredef, - mainPredef = predef, - wd = wd, - welcomeBanner = welcomeBanner, - replArgs = replArgs, - remoteLogger = remoteLogger, - initialColors = colors - ) } def instantiateInterpreter() = { - val augmentedPredef = Main.maybeDefaultPredef(defaultPredef, Defaults.predefString) - - val (colorsRef, _, _, printer) = Interpreter.initPrinters( - colors, - outputStream, - errorStream, - verboseOutput - ) - val frame = Frame.createInitial() - - val interp: Interpreter = new Interpreter( - printer, - storageBackend, - Seq( - PredefInfo(Name("defaultPredef"), augmentedPredef, false, None), - PredefInfo(Name("predef"), predef, false, None) - ), - Vector.empty, - wd, - colorsRef, - verboseOutput, - () => frame - ) - interp.initializePredef() match{ - case None => Right(interp) - case Some(problems) => Left(problems) + loadedPredefFile.right.flatMap { predefFileInfoOpt => + val augmentedPredef = Main.maybeDefaultPredef(defaultPredef, Defaults.predefString) + + val (colorsRef, _, _, printer) = Interpreter.initPrinters( + colors, + outputStream, + errorStream, + verboseOutput + ) + val frame = Frame.createInitial() + + val interp: Interpreter = new Interpreter( + printer, + storageBackend, + basePredefs = Seq( + PredefInfo(Name("DefaultPredef"), augmentedPredef, false, None) + ), + predefFileInfoOpt.toSeq ++ Seq( + PredefInfo(Name("CodePredef"), predefCode, false, None) + ), + Vector.empty, + wd, + colorsRef, + verboseOutput, + () => frame + ) + interp.initializePredef() match{ + case None => Right(interp) + case Some(problems) => Left(problems) + } } + } /** @@ -133,25 +172,27 @@ case class Main(predef: String = "", remoteLogger.foreach(_.apply("Boot")) - val repl = instantiateRepl(replArgs.toIndexedSeq, remoteLogger) - - repl.initializePredef().getOrElse{ - // Warm up the compilation logic in the background, hopefully while the - // user is typing their first command, so by the time the command is - // submitted it can be processed by a warm compiler - val warmupThread = new Thread(new Runnable{ - def run() = repl.warmup() - }) - // This thread will terminal eventually on its own, but if the - // JVM wants to exit earlier this thread shouldn't stop it - warmupThread.setDaemon(true) - warmupThread.start() - - try{ - val exitValue = Res.Success(repl.run()) - (exitValue.map(repl.beforeExit), repl.interp.watchedFiles) - }finally{ - remoteLogger.foreach(_.close()) + instantiateRepl(replArgs.toIndexedSeq, remoteLogger) match{ + case Left(missingPredefInfo) => missingPredefInfo + case Right(repl) => + repl.initializePredef().getOrElse{ + // Warm up the compilation logic in the background, hopefully while the + // user is typing their first command, so by the time the command is + // submitted it can be processed by a warm compiler + val warmupThread = new Thread(new Runnable{ + def run() = repl.warmup() + }) + // This thread will terminal eventually on its own, but if the + // JVM wants to exit earlier this thread shouldn't stop it + warmupThread.setDaemon(true) + warmupThread.start() + + try{ + val exitValue = Res.Success(repl.run()) + (exitValue.map(repl.beforeExit), repl.interp.watchedFiles) + }finally{ + remoteLogger.foreach(_.close()) + } } } } @@ -291,10 +332,8 @@ class MainRunner(cliConfig: Cli.Config, @tailrec final def watchLoop[T](isRepl: Boolean, run: Main => (Res[T], Seq[(Path, Long)])): Boolean = { - val (result, watched) = initMain(isRepl) match{ - case Left((msg, watched)) => (Res.Failure(msg), watched) - case Right(main) => run(main) - } + val (result, watched) = run(initMain(isRepl)) + val success = handleWatchRes(result) if (!cliConfig.watch) success else{ @@ -353,27 +392,27 @@ class MainRunner(cliConfig: Cli.Config, Left(("Script file not found: " + file, Seq(file -> 0L))) } } - val storage = loadedPredef.right.map{ - case None => new Storage.Folder(cliConfig.home, isRepl) - case Some((predefCode, predefFile)) => - new Storage.Folder(cliConfig.home, isRepl) { - override def loadPredef = Some((predefCode, predefFile)) - } - } - storage.right.map{ storage => - Main( - cliConfig.predef, - cliConfig.defaultPredef, - storage, - inputStream = stdIn, - outputStream = stdOut, - errorStream = stdErr, - welcomeBanner = cliConfig.welcomeBanner, - verboseOutput = cliConfig.verboseOutput, - remoteLogging = cliConfig.remoteLogging, - colors = colors - ) + val storage = if (!cliConfig.homePredef) { + new Storage.Folder(cliConfig.home, isRepl) { + override def loadPredef = None + } + }else{ + new Storage.Folder(cliConfig.home, isRepl) } + Main( + cliConfig.predefCode, + cliConfig.predefFile, + cliConfig.defaultPredef, + storage, + inputStream = stdIn, + outputStream = stdOut, + errorStream = stdErr, + welcomeBanner = cliConfig.welcomeBanner, + verboseOutput = cliConfig.verboseOutput, + remoteLogging = cliConfig.remoteLogging, + colors = colors + ) + } } \ No newline at end of file diff --git a/amm/src/main/scala/ammonite/main/Cli.scala b/amm/src/main/scala/ammonite/main/Cli.scala index d86cc78bb..d1a16c57c 100644 --- a/amm/src/main/scala/ammonite/main/Cli.scala +++ b/amm/src/main/scala/ammonite/main/Cli.scala @@ -17,8 +17,9 @@ object Cli{ (implicit val reader: scopt.Read[V]){ def runAction(t: T, s: String) = action(t, reader.reads(s)) } - case class Config(predef: String = "", + case class Config(predefCode: String = "", defaultPredef: Boolean = true, + homePredef: Boolean = true, storageBackend: Storage = new Storage.Folder(Defaults.ammoniteHome), wd: Path = ammonite.ops.pwd, welcomeBanner: Option[String] = Some(Defaults.welcomeBanner), @@ -31,20 +32,13 @@ object Cli{ help: Boolean = false, colored: Option[Boolean] = None) - + import ammonite.main.Scripts.pathScoptRead val genericSignature = Seq( Arg[Config, String]( - "predef", Some('p'), + "predef-code", None, "Any commands you want to execute at the start of the REPL session", - (c, v) => c.copy(predef = v) - ), - Arg[Config, Unit]( - "no-default-predef", None, - """Disable the default predef and run Ammonite with the minimal predef - |possible - |""".stripMargin, - (c, v) => c.copy(defaultPredef = false) + (c, v) => c.copy(predefCode = v) ), Arg[Config, String]( @@ -58,11 +52,28 @@ object Cli{ (c, v) => c.copy(home = v) ), Arg[Config, Path]( - "predef-file", Some('f'), + "predef", Some('p'), """Lets you load your predef from a custom location, rather than the |default location in your Ammonite home""".stripMargin, (c, v) => c.copy(predefFile = Some(v)) ), + Arg[Config, Unit]( + "no-home-predef", None, + """Disables the default behavior of loading predef files from your + |~/.ammonite/predef.sc, predefScript.sc, or predefShared.sc. You can + |choose an additional predef to use using `--predef + |""".stripMargin, + (c, v) => c.copy(homePredef = false) + ), + + Arg[Config, Unit]( + "no-default-predef", None, + """Disable the default predef and run Ammonite with the minimal predef + |possible + |""".stripMargin, + (c, v) => c.copy(defaultPredef = false) + ), + Arg[Config, Unit]( "silent", Some('s'), """Make ivy logs go silent instead of printing though failures will diff --git a/amm/src/test/scala/ammonite/TestRepl.scala b/amm/src/test/scala/ammonite/TestRepl.scala index 574472b63..0de0c6b82 100644 --- a/amm/src/test/scala/ammonite/TestRepl.scala +++ b/amm/src/test/scala/ammonite/TestRepl.scala @@ -52,7 +52,7 @@ class TestRepl { printer, storage = storage, wd = ammonite.ops.pwd, - customPredefs = Seq( + basePredefs = Seq( PredefInfo( Name("defaultPredef"), ammonite.main.Defaults.replPredef + ammonite.main.Defaults.predefString, @@ -61,6 +61,7 @@ class TestRepl { ), PredefInfo(Name("testPredef"), predef._1, false, predef._2) ), + customPredefs = Seq(), extraBridges = Seq(( "ammonite.repl.ReplBridge", "repl", diff --git a/amm/src/test/scala/ammonite/TestUtils.scala b/amm/src/test/scala/ammonite/TestUtils.scala index ecd1f55a0..78cd265f8 100644 --- a/amm/src/test/scala/ammonite/TestUtils.scala +++ b/amm/src/test/scala/ammonite/TestUtils.scala @@ -23,6 +23,7 @@ object TestUtils { storage = storage, wd = ammonite.ops.pwd, // Provide a custom predef so we can verify in tests that the predef gets cached + basePredefs = Seq(), customPredefs = Seq( PredefInfo(Name("predef"), predef, false, None) ), diff --git a/integration/src/test/scala/ammonite/integration/BasicTests.scala b/integration/src/test/scala/ammonite/integration/BasicTests.scala index 0e3fdc412..d14fa0921 100644 --- a/integration/src/test/scala/ammonite/integration/BasicTests.scala +++ b/integration/src/test/scala/ammonite/integration/BasicTests.scala @@ -110,7 +110,8 @@ object BasicTests extends TestSuite{ // from ivy, and make use of `cd!` and `wd` inside the executed script. val res = %%bash( executable, - "--predef-file", + "--no-home-predef", + "--predef", exampleBarePredef, "-c", """val x = wd diff --git a/integration/src/test/scala/ammonite/integration/TestMain.scala b/integration/src/test/scala/ammonite/integration/TestMain.scala index f8d4595ad..acc3d88a7 100644 --- a/integration/src/test/scala/ammonite/integration/TestMain.scala +++ b/integration/src/test/scala/ammonite/integration/TestMain.scala @@ -4,7 +4,7 @@ object TestMain { val hello = "Hello" // Break into debug REPL with ammonite.Main( - predef = "println(\"Starting Debugging!\")" + predefCode = "println(\"Starting Debugging!\")" ).run( "hello" -> hello, "fooValue" -> foo() diff --git a/internals-docs/predef.md b/internals-docs/predef.md index 071861441..51444a93d 100644 --- a/internals-docs/predef.md +++ b/internals-docs/predef.md @@ -33,7 +33,7 @@ The Predef itself is made of several parts: - Any predef file, which defaults `~/.ammonite/predef.sc` for the REPL and `~/.ammonite/predefScript.sc` for scripts, but can be set manually via - `--predef-file ...`. This split allows you to e.g. put REPL-specific setup + `--predef ...`. This split allows you to e.g. put REPL-specific setup code inside `predef.sc`, without it messing up scripts. Both the REPL and scripts also run `~/.ammonite/predefShared.sc` for setup code you want to apply to both diff --git a/readme/Footer.scalatex b/readme/Footer.scalatex index 4b84025af..6972ee44b 100644 --- a/readme/Footer.scalatex +++ b/readme/Footer.scalatex @@ -209,6 +209,44 @@ calls to load Scala libraries cross-published against the full Scala version (e.g. @code{2.12.2} rather than just @code{2.12}, thanks to @lnk("aeffrig", "https://github.com/aeffrig") + + @li + Ammonite now supports opening a @sect.ref{Script Debug REPL} after a + script runs, with your script's scope loaded so you can poke around + it interactively + + @li + The command line flags @code{--predef}/@code{--predef-file} are now + @code{--predef-code}/@code{--predef} respectively, to reflect that + using a custom predef file is much more common than a custom predef + code snippet. + @li + @code{ammonite.Main}'s argument @code{predef} has been renamed to + @code{predefCode}, and a new argument @code{predefFile} has been + added to match the command-line interface + @li + @code{--predef} (previously @code{--predef-file}) now adds a file + in addition to the existing @code{predef.sc} or @code{predefScript.sc} + in @code{~/.ammonite}. If you want it to @i{replace} the predef in + @code{~/.ammonite}, there is a new flag @code{--no-home-predef} that + disables the predef in @code{~/.ammonite} so your @code{--predef} + file stands alone + + @li + @code{~/.ammonite/predefShared.sc} is gone; if you want it back, you + can do it yourself by adding @hl.scala{import $exec.predefShared} + within your @code{predef.sc} and @code{predefScript.sc} files. You + can also import any others files into your @code{predef.sc} and + @code{predefScript.sc} the same way + + @li + Ammonite now loads linux HTTP proxy environment variables into your + JVM's @code{sys.props} by default, when running as a standalone + executable. When using Ammonite within a SBT project, you will need + to do this manually, e.g. calling + @code{ammonite.main.ProxyFromEnv.setPropProxyFromEnv()} within the + @code{predefCode} that you pass to @code{ammonite.Main} + @sect{0.9.9} @ul @li diff --git a/readme/Repl.scalatex b/readme/Repl.scalatex index bf064de08..ce7c08a4e 100644 --- a/readme/Repl.scalatex +++ b/readme/Repl.scalatex @@ -383,8 +383,26 @@ @p This is similar to @hl.scala{import $file}, except that it dumps the definitions and imports from the script into your REPL session. This is - useful if you are using a script to hold a set of common imports, the - docs for @sect.ref{Scala Scripts} has more on this. + useful if you are using a script to hold a set of common imports: + using @sect.ref{import $file} to import a script doesn't propagate + imports from that script into your REPL. + + @p + Alternatively, this is also useful if you want to split up your + @code{~/.ammonite/predef.sc} file into multiple scripts: e.g. if you + want to break up your @code{predef.sc} into two scripts + @code{~/.ammonite/predef.sc} and @code{~/.ammonite/helper.sc}. While + you could use @set.ref{import $file} to + @hl.scala{import $file.helper} within your @code{predef.sc} file, it + will only bring the @code{helper} object into scope within + @code{predef.sc} or within your REPL. @hl.scala{import exec.helper} + will properly "dump" all the definitions from @code{helper.sc} into + your local scope, which is often what you want when dealing with + predef files. + + @p + See the docs for @sect.ref{Scala Scripts} for more on how script + files work in general. @sect{import $ivy} @p diff --git a/readme/Scripts.scalatex b/readme/Scripts.scalatex index 51ba71dfd..8db4d3992 100644 --- a/readme/Scripts.scalatex +++ b/readme/Scripts.scalatex @@ -533,6 +533,40 @@ if you are iterating on a script together with some external data files the script depends on, and you want to + @sect{Script Debug REPL} + @p + When a script is not working as intended, it is useful to be able to + poke around in a REPL after the script has run, in order to see what + values are stored in which variables or what methods are available via + autocomplete. To do so, you can run the script using the + @code{--predef}/@code{-p} flag. + + @code + $ amm --predef foo.sc + + @p + This will run the script as normal, but on completion open up a REPL + which has all the values defined in that script imported and ready to + use. You can then poke around within the REPL as you wish. + + @p + Using @code{--predef}/@code{-p} to run a script and then open an + interactive REPL can be combined with the @code{--watch}/@code{-w} + flag: + + @code + $ amm --watch --predef foo.sc + + @p + This will open up a REPL after the script runs, and when you exit the + REPL it will watch the script file and the files that the script depends + on, re-running the script and REPL if any of them change. + + @p + @code{--predef}/@code{-p} can be used to include a script file as a + predef before running any script or REPL, which is useful for a range + of things apart from serving as a debug REPL on any script. + @sect{From the REPL} @p You can load any script into the Ammonite REPL using the diff --git a/shell/src/test/scala/ammonite/shell/TestMain.scala b/shell/src/test/scala/ammonite/shell/TestMain.scala index 2eb03fcfd..fe57e3737 100644 --- a/shell/src/test/scala/ammonite/shell/TestMain.scala +++ b/shell/src/test/scala/ammonite/shell/TestMain.scala @@ -10,7 +10,8 @@ object TestMain { System.setProperty("ammonite-sbt-build", "true") ammonite.Main.main(args ++ Array( "--home", "target/tempAmmoniteHome", - "--predef-file", examplePredef + "--predef", examplePredef, + "--no-home-predef" )) } } diff --git a/sshd/src/main/scala/ammonite/sshd/SshdRepl.scala b/sshd/src/main/scala/ammonite/sshd/SshdRepl.scala index fa4801048..d2156c9bc 100644 --- a/sshd/src/main/scala/ammonite/sshd/SshdRepl.scala +++ b/sshd/src/main/scala/ammonite/sshd/SshdRepl.scala @@ -5,10 +5,9 @@ import java.io.{InputStream, OutputStream, PrintStream} import ammonite.ops.Path import ammonite.sshd.util.Environment -import ammonite.util.Bind +import ammonite.util.{Bind, Name, PredefInfo} import ammonite.runtime.Storage import ammonite.repl.Repl -import org.apache.sshd.server.session.ServerSession /** * An ssh server which serves ammonite repl as it's shell channel. @@ -50,7 +49,7 @@ class SshdRepl(sshConfig: SshServerConfig, object SshdRepl { // Actually runs a repl inside of session serving a remote user shell. private def runRepl(homePath: Path, - predef: String, + predefCode: String, defaultPredef: Boolean, wd: Path, replArgs: Seq[Bind[_]], @@ -69,7 +68,13 @@ object SshdRepl { ) new Repl( in, out, out, - new Storage.Folder(homePath), augmentedPredef, predef, + new Storage.Folder(homePath), + basePredefs = Seq( + PredefInfo(Name("DefaultPredef"), augmentedPredef, true, None) + ), + customPredefs = Seq( + PredefInfo(Name("CodePredef"), predefCode, false, None) + ), wd, Some(ammonite.main.Defaults.welcomeBanner), remoteLogger = None ).run()