@@ -81,6 +81,9 @@ const kQuestionCancel = Symbol('kQuestionCancel');
8181// GNU readline library - keyseq-timeout is 500ms (default) 
8282const  ESCAPE_CODE_TIMEOUT  =  500 ; 
8383
84+ // Max length of the kill ring 
85+ const  kMaxLengthOfKillRing  =  32 ; 
86+ 
8487const  kAddHistory  =  Symbol ( '_addHistory' ) ; 
8588const  kBeforeEdit  =  Symbol ( '_beforeEdit' ) ; 
8689const  kDecoder  =  Symbol ( '_decoder' ) ; 
@@ -96,12 +99,15 @@ const kHistoryPrev = Symbol('_historyPrev');
9699const  kInsertString  =  Symbol ( '_insertString' ) ; 
97100const  kLine  =  Symbol ( '_line' ) ; 
98101const  kLine_buffer  =  Symbol ( '_line_buffer' ) ; 
102+ const  kKillRing  =  Symbol ( '_killRing' ) ; 
103+ const  kKillRingCursor  =  Symbol ( '_killRingCursor' ) ; 
99104const  kMoveCursor  =  Symbol ( '_moveCursor' ) ; 
100105const  kNormalWrite  =  Symbol ( '_normalWrite' ) ; 
101106const  kOldPrompt  =  Symbol ( '_oldPrompt' ) ; 
102107const  kOnLine  =  Symbol ( '_onLine' ) ; 
103108const  kPreviousKey  =  Symbol ( '_previousKey' ) ; 
104109const  kPrompt  =  Symbol ( '_prompt' ) ; 
110+ const  kPushToKillRing  =  Symbol ( '_pushToKillRing' ) ; 
105111const  kPushToUndoStack  =  Symbol ( '_pushToUndoStack' ) ; 
106112const  kQuestionCallback  =  Symbol ( '_questionCallback' ) ; 
107113const  kRedo  =  Symbol ( '_redo' ) ; 
@@ -118,6 +124,9 @@ const kUndoStack = Symbol('_undoStack');
118124const  kWordLeft  =  Symbol ( '_wordLeft' ) ; 
119125const  kWordRight  =  Symbol ( '_wordRight' ) ; 
120126const  kWriteToOutput  =  Symbol ( '_writeToOutput' ) ; 
127+ const  kYank  =  Symbol ( '_yank' ) ; 
128+ const  kYanking  =  Symbol ( '_yanking' ) ; 
129+ const  kYankPop  =  Symbol ( '_yankPop' ) ; 
121130
122131function  InterfaceConstructor ( input ,  output ,  completer ,  terminal )  { 
123132  this [ kSawReturnAt ]  =  0 ; 
@@ -211,6 +220,15 @@ function InterfaceConstructor(input, output, completer, terminal) {
211220  this [ kRedoStack ]  =  [ ] ; 
212221  this . history  =  history ; 
213222  this . historySize  =  historySize ; 
223+ 
224+   // The kill ring is a global list of blocks of text that were previously 
225+   // killed (deleted). If its size exceeds kMaxLengthOfKillRing, the oldest 
226+   // element will be removed to make room for the latest deletion. With kill 
227+   // ring, users are able to recall (yank) or cycle (yank pop) among previously 
228+   // killed texts, quite similar to the behavior of Emacs. 
229+   this [ kKillRing ]  =  [ ] ; 
230+   this [ kKillRingCursor ]  =  0 ; 
231+ 
214232  this . removeHistoryDuplicates  =  ! ! removeHistoryDuplicates ; 
215233  this . crlfDelay  =  crlfDelay  ?
216234    MathMax ( kMincrlfDelay ,  crlfDelay )  :
@@ -606,10 +624,12 @@ class Interface extends InterfaceConstructor {
606624      this . cursor  +=  c . length ; 
607625      this [ kRefreshLine ] ( ) ; 
608626    }  else  { 
627+       const  oldPos  =  this . getCursorPos ( ) ; 
609628      this . line  +=  c ; 
610629      this . cursor  +=  c . length ; 
630+       const  newPos  =  this . getCursorPos ( ) ; 
611631
612-       if  ( this . getCursorPos ( ) . cols   ===   0 )  { 
632+       if  ( oldPos . rows   <   newPos . rows )  { 
613633        this [ kRefreshLine ] ( ) ; 
614634      }  else  { 
615635        this [ kWriteToOutput ] ( c ) ; 
@@ -792,17 +812,57 @@ class Interface extends InterfaceConstructor {
792812
793813  [ kDeleteLineLeft ] ( )  { 
794814    this [ kBeforeEdit ] ( this . line ,  this . cursor ) ; 
815+     const  del  =  StringPrototypeSlice ( this . line ,  0 ,  this . cursor ) ; 
795816    this . line  =  StringPrototypeSlice ( this . line ,  this . cursor ) ; 
796817    this . cursor  =  0 ; 
818+     this [ kPushToKillRing ] ( del ) ; 
797819    this [ kRefreshLine ] ( ) ; 
798820  } 
799821
800822  [ kDeleteLineRight ] ( )  { 
801823    this [ kBeforeEdit ] ( this . line ,  this . cursor ) ; 
824+     const  del  =  StringPrototypeSlice ( this . line ,  this . cursor ) ; 
802825    this . line  =  StringPrototypeSlice ( this . line ,  0 ,  this . cursor ) ; 
826+     this [ kPushToKillRing ] ( del ) ; 
803827    this [ kRefreshLine ] ( ) ; 
804828  } 
805829
830+   [ kPushToKillRing ] ( del )  { 
831+     if  ( ! del  ||  del  ===  this [ kKillRing ] [ 0 ] )  return ; 
832+     ArrayPrototypeUnshift ( this [ kKillRing ] ,  del ) ; 
833+     this [ kKillRingCursor ]  =  0 ; 
834+     while  ( this [ kKillRing ] . length  >  kMaxLengthOfKillRing ) 
835+       ArrayPrototypePop ( this [ kKillRing ] ) ; 
836+   } 
837+ 
838+   [ kYank ] ( )  { 
839+     if  ( this [ kKillRing ] . length  >  0 )  { 
840+       this [ kYanking ]  =  true ; 
841+       this [ kInsertString ] ( this [ kKillRing ] [ this [ kKillRingCursor ] ] ) ; 
842+     } 
843+   } 
844+ 
845+   [ kYankPop ] ( )  { 
846+     if  ( ! this [ kYanking ] )  { 
847+       return ; 
848+     } 
849+     if  ( this [ kKillRing ] . length  >  1 )  { 
850+       const  lastYank  =  this [ kKillRing ] [ this [ kKillRingCursor ] ] ; 
851+       this [ kKillRingCursor ] ++ ; 
852+       if  ( this [ kKillRingCursor ]  >=  this [ kKillRing ] . length )  { 
853+         this [ kKillRingCursor ]  =  0 ; 
854+       } 
855+       const  currentYank  =  this [ kKillRing ] [ this [ kKillRingCursor ] ] ; 
856+       const  head  = 
857+             StringPrototypeSlice ( this . line ,  0 ,  this . cursor  -  lastYank . length ) ; 
858+       const  tail  = 
859+             StringPrototypeSlice ( this . line ,  this . cursor ) ; 
860+       this . line  =  head  +  currentYank  +  tail ; 
861+       this . cursor  =  head . length  +  currentYank . length ; 
862+       this [ kRefreshLine ] ( ) ; 
863+     } 
864+   } 
865+ 
806866  clearLine ( )  { 
807867    this [ kMoveCursor ] ( + Infinity ) ; 
808868    this [ kWriteToOutput ] ( '\r\n' ) ; 
@@ -984,6 +1044,11 @@ class Interface extends InterfaceConstructor {
9841044    key  =  key  ||  { } ; 
9851045    this [ kPreviousKey ]  =  key ; 
9861046
1047+     if  ( ! key . meta  ||  key . name  !==  'y' )  { 
1048+       // Reset yanking state unless we are doing yank pop. 
1049+       this [ kYanking ]  =  false ; 
1050+     } 
1051+ 
9871052    // Activate or deactivate substring search. 
9881053    if  ( 
9891054      ( key . name  ===  'up'  ||  key . name  ===  'down' )  && 
@@ -1094,6 +1159,10 @@ class Interface extends InterfaceConstructor {
10941159          this [ kHistoryPrev ] ( ) ; 
10951160          break ; 
10961161
1162+         case  'y' : // Yank killed string 
1163+           this [ kYank ] ( ) ; 
1164+           break ; 
1165+ 
10971166        case  'z' :
10981167          if  ( process . platform  ===  'win32' )  break ; 
10991168          if  ( this . listenerCount ( 'SIGTSTP' )  >  0 )  { 
@@ -1158,6 +1227,10 @@ class Interface extends InterfaceConstructor {
11581227        case  'backspace' : // Delete backwards to a word boundary 
11591228          this [ kDeleteWordLeft ] ( ) ; 
11601229          break ; 
1230+ 
1231+         case  'y' : // Doing yank pop 
1232+           this [ kYankPop ] ( ) ; 
1233+           break ; 
11611234      } 
11621235    }  else  { 
11631236      /* No modifier keys used */ 
0 commit comments