@@ -298,16 +298,20 @@ def smarter_request_reload(desc):
298298 self .stdin = FakeStdin (self .coderunner , self , self .edit_keys )
299299
300300 self .request_paint_to_clear_screen = False # next paint should clear screen
301- self .last_events = [None ] * 50
302- self .presentation_mode = False
303- self .paste_mode = False
304- self .current_match = None
305- self .list_win_visible = False
306- self .watching_files = False
301+ self .last_events = [None ] * 50 # some commands act differently based on the prev event
302+ # this list doesn't include instances of event.Event,
303+ # only keypress-type events (no refresh screen events etc.)
304+ self .presentation_mode = False # displays prev events in a column on the right hand side
305+ self .paste_mode = False # currently processing a paste event
306+ self .current_match = None # currently tab-selected autocompletion suggestion
307+ self .list_win_visible = False # whether the infobox (suggestions, docstring) is visible
308+ self .watching_files = False # auto reloading turned on
309+ self .special_mode = None # 'reverse_incremental_search' and 'incremental_search'
310+ self .incremental_search_target = ''
307311
308312 self .original_modules = sys .modules .keys ()
309313
310- self .width = None # will both be set by a window resize event
314+ self .width = None
311315 self .height = None
312316
313317 self .status_bar .message (banner )
@@ -460,6 +464,12 @@ def process_key_event(self, e):
460464 self .down_one_line ()
461465 elif e in ("<Ctrl-d>" ,):
462466 self .on_control_d ()
467+ elif e in ("<Esc+r>" ,):
468+ self .incremental_search (reverse = True )
469+ elif e in ("<Esc+s>" ,):
470+ self .incremental_search ()
471+ elif e in ("<BACKSPACE>" , '<Ctrl-h>' ) and self .special_mode :
472+ self .add_to_incremental_search (self , backspace = True )
463473 elif e in self .edit_keys .cut_buffer_edits :
464474 self .readline_kill (e )
465475 elif e in self .edit_keys .simple_edits :
@@ -503,12 +513,34 @@ def process_key_event(self, e):
503513 elif e in key_dispatch [self .config .edit_current_block_key ]:
504514 self .send_current_block_to_external_editor ()
505515 elif e in ["<ESC>" ]: #ESC
506- pass
516+ self . special_mode = None
507517 elif e in ["<SPACE>" ]:
508518 self .add_normal_character (' ' )
509519 else :
510520 self .add_normal_character (e )
511521
522+ def incremental_search (self , reverse = False , include_current = False ):
523+ if self .special_mode == None :
524+ self .rl_history .enter (self .current_line )
525+ self .incremental_search_target = ''
526+ else :
527+ if self .incremental_search_target :
528+ line = (self .rl_history .back (False , search = True ,
529+ target = self .incremental_search_target ,
530+ include_current = include_current )
531+ if reverse else
532+ self .rl_history .forward (False , search = True ,
533+ target = self .incremental_search_target ,
534+ include_current = include_current ))
535+ self ._set_current_line (line ,
536+ reset_rl_history = False , clear_special_mode = False )
537+ self ._set_cursor_offset (len (self .current_line ),
538+ reset_rl_history = False , clear_special_mode = False )
539+ if reverse :
540+ self .special_mode = 'reverse_incremental_search'
541+ else :
542+ self .special_mode = 'incremental_search'
543+
512544 def readline_kill (self , e ):
513545 func = self .edit_keys [e ]
514546 self .cursor_offset , self .current_line , cut = func (self .cursor_offset , self .current_line )
@@ -661,14 +693,35 @@ def toggle_file_watch(self):
661693 def add_normal_character (self , char ):
662694 if len (char ) > 1 or is_nop (char ):
663695 return
664- self .current_line = (self .current_line [:self .cursor_offset ] +
665- char +
666- self .current_line [self .cursor_offset :])
667- self .cursor_offset += 1
696+ if self .special_mode :
697+ self .add_to_incremental_search (char )
698+ else :
699+ self .current_line = (self .current_line [:self .cursor_offset ] +
700+ char +
701+ self .current_line [self .cursor_offset :])
702+ self .cursor_offset += 1
668703 if self .config .cli_trim_prompts and self .current_line .startswith (self .ps1 ):
669704 self .current_line = self .current_line [4 :]
670705 self .cursor_offset = max (0 , self .cursor_offset - 4 )
671706
707+ def add_to_incremental_search (self , char = None , backspace = False ):
708+ """Modify the current search term while in incremental search.
709+
710+ The only operations allowed in incremental search mode are
711+ adding characters and backspacing."""
712+ if char is None and not backspace :
713+ raise ValueError ("must provide a char or set backspace to True" )
714+ if backspace :
715+ self .incremental_search_target = self .incremental_search_target [:- 1 ]
716+ else :
717+ self .incremental_search_target += char
718+ if self .special_mode == 'reverse_incremental_search' :
719+ self .incremental_search (reverse = True , include_current = True )
720+ elif self .special_mode == 'incremental_search' :
721+ self .incremental_search (include_current = True )
722+ else :
723+ raise ValueError ('add_to_incremental_search should only be called in a special mode' )
724+
672725 def update_completion (self , tab = False ):
673726 """Update visible docstring and matches, and possibly hide/show completion box"""
674727 #Update autocomplete info; self.matches_iter and self.argspec
@@ -837,7 +890,10 @@ def current_line_formatted(self):
837890 """The colored current line (no prompt, not wrapped)"""
838891 if self .config .syntax :
839892 fs = bpythonparse (format (self .tokenize (self .current_line ), self .formatter ))
840- if self .rl_history .saved_line in self .current_line :
893+ if self .special_mode :
894+ if self .incremental_search_target in self .current_line :
895+ fs = fmtfuncs .on_magenta (self .incremental_search_target ).join (fs .split (self .incremental_search_target ))
896+ elif self .rl_history .saved_line and self .rl_history .saved_line in self .current_line :
841897 if self .config .curtsies_right_arrow_completion :
842898 fs = fmtfuncs .on_magenta (self .rl_history .saved_line ).join (fs .split (self .rl_history .saved_line ))
843899 logger .debug ('Display line %r -> %r' , self .current_line , fs )
@@ -868,6 +924,12 @@ def display_buffer_lines(self):
868924 @property
869925 def display_line_with_prompt (self ):
870926 """colored line with prompt"""
927+ if self .special_mode == 'reverse_incremental_search' :
928+ return func_for_letter (self .config .color_scheme ['prompt' ])(
929+ '(reverse-i-search)`%s\' : ' % (self .incremental_search_target ,)) + self .current_line_formatted
930+ elif self .special_mode == 'incremental_search' :
931+ return func_for_letter (self .config .color_scheme ['prompt' ])(
932+ '(i-search)`%s\' : ' % (self .incremental_search_target ,)) + self .current_line_formatted
871933 return (func_for_letter (self .config .color_scheme ['prompt' ])(self .ps1 )
872934 if self .done else
873935 func_for_letter (self .config .color_scheme ['prompt_more' ])(self .ps2 )) + self .current_line_formatted
@@ -1087,21 +1149,25 @@ def __repr__(self):
10871149
10881150 def _get_current_line (self ):
10891151 return self ._current_line
1090- def _set_current_line (self , line , update_completion = True , reset_rl_history = True ):
1152+ def _set_current_line (self , line , update_completion = True , reset_rl_history = True , clear_special_mode = True ):
10911153 self ._current_line = line
10921154 if update_completion :
10931155 self .update_completion ()
10941156 if reset_rl_history :
10951157 self .rl_history .reset ()
1158+ if clear_special_mode :
1159+ self .special_mode = None
10961160 current_line = property (_get_current_line , _set_current_line , None ,
10971161 "The current line" )
10981162 def _get_cursor_offset (self ):
10991163 return self ._cursor_offset
1100- def _set_cursor_offset (self , offset , update_completion = True , reset_rl_history = True ):
1164+ def _set_cursor_offset (self , offset , update_completion = True , reset_rl_history = True , clear_special_mode = True ):
11011165 if update_completion :
11021166 self .update_completion ()
11031167 if reset_rl_history :
11041168 self .rl_history .reset ()
1169+ if clear_special_mode :
1170+ self .special_mode = None
11051171 self ._cursor_offset = offset
11061172 self .update_completion ()
11071173 cursor_offset = property (_get_cursor_offset , _set_cursor_offset , None ,
0 commit comments