@@ -208,12 +208,14 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
208
208
/** Standard commands **/
209
209
lazy val standardCommands = List (
210
210
cmd(" cp" , " <path>" , " add a jar or directory to the classpath" , addClasspath),
211
+ cmd(" edit" , " <id>|<line>" , " edit history" , editCommand),
211
212
cmd(" help" , " [command]" , " print this summary or command-specific help" , helpCommand),
212
213
historyCommand,
213
214
cmd(" h?" , " <string>" , " search the history" , searchHistory),
214
215
cmd(" imports" , " [name name ...]" , " show import history, identifying sources of names" , importsCommand),
215
216
cmd(" implicits" , " [-v]" , " show the implicits in scope" , intp.implicitsCommand),
216
217
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),
217
219
cmd(" load" , " <path>" , " load and interpret a Scala file" , loadCommand),
218
220
nullary(" paste" , " enter paste mode: all input up to ctrl-D compiled together" , pasteCommand),
219
221
nullary(" power" , " enable power user mode" , powerCmd),
@@ -442,6 +444,90 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter)
442
444
unleashAndSetPhase()
443
445
}
444
446
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
+
445
531
/** fork a shell and run a command */
446
532
lazy val shCommand = new LoopCommand (" sh" , " run a shell command (result is implicitly => List[String])" ) {
447
533
override def usage = " <command line>"
0 commit comments