diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 007b86fd9..a17ac2031 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -67,6 +67,7 @@ Changed - Completions for `:help` and `:bind` now also show hidden commands - The `:buffer` completion now also filters using the first column (id). - `:undo` has been improved to reopen tabs at the position they were closed. +- `:navigate` now takes a count for `up`/`increment`/`decrement`. Deprecated ~~~~~~~~~~ diff --git a/README.asciidoc b/README.asciidoc index 9d4b8d986..420c5cc21 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -183,6 +183,7 @@ Contributors, sorted by the number of commits in descending order: * skinnay * Zach-Button * Tomasz Kramkowski +* Peter Rice * Ismail S * Halfwit * David Vogt diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 4a34ba59a..1b93a2d65 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -481,6 +481,10 @@ This tries to automatically click on typical _Previous Page_ or _Next Page_ link * +*-b*+, +*--bg*+: Open in a background tab. * +*-w*+, +*--window*+: Open in a new window. +==== count +For `increment` and `decrement`, the number to change the URL by. For `up`, the number of levels to go up in the URL. + + [[open]] === open Syntax: +:open [*--implicit*] [*--bg*] [*--tab*] [*--window*] ['url']+ diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 58b1f4d01..e13651b23 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -484,7 +484,8 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') @cmdutils.argument('where', choices=['prev', 'next', 'up', 'increment', 'decrement']) - def navigate(self, where: str, tab=False, bg=False, window=False): + @cmdutils.argument('count', count=True) + def navigate(self, where: str, tab=False, bg=False, window=False, count=1): """Open typical prev/next links or navigate using the URL path. This tries to automatically click on typical _Previous Page_ or @@ -504,6 +505,8 @@ class CommandDispatcher: tab: Open in a new tab. bg: Open in a background tab. window: Open in a new window. + count: For `increment` and `decrement`, the number to change the URL + by. For `up`, the number of levels to go up in the URL. """ # save the pre-jump position in the special ' mark self.set_mark("'") @@ -528,7 +531,7 @@ class CommandDispatcher: handler(browsertab=widget, win_id=self._win_id, baseurl=url, tab=tab, background=bg, window=window) elif where in ['up', 'increment', 'decrement']: - new_url = handlers[where](url) + new_url = handlers[where](url, count) self._open(new_url, tab, bg, window) else: # pragma: no cover raise ValueError("Got called with invalid value {} for " diff --git a/qutebrowser/browser/navigate.py b/qutebrowser/browser/navigate.py index 86e7eeb77..12492e673 100644 --- a/qutebrowser/browser/navigate.py +++ b/qutebrowser/browser/navigate.py @@ -31,11 +31,12 @@ class Error(Exception): """Raised when the navigation can't be done.""" -def incdec(url, inc_or_dec): +def incdec(url, count, inc_or_dec): """Helper method for :navigate when `where' is increment/decrement. Args: url: The current url. + count: How much to increment or decrement by. inc_or_dec: Either 'increment' or 'decrement'. tab: Whether to open the link in a new tab. background: Open the link in a new background tab. @@ -43,23 +44,26 @@ def incdec(url, inc_or_dec): """ segments = set(config.get('general', 'url-incdec-segments')) try: - new_url = urlutils.incdec_number(url, inc_or_dec, segments=segments) + new_url = urlutils.incdec_number(url, inc_or_dec, count, + segments=segments) except urlutils.IncDecError as error: raise Error(error.msg) return new_url -def path_up(url): +def path_up(url, count): """Helper method for :navigate when `where' is up. Args: url: The current url. + count: The number of levels to go up in the url. """ path = url.path() if not path or path == '/': raise Error("Can't go up!") - new_path = posixpath.join(path, posixpath.pardir) - url.setPath(new_path) + for _i in range(0, min(count, path.count('/'))): + path = posixpath.join(path, posixpath.pardir) + url.setPath(path) return url diff --git a/qutebrowser/utils/urlutils.py b/qutebrowser/utils/urlutils.py index 2fe465aa4..7e1b8ea9e 100644 --- a/qutebrowser/utils/urlutils.py +++ b/qutebrowser/utils/urlutils.py @@ -499,7 +499,7 @@ class IncDecError(Exception): return '{}: {}'.format(self.msg, self.url.toString()) -def _get_incdec_value(match, incdec, url): +def _get_incdec_value(match, incdec, url, count): """Get an incremented/decremented URL based on a URL match.""" pre, zeroes, number, post = match.groups() # This should always succeed because we match \d+ @@ -507,9 +507,9 @@ def _get_incdec_value(match, incdec, url): if incdec == 'decrement': if val <= 0: raise IncDecError("Can't decrement {}!".format(val), url) - val -= 1 + val -= count elif incdec == 'increment': - val += 1 + val += count else: raise ValueError("Invalid value {} for indec!".format(incdec)) if zeroes: @@ -521,12 +521,13 @@ def _get_incdec_value(match, incdec, url): return ''.join([pre, zeroes, str(val), post]) -def incdec_number(url, incdec, segments=None): +def incdec_number(url, incdec, count=1, segments=None): """Find a number in the url and increment or decrement it. Args: url: The current url incdec: Either 'increment' or 'decrement' + count: The number to increment or decrement by segments: A set of URL segments to search. Valid segments are: 'host', 'path', 'query', 'anchor'. Default: {'path', 'query'} @@ -566,7 +567,7 @@ def incdec_number(url, incdec, segments=None): if not match: continue - setter(_get_incdec_value(match, incdec, url)) + setter(_get_incdec_value(match, incdec, url, count)) return url raise IncDecError("No number found in URL!", url) diff --git a/tests/end2end/features/navigate.feature b/tests/end2end/features/navigate.feature index c43ff1c18..1041fcdee 100644 --- a/tests/end2end/features/navigate.feature +++ b/tests/end2end/features/navigate.feature @@ -11,6 +11,11 @@ Feature: Using :navigate And I run :navigate up Then data/navigate should be loaded + Scenario: Navigating up by count + When I open data/navigate/sub/index.html + And I run :navigate up with count 2 + Then data/navigate should be loaded + # prev/next Scenario: Navigating to previous page @@ -60,6 +65,16 @@ Feature: Using :navigate And I run :navigate increment Then the error "No number found in URL!" should be shown + Scenario: Incrementing number in URL by count + When I open data/numbers/3.txt + And I run :navigate increment with count 3 + Then data/numbers/6.txt should be loaded + + Scenario: Decrementing number in URL by count + When I open data/numbers/8.txt + And I run :navigate decrement with count 5 + Then data/numbers/3.txt should be loaded + Scenario: Setting url-incdec-segments When I set general -> url-incdec-segments to anchor And I open data/numbers/1.txt diff --git a/tests/unit/utils/test_urlutils.py b/tests/unit/utils/test_urlutils.py index af0e62ed3..e036c4b9e 100644 --- a/tests/unit/utils/test_urlutils.py +++ b/tests/unit/utils/test_urlutils.py @@ -620,6 +620,33 @@ class TestIncDecNumber: base_url, incdec, segments={'host', 'path', 'query', 'anchor'}) assert new_url == expected_url + @pytest.mark.parametrize('incdec', ['increment', 'decrement']) + @pytest.mark.parametrize('value', [ + '{}foo', 'foo{}', 'foo{}bar', '42foo{}' + ]) + @pytest.mark.parametrize('url', [ + 'http://example.com:80/v1/path/{}/test', + 'http://example.com:80/v1/query_test?value={}', + 'http://example.com:80/v1/anchor_test#{}', + 'http://host_{}_test.com:80', + 'http://m4ny.c0m:80/number5/3very?where=yes#{}' + ]) + @pytest.mark.parametrize('count', [1, 5, 100]) + def test_incdec_number_count(self, incdec, value, url, count): + """Test incdec_number with valid URLs and a count.""" + base_value = value.format(20) + if incdec == 'increment': + expected_value = value.format(20 + count) + else: + expected_value = value.format(20 - count) + + base_url = QUrl(url.format(base_value)) + expected_url = QUrl(url.format(expected_value)) + new_url = urlutils.incdec_number( + base_url, incdec, count, + segments={'host', 'path', 'query', 'anchor'}) + assert new_url == expected_url + @pytest.mark.parametrize('number, expected, incdec', [ ('01', '02', 'increment'), ('09', '10', 'increment'),