From a086095954b973b86398a4013344ba3afa760038 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Sat, 30 Jul 2016 19:02:05 -0400 Subject: [PATCH 1/3] Implement unix_filename_rubout. unix_filename_rubout deletes to the previous slash or whitespace, unlike the previously implemented backwards-kill-word which treats and non-alphanumeric character as a boundary. To illustrate, given the text 'foo/bar.baz', unix_filename_rubout will delete 'bar.baz' while backwards-kill-word will delete only 'baz'. See #1710. --- qutebrowser/misc/readline.py | 41 +++++++++++++++++++++----------- tests/unit/misc/test_readline.py | 19 +++++++++++++++ 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/qutebrowser/misc/readline.py b/qutebrowser/misc/readline.py index 517837a59..0089ebe7c 100644 --- a/qutebrowser/misc/readline.py +++ b/qutebrowser/misc/readline.py @@ -148,14 +148,8 @@ class ReadlineBridge: self._deleted[widget] = widget.selectedText() widget.del_() - @cmdutils.register(instance='readline-bridge', hide=True, - modes=[typ.KeyMode.command, typ.KeyMode.prompt]) - def rl_unix_word_rubout(self): - """Remove chars from the cursor to the beginning of the word. - - This acts like readline's unix-word-rubout. Whitespace is used as a - word delimiter. - """ + def _rubout(self, delim): + """Delete backwards using the characters in delim as boundaries.""" widget = self._widget() if widget is None: return @@ -164,14 +158,14 @@ class ReadlineBridge: target_position = cursor_position - is_word_boundary = True - while is_word_boundary and target_position > 0: - is_word_boundary = text[target_position - 1] == " " + is_boundary = True + while is_boundary and target_position > 0: + is_boundary = text[target_position - 1] in delim target_position -= 1 - is_word_boundary = False - while not is_word_boundary and target_position > 0: - is_word_boundary = text[target_position - 1] == " " + is_boundary = False + while not is_boundary and target_position > 0: + is_boundary = text[target_position - 1] in delim target_position -= 1 moveby = cursor_position - target_position - 1 @@ -179,6 +173,25 @@ class ReadlineBridge: self._deleted[widget] = widget.selectedText() widget.del_() + @cmdutils.register(instance='readline-bridge', hide=True, + modes=[typ.KeyMode.command, typ.KeyMode.prompt]) + def rl_unix_word_rubout(self): + """Remove chars from the cursor to the beginning of the word. + + This acts like readline's unix-word-rubout. Whitespace is used as a + word delimiter. + """ + self._rubout([' ']) + + @cmdutils.register(instance='readline-bridge', hide=True, + modes=[typ.KeyMode.command, typ.KeyMode.prompt]) + def rl_unix_filename_rubout(self): + """Remove chars from the cursor to the previous path separator. + + This acts like readline's unix-filename-rubout. + """ + self._rubout([' ', '/']) + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_backward_kill_word(self): diff --git a/tests/unit/misc/test_readline.py b/tests/unit/misc/test_readline.py index 419ad6f04..aec927d85 100644 --- a/tests/unit/misc/test_readline.py +++ b/tests/unit/misc/test_readline.py @@ -250,6 +250,24 @@ def test_rl_unix_word_rubout(lineedit, bridge, text, deleted, rest): assert lineedit.aug_text() == deleted + '|' +@pytest.mark.parametrize('text, deleted, rest', [ + ('test delete|foobar', 'delete', 'test |foobar'), + ('test delete |foobar', 'delete ', 'test |foobar'), + ('open -t github.com/foo/bar |', 'bar ', 'open -t github.com/foo/|'), + ('open -t |github.com/foo/bar', '-t ', 'open |github.com/foo/bar'), + ('open foo/bar.baz|', 'bar.baz', 'open foo/|'), +]) +def test_rl_unix_filename_rubout(lineedit, bridge, text, deleted, rest): + """Delete filename segment and see if it comes back with yank.""" + lineedit.set_aug_text(text) + bridge.rl_unix_filename_rubout() + assert bridge._deleted[lineedit] == deleted + assert lineedit.aug_text() == rest + lineedit.clear() + bridge.rl_yank() + assert lineedit.aug_text() == deleted + '|' + + @pytest.mark.parametrize('text, deleted, rest', [ fixme(('test foobar| delete', ' delete', 'test foobar|')), ('test foobar| delete', ' ', 'test foobar|delete'), # wrong @@ -276,6 +294,7 @@ def test_rl_kill_word(lineedit, bridge, text, deleted, rest): ('open -t |github.com/foo/bar', 't ', 'open -|github.com/foo/bar'), fixme(('test delfoobar', 'delete', 'test |foobar')), ('test delfoobar', 'del', 'test |ete foobar'), # wrong + ('open foo/bar.baz|', 'baz', 'open foo/bar.|'), ]) def test_rl_backward_kill_word(lineedit, bridge, text, deleted, rest): """Delete to word beginning and see if it comes back with yank.""" From 86a08d17c21aca370eb98efe56655a6940507a1d Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Sat, 30 Jul 2016 22:00:12 -0400 Subject: [PATCH 2/3] Dedupe code in test_readline. Many of the tests repeated the same block of logic, so consolidate it into one function. --- tests/unit/misc/test_readline.py | 74 ++++++++++++++------------------ 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/tests/unit/misc/test_readline.py b/tests/unit/misc/test_readline.py index aec927d85..70bd887e4 100644 --- a/tests/unit/misc/test_readline.py +++ b/tests/unit/misc/test_readline.py @@ -96,6 +96,26 @@ class LineEdit(QLineEdit): return ''.join(chars) +def _validate_deletion(lineedit, bridge, method, text, deleted, rest): + """Run and validate a text deletion method on the ReadLine bridge. + + Args: + lineedit: The LineEdit instance. + bridge: The ReadlineBridge instance. + method: Reference to the method on the bridge to test. + text: The starting 'augmented' text (see LineEdit.set_aug_text) + deleted: The text that should be deleted when the method is invoked. + rest: The augmented text that should remain after method is invoked. + """ + lineedit.set_aug_text(text) + method() + assert bridge._deleted[lineedit] == deleted + assert lineedit.aug_text() == rest + lineedit.clear() + bridge.rl_yank() + assert lineedit.aug_text() == deleted + '|' + + @pytest.fixture def lineedit(qtbot, monkeypatch): """Fixture providing a LineEdit.""" @@ -206,13 +226,8 @@ def test_rl_backward_delete_char(text, expected, lineedit, bridge): ]) def test_rl_unix_line_discard(lineedit, bridge, text, deleted, rest): """Delete from the cursor to the beginning of the line and yank back.""" - lineedit.set_aug_text(text) - bridge.rl_unix_line_discard() - assert bridge._deleted[lineedit] == deleted - assert lineedit.aug_text() == rest - lineedit.clear() - bridge.rl_yank() - assert lineedit.aug_text() == deleted + '|' + _validate_deletion(lineedit, bridge, bridge.rl_unix_line_discard, text, + deleted, rest) @pytest.mark.parametrize('text, deleted, rest', [ @@ -222,13 +237,8 @@ def test_rl_unix_line_discard(lineedit, bridge, text, deleted, rest): ]) def test_rl_kill_line(lineedit, bridge, text, deleted, rest): """Delete from the cursor to the end of line and yank back.""" - lineedit.set_aug_text(text) - bridge.rl_kill_line() - assert bridge._deleted[lineedit] == deleted - assert lineedit.aug_text() == rest - lineedit.clear() - bridge.rl_yank() - assert lineedit.aug_text() == deleted + '|' + _validate_deletion(lineedit, bridge, bridge.rl_kill_line, text, deleted, + rest) @pytest.mark.parametrize('text, deleted, rest', [ @@ -241,13 +251,8 @@ def test_rl_kill_line(lineedit, bridge, text, deleted, rest): ]) def test_rl_unix_word_rubout(lineedit, bridge, text, deleted, rest): """Delete to word beginning and see if it comes back with yank.""" - lineedit.set_aug_text(text) - bridge.rl_unix_word_rubout() - assert bridge._deleted[lineedit] == deleted - assert lineedit.aug_text() == rest - lineedit.clear() - bridge.rl_yank() - assert lineedit.aug_text() == deleted + '|' + _validate_deletion(lineedit, bridge, bridge.rl_unix_word_rubout, text, + deleted, rest) @pytest.mark.parametrize('text, deleted, rest', [ @@ -259,13 +264,8 @@ def test_rl_unix_word_rubout(lineedit, bridge, text, deleted, rest): ]) def test_rl_unix_filename_rubout(lineedit, bridge, text, deleted, rest): """Delete filename segment and see if it comes back with yank.""" - lineedit.set_aug_text(text) - bridge.rl_unix_filename_rubout() - assert bridge._deleted[lineedit] == deleted - assert lineedit.aug_text() == rest - lineedit.clear() - bridge.rl_yank() - assert lineedit.aug_text() == deleted + '|' + _validate_deletion(lineedit, bridge, bridge.rl_unix_filename_rubout, text, + deleted, rest) @pytest.mark.parametrize('text, deleted, rest', [ @@ -278,13 +278,8 @@ def test_rl_unix_filename_rubout(lineedit, bridge, text, deleted, rest): ]) def test_rl_kill_word(lineedit, bridge, text, deleted, rest): """Delete to word end and see if it comes back with yank.""" - lineedit.set_aug_text(text) - bridge.rl_kill_word() - assert bridge._deleted[lineedit] == deleted - assert lineedit.aug_text() == rest - lineedit.clear() - bridge.rl_yank() - assert lineedit.aug_text() == deleted + '|' + _validate_deletion(lineedit, bridge, bridge.rl_kill_word, text, deleted, + rest) @pytest.mark.parametrize('text, deleted, rest', [ @@ -298,13 +293,8 @@ def test_rl_kill_word(lineedit, bridge, text, deleted, rest): ]) def test_rl_backward_kill_word(lineedit, bridge, text, deleted, rest): """Delete to word beginning and see if it comes back with yank.""" - lineedit.set_aug_text(text) - bridge.rl_backward_kill_word() - assert bridge._deleted[lineedit] == deleted - assert lineedit.aug_text() == rest - lineedit.clear() - bridge.rl_yank() - assert lineedit.aug_text() == deleted + '|' + _validate_deletion(lineedit, bridge, bridge.rl_backward_kill_word, text, + deleted, rest) def test_rl_yank_no_text(lineedit, bridge): From 77f9696a9661fd876b17acbfe76424a16debe9f0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 1 Aug 2016 11:01:33 +0200 Subject: [PATCH 3/3] Update docs --- CHANGELOG.asciidoc | 2 ++ doc/help/commands.asciidoc | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 49cae0f5c..d5cefaa76 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -22,6 +22,8 @@ Added - New `:rl-backward-kill-word` command which does what `:rl-unix-word-rubout` did before v0.8.0. +- New `:rl-unix-filename-rubout` command which is similar to readline's + `unix-filename-rubout`. Changed ~~~~~~~ diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 1b0d6a734..fd37f3adc 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -955,6 +955,7 @@ How many steps to zoom out. |<>|Move forward to the end of the next word. |<>|Remove chars from the cursor to the end of the line. |<>|Remove chars from the cursor to the end of the current word. +|<>|Remove chars from the cursor to the previous path separator. |<>|Remove chars backward from the cursor to the beginning of the line. |<>|Remove chars from the cursor to the beginning of the word. |<>|Paste the most recently deleted text. @@ -1248,6 +1249,12 @@ Remove chars from the cursor to the end of the current word. This acts like readline's kill-word. +[[rl-unix-filename-rubout]] +=== rl-unix-filename-rubout +Remove chars from the cursor to the previous path separator. + +This acts like readline's unix-filename-rubout. + [[rl-unix-line-discard]] === rl-unix-line-discard Remove chars backward from the cursor to the beginning of the line.