Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for backword #319

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions babi/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,46 @@ 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
# 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)
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:
Expand Down Expand Up @@ -901,6 +941,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,
Expand Down
1 change: 1 addition & 0 deletions babi/screen.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/features/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ def value(self) -> int:
Key('^End', b'kEND5', 530),
Key('^S-Up', b'kUP6', 567),
Key('^S-Down', b'kDN6', 526),
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),
Expand Down
79 changes: 79 additions & 0 deletions tests/features/text_editing_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,85 @@ 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_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')

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')
Expand Down
Loading