@@ -208,12 +208,14 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
208208 /** Standard commands **/
209209 lazy val standardCommands = List (
210210 cmd(" cp" , " <path>" , " add a jar or directory to the classpath" , addClasspath),
211+ cmd(" edit" , " <id>|<line>" , " edit history" , editCommand),
211212 cmd(" help" , " [command]" , " print this summary or command-specific help" , helpCommand),
212213 historyCommand,
213214 cmd(" h?" , " <string>" , " search the history" , searchHistory),
214215 cmd(" imports" , " [name name ...]" , " show import history, identifying sources of names" , importsCommand),
215216 cmd(" implicits" , " [-v]" , " show the implicits in scope" , intp.implicitsCommand),
216217 cmd(" javap" , " <path|class>" , " disassemble a file or class name" , javapCommand),
218+ cmd(" line" , " <id>|<line>" , " place line(s) at the end of history" , lineCommand),
217219 cmd(" load" , " <path>" , " load and interpret a Scala file" , loadCommand),
218220 nullary(" paste" , " enter paste mode: all input up to ctrl-D compiled together" , pasteCommand),
219221 nullary(" power" , " enable power user mode" , powerCmd),
@@ -442,6 +444,90 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
442444 unleashAndSetPhase()
443445 }
444446
447+ def lineCommand (what : String ): Result = editCommand(what, None )
448+
449+ // :edit id or :edit line
450+ def editCommand (what : String ): Result = editCommand(what, Properties .envOrNone(" EDITOR" ))
451+
452+ def editCommand (what : String , editor : Option [String ]): Result = {
453+ def diagnose (code : String ) = {
454+ echo(" The edited code is incomplete!\n " )
455+ val errless = intp compileSources new BatchSourceFile (" <pastie>" , s " object pastel { \n $code\n } " )
456+ if (errless) echo(" The compiler reports no errors." )
457+ }
458+ def historicize (text : String ) = history match {
459+ case jlh : JLineHistory => text.lines foreach jlh.add ; jlh.moveToEnd() ; true
460+ case _ => false
461+ }
462+ def edit (text : String ): Result = editor match {
463+ case Some (ed) =>
464+ val tmp = File .makeTemp()
465+ tmp.writeAll(text)
466+ try {
467+ val pr = new ProcessResult (s " $ed ${tmp.path}" )
468+ pr.exitCode match {
469+ case 0 =>
470+ tmp.safeSlurp() match {
471+ case Some (edited) if edited.trim.isEmpty => echo(" Edited text is empty." )
472+ case Some (edited) =>
473+ echo(edited.lines map (" +" + _) mkString " \n " )
474+ val res = intp interpret edited
475+ if (res == IR .Incomplete ) diagnose(edited)
476+ else {
477+ historicize(edited)
478+ Result (lineToRecord = Some (edited), keepRunning = true )
479+ }
480+ case None => echo(" Can't read edited text. Did you delete it?" )
481+ }
482+ case x => echo(s " Error exit from $ed ( $x), ignoring " )
483+ }
484+ } finally {
485+ tmp.delete()
486+ }
487+ case None =>
488+ if (historicize(text)) echo(" Placing text in recent history." )
489+ else echo(f " No EDITOR defined and you can't change history, echoing your text:%n $text" )
490+ }
491+
492+ // if what is a number, use it as a line number or range in history
493+ def isNum = what forall (c => c.isDigit || c == '-' || c == '+' )
494+ // except that "-" means last value
495+ def isLast = (what == " -" )
496+ if (isLast || ! isNum) {
497+ val name = if (isLast) intp.mostRecentVar else what
498+ val sym = intp.symbolOfIdent(name)
499+ intp.prevRequestList collectFirst { case r if r.defines contains sym => r } match {
500+ case Some (req) => edit(req.line)
501+ case None => echo(s " No symbol in scope: $what" )
502+ }
503+ } else try {
504+ val s = what
505+ // line 123, 120+3, -3, 120-123, 120-, note -3 is not 0-3 but (cur-3,cur)
506+ val (start, len) =
507+ if ((s indexOf '+' ) > 0 ) {
508+ val (a,b) = s splitAt (s indexOf '+' )
509+ (a.toInt, b.drop(1 ).toInt)
510+ } else {
511+ (s indexOf '-' ) match {
512+ case - 1 => (s.toInt, 1 )
513+ case 0 => val n = s.drop(1 ).toInt ; (history.index - n, n)
514+ case _ if s.last == '-' => val n = s.init.toInt ; (n, history.index - n)
515+ case i => val n = s.take(i).toInt ; (n, s.drop(i+ 1 ).toInt - n)
516+ }
517+ }
518+ import scala .collection .JavaConverters ._
519+ val index = (start - 1 ) max 0
520+ val text = history match {
521+ case jlh : JLineHistory => jlh.entries(index).asScala.take(len) map (_.value) mkString " \n "
522+ case _ => history.asStrings.slice(index, index + len) mkString " \n "
523+ }
524+ edit(text)
525+ } catch {
526+ case _ : NumberFormatException => echo(s " Bad range ' $what' " )
527+ echo(" Use line 123, 120+3, -3, 120-123, 120-, note -3 is not 0-3 but (cur-3,cur)" )
528+ }
529+ }
530+
445531 /** fork a shell and run a command */
446532 lazy val shCommand = new LoopCommand (" sh" , " run a shell command (result is implicitly => List[String])" ) {
447533 override def usage = " <command line>"
0 commit comments