@@ -52,6 +52,10 @@ const {
5252 kClearScreenDown
5353} = CSI ;
5454
55+ // stuff used for reverse search
56+ const kReverseSearchPrompt = "(reverse-i-search)`':" ;
57+ const kFailedReverseSearchPrompt = '(failed-reverse-i-search)`' ;
58+
5559// Lazy load StringDecoder for startup performance.
5660let StringDecoder ;
5761
@@ -75,6 +79,13 @@ function createInterface(input, output, completer, terminal) {
7579 return new Interface ( input , output , completer , terminal ) ;
7680}
7781
82+ function buildReverseSearchPrompt ( text , match ) {
83+ if ( text === undefined )
84+ return kReverseSearchPrompt ;
85+ if ( match === '' )
86+ return `${ kFailedReverseSearchPrompt } ${ text } ':` ;
87+ return `(reverse-i-search)\`${ text } ': ${ match } ` ;
88+ }
7889
7990function Interface ( input , output , completer , terminal ) {
8091 if ( ! ( this instanceof Interface ) ) {
@@ -90,6 +101,9 @@ function Interface(input, output, completer, terminal) {
90101 this . _previousKey = null ;
91102 this . escapeCodeTimeout = ESCAPE_CODE_TIMEOUT ;
92103
104+ this . inReverseSearch = false ;
105+ this . reverseSearchIndex = 0 ;
106+
93107 EventEmitter . call ( this ) ;
94108 var historySize ;
95109 var removeHistoryDuplicates = false ;
@@ -347,7 +361,8 @@ Interface.prototype._addHistory = function() {
347361
348362Interface . prototype . _refreshLine = function ( ) {
349363 // line length
350- var line = this . _prompt + this . line ;
364+ const line = this . _prompt + this . line ;
365+
351366 var dispPos = this . _getDisplayPos ( line ) ;
352367 var lineCols = dispPos . cols ;
353368 var lineRows = dispPos . rows ;
@@ -383,6 +398,8 @@ Interface.prototype._refreshLine = function() {
383398 }
384399
385400 this . prevRows = cursorPos . rows ;
401+
402+ searchHistory . call ( this ) ;
386403} ;
387404
388405
@@ -474,8 +491,60 @@ Interface.prototype._insertString = function(c) {
474491 // a hack to get the line refreshed if it's needed
475492 this . _moveCursor ( 0 ) ;
476493 }
494+
495+ searchHistory . call ( this ) ;
477496} ;
478497
498+ function appendSearchResult ( result ) {
499+ // this.previewResult = result;
500+
501+ // Cursor to left edge.
502+ cursorTo ( this . output , 0 ) ;
503+ clearScreenDown ( this . output ) ;
504+
505+ // Based on the line and match result
506+ // write the data
507+ if ( result !== '' ) {
508+ this . output . write ( buildReverseSearchPrompt ( this . line , result ) ) ;
509+ cursorTo ( this . output , this . cursor + kReverseSearchPrompt . length - 2 ) ;
510+ } else if ( this . line === '' ) {
511+ this . output . write ( buildReverseSearchPrompt ( ) ) ;
512+ cursorTo ( this . output , this . cursor + kReverseSearchPrompt . length - 2 ) ;
513+ } else {
514+ this . output . write ( buildReverseSearchPrompt ( this . line , '' ) ) ;
515+ cursorTo ( this . output , this . cursor + kFailedReverseSearchPrompt . length ) ;
516+ }
517+
518+ }
519+
520+ function searchText ( ) {
521+ let result = '' ;
522+ const historySet = new Set ( this . history ) ;
523+ for ( ; this . reverseSearchIndex < [ ...historySet ] . length ;
524+ this . reverseSearchIndex ++ ) {
525+ if ( this . line . trim ( ) !== '' &&
526+ this . history [ this . reverseSearchIndex ] . includes ( this . line ) ) {
527+ result = this . history [ this . reverseSearchIndex ++ ] ;
528+ break ;
529+ }
530+ }
531+
532+ return result ;
533+ }
534+
535+ function searchHistory ( ) {
536+ if ( this . inReverseSearch ) {
537+ let result = searchText . call ( this ) ;
538+ const historySet = new Set ( this . history ) ;
539+ if ( this . reverseSearchIndex >= [ ...historySet ] . length ) {
540+ this . reverseSearchIndex = 0 ;
541+
542+ result = searchText . call ( this ) ;
543+ }
544+ appendSearchResult . call ( this , result ) ;
545+ }
546+ }
547+
479548Interface . prototype . _tabComplete = function ( lastKeypressWasTab ) {
480549 var self = this ;
481550
@@ -768,16 +837,25 @@ Interface.prototype._moveCursor = function(dx) {
768837 }
769838} ;
770839
840+ function breakOutOfReverseSearch ( ) {
841+ this . inReverseSearch = false ;
842+ this . _refreshLine ( ) ;
843+ }
771844
772845// handle a write from the tty
773846Interface . prototype . _ttyWrite = function ( s , key ) {
774847 const previousKey = this . _previousKey ;
775848 key = key || { } ;
776849 this . _previousKey = key ;
777850
778- // Ignore escape key, fixes
779- // https://github.com/nodejs/node-v0.x-archive/issues/2876.
780- if ( key . name === 'escape' ) return ;
851+ if ( key . name === 'escape' ) {
852+ if ( this . inReverseSearch ) {
853+ breakOutOfReverseSearch . call ( this ) ;
854+ }
855+ // Else, ignore escape key. Fixes:
856+ // https://github.com/nodejs/node-v0.x-archive/issues/2876.
857+ return ;
858+ }
781859
782860 if ( key . ctrl && key . shift ) {
783861 /* Control and shift pressed */
@@ -802,6 +880,11 @@ Interface.prototype._ttyWrite = function(s, key) {
802880 // This readline instance is finished
803881 this . close ( ) ;
804882 }
883+
884+ if ( this . inReverseSearch ) {
885+ breakOutOfReverseSearch . call ( this ) ;
886+ }
887+
805888 break ;
806889
807890 case 'h' : // delete left
@@ -897,6 +980,11 @@ Interface.prototype._ttyWrite = function(s, key) {
897980 case 'right' :
898981 this . _wordRight ( ) ;
899982 break ;
983+
984+ case 'r' :
985+ if ( ! this . inReverseSearch )
986+ this . inReverseSearch = true ;
987+ searchHistory . call ( this ) ;
900988 }
901989
902990 } else if ( key . meta ) {
@@ -931,6 +1019,11 @@ Interface.prototype._ttyWrite = function(s, key) {
9311019 switch ( key . name ) {
9321020 case 'return' : // carriage return, i.e. \r
9331021 this . _sawReturnAt = Date . now ( ) ;
1022+ if ( this . inReverseSearch ) {
1023+ this . line = this . history [ this . reverseSearchIndex - 1 ] ;
1024+ this . inReverseSearch = false ;
1025+ this . reverseSearchIndex = 0 ;
1026+ }
9341027 this . _line ( ) ;
9351028 break ;
9361029
0 commit comments