diff --git a/babi/file.py b/babi/file.py index 6d46f58..129ce89 100644 --- a/babi/file.py +++ b/babi/file.py @@ -641,9 +641,9 @@ class File: self.buf[self.buf.y] += self.buf.pop(self.buf.y + 1) self.buf.restore_eof_invariant() - def _sort(self, margin: Margin, s_y: int, e_y: int) -> None: + def _sort(self, margin: Margin, s_y: int, e_y: int, reverse: bool) -> None: # self.buf intentionally does not support slicing so we use islice - lines = sorted(itertools.islice(self.buf, s_y, e_y)) + lines = sorted(itertools.islice(self.buf, s_y, e_y), reverse=reverse) for i, line in zip(range(s_y, e_y), lines): self.buf[i] = line @@ -652,17 +652,17 @@ class File: self.buf.scroll_screen_if_needed(margin) @edit_action('sort', final=True) - def sort(self, margin: Margin) -> None: - self._sort(margin, 0, len(self.buf) - 1) + def sort(self, margin: Margin, reverse: bool = False) -> None: + self._sort(margin, 0, len(self.buf) - 1, reverse=reverse) @edit_action('sort selection', final=True) @clear_selection - def sort_selection(self, margin: Margin) -> None: + def sort_selection(self, margin: Margin, reverse: bool = False) -> None: (s_y, _), (e_y, _) = self.selection.get() e_y = min(e_y + 1, len(self.buf) - 1) if self.buf[e_y - 1] == '': e_y -= 1 - self._sort(margin, s_y, e_y) + self._sort(margin, s_y, e_y, reverse=reverse) DISPATCH = { # movement diff --git a/babi/screen.py b/babi/screen.py index 8ce6688..bfccc37 100644 --- a/babi/screen.py +++ b/babi/screen.py @@ -433,6 +433,12 @@ class Screen: else: self.file.sort(self.margin) self.status.update('sorted!') + elif response == ':sort!': + if self.file.selection.start: + self.file.sort_selection(self.margin, reverse=True) + else: + self.file.sort(self.margin, reverse=True) + self.status.update('sorted!') elif response is not PromptResult.CANCELLED: self.status.update(f'invalid command: {response}') return None diff --git a/tests/features/sort_test.py b/tests/features/sort_test.py index 5b2b114..e83d4a4 100644 --- a/tests/features/sort_test.py +++ b/tests/features/sort_test.py @@ -21,6 +21,16 @@ def test_sort_entire_file(run, unsorted): assert unsorted.read() == 'a\nb\nc\nd\n' +def test_reverse_sort_entire_file(run, 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() == 'd\nc\nb\na\n' + + def test_sort_selection(run, unsorted): with run(str(unsorted)) as h, and_exit(h): h.press('S-Down') @@ -32,6 +42,18 @@ def test_sort_selection(run, unsorted): assert unsorted.read() == 'b\nd\nc\na\n' +def test_reverse_sort_selection(run, unsorted): + with run(str(unsorted)) as h, and_exit(h): + h.press('Down') + h.press('S-Down') + trigger_command_mode(h) + h.press_and_enter(':sort!') + h.await_text('sorted!') + h.await_cursor_position(x=0, y=2) + h.press('^S') + assert unsorted.read() == 'd\nc\nb\na\n' + + def test_sort_selection_does_not_include_eof(run, unsorted): with run(str(unsorted)) as h, and_exit(h): for _ in range(5):