diff --git a/pyvim/commands/commands.py b/pyvim/commands/commands.py index 7309059..3f5a25a 100644 --- a/pyvim/commands/commands.py +++ b/pyvim/commands/commands.py @@ -209,6 +209,8 @@ def buffer_add(editor, location): editor.window_arrangement.open_buffer(location) +@cmd('files') +@cmd('ls') @cmd('buffers') def buffer_list(editor): """ @@ -280,22 +282,18 @@ def buffer_edit(editor, location, force=False): @cmd('q', accepts_force=True) @cmd('quit', accepts_force=True) -def quit(editor, all_=False, force=False): +def quit(editor, force=False): """ Quit. """ - ebs = editor.window_arrangement.editor_buffers - - # When there are buffers that have unsaved changes, show balloon. - if not force and any(eb.has_unsaved_changes for eb in ebs): + eb = editor.window_arrangement.active_editor_buffer + eb_is_open_in_another_window = len(list(editor.window_arrangement.get_windows_for_buffer(eb))) > 1 + if not force and eb.has_unsaved_changes and not eb_is_open_in_another_window: editor.show_message(_NO_WRITE_SINCE_LAST_CHANGE_TEXT) - - # When there is more than one buffer open. - elif not all_ and len(ebs) > 1: - editor.show_message('%i more files to edit' % (len(ebs) - 1)) - - else: + elif editor.window_arrangement.active_tab.window_count() == 1 and len(editor.window_arrangement.tab_pages) == 1: editor.application.exit() + else: + editor.window_arrangement.close_window() @cmd('qa', accepts_force=True) @@ -304,7 +302,11 @@ def quit_all(editor, force=False): """ Quit all. """ - quit(editor, all_=True, force=force) + ebs = editor.window_arrangement.editor_buffers + if not force and any(eb.has_unsaved_changes for eb in ebs): + editor.show_message(_NO_WRITE_SINCE_LAST_CHANGE_TEXT) + else: + editor.application.exit() @location_cmd('w', accepts_force=True) @@ -329,7 +331,7 @@ def write_and_quit(editor, location, force=False): Write file and quit. """ write(editor, location, force=force) - editor.application.exit() + quit(editor) @cmd('cq') @@ -343,17 +345,26 @@ def quit_nonzero(editor): editor.application.exit() +@cmd('wa') +def write_all(editor): + """ + Write all changed buffers + """ + for eb in editor.window_arrangement.editor_buffers: + if eb.location is None: + editor.show_message(_NO_FILE_NAME) + break + else: + eb.write() + + @cmd('wqa') def write_and_quit_all(editor): """ - Write current buffer and quit all. + Write all changed buffers and quit all. """ - eb = editor.window_arrangement.active_editor_buffer - if eb.location is None: - editor.show_message(_NO_FILE_NAME) - else: - eb.write() - quit(editor, all_=True, force=False) + write_all(editor) + quit_all(editor) @cmd('h') diff --git a/pyvim/key_bindings.py b/pyvim/key_bindings.py index e81fff6..b0da8aa 100644 --- a/pyvim/key_bindings.py +++ b/pyvim/key_bindings.py @@ -2,6 +2,8 @@ from prompt_toolkit.filters import Condition, has_focus, vi_insert_mode, vi_navigation_mode from prompt_toolkit.key_binding import KeyBindings +from .commands.commands import write_and_quit, quit + import os __all__ = ( @@ -36,6 +38,29 @@ def vi_buffer_focussed(): in_insert_mode = vi_insert_mode & vi_buffer_focussed in_navigation_mode = vi_navigation_mode & vi_buffer_focussed + @kb.add('Z', 'Z', filter=in_navigation_mode) + def _(event): + """ + Write and quit. + """ + write_and_quit(editor, None) + editor.sync_with_prompt_toolkit() + + @kb.add('Z', 'Q', filter=in_navigation_mode) + def _(event): + """ + Quit and discard changes. + """ + quit(editor, force=True) + editor.sync_with_prompt_toolkit() + + @kb.add('c-z', filter=in_navigation_mode) + def _(event): + """ + Suspend process to background. + """ + event.app.suspend_to_background() + @kb.add('c-t') def _(event): """ @@ -50,7 +75,7 @@ def indent_line(event): """ Indent current line. """ - b = event.application.current_buffer + b = event.app.current_buffer # Move to start of line. pos = b.document.get_start_of_line_position(after_whitespace=True) diff --git a/pyvim/window_arrangement.py b/pyvim/window_arrangement.py index 54f6815..4dc8aa1 100644 --- a/pyvim/window_arrangement.py +++ b/pyvim/window_arrangement.py @@ -20,7 +20,7 @@ class HSplit(list): class VSplit(list): - """ Horizontal split. """ + """ Vertical split. """ class Window(object): @@ -101,6 +101,10 @@ def _get_split_parent(self, split): if child == split: return parent + def get_windows_for_buffer(self, editor_buffer): + """ Return a list of all windows in this tab page. """ + return (window for _, window in self._walk_through_windows() if window.editor_buffer == editor_buffer) + def _split(self, split_cls, editor_buffer=None): """ Split horizontal or vertical. @@ -258,11 +262,18 @@ def get_editor_buffer_for_buffer_name(self, buffer_name): if eb.buffer_name == buffer_name: return eb + def get_windows_for_buffer(self, editor_buffer): + """ Return a list of all windows in this tab page. """ + return (b for t in self.tab_pages for b in t.get_windows_for_buffer(editor_buffer)) + def close_window(self): """ Close active window of active tab. """ - self.active_tab.close_active_window() + if self.active_tab.window_count() > 1: + self.active_tab.close_active_window() + else: + self.close_tab() # Clean up buffers. self._auto_close_new_empty_buffers() diff --git a/tests/conftest.py b/tests/conftest.py index a313253..c4c3d52 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,3 +24,19 @@ def window(editor_buffer): @pytest.fixture def tab_page(window): return TabPage(window) + + +@pytest.fixture +def tab_page_with_splits(editor_buffer, window): + editor_buffer2 = EditorBuffer(editor) + + tab_page = TabPage(Window(editor_buffer)) + tab_page.vsplit(editor_buffer) + tab_page.vsplit(editor_buffer2) + tab_page.hsplit(editor_buffer) + return tab_page + + +@pytest.fixture +def window_arrangement(editor): + return WindowArrangement(editor) diff --git a/tests/test_window_arrangements.py b/tests/test_window_arrangements.py index f4bfba3..99ea17c 100644 --- a/tests/test_window_arrangements.py +++ b/tests/test_window_arrangements.py @@ -15,3 +15,39 @@ def test_vsplit(editor, tab_page): assert isinstance(tab_page.root, VSplit) assert len(tab_page.root) == 2 + + +def test_tab_page_get_windows_for_buffer(editor, editor_buffer, tab_page_with_splits): + tab_page1 = tab_page_with_splits + + windows = list(tab_page1.get_windows_for_buffer(editor_buffer)) + assert all(w.editor_buffer == editor_buffer for w in windows) + assert len(windows) == 3 + + +def test_window_arrangement_get_windows_for_buffer(editor, editor_buffer, tab_page_with_splits, window_arrangement): + tab_page1 = tab_page_with_splits + tab_page2 = TabPage(Window(editor_buffer)) + + window_arrangement.tab_pages[:] = [tab_page1, tab_page2] + windows = list(window_arrangement.get_windows_for_buffer(editor_buffer)) + assert all(w.editor_buffer == editor_buffer for w in windows) + assert len(windows) == 4 + + +def test_close_window_closes_split(editor): + editor.window_arrangement.create_tab() + editor.window_arrangement.hsplit() + assert len(editor.window_arrangement.tab_pages) == 2 + + assert editor.window_arrangement.active_tab.window_count() == 2 + editor.window_arrangement.close_window() + assert editor.window_arrangement.active_tab.window_count() == 1 + + +def test_close_window_also_closes_empty_tab(editor): + editor.window_arrangement.create_tab() + + assert len(editor.window_arrangement.tab_pages) == 2 + editor.window_arrangement.close_window() + assert len(editor.window_arrangement.tab_pages) == 1