Skip to content

Commit 169536d

Browse files
incremental search and reverse working alright
not very tested yet
1 parent fc7ee66 commit 169536d

File tree

2 files changed

+66
-49
lines changed

2 files changed

+66
-49
lines changed

bpython/curtsiesfrontend/repl.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,7 @@ def smarter_request_reload(desc):
307307
self.list_win_visible = False # whether the infobox (suggestions, docstring) is visible
308308
self.watching_files = False # auto reloading turned on
309309
self.special_mode = None # 'reverse_incremental_search' and 'incremental_search'
310+
self.incremental_search_target = ''
310311

311312
self.original_modules = sys.modules.keys()
312313

@@ -510,30 +511,33 @@ def process_key_event(self, e):
510511
elif e in key_dispatch[self.config.edit_current_block_key]:
511512
self.send_current_block_to_external_editor()
512513
elif e in ["<ESC>"]: #ESC
513-
pass
514+
self.special_mode = None
514515
elif e in ["<SPACE>"]:
515516
self.add_normal_character(' ')
516517
else:
517518
self.add_normal_character(e)
518519

519-
def incremental_search(self, reverse=False):
520+
def incremental_search(self, reverse=False, include_current=False):
520521
if self.special_mode == None:
521-
current_line = ''
522-
if reverse:
523-
self.special_mode = 'reverse_incremental_search'
524-
else:
525-
self.special_mode = 'incremental_search'
522+
self.rl_history.enter(self.current_line)
523+
self.incremental_search_target = ''
526524
else:
527-
if self.rl_history.saved_line:
528-
line = (self.rl_history.back(False, search=True)
525+
if self.incremental_search_target:
526+
line = (self.rl_history.back(False, search=True,
527+
target=self.incremental_search_target,
528+
include_current=include_current)
529529
if reverse else
530-
self.rl_history.forward(False, search=True))
531-
if self.rl_history.is_at_start:
532-
line = '' # incremental search's search string isn't the current line
530+
self.rl_history.forward(False, search=True,
531+
target=self.incremental_search_target,
532+
include_current=include_current))
533533
self._set_current_line(line,
534534
reset_rl_history=False, clear_special_mode=False)
535535
self._set_cursor_offset(len(self.current_line),
536536
reset_rl_history=False, clear_special_mode=False)
537+
if reverse:
538+
self.special_mode = 'reverse_incremental_search'
539+
else:
540+
self.special_mode = 'incremental_search'
537541

