Add :sort command
This commit is contained in:
28
babi.py
28
babi.py
@@ -6,6 +6,7 @@ import enum
|
||||
import functools
|
||||
import hashlib
|
||||
import io
|
||||
import itertools
|
||||
import os
|
||||
import re
|
||||
import signal
|
||||
@@ -1051,6 +1052,27 @@ class File:
|
||||
self.x = self.x_hint = len(self.lines[self.y])
|
||||
self.lines[self.y] += self.lines.pop(self.y + 1)
|
||||
|
||||
def _sort(self, margin: Margin, s_y: int, e_y: int) -> None:
|
||||
# self.lines intentionally does not support slicing so we use islice
|
||||
lines = sorted(itertools.islice(self.lines, s_y, e_y))
|
||||
for i, line in zip(range(s_y, e_y), lines):
|
||||
self.lines[i] = line
|
||||
|
||||
self.y = s_y
|
||||
self.x = self.x_hint = 0
|
||||
self.scroll_screen_if_needed(margin)
|
||||
|
||||
@edit_action('sort', final=True)
|
||||
def sort(self, margin: Margin) -> None:
|
||||
self._sort(margin, 0, len(self.lines) - 1)
|
||||
|
||||
@edit_action('sort selection', final=True)
|
||||
@clear_selection
|
||||
def sort_selection(self, margin: Margin) -> None:
|
||||
(s_y, _), (e_y, _) = self._get_selection()
|
||||
e_y = min(e_y + 1, len(self.lines) - 1)
|
||||
self._sort(margin, s_y, e_y)
|
||||
|
||||
DISPATCH = {
|
||||
# movement
|
||||
b'KEY_UP': up,
|
||||
@@ -1448,6 +1470,12 @@ class Screen:
|
||||
elif response == ':wq':
|
||||
self.save()
|
||||
return EditResult.EXIT
|
||||
elif response == ':sort':
|
||||
if self.file.select_start:
|
||||
self.file.sort_selection(self.margin)
|
||||
else:
|
||||
self.file.sort(self.margin)
|
||||
self.status.update('sorted!')
|
||||
elif response is not PromptResult.CANCELLED:
|
||||
self.status.update(f'invalid command: {response}')
|
||||
return None
|
||||
|
||||
@@ -143,3 +143,14 @@ def and_exit(h):
|
||||
if ' *' in h.get_screen_line(0):
|
||||
h.press('n')
|
||||
h.await_exit()
|
||||
|
||||
|
||||
def trigger_command_mode(h):
|
||||
# in order to enter a steady state, trigger an unknown key first and then
|
||||
# press escape to open the command mode. this is necessary as `Escape` is
|
||||
# the start of "escape sequences" and sending characters too quickly will
|
||||
# be interpreted as a single keypress
|
||||
h.press('^J')
|
||||
h.await_text('unknown key')
|
||||
h.press('Escape')
|
||||
h.await_text_missing('unknown key')
|
||||
|
||||
@@ -2,17 +2,7 @@ import pytest
|
||||
|
||||
from testing.runner import and_exit
|
||||
from testing.runner import run
|
||||
|
||||
|
||||
def trigger_command_mode(h):
|
||||
# in order to enter a steady state, trigger an unknown key first and then
|
||||
# press escape to open the command mode. this is necessary as `Escape` is
|
||||
# the start of "escape sequences" and sending characters too quickly will
|
||||
# be interpreted as a single keypress
|
||||
h.press('^J')
|
||||
h.await_text('unknown key')
|
||||
h.press('Escape')
|
||||
h.await_text_missing('unknown key')
|
||||
from testing.runner import trigger_command_mode
|
||||
|
||||
|
||||
def test_quit_via_colon_q():
|
||||
|
||||
45
tests/sort_test.py
Normal file
45
tests/sort_test.py
Normal file
@@ -0,0 +1,45 @@
|
||||
import pytest
|
||||
|
||||
from testing.runner import and_exit
|
||||
from testing.runner import run
|
||||
from testing.runner import trigger_command_mode
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def unsorted(tmpdir):
|
||||
f = tmpdir.join('f')
|
||||
f.write('d\nb\nc\na\n')
|
||||
return f
|
||||
|
||||
|
||||
def test_sort_entire_file(unsorted):
|
||||
with run(str(unsorted)) as h, and_exit(h):
|
||||
trigger_command_mode(h)
|
||||
h.press_and_enter(':sort')
|
||||
h.await_text('sorted!')
|
||||
h.await_cursor_position(x=0, y=1)
|
||||
h.press('^S')
|
||||
assert unsorted.read() == 'a\nb\nc\nd\n'
|
||||
|
||||
|
||||
def test_sort_selection(unsorted):
|
||||
with run(str(unsorted)) as h, and_exit(h):
|
||||
h.press('S-Down')
|
||||
trigger_command_mode(h)
|
||||
h.press_and_enter(':sort')
|
||||
h.await_text('sorted!')
|
||||
h.await_cursor_position(x=0, y=1)
|
||||
h.press('^S')
|
||||
assert unsorted.read() == 'b\nd\nc\na\n'
|
||||
|
||||
|
||||
def test_sort_selection_does_not_include_eof(unsorted):
|
||||
with run(str(unsorted)) as h, and_exit(h):
|
||||
for _ in range(5):
|
||||
h.press('S-Down')
|
||||
trigger_command_mode(h)
|
||||
h.press_and_enter(':sort')
|
||||
h.await_text('sorted!')
|
||||
h.await_cursor_position(x=0, y=1)
|
||||
h.press('^S')
|
||||
assert unsorted.read() == 'a\nb\nc\nd\n'
|
||||
Reference in New Issue
Block a user