summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2022-03-31 17:46:34 +0200
committerFlorian Bruhin <me@the-compiler.org>2022-03-31 17:46:34 +0200
commitab65c542a0551abf105eeb58803cd08bd040753b (patch)
tree0996adf80cf23c6224270835bf7ee56e8aba2cf0
parentebdd791b744d8973495ba4bbf573444849b6bc03 (diff)
downloadqutebrowser-ab65c542a0551abf105eeb58803cd08bd040753b.tar.gz
qutebrowser-ab65c542a0551abf105eeb58803cd08bd040753b.zip
Add :rl-rubout and :rl-filename-rubout
Closes #4561
-rw-r--r--doc/changelog.asciidoc14
-rw-r--r--doc/help/commands.asciidoc30
-rw-r--r--doc/help/settings.asciidoc6
-rw-r--r--qutebrowser/components/readlinecommands.py55
-rw-r--r--qutebrowser/config/configdata.yml6
-rw-r--r--qutebrowser/mainwindow/prompt.py1
-rw-r--r--tests/unit/components/test_readlinecommands.py63
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:[&lt;Ctrl-Return&gt;]+: +pass:[command-accept --rapid]+
* +pass:[&lt;Ctrl-Shift-C&gt;]+: +pass:[completion-item-yank --sel]+
* +pass:[&lt;Ctrl-Shift-Tab&gt;]+: +pass:[completion-item-focus prev-category]+
+* +pass:[&lt;Ctrl-Shift-W&gt;]+: +pass:[rl-filename-rubout]+
* +pass:[&lt;Ctrl-Tab&gt;]+: +pass:[completion-item-focus next-category]+
* +pass:[&lt;Ctrl-U&gt;]+: +pass:[rl-unix-line-discard]+
-* +pass:[&lt;Ctrl-W&gt;]+: +pass:[rl-unix-word-rubout]+
+* +pass:[&lt;Ctrl-W&gt;]+: +pass:[rl-rubout &quot; &quot;]+
* +pass:[&lt;Ctrl-Y&gt;]+: +pass:[rl-yank]+
* +pass:[&lt;Down&gt;]+: +pass:[completion-item-focus --history next]+
* +pass:[&lt;Escape&gt;]+: +pass:[mode-leave]+
@@ -760,8 +761,9 @@ Default:
* +pass:[&lt;Ctrl-H&gt;]+: +pass:[rl-backward-delete-char]+
* +pass:[&lt;Ctrl-K&gt;]+: +pass:[rl-kill-line]+
* +pass:[&lt;Ctrl-P&gt;]+: +pass:[prompt-open-download --pdfjs]+
+* +pass:[&lt;Ctrl-Shift-W&gt;]+: +pass:[rl-filename-rubout]+
* +pass:[&lt;Ctrl-U&gt;]+: +pass:[rl-unix-line-discard]+
-* +pass:[&lt;Ctrl-W&gt;]+: +pass:[rl-unix-word-rubout]+
+* +pass:[&lt;Ctrl-W&gt;]+: +pass:[rl-rubout &quot; &quot;]+
* +pass:[&lt;Ctrl-X&gt;]+: +pass:[prompt-open-download]+
* +pass:[&lt;Ctrl-Y&gt;]+: +pass:[rl-yank]+
* +pass:[&lt;Down&gt;]+: +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)