538542
def readline_kill(self, e):
539543
func = self.edit_keys[e]
@@ -687,7 +691,7 @@ def toggle_file_watch(self):
687691
def add_normal_character(self, char):
688692
if len(char) > 1 or is_nop(char):
689693
return
690-
if self.special_mode == 'reverse_incremental_search':
694+
if self.special_mode:
691695
self.add_to_incremental_search(char)
692696
else:
693697
self.current_line = (self.current_line[:self.cursor_offset] +
@@ -705,18 +709,14 @@ def add_to_incremental_search(self, char=None, backspace=False):
705709
adding characters and backspacing."""
706710
if char is None and not backspace:
707711
raise ValueError("must provide a char or set backspace to True")
708-
saved_line = self.rl_history.saved_line
709712
if backspace:
710-
saved_line = saved_line[:-1]
713+
self.incremental_search_target = self.incremental_search_target[:-1]
711714
else:
712-
saved_line += char
713-
self.update_completion()
714-
self.rl_history.reset()
715-
self.rl_history.enter(saved_line)
715+
self.incremental_search_target += char
716716
if self.special_mode == 'reverse_incremental_search':
717-
self.incremental_search(reverse=True)
717+
self.incremental_search(reverse=True, include_current=True)
718718
elif self.special_mode == 'incremental_search':
719-
self.incremental_search()
719+
self.incremental_search(include_current=True)
720720
else:
721721
raise ValueError('add_to_incremental_search should only be called in a special mode')
722722

@@ -888,7 +888,10 @@ def current_line_formatted(self):
888888
"""The colored current line (no prompt, not wrapped)"""
889889
if self.config.syntax:
890890
fs = bpythonparse(format(self.tokenize(self.current_line), self.formatter))
891-
if self.rl_history.saved_line in self.current_line:
891+
if self.special_mode:
892+
if self.incremental_search_target in self.current_line:
893+
fs = fmtfuncs.on_magenta(self.incremental_search_target).join(fs.split(self.incremental_search_target))
894+
elif self.rl_history.saved_line and self.rl_history.saved_line in self.current_line:
892895
if self.config.curtsies_right_arrow_completion:
893896
fs = fmtfuncs.on_magenta(self.rl_history.saved_line).join(fs.split(self.rl_history.saved_line))
894897
logger.debug('Display line %r -> %r', self.current_line, fs)
@@ -921,7 +924,10 @@ def display_line_with_prompt(self):
921924
"""colored line with prompt"""
922925
if self.special_mode == 'reverse_incremental_search':
923926
return func_for_letter(self.config.color_scheme['prompt'])(
924-
'(reverse-i-search)`%s\': ' % (self.rl_history.saved_line,)) + self.current_line_formatted
927+
'(reverse-i-search)`%s\': ' % (self.incremental_search_target,)) + self.current_line_formatted
928+
elif self.special_mode == 'incremental_search':
929+
return func_for_letter(self.config.color_scheme['prompt'])(
930+
'(i-search)`%s\': ' % (self.incremental_search_target,)) + self.current_line_formatted
925931
return (func_for_letter(self.config.color_scheme['prompt'])(self.ps1)
926932
if self.done else
927933
func_for_letter(self.config.color_scheme['prompt_more'])(self.ps2)) + self.current_line_formatted

bpython/repl.py

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,16 @@ def writetb(self, lines):
139139

140140

141141
class History(object):
142-
"""Stores readline-style history allows access to it"""
142+
"""Stores readline-style history and current place in it"""
143143

144144
def __init__(self, entries=None, duplicates=False):
145145
if entries is None:
146146
self.entries = ['']
147147
else:
148148
self.entries = list(entries)
149-
self.index = 0
150-
self.saved_line = ''
149+
self.index = 0 # how many lines back in history is currently selected
150+
# where 0 is the saved typed line, 1 the prev entered line
151+
self.saved_line = '' # what was on the prompt before using history
151152
self.duplicates = duplicates
152153

153154
def append(self, line):
@@ -168,58 +169,68 @@ def first(self):
168169
self.index = len(self.entries)
169170
return self.entries[-self.index]
170171

171-
def back(self, start=True, search=False):
172+
def back(self, start=True, search=False, target=None, include_current=False):
172173
"""Move one step back in the history."""
174+
if target is None:
175+
target = self.saved_line
173176
if not self.is_at_end:
174177
if search:
175-
self.index += self.find_partial_match_backward(self.saved_line)
178+
self.index += self.find_partial_match_backward(target, include_current)
176179
elif start:
177-
self.index += self.find_match_backward(self.saved_line)
180+
self.index += self.find_match_backward(target, include_current)
178181
else:
179182
self.index += 1
183+
return self.entry
184+
185+
@property
186+
def entry(self):
187+
"""The current entry, which may be the saved line"""
180188
return self.entries[-self.index] if self.index else self.saved_line
181189

182-
def find_match_backward(self, search_term):
183-
filtered_list_len = len(self.entries) - self.index
184-
for idx, val in enumerate(reversed(self.entries[:filtered_list_len])):
190+
@property
191+
def entries_by_index(self):
192+
return list(reversed(self.entries + [self.saved_line]))
193+
194+
def find_match_backward(self, search_term, include_current=False):
195+
for idx, val in enumerate(self.entries_by_index[self.index + (0 if include_current else 1):]):
185196
if val.startswith(search_term):
186-
return idx + 1
197+
return idx + (0 if include_current else 1)
187198
return 0
188199

189-
def find_partial_match_backward(self, search_term):
190-
filtered_list_len = len(self.entries) - self.index
191-
for idx, val in enumerate(reversed(self.entries[:filtered_list_len])):
200+
def find_partial_match_backward(self, search_term, include_current=False):
201+
for idx, val in enumerate(self.entries_by_index[self.index + (0 if include_current else 1):]):
192202
if search_term in val:
193-
return idx + 1
203+
return idx + (0 if include_current else 1)
194204
return 0
195205

196206

197-
def forward(self, start=True, search=False):
207+
def forward(self, start=True, search=False, target=None, include_current=False):
198208
"""Move one step forward in the history."""
209+
if target is None:
210+
target = self.saved_line
199211
if self.index > 1:
200212
if search:
201-
self.index -= self.find_partial_match_forward(self.saved_line)
213+
self.index -= self.find_partial_match_forward(target, include_current)
202214
elif start:
203-
self.index -= self.find_match_forward(self.saved_line)
215+
self.index -= self.find_match_forward(target, include_current)
204216
else:
205217
self.index -= 1
206-
return self.entries[-self.index] if self.index else self.saved_line
218+
return self.entry
207219
else:
208220
self.index = 0
209221
return self.saved_line
210222

211-
def find_match_forward(self, search_term):
212-
filtered_list_len = len(self.entries) - self.index + 1
213-
for idx, val in enumerate(self.entries[filtered_list_len:]):
223+
def find_match_forward(self, search_term, include_current=False):
224+
#TODO these are no longer efficient, because we realize the whole list. Does this matter?
225+
for idx, val in enumerate(reversed(self.entries_by_index[:max(0, self.index - (1 if include_current else 0))])):
214226
if val.startswith(search_term):
215-
return idx + 1
227+
return idx + (0 if include_current else 1)
216228
return self.index
217229

218-
def find_partial_match_forward(self, search_term):
219-
filtered_list_len = len(self.entries) - self.index + 1
220-
for idx, val in enumerate(self.entries[filtered_list_len:]):
230+
def find_partial_match_forward(self, search_term, include_current=False):
231+
for idx, val in enumerate(reversed(self.entries_by_index[:max(0, self.index - (1 if include_current else 0))])):
221232
if search_term in val:
222-
return idx + 1
233+
return idx + (0 if include_current else 1)
223234
return self.index
224235

225236

0 commit comments

Comments
 (0)