diff options
Diffstat (limited to 'src/repl')
6 files changed, 100 insertions, 9 deletions
diff --git a/src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala b/src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala index 48af261937..cf03ecb480 100644 --- a/src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala +++ b/src/repl/scala/tools/nsc/interpreter/ConsoleReaderHelper.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package interpreter -import scala.tools.jline.console.{ ConsoleReader, CursorBuffer } +import jline.console.{ ConsoleReader, CursorBuffer } trait ConsoleReaderHelper extends ConsoleReader { def terminal = getTerminal() diff --git a/src/repl/scala/tools/nsc/interpreter/Delimited.scala b/src/repl/scala/tools/nsc/interpreter/Delimited.scala index e88a044931..b7f06f1d0a 100644 --- a/src/repl/scala/tools/nsc/interpreter/Delimited.scala +++ b/src/repl/scala/tools/nsc/interpreter/Delimited.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package interpreter -import scala.tools.jline.console.completer.ArgumentCompleter.{ ArgumentDelimiter, ArgumentList } +import jline.console.completer.ArgumentCompleter.{ ArgumentDelimiter, ArgumentList } class JLineDelimiter extends ArgumentDelimiter { def toJLine(args: List[String], cursor: Int) = args match { diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index e8265e55f4..dc5183fdf6 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -208,12 +208,14 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) /** Standard commands **/ lazy val standardCommands = List( cmd("cp", "<path>", "add a jar or directory to the classpath", addClasspath), + cmd("edit", "<id>|<line>", "edit history", editCommand), cmd("help", "[command]", "print this summary or command-specific help", helpCommand), historyCommand, cmd("h?", "<string>", "search the history", searchHistory), cmd("imports", "[name name ...]", "show import history, identifying sources of names", importsCommand), cmd("implicits", "[-v]", "show the implicits in scope", intp.implicitsCommand), cmd("javap", "<path|class>", "disassemble a file or class name", javapCommand), + cmd("line", "<id>|<line>", "place line(s) at the end of history", lineCommand), cmd("load", "<path>", "load and interpret a Scala file", loadCommand), nullary("paste", "enter paste mode: all input up to ctrl-D compiled together", pasteCommand), nullary("power", "enable power user mode", powerCmd), @@ -486,6 +488,90 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) unleashAndSetPhase() } + def lineCommand(what: String): Result = editCommand(what, None) + + // :edit id or :edit line + def editCommand(what: String): Result = editCommand(what, Properties.envOrNone("EDITOR")) + + def editCommand(what: String, editor: Option[String]): Result = { + def diagnose(code: String) = { + echo("The edited code is incomplete!\n") + val errless = intp compileSources new BatchSourceFile("<pastie>", s"object pastel {\n$code\n}") + if (errless) echo("The compiler reports no errors.") + } + def historicize(text: String) = history match { + case jlh: JLineHistory => text.lines foreach jlh.add ; jlh.moveToEnd() ; true + case _ => false + } + def edit(text: String): Result = editor match { + case Some(ed) => + val tmp = File.makeTemp() + tmp.writeAll(text) + try { + val pr = new ProcessResult(s"$ed ${tmp.path}") + pr.exitCode match { + case 0 => + tmp.safeSlurp() match { + case Some(edited) if edited.trim.isEmpty => echo("Edited text is empty.") + case Some(edited) => + echo(edited.lines map ("+" + _) mkString "\n") + val res = intp interpret edited + if (res == IR.Incomplete) diagnose(edited) + else { + historicize(edited) + Result(lineToRecord = Some(edited), keepRunning = true) + } + case None => echo("Can't read edited text. Did you delete it?") + } + case x => echo(s"Error exit from $ed ($x), ignoring") + } + } finally { + tmp.delete() + } + case None => + if (historicize(text)) echo("Placing text in recent history.") + else echo(f"No EDITOR defined and you can't change history, echoing your text:%n$text") + } + + // if what is a number, use it as a line number or range in history + def isNum = what forall (c => c.isDigit || c == '-' || c == '+') + // except that "-" means last value + def isLast = (what == "-") + if (isLast || !isNum) { + val name = if (isLast) intp.mostRecentVar else what + val sym = intp.symbolOfIdent(name) + intp.prevRequestList collectFirst { case r if r.defines contains sym => r } match { + case Some(req) => edit(req.line) + case None => echo(s"No symbol in scope: $what") + } + } else try { + val s = what + // line 123, 120+3, -3, 120-123, 120-, note -3 is not 0-3 but (cur-3,cur) + val (start, len) = + if ((s indexOf '+') > 0) { + val (a,b) = s splitAt (s indexOf '+') + (a.toInt, b.drop(1).toInt) + } else { + (s indexOf '-') match { + case -1 => (s.toInt, 1) + case 0 => val n = s.drop(1).toInt ; (history.index - n, n) + case _ if s.last == '-' => val n = s.init.toInt ; (n, history.index - n) + case i => val n = s.take(i).toInt ; (n, s.drop(i+1).toInt - n) + } + } + import scala.collection.JavaConverters._ + val index = (start - 1) max 0 + val text = history match { + case jlh: JLineHistory => jlh.entries(index).asScala.take(len) map (_.value) mkString "\n" + case _ => history.asStrings.slice(index, index + len) mkString "\n" + } + edit(text) + } catch { + case _: NumberFormatException => echo(s"Bad range '$what'") + echo("Use line 123, 120+3, -3, 120-123, 120-, note -3 is not 0-3 but (cur-3,cur)") + } + } + /** fork a shell and run a command */ lazy val shCommand = new LoopCommand("sh", "run a shell command (result is implicitly => List[String])") { override def usage = "<command line>" diff --git a/src/repl/scala/tools/nsc/interpreter/JLineReader.scala b/src/repl/scala/tools/nsc/interpreter/JLineReader.scala index 5d41f1bbb4..d8a876feb2 100644 --- a/src/repl/scala/tools/nsc/interpreter/JLineReader.scala +++ b/src/repl/scala/tools/nsc/interpreter/JLineReader.scala @@ -6,8 +6,8 @@ package scala.tools.nsc package interpreter -import scala.tools.jline.console.ConsoleReader -import scala.tools.jline.console.completer._ +import jline.console.ConsoleReader +import jline.console.completer._ import session._ import Completion._ @@ -42,7 +42,7 @@ class JLineReader(_completion: => Completion) extends InteractiveReader { def readOneKey(prompt: String) = { this.print(prompt) this.flush() - this.readVirtualKey() + this.readCharacter() } def eraseLine() = consoleReader.resetPromptLine("", "", 0) def redrawLineAndFlush(): Unit = { flush() ; drawLine() ; flush() } diff --git a/src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala b/src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala index 89998e438a..7c49b91296 100644 --- a/src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala +++ b/src/repl/scala/tools/nsc/interpreter/session/SimpleHistory.scala @@ -46,6 +46,11 @@ class SimpleHistory extends JLineHistory { def entries(): JListIterator[JEntry] = toEntries().asJava.listIterator() def iterator: JIterator[JEntry] = toEntries().iterator.asJava + def remove(idx: Int): CharSequence = buf remove idx + def removeFirst(): CharSequence = buf remove 0 + def removeLast(): CharSequence = buf remove lastIndex + def set(idx: Int, to: CharSequence): Unit = buf(idx) = to + def current() = if (index >= 0 && index < buf.size) buf(index) else fail("current()") def previous() = (index > 0) && minusOne def next() = (index <= lastIndex) && plusOne diff --git a/src/repl/scala/tools/nsc/interpreter/session/package.scala b/src/repl/scala/tools/nsc/interpreter/session/package.scala index c62cf21151..a3d7312c98 100644 --- a/src/repl/scala/tools/nsc/interpreter/session/package.scala +++ b/src/repl/scala/tools/nsc/interpreter/session/package.scala @@ -14,10 +14,10 @@ package object session { type JIterator[T] = java.util.Iterator[T] type JListIterator[T] = java.util.ListIterator[T] - type JEntry = scala.tools.jline.console.history.History.Entry - type JHistory = scala.tools.jline.console.history.History - type JMemoryHistory = scala.tools.jline.console.history.MemoryHistory - type JPersistentHistory = scala.tools.jline.console.history.PersistentHistory + type JEntry = jline.console.history.History.Entry + type JHistory = jline.console.history.History + type JMemoryHistory = jline.console.history.MemoryHistory + type JPersistentHistory = jline.console.history.PersistentHistory private[interpreter] implicit def charSequenceFix(x: CharSequence): String = x.toString } |