Implement go to line (^_)
This commit is contained in:
37
babi.py
37
babi.py
@@ -232,11 +232,18 @@ class Status:
|
||||
pos = 0
|
||||
buf = ''
|
||||
while True:
|
||||
width = curses.COLS - len(prompt)
|
||||
cmd = f'{prompt}{_scrolled_line(buf, pos, width, current=True)}'
|
||||
if not prompt or curses.COLS < 7:
|
||||
prompt_s = ''
|
||||
elif len(prompt) > curses.COLS - 6:
|
||||
prompt_s = f'{prompt[:curses.COLS - 7]}…: '
|
||||
else:
|
||||
prompt_s = f'{prompt}: '
|
||||
|
||||
width = curses.COLS - len(prompt_s)
|
||||
cmd = f'{prompt_s}{_scrolled_line(buf, pos, width, current=True)}'
|
||||
screen.stdscr.insstr(curses.LINES - 1, 0, cmd, curses.A_REVERSE)
|
||||
line_x = _line_x(pos, width)
|
||||
screen.stdscr.move(curses.LINES - 1, pos - line_x)
|
||||
screen.stdscr.move(curses.LINES - 1, len(prompt_s) + pos - line_x)
|
||||
key = _get_char(screen.stdscr)
|
||||
|
||||
if key.key == curses.KEY_RESIZE:
|
||||
@@ -494,6 +501,19 @@ class File:
|
||||
self.cursor_y = len(self.lines) - 1
|
||||
self._scroll_screen_if_needed(margin)
|
||||
|
||||
@action
|
||||
def go_to_line(self, lineno: int, margin: Margin) -> None:
|
||||
self.x = self.x_hint = 0
|
||||
if lineno == 0:
|
||||
self.cursor_y = 0
|
||||
elif lineno > len(self.lines):
|
||||
self.cursor_y = len(self.lines) - 1
|
||||
elif lineno < 0:
|
||||
self.cursor_y = max(0, lineno + len(self.lines))
|
||||
else:
|
||||
self.cursor_y = lineno - 1
|
||||
self._scroll_screen_if_needed(margin)
|
||||
|
||||
@action
|
||||
def page_up(self, margin: Margin) -> None:
|
||||
if self.cursor_y < margin.body_lines:
|
||||
@@ -864,6 +884,17 @@ def _edit(screen: Screen) -> EditResult:
|
||||
screen.file.undo(screen.status, screen.margin)
|
||||
elif key.keyname == b'M-U':
|
||||
screen.file.redo(screen.status, screen.margin)
|
||||
elif key.keyname == b'^_':
|
||||
response = screen.status.prompt(screen, 'enter line number')
|
||||
if response == '':
|
||||
screen.status.update('cancelled')
|
||||
else:
|
||||
try:
|
||||
lineno = int(response)
|
||||
except ValueError:
|
||||
screen.status.update(f'not an integer: {response!r}')
|
||||
else:
|
||||
screen.file.go_to_line(lineno, screen.margin)
|
||||
elif key.keyname == b'^[': # escape
|
||||
response = screen.status.prompt(screen, '')
|
||||
if response == ':q':
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
coverage
|
||||
git+https://github.com/mjsir911/hecate@092f811
|
||||
git+https://github.com/asottile/hecate@ebe6dfb
|
||||
pytest
|
||||
remote-pdb
|
||||
|
||||
@@ -337,6 +337,13 @@ def trigger_command_mode(h):
|
||||
h.await_text_missing('unknown key')
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ten_lines(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
yield f
|
||||
|
||||
|
||||
@pytest.mark.parametrize('color', (True, False))
|
||||
def test_color_test(color):
|
||||
with run('--color-test', color=color) as h, and_exit(h):
|
||||
@@ -486,11 +493,8 @@ def test_arrow_key_movement(tmpdir):
|
||||
('page_up', 'page_down'),
|
||||
(('PageUp', 'PageDown'), ('^Y', '^V')),
|
||||
)
|
||||
def test_page_up_and_page_down(tmpdir, page_up, page_down):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
|
||||
with run(str(f), height=10) as h, and_exit(h):
|
||||
def test_page_up_and_page_down(ten_lines, page_up, page_down):
|
||||
with run(str(ten_lines), height=10) as h, and_exit(h):
|
||||
h.press('Down')
|
||||
h.press('Down')
|
||||
h.press(page_up)
|
||||
@@ -515,11 +519,8 @@ def test_page_up_and_page_down(tmpdir, page_up, page_down):
|
||||
assert h.get_cursor_line() == 'line_9'
|
||||
|
||||
|
||||
def test_page_up_page_down_size_small_window(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
|
||||
with run(str(f), height=4) as h, and_exit(h):
|
||||
def test_page_up_page_down_size_small_window(ten_lines):
|
||||
with run(str(ten_lines), height=4) as h, and_exit(h):
|
||||
h.press('PageDown')
|
||||
h.await_text('line_2')
|
||||
h.await_cursor_position(x=0, y=1)
|
||||
@@ -532,11 +533,8 @@ def test_page_up_page_down_size_small_window(tmpdir):
|
||||
assert h.get_cursor_line() == 'line_0'
|
||||
|
||||
|
||||
def test_ctrl_home(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
|
||||
with run(str(f), height=4) as h, and_exit(h):
|
||||
def test_ctrl_home(ten_lines):
|
||||
with run(str(ten_lines), height=4) as h, and_exit(h):
|
||||
for _ in range(3):
|
||||
h.press('PageDown')
|
||||
h.await_text_missing('line_0')
|
||||
@@ -546,21 +544,15 @@ def test_ctrl_home(tmpdir):
|
||||
h.await_cursor_position(x=0, y=1)
|
||||
|
||||
|
||||
def test_ctrl_end(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
|
||||
with run(str(f), height=6) as h, and_exit(h):
|
||||
def test_ctrl_end(ten_lines):
|
||||
with run(str(ten_lines), height=6) as h, and_exit(h):
|
||||
h.press('^End')
|
||||
h.await_cursor_position(x=0, y=3)
|
||||
assert h.get_screen_line(2) == 'line_9'
|
||||
|
||||
|
||||
def test_ctrl_end_already_on_last_page(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
|
||||
with run(str(f), height=9) as h, and_exit(h):
|
||||
def test_ctrl_end_already_on_last_page(ten_lines):
|
||||
with run(str(ten_lines), height=9) as h, and_exit(h):
|
||||
h.press('PageDown')
|
||||
h.await_cursor_position(x=0, y=1)
|
||||
h.await_text('line_9')
|
||||
@@ -570,11 +562,73 @@ def test_ctrl_end_already_on_last_page(tmpdir):
|
||||
assert h.get_screen_line(5) == 'line_9'
|
||||
|
||||
|
||||
def test_scrolling_arrow_key_movement(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
def test_prompt_window_width():
|
||||
with run() as h, and_exit(h):
|
||||
h.press('^_')
|
||||
h.await_text('enter line number:')
|
||||
h.press('123')
|
||||
with h.resize(width=23, height=24):
|
||||
h.await_text('\nenter line number: «3')
|
||||
with h.resize(width=22, height=24):
|
||||
h.await_text('\nenter line numb…: «3')
|
||||
with h.resize(width=7, height=24):
|
||||
h.await_text('\n…: «3')
|
||||
with h.resize(width=6, height=24):
|
||||
h.await_text('\n123')
|
||||
h.press('Enter')
|
||||
|
||||
with run(str(f), height=10) as h, and_exit(h):
|
||||
|
||||
def test_go_to_line_line(ten_lines):
|
||||
def _jump_to_line(n):
|
||||
h.press('^_')
|
||||
h.await_text('enter line number:')
|
||||
h.press_and_enter(str(n))
|
||||
h.await_text_missing('enter line number:')
|
||||
|
||||
with run(str(ten_lines), height=9) as h, and_exit(h):
|
||||
# still on screen
|
||||
_jump_to_line(3)
|
||||
h.await_cursor_position(x=0, y=3)
|
||||
# should go to beginning of file
|
||||
_jump_to_line(0)
|
||||
h.await_cursor_position(x=0, y=1)
|
||||
# should go to end of the file
|
||||
_jump_to_line(999)
|
||||
h.await_cursor_position(x=0, y=4)
|
||||
assert h.get_screen_line(3) == 'line_9'
|
||||
# should also go to the end of the file
|
||||
_jump_to_line(-1)
|
||||
h.await_cursor_position(x=0, y=4)
|
||||
assert h.get_screen_line(3) == 'line_9'
|
||||
# should go to beginning of file
|
||||
_jump_to_line(-999)
|
||||
h.await_cursor_position(x=0, y=1)
|
||||
assert h.get_cursor_line() == 'line_0'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('key', ('Enter', '^C'))
|
||||
def test_go_to_line_cancel(ten_lines, key):
|
||||
with run(str(ten_lines)) as h, and_exit(h):
|
||||
h.press('Down')
|
||||
h.await_cursor_position(x=0, y=2)
|
||||
|
||||
h.press('^_')
|
||||
h.await_text('enter line number:')
|
||||
h.press(key)
|
||||
h.await_cursor_position(x=0, y=2)
|
||||
h.await_text('cancelled')
|
||||
|
||||
|
||||
def test_go_to_line_not_an_integer():
|
||||
with run() as h, and_exit(h):
|
||||
h.press('^_')
|
||||
h.await_text('enter line number:')
|
||||
h.press_and_enter('asdf')
|
||||
h.await_text("not an integer: 'asdf'")
|
||||
|
||||
|
||||
def test_scrolling_arrow_key_movement(ten_lines):
|
||||
with run(str(ten_lines), height=10) as h, and_exit(h):
|
||||
h.await_text('line_7')
|
||||
# we should not have scrolled after 7 presses
|
||||
for _ in range(7):
|
||||
@@ -625,11 +679,8 @@ def test_home_key(tmpdir, k):
|
||||
h.await_cursor_position(x=0, y=2)
|
||||
|
||||
|
||||
def test_resize_scrolls_up(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
|
||||
with run(str(f)) as h, and_exit(h):
|
||||
def test_resize_scrolls_up(ten_lines):
|
||||
with run(str(ten_lines)) as h, and_exit(h):
|
||||
h.await_text('line_9')
|
||||
|
||||
for _ in range(7):
|
||||
@@ -651,11 +702,8 @@ def test_resize_scrolls_up(tmpdir):
|
||||
assert h.get_cursor_line() == 'line_7'
|
||||
|
||||
|
||||
def test_resize_scroll_does_not_go_negative(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
|
||||
with run(str(f)) as h, and_exit(h):
|
||||
def test_resize_scroll_does_not_go_negative(ten_lines):
|
||||
with run(str(ten_lines)) as h, and_exit(h):
|
||||
for _ in range(5):
|
||||
h.press('Down')
|
||||
h.await_cursor_position(x=0, y=6)
|
||||
@@ -670,11 +718,8 @@ def test_resize_scroll_does_not_go_negative(tmpdir):
|
||||
assert h.get_screen_line(1) == 'line_0'
|
||||
|
||||
|
||||
def test_page_up_does_not_go_negative(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
|
||||
with run(str(f), height=10) as h, and_exit(h):
|
||||
def test_page_up_does_not_go_negative(ten_lines):
|
||||
with run(str(ten_lines), height=10) as h, and_exit(h):
|
||||
for _ in range(8):
|
||||
h.press('Down')
|
||||
h.await_cursor_position(x=0, y=4)
|
||||
@@ -1147,11 +1192,8 @@ def test_cancel_command_mode():
|
||||
h.await_text_missing('invalid command')
|
||||
|
||||
|
||||
def test_cut_and_uncut(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('\n'.join(f'line_{i}' for i in range(10)))
|
||||
|
||||
with run(str(f)) as h, and_exit(h):
|
||||
def test_cut_and_uncut(ten_lines):
|
||||
with run(str(ten_lines)) as h, and_exit(h):
|
||||
h.press('^K')
|
||||
h.await_text_missing('line_0')
|
||||
h.await_text(' *')
|
||||
|
||||
Reference in New Issue
Block a user