diff --git a/pyvim/commands/commands.py b/pyvim/commands/commands.py index def2a59..cbedff0 100644 --- a/pyvim/commands/commands.py +++ b/pyvim/commands/commands.py @@ -208,6 +208,8 @@ def buffer_add(editor, location): editor.window_arrangement.open_buffer(location) +@cmd('files') +@cmd('ls') @cmd('buffers') def buffer_list(editor): """ @@ -279,22 +281,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) @@ -303,7 +301,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) @@ -328,7 +330,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') @@ -342,17 +344,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') @@ -409,10 +420,10 @@ def pwd(editor): @location_cmd('cd', accepts_force=False) -def pwd(editor, location): +def cd(editor, location): " Change working directory. " try: - os.chdir(location) + os.chdir(os.path.expanduser(location)) except OSError as e: editor.show_message('{}'.format(e)) diff --git a/pyvim/key_bindings.py b/pyvim/key_bindings.py index e3209ee..73788b8 100644 --- a/pyvim/key_bindings.py +++ b/pyvim/key_bindings.py @@ -4,6 +4,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__ = ( @@ -38,6 +40,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): """ @@ -52,7 +77,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 3984168..ef3a37c 100644 --- a/pyvim/window_arrangement.py +++ b/pyvim/window_arrangement.py @@ -22,7 +22,7 @@ class HSplit(list): class VSplit(list): - """ Horizontal split. """ + """ Vertical split. """ class Window(object): @@ -103,6 +103,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. @@ -260,11 +264,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 4fda75b..60bf78b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,11 +2,10 @@ import pytest -from prompt_toolkit.buffer import Buffer from prompt_toolkit.output import DummyOutput from prompt_toolkit.input import DummyInput from pyvim.editor import Editor -from pyvim.window_arrangement import TabPage, EditorBuffer, Window +from pyvim.window_arrangement import TabPage, EditorBuffer, Window, WindowArrangement @pytest.fixture @@ -27,3 +26,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 b3d02bd..0a52a4e 100644 --- a/tests/test_window_arrangements.py +++ b/tests/test_window_arrangements.py @@ -1,7 +1,6 @@ from __future__ import unicode_literals -from prompt_toolkit.buffer import Buffer -from pyvim.window_arrangement import EditorBuffer, VSplit +from pyvim.window_arrangement import EditorBuffer, VSplit, TabPage, Window def test_initial(window, tab_page): @@ -18,3 +17,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