Permalink
Browse files

ammonite based shell

  • Loading branch information...
haifengl committed Dec 29, 2017
1 parent 73245d5 commit 9fb83d55e53962450297a008272c07e786745993
Showing with 67 additions and 79 deletions.
  1. +2 −2 shell/build.sbt
  2. +30 −71 shell/src/main/scala/unicorn/shell/Main.scala
  3. +35 −6 shell/src/main/scala/unicorn/shell/Shell.scala
View
@@ -41,6 +41,6 @@ buildInfoPackage := "unicorn.shell"
buildInfoOptions += BuildInfoOption.BuildTime
libraryDependencies += "org.scala-lang" % "scala-compiler" % "2.11.11"
libraryDependencies += "com.lihaoyi" % "ammonite" % "1.0.3" cross CrossVersion.full
libraryDependencies += "org.slf4j" % "slf4j-simple" % "1.7.18"
libraryDependencies += "org.slf4j" % "slf4j-simple" % "1.7.25"
@@ -1,5 +1,5 @@
/*******************************************************************************
* (C) Copyright 2015 ADP, LLC.
* (C) Copyright 2017 Haifeng Li
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,81 +16,40 @@
package unicorn.shell
import scala.tools.nsc._, GenericRunnerCommand._, io.File
import ammonite.main.Cli
import ammonite.ops.{Path, pwd}
/** An object that runs Smile script or interactive shell.
* Based on Scala MainGenericRunner.
/** An object that runs interactive shell.
*
* @author Haifeng Li
*/
object Main extends App {
// This is actually the main function
if (!process(args)) sys.exit(1)
def errorFn(str: String, e: Option[Throwable] = None, isFailure: Boolean = true): Boolean = {
if (str.nonEmpty) Console.err println str
e foreach (_.printStackTrace())
!isFailure
}
def process(args: Array[String]): Boolean = {
val command = new GenericRunnerCommand(args.toList, (x: String) => errorFn(x))
import command.{ settings, howToRun, thingToRun, shortUsageMsg, shouldStopWithInfo }
settings.usejavacp.value = true
settings.deprecation.value = true
def sampleCompiler = new Global(settings) // def so it's not created unless needed
def run(): Boolean = {
def isE = !settings.execute.isDefault
def dashe = settings.execute.value
def isI = !settings.loadfiles.isDefault
def dashi = settings.loadfiles.value
// Deadlocks on startup under -i unless we disable async.
if (isI)
settings.Yreplsync.value = true
def combinedCode = {
val files = if (isI) dashi map (file => File(file).slurp()) else Nil
val str = if (isE) List(dashe) else Nil
files ++ str mkString "\n\n"
}
def runTarget(): Either[Throwable, Boolean] = howToRun match {
case AsObject =>
ObjectRunner.runAndCatch(settings.classpathURLs, thingToRun, command.arguments)
case AsScript =>
ScriptRunner.runScriptAndCatch(settings, thingToRun, command.arguments)
case Error =>
Right(false)
case _ =>
Right(new Shell process settings)
Cli.groupArgs(args.toList, Cli.ammoniteArgSignature, Cli.Config()) match {
case Left(msg) =>
println(msg)
false
case Right((cliConfig, leftoverArgs)) =>
if (cliConfig.help) {
println(Cli.ammoniteHelp)
true
} else {
(cliConfig.code, leftoverArgs) match {
case (Some(code), Nil) =>
AmmoniteREPL.runCode(code)
case (None, Nil) =>
AmmoniteREPL.run()
true
case (None, head :: rest) if head.startsWith("-") =>
val failureMsg = s"Unknown option: $head\nUse --help to list possible options"
println(failureMsg)
false
case (None, head :: rest) =>
val success = AmmoniteREPL.runScript(Path(head, pwd)) // ignore script args for now
success
}
}
/** If -e and -i were both given, we want to execute the -e code after the
* -i files have been included, so they are read into strings and prepended to
* the code given in -e. The -i option is documented to only make sense
* interactively so this is a pretty reasonable assumption.
*
* This all needs a rewrite though.
*/
if (isE) {
ScriptRunner.runCommand(settings, combinedCode, thingToRun +: command.arguments)
}
else runTarget() match {
case Left(ex) => errorFn("", Some(ex)) // there must be a useful message of hope to offer here
case Right(b) => b
}
}
if (!command.ok)
errorFn(f"%n$shortUsageMsg")
else if (shouldStopWithInfo)
errorFn(command getInfoMessage sampleCompiler, isFailure = false)
else
run()
}
}
@@ -1,5 +1,5 @@
/*******************************************************************************
* (C) Copyright 2015 ADP, LLC.
* (C) Copyright 2017 Haifeng Li
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,13 +18,17 @@ package unicorn.shell
import scala.tools.nsc.interpreter.ILoop
/** Unicorn shell.
import ammonite.ops.Path
import ammonite.runtime.Storage
/** Ammonite REPL based shell.
*
* @author Haifeng Li
*/
class Shell extends ILoop {
override def prompt = "unicorn> "
override def printWelcome = echo(
object AmmoniteREPL {
val home = Path(System.getProperty("user.home")) / ".smile"
val prompt = "smile> "
val welcome =
raw"""
| . . . .
| ,`,`,`,`,
@@ -57,5 +61,30 @@ class Shell extends ILoop {
| Version ${BuildInfo.version}, Scala ${BuildInfo.scalaVersion}, SBT ${BuildInfo.sbtVersion}, Built at ${BuildInfo.builtAtString}
|===============================================================================
""".stripMargin
val imports =
s"""
|import java.util.{Date, UUID}
|import java.time.{LocalDate, LocalTime, LocalDateTime}
|import java.sql.Timestamp
|import unicorn.json._
|import unicorn.bigtable.hbase.HBase
|import unicorn.unibase._
|import unicorn.unibase.graph._
|import unicorn.unibase.sql._
|import unicorn.narwhal._
|repl.prompt() = "unicorn> "
""".stripMargin
val repl = ammonite.Main(
predefCode = imports,
defaultPredef = true,
storageBackend = new Storage.Folder(home),
welcomeBanner = Some(welcome),
verboseOutput = false
)
}
def run() = repl.run()
def runCode(code: String) = repl.runCode(code)
def runScript(path: Path) = repl.runScript(path, Seq.empty)
}

0 comments on commit 9fb83d5

Please sign in to comment.