From 23d0367092c83b9fc179d6989919189ff6370187 Mon Sep 17 00:00:00 2001 From: Qexat <43090614+qexat@users.noreply.github.com> Date: Sun, 17 Sep 2023 01:03:08 +0200 Subject: [PATCH 1/5] Add support for backword --- babi/file.py | 34 ++++++++++++++++++ babi/screen.py | 1 + tests/features/conftest.py | 2 ++ tests/features/text_editing_test.py | 54 +++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/babi/file.py b/babi/file.py index 7a0d3531..8f0bac84 100644 --- a/babi/file.py +++ b/babi/file.py @@ -608,6 +608,39 @@ def backspace(self, dim: Dim) -> None: self.buf[self.buf.y] = s[:self.buf.x - 1] + s[self.buf.x:] self.buf.left(dim) + @edit_action('backword text', final=False) + @clear_selection + def backword(self, dim: Dim) -> None: + line = self.buf[self.buf.y] + if self.buf.x == 0 and self.buf.y == 0: + pass + elif self.buf.x == 0: + y, victim = self.buf.y, self.buf.pop(self.buf.y) + self.buf.left(dim) + self.buf[y - 1] += victim + elif self.buf.x > 0 and line[:self.buf.x].isspace(): + while self.buf.x > 0: + s = self.buf[self.buf.y] + self.buf[self.buf.y] = s[:self.buf.x - 1] + s[self.buf.x:] + self.buf.left(dim) + else: + tp = line[self.buf.x - 1].isalnum() + # we keep track of whether we're deleting spaces + are_spaces = line[self.buf.x - 1].isspace() + while self.buf.x > 0 and tp == line[self.buf.x - 1].isalnum(): + s = self.buf[self.buf.y] + self.buf[self.buf.y] = s[:self.buf.x - 1] + s[self.buf.x:] + self.buf.left(dim) + + # if we were deleting spaces, this means we haven't deleted the + # word yet + tp = line[self.buf.x - 1].isalnum() + if are_spaces: + while self.buf.x > 0 and line[self.buf.x - 1].isalnum(): + s = self.buf[self.buf.y] + self.buf[self.buf.y] = s[:self.buf.x - 1] + s[self.buf.x:] + self.buf.left(dim) + @edit_action('delete text', final=False) @clear_selection def delete(self, dim: Dim) -> None: @@ -901,6 +934,7 @@ def reload(self, status: Status, dim: Dim) -> None: b'kDN3': alt_down, # editing b'KEY_BACKSPACE': backspace, + b'KEY_BACKWORD': backword, b'KEY_DC': delete, b'^M': enter, b'^I': tab, diff --git a/babi/screen.py b/babi/screen.py index e26c7026..00e35dd2 100644 --- a/babi/screen.py +++ b/babi/screen.py @@ -67,6 +67,7 @@ '\x1b[1;6H': b'kHOM6', # Shift + ^Home '\x1b[1;6F': b'kEND6', # Shift + ^End '\x1b[~': b'KEY_BTAB', # Shift + Tab + '\x1b(263)': b'KEY_BACKWORD', # M-Backspace } KEYNAME_REWRITE = { # windows-curses: numeric pad arrow keys diff --git a/tests/features/conftest.py b/tests/features/conftest.py index d1b0851d..a1851b21 100644 --- a/tests/features/conftest.py +++ b/tests/features/conftest.py @@ -268,6 +268,8 @@ def value(self) -> int: Key('^End', b'kEND5', 530), Key('^S-Up', b'kUP6', 567), Key('^S-Down', b'kDN6', 526), + # 504 is backword but it doesn't work? + Key('M-BSpace', b'KEY_BACKWORD', 504), Key('M-Up', b'kUP3', 564), Key('M-Down', b'kDN3', 523), Key('M-Right', b'kRIT3', 558), diff --git a/tests/features/text_editing_test.py b/tests/features/text_editing_test.py index dd571df3..21de5635 100644 --- a/tests/features/text_editing_test.py +++ b/tests/features/text_editing_test.py @@ -79,6 +79,60 @@ def test_backspace_deletes_text(run, tmpdir, key): h.await_cursor_position(x=2, y=1) +def test_backword_at_beginning_of_file(run): + # same behavior as backspace + with run() as h, and_exit(h): + h.press('M-BSpace') + h.await_text_missing('unknown key') + h.assert_cursor_line_equal('') + h.await_text_missing('*') + + +def test_backword_joins_lines(run, tmpdir): + # same behavior as backspace + f = tmpdir.join('f') + f.write('foo\nbar\nbaz\n') + + with run(str(f)) as h, and_exit(h): + h.await_text('foo') + h.press('Down') + h.press('M-BSpace') + h.await_text('foobar') + h.await_text('f *') + h.await_cursor_position(x=3, y=1) + # pressing down should retain the X position + h.press('Down') + h.await_cursor_position(x=3, y=2) + + +def test_backword_deletes_text(run, tmpdir): + f = tmpdir.join('f') + f.write('ohai there') + + with run(str(f)) as h, and_exit(h): + h.await_text('ohai there') + for _ in range(3): + h.press('Right') + h.press('M-BSpace') + h.await_text('i') + h.await_text('f *') + h.await_cursor_position(x=0, y=1) + + +def test_backword_deletes_text_with_space_after(run, tmpdir): + f = tmpdir.join('f') + f.write('ohai there ') + + with run(str(f)) as h, and_exit(h): + h.await_text('ohai there') + for _ in range(13): + h.press('Right') + h.press('M-BSpace') + h.await_text('ohai') + h.await_text('f *') + h.await_cursor_position(x=5, y=1) + + def test_delete_at_end_of_file(run, tmpdir): with run() as h, and_exit(h): h.press('DC') From 1c9919da28866c081a89c5539ff81684123a5dbf Mon Sep 17 00:00:00 2001 From: Qexat <43090614+qexat@users.noreply.github.com> Date: Sun, 17 Sep 2023 01:04:31 +0200 Subject: [PATCH 2/5] Remove misleading comment --- tests/features/conftest.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/features/conftest.py b/tests/features/conftest.py index a1851b21..a6e54e4d 100644 --- a/tests/features/conftest.py +++ b/tests/features/conftest.py @@ -268,7 +268,6 @@ def value(self) -> int: Key('^End', b'kEND5', 530), Key('^S-Up', b'kUP6', 567), Key('^S-Down', b'kDN6', 526), - # 504 is backword but it doesn't work? Key('M-BSpace', b'KEY_BACKWORD', 504), Key('M-Up', b'kUP3', 564), Key('M-Down', b'kDN3', 523), From 71827244248315934d64df9532542b13a4152565 Mon Sep 17 00:00:00 2001 From: Qexat <43090614+qexat@users.noreply.github.com> Date: Sat, 30 Sep 2023 20:56:21 +0200 Subject: [PATCH 3/5] Fix EOF behavior --- babi/file.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/babi/file.py b/babi/file.py index 8f0bac84..fcf19014 100644 --- a/babi/file.py +++ b/babi/file.py @@ -614,6 +614,13 @@ def backword(self, dim: Dim) -> None: line = self.buf[self.buf.y] if self.buf.x == 0 and self.buf.y == 0: pass + # backword at the end of the file does not change the contents + elif ( + self.buf.y == len(self.buf) - 1 and + # still allow backword if there are 2+ blank lines + self.buf[self.buf.y - 1] != '' + ): + self.buf.left(dim) elif self.buf.x == 0: y, victim = self.buf.y, self.buf.pop(self.buf.y) self.buf.left(dim) From 3feb93a2ab47c71994f2255407edda925cdbe070 Mon Sep 17 00:00:00 2001 From: Qexat <43090614+qexat@users.noreply.github.com> Date: Sat, 30 Sep 2023 21:11:31 +0200 Subject: [PATCH 4/5] an attempt to fix that test --- tests/features/movement_test.py | 2 +- tests/features/text_editing_test.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/features/movement_test.py b/tests/features/movement_test.py index 84df1978..fa05b08e 100644 --- a/tests/features/movement_test.py +++ b/tests/features/movement_test.py @@ -412,7 +412,7 @@ def test_sequence_handling(run_only_fake): h.press('\x1b[1;') h.press(' test7') h.await_text('test1 test2 test3 test4 test5 test6 test7') - h.await_text(r'\x1b[1;') + h.await_text(r'\x1b(263)') def test_indentation_using_tabs(run, tmpdir): diff --git a/tests/features/text_editing_test.py b/tests/features/text_editing_test.py index 21de5635..1f336237 100644 --- a/tests/features/text_editing_test.py +++ b/tests/features/text_editing_test.py @@ -105,6 +105,31 @@ def test_backword_joins_lines(run, tmpdir): h.await_cursor_position(x=3, y=2) +def test_backword_at_end_of_file_still_allows_scrolling_down(run, tmpdir): + f = tmpdir.join('f') + f.write('hello world') + + with run(str(f)) as h, and_exit(h): + h.await_text('hello world') + h.press('Down') + h.press('M-BSpace') + h.press('Down') + h.await_cursor_position(x=0, y=2) + h.await_text_missing('*') + + +def test_backword_deletes_newline_at_end_of_file(run, tmpdir): + f = tmpdir.join('f') + f.write('foo\n\n') + + with run(str(f)) as h, and_exit(h): + h.press('^End') + h.press('M-BSpace') + h.press('^S') + + assert f.read() == 'foo\n' + + def test_backword_deletes_text(run, tmpdir): f = tmpdir.join('f') f.write('ohai there') From f29171936c72ffe62a0ac247f844aac745af6e31 Mon Sep 17 00:00:00 2001 From: Qexat <43090614+qexat@users.noreply.github.com> Date: Sat, 30 Sep 2023 21:34:05 +0200 Subject: [PATCH 5/5] fix the fix because it was definitely wrong --- tests/features/movement_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/features/movement_test.py b/tests/features/movement_test.py index fa05b08e..84df1978 100644 --- a/tests/features/movement_test.py +++ b/tests/features/movement_test.py @@ -412,7 +412,7 @@ def test_sequence_handling(run_only_fake): h.press('\x1b[1;') h.press(' test7') h.await_text('test1 test2 test3 test4 test5 test6 test7') - h.await_text(r'\x1b(263)') + h.await_text(r'\x1b[1;') def test_indentation_using_tabs(run, tmpdir):