@@ -298,16 +298,20 @@ def smarter_request_reload(desc):
298
298
self .stdin = FakeStdin (self .coderunner , self , self .edit_keys )
299
299
300
300
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 = ''
307
311
308
312
self .original_modules = sys .modules .keys ()
309
313
310
- self .width = None # will both be set by a window resize event
314
+ self .width = None
311
315
self .height = None
312
316
313
317
self .status_bar .message (banner )
@@ -460,6 +464,12 @@ def process_key_event(self, e):
460
464
self .down_one_line ()
461
465
elif e in ("<Ctrl-d>" ,):
462
466
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 )
463
473
elif e in self .edit_keys .cut_buffer_edits :
464
474
self .readline_kill (e )
465
475
elif e in self .edit_keys .simple_edits :
@@ -503,12 +513,34 @@ def process_key_event(self, e):
503
513
elif e in key_dispatch [self .config .edit_current_block_key ]:
504
514
self .send_current_block_to_external_editor ()
505
515
elif e in ["<ESC>" ]: #ESC
506
- pass
516
+ self . special_mode = None
507
517
elif e in ["<SPACE>" ]:
508
518
self .add_normal_character (' ' )
509
519
else :
510
520
self .add_normal_character (e )
511
521
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
+
512
544
def readline_kill (self , e ):
513
545
func = self .edit_keys [e ]
514
546
self .cursor_offset , self .current_line , cut = func (self .cursor_offset , self .current_line )
@@ -661,14 +693,35 @@ def toggle_file_watch(self):
661
693
def add_normal_character (self , char ):
662
694
if len (char ) > 1 or is_nop (char ):
663
695
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
668
703
if self .config .cli_trim_prompts and self .current_line .startswith (self .ps1 ):
669
704
self .current_line = self .current_line [4 :]
670
705
self .cursor_offset = max (0 , self .cursor_offset - 4 )
671
706
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
+
672
725
def update_completion (self , tab = False ):
673
726
"""Update visible docstring and matches, and possibly hide/show completion box"""
674
727
#Update autocomplete info; self.matches_iter and self.argspec
@@ -837,7 +890,10 @@ def current_line_formatted(self):
837
890
"""The colored current line (no prompt, not wrapped)"""
838
891
if self .config .syntax :
839
892
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 :
841
897
if self .config .curtsies_right_arrow_completion :
842
898
fs = fmtfuncs .on_magenta (self .rl_history .saved_line ).join (fs .split (self .rl_history .saved_line ))
843
899
logger .debug ('Display line %r -> %r' , self .current_line , fs )
@@ -868,6 +924,12 @@ def display_buffer_lines(self):
868
924
@property
869
925
def display_line_with_prompt (self ):
870
926
"""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
871
933
return (func_for_letter (self .config .color_scheme ['prompt' ])(self .ps1 )
872
934
if self .done else
873
935
func_for_letter (self .config .color_scheme ['prompt_more' ])(self .ps2 )) + self .current_line_formatted
@@ -1087,21 +1149,25 @@ def __repr__(self):
1087
1149
1088
1150
def _get_current_line (self ):
1089
1151
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 ):
1091
1153
self ._current_line = line
1092
1154
if update_completion :
1093
1155
self .update_completion ()
1094
1156
if reset_rl_history :
1095
1157
self .rl_history .reset ()
1158
+ if clear_special_mode :
1159
+ self .special_mode = None
1096
1160
current_line = property (_get_current_line , _set_current_line , None ,
1097
1161
"The current line" )
1098
1162
def _get_cursor_offset (self ):
1099
1163
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 ):
1101
1165
if update_completion :
1102
1166
self .update_completion ()
1103
1167
if reset_rl_history :
1104
1168
self .rl_history .reset ()
1169
+ if clear_special_mode :
1170
+ self .special_mode = None
1105
1171
self ._cursor_offset = offset
1106
1172
self .update_completion ()
1107
1173
cursor_offset = property (_get_cursor_offset , _set_cursor_offset , None ,
0 commit comments