diff options
author | Florian Bruhin <me@the-compiler.org> | 2022-03-31 17:46:34 +0200 |
---|---|---|
committer | Florian Bruhin <me@the-compiler.org> | 2022-03-31 17:46:34 +0200 |
commit | ab65c542a0551abf105eeb58803cd08bd040753b (patch) | |
tree | 0996adf80cf23c6224270835bf7ee56e8aba2cf0 | |
parent | ebdd791b744d8973495ba4bbf573444849b6bc03 (diff) | |
download | qutebrowser-ab65c542a0551abf105eeb58803cd08bd040753b.tar.gz qutebrowser-ab65c542a0551abf105eeb58803cd08bd040753b.zip |
Add :rl-rubout and :rl-filename-rubout
Closes #4561
-rw-r--r-- | doc/changelog.asciidoc | 14 | ||||
-rw-r--r-- | doc/help/commands.asciidoc | 30 | ||||
-rw-r--r-- | doc/help/settings.asciidoc | 6 | ||||
-rw-r--r-- | qutebrowser/components/readlinecommands.py | 55 | ||||
-rw-r--r-- | qutebrowser/config/configdata.yml | 6 | ||||
-rw-r--r-- | qutebrowser/mainwindow/prompt.py | 1 | ||||
-rw-r--r-- | tests/unit/components/test_readlinecommands.py | 63 |
7 files changed, 136 insertions, 39 deletions
diff --git a/doc/changelog.asciidoc b/doc/changelog.asciidoc index 1c2c92e0b..e96dfb41c 100644 --- a/doc/changelog.asciidoc +++ b/doc/changelog.asciidoc @@ -19,6 +19,15 @@ breaking changes (such as renamed commands) can happen in minor releases. v2.5.0 (unreleased) ------------------- +Deprecated +~~~~~~~~~~ + +- The `:rl-unix-word-rubout` command (`<Ctrl-W>` in command/prompt modes) has + been deprecated. Use `:rl-rubout " "` instead. +- The `:rl-unix-filename-rubout` command has been deprecated. Use either + `:rl-rubout "/ "` (classic readline behavior) or `:rl-filename-rubout` (using + OS path separator and ignoring spaces) instead. + Changed ~~~~~~~ @@ -78,6 +87,11 @@ Added current tab. - New `editor.remove_file` setting which can be set to `False` to keep all temporary editor files after closing the external editor. +- New `:rl-rubout` command replacing `:rl-unix-word-rubout` (and optionally + `:rl-unix-filename-rubout`), taking a delimiter as argument. +- New `:rl-filename-rubout` command, using the OS path separator and ignoring + spaces. The command also gets shown in the suggested commands for a download + filename prompt now. Fixed ~~~~~ diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 442c136a7..58019f937 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -1681,13 +1681,13 @@ How many steps to zoom out. |<<rl-beginning-of-line,rl-beginning-of-line>>|Move to the start of the line. |<<rl-delete-char,rl-delete-char>>|Delete the character after the cursor. |<<rl-end-of-line,rl-end-of-line>>|Move to the end of the line. +|<<rl-filename-rubout,rl-filename-rubout>>|Delete backwards using the OS path separator as boundary. |<<rl-forward-char,rl-forward-char>>|Move forward a character. |<<rl-forward-word,rl-forward-word>>|Move forward to the end of the next word. |<<rl-kill-line,rl-kill-line>>|Remove chars from the cursor to the end of the line. |<<rl-kill-word,rl-kill-word>>|Remove chars from the cursor to the end of the current word. -|<<rl-unix-filename-rubout,rl-unix-filename-rubout>>|Remove chars from the cursor to the previous path separator. +|<<rl-rubout,rl-rubout>>|Delete backwards using the given characters as boundaries. |<<rl-unix-line-discard,rl-unix-line-discard>>|Remove chars backward from the cursor to the beginning of the line. -|<<rl-unix-word-rubout,rl-unix-word-rubout>>|Remove chars from the cursor to the beginning of the word. |<<rl-yank,rl-yank>>|Paste the most recently deleted text. |<<selection-drop,selection-drop>>|Drop selection and keep selection mode enabled. |<<selection-reverse,selection-reverse>>|Swap the stationary and moving end of the current selection. @@ -1939,6 +1939,12 @@ Move to the end of the line. This acts like readline's end-of-line. +[[rl-filename-rubout]] +=== rl-filename-rubout +Delete backwards using the OS path separator as boundary. + +For behavior that matches readline's `unix-filename-rubout` exactly, use `:rl-rubout "/ "` instead. This command uses the OS path seperator (i.e. `\` on Windows) and ignores spaces. + [[rl-forward-char]] === rl-forward-char Move forward a character. @@ -1963,11 +1969,17 @@ 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. +[[rl-rubout]] +=== rl-rubout +Syntax: +:rl-rubout 'delim'+ + +Delete backwards using the given characters as boundaries. + +With " ", this acts like readline's `unix-word-rubout`. With " /", this acts like readline's `unix-filename-rubout`, but consider using `:rl-filename-rubout` instead: It uses the OS path seperator (i.e. `\` on Windows) and ignores spaces. + +==== positional arguments +* +'delim'+: A string of characters (or a single character) until which text will be deleted. -This acts like readline's unix-filename-rubout. [[rl-unix-line-discard]] === rl-unix-line-discard @@ -1975,12 +1987,6 @@ Remove chars backward from the cursor to the beginning of the line. This acts like readline's unix-line-discard. -[[rl-unix-word-rubout]] -=== rl-unix-word-rubout -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. - [[rl-yank]] === rl-yank Paste the most recently deleted text. diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index bdc5bef99..c522f44c2 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -524,9 +524,10 @@ Default: * +pass:[<Ctrl-Return>]+: +pass:[command-accept --rapid]+ * +pass:[<Ctrl-Shift-C>]+: +pass:[completion-item-yank --sel]+ * +pass:[<Ctrl-Shift-Tab>]+: +pass:[completion-item-focus prev-category]+ +* +pass:[<Ctrl-Shift-W>]+: +pass:[rl-filename-rubout]+ * +pass:[<Ctrl-Tab>]+: +pass:[completion-item-focus next-category]+ * +pass:[<Ctrl-U>]+: +pass:[rl-unix-line-discard]+ -* +pass:[<Ctrl-W>]+: +pass:[rl-unix-word-rubout]+ +* +pass:[<Ctrl-W>]+: +pass:[rl-rubout " "]+ * +pass:[<Ctrl-Y>]+: +pass:[rl-yank]+ * +pass:[<Down>]+: +pass:[completion-item-focus --history next]+ * +pass:[<Escape>]+: +pass:[mode-leave]+ @@ -760,8 +761,9 @@ Default: * +pass:[<Ctrl-H>]+: +pass:[rl-backward-delete-char]+ * +pass:[<Ctrl-K>]+: +pass:[rl-kill-line]+ * +pass:[<Ctrl-P>]+: +pass:[prompt-open-download --pdfjs]+ +* +pass:[<Ctrl-Shift-W>]+: +pass:[rl-filename-rubout]+ * +pass:[<Ctrl-U>]+: +pass:[rl-unix-line-discard]+ -* +pass:[<Ctrl-W>]+: +pass:[rl-unix-word-rubout]+ +* +pass:[<Ctrl-W>]+: +pass:[rl-rubout " "]+ * +pass:[<Ctrl-X>]+: +pass:[prompt-open-download]+ * +pass:[<Ctrl-Y>]+: +pass:[rl-yank]+ * +pass:[<Down>]+: +pass:[prompt-item-focus next]+ diff --git a/qutebrowser/components/readlinecommands.py b/qutebrowser/components/readlinecommands.py index 335cc518b..7d5b73798 100644 --- a/qutebrowser/components/readlinecommands.py +++ b/qutebrowser/components/readlinecommands.py @@ -19,6 +19,7 @@ """Bridge to provide readline-like shortcuts for QLineEdits.""" +import os from typing import Iterable, Optional, MutableMapping from PyQt5.QtWidgets import QApplication, QLineEdit @@ -90,8 +91,13 @@ class _ReadlineBridge: def kill_line(self) -> None: self._dispatch('end', mark=True, delete=True) - def _rubout(self, delim: Iterable[str]) -> None: - """Delete backwards using the characters in delim as boundaries.""" + def rubout(self, delim: Iterable[str]) -> None: + """Delete backwards using the characters in delim as boundaries. + + With delim=[' '], this acts like unix-word-rubout. + With delim=[' ', '/'], this acts like unix-filename-rubout. + With delim=[os.sep], this serves as a more useful filename-rubout. + """ widget = self._widget() if widget is None: return @@ -115,12 +121,6 @@ class _ReadlineBridge: self._deleted[widget] = widget.selectedText() widget.del_() - def unix_word_rubout(self) -> None: - self._rubout([' ']) - - def unix_filename_rubout(self) -> None: - self._rubout([' ', '/']) - def backward_kill_word(self) -> None: self._dispatch('cursorWordBackward', mark=True, delete=True) @@ -221,23 +221,54 @@ def rl_kill_line() -> None: bridge.kill_line() -@_register() +@_register(deprecated="Use :rl-rubout ' ' instead.") def rl_unix_word_rubout() -> None: """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. """ - bridge.unix_word_rubout() + bridge.rubout([" "]) -@_register() +@_register( + deprecated='Use :rl-filename-rubout or :rl-rubout " /" instead ' + '(see their `:help` for details).' +) def rl_unix_filename_rubout() -> None: """Remove chars from the cursor to the previous path separator. This acts like readline's unix-filename-rubout. """ - bridge.unix_filename_rubout() + bridge.rubout([" ", "/"]) + + +@_register() +def rl_rubout(delim: str) -> None: + """Delete backwards using the given characters as boundaries. + + With " ", this acts like readline's `unix-word-rubout`. + + With " /", this acts like readline's `unix-filename-rubout`, but consider + using `:rl-filename-rubout` instead: It uses the OS path seperator (i.e. `\\` + on Windows) and ignores spaces. + + Args: + delim: A string of characters (or a single character) until which text + will be deleted. + """ + bridge.rubout(list(delim)) + + +@_register() +def rl_filename_rubout() -> None: + """Delete backwards using the OS path separator as boundary. + + For behavior that matches readline's `unix-filename-rubout` exactly, use + `:rl-rubout "/ "` instead. This command uses the OS path seperator (i.e. + `\\` on Windows) and ignores spaces. + """ + bridge.rubout(os.sep) @_register() diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index 95338b36f..490459681 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -3764,7 +3764,8 @@ bindings.default: <Ctrl-U>: rl-unix-line-discard <Ctrl-K>: rl-kill-line <Alt-D>: rl-kill-word - <Ctrl-W>: rl-unix-word-rubout + <Ctrl-W>: rl-rubout " " + <Ctrl-Shift-W>: rl-filename-rubout <Alt-Backspace>: rl-backward-kill-word <Ctrl-Y>: rl-yank <Ctrl-?>: rl-delete-char @@ -3789,7 +3790,8 @@ bindings.default: <Ctrl-U>: rl-unix-line-discard <Ctrl-K>: rl-kill-line <Alt-D>: rl-kill-word - <Ctrl-W>: rl-unix-word-rubout + <Ctrl-W>: rl-rubout " " + <Ctrl-Shift-W>: rl-filename-rubout <Alt-Backspace>: rl-backward-kill-word <Ctrl-?>: rl-delete-char <Ctrl-H>: rl-backward-delete-char diff --git a/qutebrowser/mainwindow/prompt.py b/qutebrowser/mainwindow/prompt.py index 21976f383..6fea16093 100644 --- a/qutebrowser/mainwindow/prompt.py +++ b/qutebrowser/mainwindow/prompt.py @@ -831,6 +831,7 @@ class DownloadFilenamePrompt(FilenamePrompt): cmds = [ ('prompt-accept', 'Accept'), ('mode-leave', 'Abort'), + ('rl-filename-rubout', "Go to parent directory"), ('prompt-open-download', "Open download"), ('prompt-open-download --pdfjs', "Open download via PDF.js"), ('prompt-yank', "Yank URL"), diff --git a/tests/unit/components/test_readlinecommands.py b/tests/unit/components/test_readlinecommands.py index 334e2ef9e..b3ca9c301 100644 --- a/tests/unit/components/test_readlinecommands.py +++ b/tests/unit/components/test_readlinecommands.py @@ -17,6 +17,7 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see <https://www.gnu.org/licenses/>. +import os import re import inspect @@ -94,18 +95,19 @@ class LineEdit(QLineEdit): return ''.join(chars) -def _validate_deletion(lineedit, method, text, deleted, rest): +def _validate_deletion(lineedit, method, args, text, deleted, rest): """Run and validate a text deletion method on the ReadLine bridge. Args: lineedit: The LineEdit instance. method: Reference to the method on the bridge to test. + args: Arguments to pass to the method. 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() + method(*args) assert readlinecommands.bridge._deleted[lineedit] == deleted assert lineedit.aug_text() == rest lineedit.clear() @@ -127,7 +129,9 @@ def test_none(qtbot): assert QApplication.instance().focusWidget() is None for name, method in inspect.getmembers(readlinecommands, inspect.isfunction): - if name.startswith('rl_'): + if name == "rl_rubout": + method(delim=" ") + elif name.startswith('rl_'): method() @@ -218,7 +222,8 @@ def test_rl_backward_delete_char(text, expected, lineedit): ]) def test_rl_unix_line_discard(lineedit, text, deleted, rest): """Delete from the cursor to the beginning of the line and yank back.""" - _validate_deletion(lineedit, readlinecommands.rl_unix_line_discard, + _validate_deletion(lineedit, + readlinecommands.rl_unix_line_discard, [], text, deleted, rest) @@ -230,7 +235,8 @@ def test_rl_unix_line_discard(lineedit, text, deleted, rest): ]) def test_rl_kill_line(lineedit, text, deleted, rest): """Delete from the cursor to the end of line and yank back.""" - _validate_deletion(lineedit, readlinecommands.rl_kill_line, + _validate_deletion(lineedit, + readlinecommands.rl_kill_line, [], text, deleted, rest) @@ -243,9 +249,14 @@ def test_rl_kill_line(lineedit, text, deleted, rest): marks=fixme), ('test del<ete >foobar', 'del', 'test |ete foobar'), # wrong ]) -def test_rl_unix_word_rubout(lineedit, text, deleted, rest): +@pytest.mark.parametrize("method, args", [ + (readlinecommands.rl_unix_word_rubout, []), # deprecated + (readlinecommands.rl_rubout, [" "]), # equivalent +]) +def test_rl_unix_word_rubout(lineedit, text, deleted, rest, method, args): """Delete to word beginning and see if it comes back with yank.""" - _validate_deletion(lineedit, readlinecommands.rl_unix_word_rubout, + _validate_deletion(lineedit, + method, args, text, deleted, rest) @@ -256,9 +267,37 @@ def test_rl_unix_word_rubout(lineedit, text, deleted, rest): ('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, text, deleted, rest): +@pytest.mark.parametrize("method, args", [ + (readlinecommands.rl_unix_filename_rubout, []), # deprecated + (readlinecommands.rl_rubout, [" /"]), # equivalent +]) +def test_rl_unix_filename_rubout(lineedit, text, deleted, rest, method, args): + """Delete filename segment and see if it comes back with yank.""" + _validate_deletion(lineedit, + method, args, + text, deleted, rest) + + +@pytest.mark.parametrize('os_sep, text, deleted, rest', [ + pytest.param('/', 'path|', 'path', '|', marks=fixme), + ('/', 'path|', 'ath', 'p|'), # wrong + ('/', '/path|', 'path', '/|'), + ('/', '/path/sub|', 'sub', '/path/|'), + ('/', '/path/trailing/|', 'trailing/', '/path/|'), + ('/', '/test/path with spaces|', 'path with spaces', '/test/|'), + ('/', r'/test/path\backslashes\eww|', r'path\backslashes\eww', '/test/|'), + pytest.param('\\', 'path|', 'path', '|', marks=fixme), + ('\\', 'path|', 'ath', 'p|'), # wrong + ('\\', r'C:\path|', 'path', r'C:\|'), + ('\\', r'C:\path\sub|', 'sub', r'C:\path\|'), + ('\\', r'C:\test\path with spaces|', 'path with spaces', r'C:\test\|'), + ('\\', r'C:\path\trailing\|', 'trailing\\', r'C:\path\|'), +]) +def test_filename_rubout(os_sep, monkeypatch, lineedit, text, deleted, rest): """Delete filename segment and see if it comes back with yank.""" - _validate_deletion(lineedit, readlinecommands.rl_unix_filename_rubout, + monkeypatch.setattr(os, "sep", os_sep) + _validate_deletion(lineedit, + readlinecommands.rl_filename_rubout, [], text, deleted, rest) @@ -275,7 +314,8 @@ def test_rl_unix_filename_rubout(lineedit, text, deleted, rest): ]) def test_rl_kill_word(lineedit, text, deleted, rest): """Delete to word end and see if it comes back with yank.""" - _validate_deletion(lineedit, readlinecommands.rl_kill_word, + _validate_deletion(lineedit, + readlinecommands.rl_kill_word, [], text, deleted, rest) @@ -290,7 +330,8 @@ def test_rl_kill_word(lineedit, text, deleted, rest): ]) def test_rl_backward_kill_word(lineedit, text, deleted, rest): """Delete to word beginning and see if it comes back with yank.""" - _validate_deletion(lineedit, readlinecommands.rl_backward_kill_word, + _validate_deletion(lineedit, + readlinecommands.rl_backward_kill_word, [], text, deleted, rest) |