From b2ebfa7b489b46836a05c9d57f12b0bbaea3f75d Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Thu, 19 Mar 2020 20:37:39 -0700 Subject: [PATCH] Improve quick prompt appearance --- babi/file.py | 4 +--- babi/screen.py | 41 ++++++++++++++++++++++++++++------ tests/features/replace_test.py | 28 +++++++++++------------ tests/features/save_test.py | 10 ++++----- 4 files changed, 54 insertions(+), 29 deletions(-) diff --git a/babi/file.py b/babi/file.py index 4dfa586..9548de4 100644 --- a/babi/file.py +++ b/babi/file.py @@ -453,9 +453,7 @@ class File: if res != 'a': # make `a` replace the rest of them with self._replace_hl.region(self.y, self.x, match.end()): screen.draw() - res = screen.quick_prompt( - 'replace [y(es), n(o), a(ll)]?', 'yna', - ) + res = screen.quick_prompt('replace', ('yes', 'no', 'all')) if res in {'y', 'a'}: count += 1 with self.edit_action_context('replace', final=True): diff --git a/babi/screen.py b/babi/screen.py index 1f1ccc3..3bc264d 100644 --- a/babi/screen.py +++ b/babi/screen.py @@ -224,13 +224,40 @@ class Screen: self.file.scroll_screen_if_needed(self.margin) self.draw() - def quick_prompt(self, prompt: str, opts: str) -> Union[str, PromptResult]: + def quick_prompt( + self, + prompt: str, + opt_strs: Tuple[str, ...], + ) -> Union[str, PromptResult]: + opts = [opt[0] for opt in opt_strs] while True: - s = prompt.ljust(curses.COLS) - if len(s) > curses.COLS: - s = f'{s[:curses.COLS - 1]}…' - self.stdscr.insstr(curses.LINES - 1, 0, s, curses.A_REVERSE) - x = min(curses.COLS - 1, len(prompt) + 1) + x = 0 + + def _write(s: str, *, attr: int = curses.A_REVERSE) -> None: + nonlocal x + + if x >= curses.COLS: + return + self.stdscr.insstr(curses.LINES - 1, x, s, attr) + x += len(s) + + _write(prompt) + _write(' [') + for i, opt_str in enumerate(opt_strs): + _write(opt_str[0], attr=curses.A_REVERSE | curses.A_BOLD) + _write(opt_str[1:]) + if i != len(opt_strs) - 1: + _write(', ') + _write(']?') + + if x < curses.COLS - 1: + s = ' ' * (curses.COLS - x) + self.stdscr.insstr(curses.LINES - 1, x, s, curses.A_REVERSE) + x += 1 + else: + x = curses.COLS - 1 + self.stdscr.insstr(curses.LINES - 1, x, '…', curses.A_REVERSE) + self.stdscr.move(curses.LINES - 1, x) key = self.get_char() @@ -427,7 +454,7 @@ class Screen: def quit_save_modified(self) -> Optional[EditResult]: if self.file.modified: response = self.quick_prompt( - 'file is modified - save [y(es), n(o)]?', 'yn', + 'file is modified - save', ('yes', 'no'), ) if response == 'y': if self.save_filename() is not PromptResult.CANCELLED: diff --git a/tests/features/replace_test.py b/tests/features/replace_test.py index afc7eac..36a209e 100644 --- a/tests/features/replace_test.py +++ b/tests/features/replace_test.py @@ -37,7 +37,7 @@ def test_replace_actual_contents(run, ten_lines): h.press_and_enter('line_0') h.await_text('replace with:') h.press_and_enter('ohai') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('y') h.await_text_missing('line_0') h.await_text('ohai') @@ -59,7 +59,7 @@ match me! h.press_and_enter('me!') h.await_text('replace with:') h.press_and_enter('youuuu') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('y') h.await_cursor_position(x=6, y=3) h.press('Up') @@ -74,7 +74,7 @@ def test_replace_cancel_at_individual_replace(run, ten_lines): h.press_and_enter(r'line_\d') h.await_text('replace with:') h.press_and_enter('ohai') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('^C') h.await_text('cancelled') @@ -86,7 +86,7 @@ def test_replace_unknown_characters_at_individual_replace(run, ten_lines): h.press_and_enter(r'line_\d') h.await_text('replace with:') h.press_and_enter('ohai') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('?') h.press('^C') h.await_text('cancelled') @@ -99,7 +99,7 @@ def test_replace_say_no_to_individual_replace(run, ten_lines): h.press_and_enter('line_[135]') h.await_text('replace with:') h.press_and_enter('ohai') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('y') h.await_text_missing('line_1') h.press('n') @@ -116,7 +116,7 @@ def test_replace_all(run, ten_lines): h.press_and_enter(r'line_(\d)') h.await_text('replace with:') h.press_and_enter(r'ohai+\1') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('a') h.await_text_missing('line') h.await_text('ohai+1') @@ -130,7 +130,7 @@ def test_replace_with_empty_string(run, ten_lines): h.press_and_enter('line_1') h.await_text('replace with:') h.press('Enter') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('y') h.await_text_missing('line_1') @@ -153,7 +153,7 @@ def test_replace_small_window_size(run, ten_lines): h.press_and_enter('line') h.await_text('replace with:') h.press_and_enter('wat') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') with h.resize(width=8, height=24): h.await_text('replace…') @@ -170,7 +170,7 @@ def test_replace_height_1_highlight(run, tmpdir): h.press_and_enter('^x+$') h.await_text('replace with:') h.press('Enter') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') with h.resize(width=80, height=1): h.await_text_missing('xxxxx') @@ -189,7 +189,7 @@ def test_replace_line_goes_off_screen(run): h.press_and_enter('b+') h.await_text('replace with:') h.press_and_enter('wat') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.await_text(f'{"a" * 20}{"b" * 59}»') h.press('y') h.await_text(f'{"a" * 20}wat') @@ -221,7 +221,7 @@ def test_replace_multiple_occurrences_in_line(run): h.press_and_enter('a+') h.await_text('replace with:') h.press_and_enter('q') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('a') h.await_text('bqbq') @@ -234,7 +234,7 @@ def test_replace_after_wrapping(run, ten_lines): h.press_and_enter('line_[02]') h.await_text('replace with:') h.press_and_enter('ohai') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('y') h.await_text_missing('line_2') h.press('y') @@ -251,7 +251,7 @@ def test_replace_after_cursor_after_wrapping(run): h.press_and_enter('b') h.await_text('replace with:') h.press_and_enter('q') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('n') h.press('y') h.await_text('replaced 1 occurrence') @@ -267,7 +267,7 @@ def test_replace_separate_line_after_wrapping(run, ten_lines): h.press_and_enter('line_[01]') h.await_text('replace with:') h.press_and_enter('_') - h.await_text('replace [y(es), n(o), a(ll)]?') + h.await_text('replace [yes, no, all]?') h.press('y') h.await_text_missing('line_0') h.press('y') diff --git a/tests/features/save_test.py b/tests/features/save_test.py index 0c79e37..c490f65 100644 --- a/tests/features/save_test.py +++ b/tests/features/save_test.py @@ -148,7 +148,7 @@ def test_save_on_exit_cancel_yn(run): h.press('hello') h.await_text('hello') h.press('^X') - h.await_text('file is modified - save [y(es), n(o)]?') + h.await_text('file is modified - save [yes, no]?') h.press('^C') h.await_text('cancelled') @@ -158,7 +158,7 @@ def test_save_on_exit_cancel_filename(run): h.press('hello') h.await_text('hello') h.press('^X') - h.await_text('file is modified - save [y(es), n(o)]?') + h.await_text('file is modified - save [yes, no]?') h.press('y') h.await_text('enter filename:') h.press('^C') @@ -171,7 +171,7 @@ def test_save_on_exit(run, tmpdir): h.press('hello') h.await_text('hello') h.press('^X') - h.await_text('file is modified - save [y(es), n(o)]?') + h.await_text('file is modified - save [yes, no]?') h.press('y') h.await_text(f'enter filename: {f}') h.press('Enter') @@ -183,9 +183,9 @@ def test_save_on_exit_resize(run, tmpdir): h.press('hello') h.await_text('hello') h.press('^X') - h.await_text('file is modified - save [y(es), n(o)]?') + h.await_text('file is modified - save [yes, no]?') with h.resize(width=10, height=24): h.await_text('file is m…') - h.await_text('file is modified - save [y(es), n(o)]?') + h.await_text('file is modified - save [yes, no]?') h.press('^C') h.await_text('cancelled')