From 0177dafbd004b88f8ec0a72be3d90407a0c04ae7 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sat, 6 Aug 2016 22:03:50 +0200 Subject: [PATCH 1/8] Add {clipboard} and {primary} to replace :paste :paste is deprecated and replaced by equivalents using :open and the new variables, and :open supports opening newline-separated lists of URLs. --- doc/help/commands.asciidoc | 17 +---- qutebrowser/browser/commands.py | 79 +++++++++++++++--------- qutebrowser/commands/runners.py | 10 ++- qutebrowser/config/configdata.py | 12 ++-- qutebrowser/utils/utils.py | 6 ++ tests/end2end/features/yankpaste.feature | 38 ++++++------ 6 files changed, 93 insertions(+), 69 deletions(-) diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index f43be5822..ebe703a44 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -34,7 +34,6 @@ |<>|Show a log of past messages. |<>|Open typical prev/next links or navigate using the URL path. |<>|Open a URL in the current/[count]th tab. -|<>|Open a page from the clipboard. |<>|Print the current/[count]th tab. |<>|Add a new quickmark. |<>|Delete a quickmark. @@ -473,6 +472,8 @@ Syntax: +:open [*--implicit*] [*--bg*] [*--tab*] [*--window*] ['url']+ Open a URL in the current/[count]th tab. +If the URL contains newlines, each line gets opened in its own tab. + ==== positional arguments * +'url'+: The URL to open. @@ -489,20 +490,6 @@ The tab index to open the URL in. ==== note * This command does not split arguments after the last argument and handles quotes literally. -[[paste]] -=== paste -Syntax: +:paste [*--sel*] [*--tab*] [*--bg*] [*--window*]+ - -Open a page from the clipboard. - -If the pasted text contains newlines, each line gets opened in its own tab. - -==== optional arguments -* +*-s*+, +*--sel*+: Use the primary selection instead of the clipboard. -* +*-t*+, +*--tab*+: Open in a new tab. -* +*-b*+, +*--bg*+: Open in a background tab. -* +*-w*+, +*--window*+: Open in new window. - [[print]] === print Syntax: +:print [*--preview*] [*--pdf* 'file']+ diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 3728ebac4..3bfb09fd6 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -236,6 +236,8 @@ class CommandDispatcher: bg=False, tab=False, window=False, count=None): """Open a URL in the current/[count]th tab. + If the URL contains newlines, each line gets opened in its own tab. + Args: url: The URL to open. bg: Open in a new background tab. @@ -245,37 +247,60 @@ class CommandDispatcher: clicking on a link). count: The tab index to open the URL in, or None. """ + force_search = False if url is None: if tab or bg or window: - url = config.get('general', 'default-page') + urls = [config.get('general', 'default-page')] else: raise cmdexc.CommandError("No URL given, but -t/-b/-w is not " "set!") else: - try: - url = objreg.get('quickmark-manager').get(url) - except urlmarks.Error: - try: - url = urlutils.fuzzy_url(url) - except urlutils.InvalidUrlError as e: - # We don't use cmdexc.CommandError here as this can be - # called async from edit_url - message.error(self._win_id, str(e)) - return - if tab or bg or window: - self._open(url, tab, bg, window, not implicit) - else: - curtab = self._cntwidget(count) - if curtab is None: - if count is None: - # We want to open a URL in the current tab, but none exists - # yet. - self._tabbed_browser.tabopen(url) - else: - # Explicit count with a tab that doesn't exist. - return + urllist = [u for u in url.split('\n') if u.strip()] + if (len(urllist) > 1 and + any(not urlutils.is_url(u) and + urlutils.get_path_if_valid(u, check_exists=True) + is None for u in urllist)): + urllist = [url] + force_search = True + urls = [x for x in [self._parse_url(u, force_search=force_search) + for u in urllist] if x is not None] + for i, cur_url in enumerate(urls): + if not window and i > 0: + tab = False + bg = True + if tab or bg or window: + self._open(cur_url, tab, bg, window, not implicit) else: - curtab.openurl(url) + curtab = self._cntwidget(count) + if curtab is None: + if count is None: + # We want to open a URL in the current tab, but none + # exists yet. + self._tabbed_browser.tabopen(cur_url) + else: + curtab.openurl(cur_url) + + def _parse_url(self, url, force_search=False): + """Parse a URL or quickmark or search query. + + Args: + url: The URL to parse. + force_search: Whether to force a search even if the content can be + interpreted as a URL or a path. + + Return: + A URL that can be opened.""" + try: + return objreg.get('quickmark-manager').get(url) + except urlmarks.Error: + try: + return urlutils.fuzzy_url(url, force_search=force_search) + except urlutils.InvalidUrlError as e: + # We don't use cmdexc.CommandError here as this can be + # called async from edit_url + message.error(self._win_id, str(e)) + return + @cmdutils.register(instance='command-dispatcher', name='reload', scope='window') @@ -776,7 +801,8 @@ class CommandDispatcher: else: raise cmdexc.CommandError("Last tab") - @cmdutils.register(instance='command-dispatcher', scope='window') + @cmdutils.register(instance='command-dispatcher', scope='window', + deprecated="Use :open {clipboard}") def paste(self, sel=False, tab=False, bg=False, window=False): """Open a page from the clipboard. @@ -796,9 +822,6 @@ class CommandDispatcher: sel = False target = "Clipboard" text = utils.get_clipboard(selection=sel) - if not text.strip(): - raise cmdexc.CommandError("{} is empty.".format(target)) - log.misc.debug("{} contained: {!r}".format(target, text)) text_urls = [u for u in text.split('\n') if u.strip()] if (len(text_urls) > 1 and not urlutils.is_url(text_urls[0]) and urlutils.get_path_if_valid( diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index f020b9199..91c8bf3c9 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -26,7 +26,7 @@ from PyQt5.QtCore import pyqtSlot, QUrl, QObject from qutebrowser.config import config, configexc from qutebrowser.commands import cmdexc, cmdutils -from qutebrowser.utils import message, objreg, qtutils +from qutebrowser.utils import message, objreg, qtutils, utils from qutebrowser.misc import split @@ -57,11 +57,19 @@ def replace_variables(win_id, arglist): QUrl.RemovePassword) if '{url:pretty}' in arglist: pretty_url = _current_url(tabbed_browser).toString(QUrl.RemovePassword) + if '{clipboard}' in arglist: + clipboard = utils.get_clipboard() + if '{primary}' in arglist: + primary = utils.get_clipboard(selection=True) for arg in arglist: if arg == '{url}': args.append(url) elif arg == '{url:pretty}': args.append(pretty_url) + elif arg == '{clipboard}': + args.append(clipboard) + elif arg == '{primary}': + args.append(primary) else: args.append(arg) return args diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 8cbfe3938..9cc275502 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1512,12 +1512,12 @@ KEY_DATA = collections.OrderedDict([ ('yank -ds', ['yD']), ('yank -p', ['yp']), ('yank -ps', ['yP']), - ('paste', ['pp']), - ('paste -s', ['pP']), - ('paste -t', ['Pp']), - ('paste -ts', ['PP']), - ('paste -w', ['wp']), - ('paste -ws', ['wP']), + ('open {clipboard}', ['pp']), + ('open {primary}', ['pP']), + ('open -t {clipboard}', ['Pp']), + ('open -t {primary}', ['PP']), + ('open -w {clipboard}', ['wp']), + ('open -w {primary}', ['wP']), ('quickmark-save', ['m']), ('set-cmd-text -s :quickmark-load', ['b']), ('set-cmd-text -s :quickmark-load -t', ['B']), diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index fd8419d12..983827d46 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -37,6 +37,7 @@ import pkg_resources import qutebrowser from qutebrowser.utils import qtutils, log +from qutebrowser.commands import cmdexc fake_clipboard = None @@ -810,6 +811,11 @@ def get_clipboard(selection=False): mode = QClipboard.Selection if selection else QClipboard.Clipboard data = QApplication.clipboard().text(mode=mode) + target = "Primary selection" if selection else "Clipboard" + if not data.strip(): + raise cmdexc.CommandError("{} is empty.".format(target)) + log.misc.debug("{} contained: {!r}".format(target, data)) + return data diff --git a/tests/end2end/features/yankpaste.feature b/tests/end2end/features/yankpaste.feature index db39d2380..abf9cf9c3 100644 --- a/tests/end2end/features/yankpaste.feature +++ b/tests/end2end/features/yankpaste.feature @@ -1,6 +1,6 @@ Feature: Yanking and pasting. - :yank and :paste can be used to copy/paste the URL or title from/to the - clipboard and primary selection. + :yank, {clipboard} and {primary} can be used to copy/paste the URL or title + from/to the clipboard and primary selection. Background: Given I run :tab-only @@ -45,11 +45,11 @@ Feature: Yanking and pasting. Then the message "Yanked URL to clipboard: http://localhost:(port)/data/title with spaces.html" should be shown And the clipboard should contain "http://localhost:(port)/data/title with spaces.html" - #### :paste + #### {clipboard} and {primary} Scenario: Pasting a URL When I put "http://localhost:(port)/data/hello.txt" into the clipboard - And I run :paste + And I run :open {clipboard} And I wait until data/hello.txt is loaded Then the requests should be: data/hello.txt @@ -57,32 +57,32 @@ Feature: Yanking and pasting. Scenario: Pasting a URL from primary selection When selection is supported And I put "http://localhost:(port)/data/hello2.txt" into the primary selection - And I run :paste --sel + And I run :open {primary} And I wait until data/hello2.txt is loaded Then the requests should be: data/hello2.txt Scenario: Pasting with empty clipboard When I put "" into the clipboard - And I run :paste + And I run :open {clipboard} (invalid command) Then the error "Clipboard is empty." should be shown Scenario: Pasting with empty selection When selection is supported And I put "" into the primary selection - And I run :paste --sel + And I run :open {primary} (invalid command) Then the error "Primary selection is empty." should be shown Scenario: Pasting with a space in clipboard When I put " " into the clipboard - And I run :paste + And I run :open {clipboard} (invalid command) Then the error "Clipboard is empty." should be shown Scenario: Pasting in a new tab Given I open about:blank When I run :tab-only And I put "http://localhost:(port)/data/hello.txt" into the clipboard - And I run :paste -t + And I run :open -t {clipboard} And I wait until data/hello.txt is loaded Then the following tabs should be open: - about:blank @@ -92,7 +92,7 @@ Feature: Yanking and pasting. Given I open about:blank When I run :tab-only And I put "http://localhost:(port)/data/hello.txt" into the clipboard - And I run :paste -b + And I run :open -b {clipboard} And I wait until data/hello.txt is loaded Then the following tabs should be open: - about:blank (active) @@ -101,7 +101,7 @@ Feature: Yanking and pasting. Scenario: Pasting in a new window Given I have a fresh instance When I put "http://localhost:(port)/data/hello.txt" into the clipboard - And I run :paste -w + And I run :open -w {clipboard} And I wait until data/hello.txt is loaded Then the session should look like: windows: @@ -119,7 +119,7 @@ Feature: Yanking and pasting. Scenario: Pasting an invalid URL When I set general -> auto-search to false And I put "foo bar" into the clipboard - And I run :paste + And I run :open {clipboard} Then the error "Invalid URL" should be shown Scenario: Pasting multiple urls in a new tab @@ -128,7 +128,7 @@ Feature: Yanking and pasting. http://localhost:(port)/data/hello.txt http://localhost:(port)/data/hello2.txt http://localhost:(port)/data/hello3.txt - And I run :paste -t + And I run :open -t {clipboard} And I wait until data/hello.txt is loaded And I wait until data/hello2.txt is loaded And I wait until data/hello3.txt is loaded @@ -145,7 +145,7 @@ Feature: Yanking and pasting. this url: http://qutebrowser.org should not open - And I run :paste -t + And I run :open -t {clipboard} And I wait until data/hello.txt?q=this%20url%3A%0Ahttp%3A//qutebrowser.org%0Ashould%20not%20open is loaded Then the following tabs should be open: - about:blank @@ -159,7 +159,7 @@ Feature: Yanking and pasting. text: should open as search - And I run :paste -t + And I run :open -t {clipboard} And I wait until data/hello.txt?q=text%3A%0Ashould%20open%0Aas%20search is loaded Then the following tabs should be open: - about:blank @@ -172,7 +172,7 @@ Feature: Yanking and pasting. http://localhost:(port)/data/hello.txt http://localhost:(port)/data/hello2.txt http://localhost:(port)/data/hello3.txt - And I run :paste -b + And I run :open -b {clipboard} And I wait until data/hello.txt is loaded And I wait until data/hello2.txt is loaded And I wait until data/hello3.txt is loaded @@ -188,7 +188,7 @@ Feature: Yanking and pasting. http://localhost:(port)/data/hello.txt http://localhost:(port)/data/hello2.txt http://localhost:(port)/data/hello3.txt - And I run :paste -w + And I run :open -w {clipboard} And I wait until data/hello.txt is loaded And I wait until data/hello2.txt is loaded And I wait until data/hello3.txt is loaded @@ -218,13 +218,13 @@ Feature: Yanking and pasting. Scenario: Pasting multiple urls with an empty one When I open about:blank And I put "http://localhost:(port)/data/hello.txt\n\nhttp://localhost:(port)/data/hello2.txt" into the clipboard - And I run :paste -t + And I run :open -t {clipboard} Then no crash should happen Scenario: Pasting multiple urls with an almost empty one When I open about:blank And I put "http://localhost:(port)/data/hello.txt\n \nhttp://localhost:(port)/data/hello2.txt" into the clipboard - And I run :paste -t + And I run :open -t {clipboard} Then no crash should happen #### :paste-primary From 96146c55afbbf8603d800cfddfca8b7035cec39a Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sat, 6 Aug 2016 22:25:08 +0200 Subject: [PATCH 2/8] Remove useless clipboard target information --- qutebrowser/browser/commands.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 3bfb09fd6..f0ed6d67a 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -816,11 +816,8 @@ class CommandDispatcher: window: Open in new window. """ force_search = False - if sel and utils.supports_selection(): - target = "Primary selection" - else: + if not utils.supports_selection(): sel = False - target = "Clipboard" text = utils.get_clipboard(selection=sel) text_urls = [u for u in text.split('\n') if u.strip()] if (len(text_urls) > 1 and not urlutils.is_url(text_urls[0]) and From 38508274e063c12fc8caf49c746795c0abd57b94 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Sun, 7 Aug 2016 00:46:23 +0200 Subject: [PATCH 3/8] Improve clipboard exceptions, migrate bindings --- qutebrowser/browser/commands.py | 14 ++++++++++---- qutebrowser/commands/runners.py | 11 +++++++---- qutebrowser/config/configdata.py | 5 +++++ qutebrowser/misc/miscwidgets.py | 3 ++- qutebrowser/utils/utils.py | 8 ++++++-- tests/unit/config/test_config.py | 4 ++++ 6 files changed, 34 insertions(+), 11 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index f0ed6d67a..6dde5d40f 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -818,7 +818,10 @@ class CommandDispatcher: force_search = False if not utils.supports_selection(): sel = False - text = utils.get_clipboard(selection=sel) + try: + text = utils.get_clipboard(selection=sel) + except utils.ClipboardEmptyError as e: + raise cmdexc.CommandError(e) text_urls = [u for u in text.split('\n') if u.strip()] if (len(text_urls) > 1 and not urlutils.is_url(text_urls[0]) and urlutils.get_path_if_valid( @@ -1462,9 +1465,12 @@ class CommandDispatcher: raise cmdexc.CommandError("Focused element is not editable!") try: - sel = utils.get_clipboard(selection=True) - except utils.SelectionUnsupportedError: - sel = utils.get_clipboard() + try: + sel = utils.get_clipboard(selection=True) + except utils.SelectionUnsupportedError: + sel = utils.get_clipboard() + except utils.ClipboardEmptyError: + return log.misc.debug("Pasting primary selection into element {}".format( elem.debug_text())) diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index 91c8bf3c9..201fc4bbe 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -57,10 +57,13 @@ def replace_variables(win_id, arglist): QUrl.RemovePassword) if '{url:pretty}' in arglist: pretty_url = _current_url(tabbed_browser).toString(QUrl.RemovePassword) - if '{clipboard}' in arglist: - clipboard = utils.get_clipboard() - if '{primary}' in arglist: - primary = utils.get_clipboard(selection=True) + try: + if '{clipboard}' in arglist: + clipboard = utils.get_clipboard() + if '{primary}' in arglist: + primary = utils.get_clipboard(selection=True) + except utils.ClipboardEmptyError as e: + raise cmdexc.CommandError(e) for arg in arglist: if arg == '{url}': args.append(url) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 9cc275502..f770f5c05 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1677,4 +1677,9 @@ CHANGED_KEY_COMMANDS = [ (re.compile(r'^download-remove --all$'), r'download-clear'), (re.compile(r'^hint links fill "([^"]*)"$'), r'hint links fill \1'), + + (re.compile(r'^paste$'), r'open {clipboard}'), + (re.compile(r'^paste -([twb])$'), r'open -\1 {clipboard}'), + (re.compile(r'^paste -([twb])s$'), r'open -\1 {primary}'), + (re.compile(r'^paste -s([twb])$'), r'open -\1 {primary}'), ] diff --git a/qutebrowser/misc/miscwidgets.py b/qutebrowser/misc/miscwidgets.py index 63a5718c6..7e84300d2 100644 --- a/qutebrowser/misc/miscwidgets.py +++ b/qutebrowser/misc/miscwidgets.py @@ -47,7 +47,8 @@ class MinimalLineEditMixin: if e.key() == Qt.Key_Insert and e.modifiers() == Qt.ShiftModifier: try: text = utils.get_clipboard(selection=True) - except utils.SelectionUnsupportedError: + except (utils.SelectionUnsupportedError, + utils.ClipboardEmptyError): pass else: e.accept() diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 983827d46..32696aacf 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -37,7 +37,6 @@ import pkg_resources import qutebrowser from qutebrowser.utils import qtutils, log -from qutebrowser.commands import cmdexc fake_clipboard = None @@ -49,6 +48,11 @@ class SelectionUnsupportedError(Exception): """Raised if [gs]et_clipboard is used and selection=True is unsupported.""" +class ClipboardEmptyError(Exception): + + """Raised if get_clipboard is used and the clipboard is empty.""" + + def elide(text, length): """Elide text so it uses a maximum of length chars.""" if length < 1: @@ -813,7 +817,7 @@ def get_clipboard(selection=False): target = "Primary selection" if selection else "Clipboard" if not data.strip(): - raise cmdexc.CommandError("{} is empty.".format(target)) + raise ClipboardEmptyError("{} is empty.".format(target)) log.misc.debug("{} contained: {!r}".format(target, data)) return data diff --git a/tests/unit/config/test_config.py b/tests/unit/config/test_config.py index a6ad3d119..53664b718 100644 --- a/tests/unit/config/test_config.py +++ b/tests/unit/config/test_config.py @@ -286,6 +286,10 @@ class TestKeyConfigParser: 'hint links fill :open {hint-url}'), ('hint links fill ":open -t {hint-url}"', 'hint links fill :open -t {hint-url}'), + + ('paste', 'open {clipboard}'), + ('paste -t', 'open -t {clipboard}'), + ('paste -ws', 'open -w {primary}'), ] ) def test_migrations(self, old, new_expected): From 7e634a1e52b0bd6e07420226e0aff6c39f41d804 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Tue, 9 Aug 2016 16:20:02 +0200 Subject: [PATCH 4/8] Make vars declarative, new function for URL lists --- qutebrowser/browser/commands.py | 37 ++++++++++++++++++++++----------- qutebrowser/commands/runners.py | 36 +++++++++++++++----------------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 293602a53..42458f0d3 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -247,7 +247,6 @@ class CommandDispatcher: clicking on a link). count: The tab index to open the URL in, or None. """ - force_search = False if url is None: if tab or bg or window: urls = [config.get('general', 'default-page')] @@ -255,15 +254,7 @@ class CommandDispatcher: raise cmdexc.CommandError("No URL given, but -t/-b/-w is not " "set!") else: - urllist = [u for u in url.split('\n') if u.strip()] - if (len(urllist) > 1 and - any(not urlutils.is_url(u) and - urlutils.get_path_if_valid(u, check_exists=True) - is None for u in urllist)): - urllist = [url] - force_search = True - urls = [x for x in [self._parse_url(u, force_search=force_search) - for u in urllist] if x is not None] + urls = self._parse_url_input(url) for i, cur_url in enumerate(urls): if not window and i > 0: tab = False @@ -277,10 +268,13 @@ class CommandDispatcher: # We want to open a URL in the current tab, but none # exists yet. self._tabbed_browser.tabopen(cur_url) + else: + # Explicit count with a tab that doesn't exist. + return else: curtab.openurl(cur_url) - def _parse_url(self, url, force_search=False): + def _parse_url(self, url, *, force_search=False): """Parse a URL or quickmark or search query. Args: @@ -299,8 +293,27 @@ class CommandDispatcher: # We don't use cmdexc.CommandError here as this can be # called async from edit_url message.error(self._win_id, str(e)) - return + return None + def _parse_url_input(self, url): + """Parse a URL or newline-separated list of URLs. + + Args: + url: The URL or list to parse. + + Return: + A list of URLs that can be opened.""" + force_search = False + urllist = [u for u in url.split('\n') if u.strip()] + if (len(urllist) > 1 and not urlutils.is_url(urllist[0]) and + urlutils.get_path_if_valid(urllist[0], check_exists=True) + is None): + urllist = [url] + force_search = True + for cur_url in urllist: + parsed = self._parse_url(cur_url, force_search=force_search) + if parsed is not None: + yield parsed @cmdutils.register(instance='command-dispatcher', name='reload', scope='window') diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index 3762c4376..33bed1d1e 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -49,31 +49,29 @@ def _current_url(tabbed_browser): def replace_variables(win_id, arglist): """Utility function to replace variables like {url} in a list of args.""" + variables = { + '{url}': lambda: _current_url(tabbed_browser).toString( + QUrl.FullyEncoded | QUrl.RemovePassword), + '{url:pretty}': lambda: _current_url(tabbed_browser).toString( + QUrl.RemovePassword), + '{clipboard}': utils.get_clipboard, + '{primary}': lambda: utils.get_clipboard(selection=True), + } + values = {} args = [] tabbed_browser = objreg.get('tabbed-browser', scope='window', window=win_id) - if any('{url}' in arg for arg in arglist): - url = _current_url(tabbed_browser).toString(QUrl.FullyEncoded | - QUrl.RemovePassword) - if any('{url:pretty}' in arg for arg in arglist): - pretty_url = _current_url(tabbed_browser).toString(QUrl.RemovePassword) + try: - if any('{clipboard}' in arg for arg in arglist): - clipboard = utils.get_clipboard() - if any('{primary}' in arg for arg in arglist): - primary = utils.get_clipboard(selection=True) + for arg in arglist: + for var, func in variables.items(): + if var in arg: + if var not in values: + values[var] = func() + arg = arg.replace(var, values[var]) + args.append(arg) except utils.ClipboardEmptyError as e: raise cmdexc.CommandError(e) - for arg in arglist: - if '{url}' in arg: - arg = arg.replace('{url}', url) - if '{url:pretty}' in arg: - arg = arg.replace('{url:pretty}', pretty_url) - if '{clipboard}' in arg: - arg = arg.replace('{clipboard}', clipboard) - if '{primary}' in arg: - arg = arg.replace('{primary}', primary) - args.append(arg) return args From 7d0064ff861ae6030294dcda22e897c0bec594c3 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Tue, 9 Aug 2016 19:23:38 +0200 Subject: [PATCH 5/8] Fix docstrings --- qutebrowser/browser/commands.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 42458f0d3..f8dbc0413 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -283,7 +283,8 @@ class CommandDispatcher: interpreted as a URL or a path. Return: - A URL that can be opened.""" + A URL that can be opened. + """ try: return objreg.get('quickmark-manager').get(url) except urlmarks.Error: @@ -302,7 +303,8 @@ class CommandDispatcher: url: The URL or list to parse. Return: - A list of URLs that can be opened.""" + A list of URLs that can be opened. + """ force_search = False urllist = [u for u in url.split('\n') if u.strip()] if (len(urllist) > 1 and not urlutils.is_url(urllist[0]) and From 63b9b61e753458306b2581b4788e0da5f08c6de5 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Wed, 10 Aug 2016 12:33:01 +0200 Subject: [PATCH 6/8] Add ClipboardError superexception --- qutebrowser/browser/commands.py | 2 +- qutebrowser/commands/runners.py | 2 +- qutebrowser/misc/miscwidgets.py | 3 +-- qutebrowser/utils/utils.py | 9 +++++++-- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index f8dbc0413..37ce914ed 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -855,7 +855,7 @@ class CommandDispatcher: sel = False try: text = utils.get_clipboard(selection=sel) - except utils.ClipboardEmptyError as e: + except utils.ClipboardError as e: raise cmdexc.CommandError(e) text_urls = [u for u in text.split('\n') if u.strip()] if (len(text_urls) > 1 and not urlutils.is_url(text_urls[0]) and diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index 33bed1d1e..6a132598d 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -70,7 +70,7 @@ def replace_variables(win_id, arglist): values[var] = func() arg = arg.replace(var, values[var]) args.append(arg) - except utils.ClipboardEmptyError as e: + except utils.ClipboardError as e: raise cmdexc.CommandError(e) return args diff --git a/qutebrowser/misc/miscwidgets.py b/qutebrowser/misc/miscwidgets.py index 7e84300d2..ba21e541a 100644 --- a/qutebrowser/misc/miscwidgets.py +++ b/qutebrowser/misc/miscwidgets.py @@ -47,8 +47,7 @@ class MinimalLineEditMixin: if e.key() == Qt.Key_Insert and e.modifiers() == Qt.ShiftModifier: try: text = utils.get_clipboard(selection=True) - except (utils.SelectionUnsupportedError, - utils.ClipboardEmptyError): + except utils.ClipboardError: pass else: e.accept() diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 32696aacf..72d6f83e2 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -43,12 +43,17 @@ fake_clipboard = None log_clipboard = False -class SelectionUnsupportedError(Exception): +class ClipboardError(Exception): + + """Raised if the clipboard contents are unavailable for some reason.""" + + +class SelectionUnsupportedError(ClipboardError): """Raised if [gs]et_clipboard is used and selection=True is unsupported.""" -class ClipboardEmptyError(Exception): +class ClipboardEmptyError(ClipboardError): """Raised if get_clipboard is used and the clipboard is empty.""" From efa53ac25e7014f777c5f34eab92915a3cd3fbf6 Mon Sep 17 00:00:00 2001 From: Jan Verbeek Date: Wed, 10 Aug 2016 19:42:03 +0200 Subject: [PATCH 7/8] Add variable tests --- tests/end2end/features/misc.feature | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/end2end/features/misc.feature b/tests/end2end/features/misc.feature index 686f5a8b4..fa20d26ac 100644 --- a/tests/end2end/features/misc.feature +++ b/tests/end2end/features/misc.feature @@ -524,3 +524,16 @@ Feature: Various utility commands. Then the following tabs should be open: - data/hints/link_blank.html - data/hello.txt (active) + + ## Variables + + Scenario: {url} as part of an argument + When I open data/hello.txt + And I run :message-info foo{url} + Then the message "foohttp://localhost:*/hello.txt" should be shown + + Scenario: Multiple variables in an argument + When I open data/hello.txt + And I put "foo" into the clipboard + And I run :message-info {clipboard}bar{url} + Then the message "foobarhttp://localhost:*/hello.txt" should be shown From 3336766034f3db93c4b0cdd79c2221ab4f259858 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 10 Aug 2016 20:48:33 +0200 Subject: [PATCH 8/8] Update docs --- CHANGELOG.asciidoc | 8 ++++++++ README.asciidoc | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index dd72442df..22008ea54 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -30,6 +30,8 @@ Added (to report bugs which are difficult to reproduce). - New `hide-unmatched-rapid-hints` option to not hide hint unmatched hint labels in rapid mode. +- New `{clipboard}` and `{primary}` replacements for the commandline which + replace the `:paste` command. Changed ~~~~~~~ @@ -65,6 +67,12 @@ Changed - The `:buffer` completion now also filters using the first column (id). - `:undo` has been improved to reopen tabs at the position they were closed. +Deprecated +~~~~~~~~~~ + +- The `:paste` command got deprecated as `:open` with `{clipboard}` and + `{primary}` can be used instead. + Removed ~~~~~~~ diff --git a/README.asciidoc b/README.asciidoc index 06fbb74e7..8a0b1b30b 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -145,12 +145,12 @@ Contributors, sorted by the number of commits in descending order: * Antoni Boucher * Lamar Pavel * Bruno Oliveira +* Jan Verbeek * Alexander Cogneau * Marshall Lochbaum * Jakub Klinkovský * Felix Van der Jeugt * Martin Tournoij -* Jan Verbeek * Raphael Pierzina * Joel Torstensson * Patric Schmitz