diff --git a/babi.py b/babi.py index d4b23b3..8705eba 100644 --- a/babi.py +++ b/babi.py @@ -207,6 +207,7 @@ class Status: self._action_counter = -1 self._history: Dict[str, List[str]] = collections.defaultdict(list) self._history_orig_len: Dict[str, int] = collections.defaultdict(int) + self._history_prev: Dict[str, str] = {} @contextlib.contextmanager def save_history(self) -> Generator[None, None, None]: @@ -260,11 +261,14 @@ class Status: prompt: str, *, history: Optional[str] = None, + default_prev: bool = False, ) -> str: self.clear() if history is not None: lst = [*self._history[history], ''] lst_pos = len(lst) - 1 + if history in self._history_prev: + prompt = f'{prompt} [{self._history_prev[history]}]' else: lst = [''] lst_pos = 0 @@ -276,11 +280,25 @@ class Status: def set_buf(s: str) -> None: lst[lst_pos] = s - def _save_history_entry() -> None: + def _save_history_and_get_retv() -> str: if history is not None: - history_lst = self._history[history] - if not history_lst or history_lst[-1] != lst[lst_pos]: - history_lst.append(lst[lst_pos]) + prev = self._history_prev.get(history) + entry = buf() + if entry: # only put non-empty things in history + history_lst = self._history[history] + if not history_lst or history_lst[-1] != entry: + history_lst.append(entry) + self._history_prev[history] = entry + + if ( + default_prev and + prev is not None and + lst_pos == len(lst) - 1 and + not lst[lst_pos] + ): + return prev + + return buf() def _render_prompt(*, base: str = prompt) -> None: if not base or curses.COLS < 7: @@ -358,8 +376,7 @@ class Status: elif key.keyname == b'^C': return '' elif key.key == ord('\r'): - _save_history_entry() - return lst[lst_pos] + return _save_history_and_get_retv() else: # python3.8+ optimizes this out # https://github.com/nedbat/coveragepy/issues/772 @@ -368,8 +385,7 @@ class Status: elif key.keyname == b'^C': return '' elif key.key == ord('\r'): - _save_history_entry() - return lst[lst_pos] + return _save_history_and_get_retv() def _restore_lines_eof_invariant(lines: MutableSequenceNoSlice) -> None: @@ -1053,7 +1069,9 @@ def _edit(screen: Screen) -> EditResult: else: screen.file.go_to_line(lineno, screen.margin) elif key.keyname == b'^W': - response = screen.status.prompt(screen, 'search', history='search') + response = screen.status.prompt( + screen, 'search', history='search', default_prev=True, + ) if response == '': screen.status.update('cancelled') else: diff --git a/tests/babi_test.py b/tests/babi_test.py index a95fd67..71d022f 100644 --- a/tests/babi_test.py +++ b/tests/babi_test.py @@ -707,6 +707,20 @@ def test_search_cancel(ten_lines, key): h.await_text('cancelled') +def test_search_repeated_search(ten_lines): + with run(str(ten_lines)) as h, and_exit(h): + h.press('^W') + h.press('line') + h.await_text('search: line') + h.press('Enter') + h.await_cursor_position(x=0, y=2) + + h.press('^W') + h.await_text('search [line]:') + h.press('Enter') + h.await_cursor_position(x=0, y=3) + + def test_search_history_recorded(): with run() as h, and_exit(h): h.press('^W') @@ -716,10 +730,10 @@ def test_search_history_recorded(): h.press('^W') h.press('Up') - h.await_text('search: asdf') + h.await_text('search [asdf]: asdf') h.press('BSpace') h.press('test') - h.await_text('search: asdtest') + h.await_text('search [asdf]: asdtest') h.press('Down') h.await_text_missing('asdtest') h.press('Down') # can't go past the end @@ -732,9 +746,9 @@ def test_search_history_recorded(): h.press('^W') h.press('Up') - h.await_text('search: asdtest') + h.await_text('search [asdtest]: asdtest') h.press('Up') - h.await_text('search: asdf') + h.await_text('search [asdtest]: asdf') h.press('^C') @@ -746,14 +760,12 @@ def test_search_history_duplicates_dont_repeat(): h.await_text('no matches') h.press('^W') - h.press('search2') - h.await_text('search:') + h.await_text('search [search1]:') h.press_and_enter('search2') h.await_text('no matches') h.press('^W') - h.press('search2') - h.await_text('search:') + h.await_text('search [search2]:') h.press_and_enter('search2') h.await_text('no matches') @@ -856,7 +868,7 @@ def test_search_reverse_search_enter_saves_entry(xdg_data_home, ten_lines): h.press('Enter') h.press('^W') h.press('Up') - h.await_text('search: line_1') + h.await_text('search [line_1]: line_1') h.press('^C')