From f5bb75a1860fe7a870ec82adca5e8343733f4271 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Mon, 11 Apr 2016 22:31:49 -0400 Subject: [PATCH 01/25] Fix docstring in scroll_px. --- qutebrowser/browser/commands.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 278380578..e7e1183dd 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -500,7 +500,7 @@ class CommandDispatcher: Args: dx: How much to scroll in x-direction. - dy: How much to scroll in x-direction. + dy: How much to scroll in y-direction. count: multiplier """ dx *= count From ddcae14ba4d3324fdc843500c6053aba653d10e0 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Mon, 11 Apr 2016 22:33:26 -0400 Subject: [PATCH 02/25] Implement set-mark and jump-mark. set-mark saves your current scroll position as mark . jump-mark jumps to the position previously set for mark . If is lowercase, it is local to the current tab. Each tab has its own set of lowercase marks. If is uppercase, it is global across tabs, and stores a url and a scroll position. Jumping to an uppercase mark navigates to that url, then scrolls to the saved position. Resolves #310. --- qutebrowser/browser/commands.py | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index e7e1183dd..f0973802e 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -62,11 +62,15 @@ class CommandDispatcher: _editor: The ExternalEditor object. _win_id: The window ID the CommandDispatcher is associated with. _tabbed_browser: The TabbedBrowser used. + _local_marks: Jump markers local to each tab + _global_marks: Jump markers used across tabs """ def __init__(self, win_id, tabbed_browser): self._win_id = win_id self._tabbed_browser = tabbed_browser + self._local_marks = {} + self._global_marks = {} def __repr__(self): return utils.get_repr(self) @@ -1877,3 +1881,52 @@ class CommandDispatcher: self.openurl, bg=bg, tab=tab, window=window, count=count)) ed.edit(url or self._current_url().toString()) + + @cmdutils.register(instance='command-dispatcher', scope='window') + def set_mark(self, key): + """Set a mark at the current scroll position in the current tab + + Args: + key: mark identifier; capital indicates a global mark + """ + y = self._current_widget().page().currentFrame().scrollPosition().y() + + if key.isupper(): + # this is a global mark, so store the position and url + # since we are already storing the scroll, strip the fragment as it + # might interfere with our scrolling + url = self._current_url().adjusted(QUrl.RemoveFragment) + self._global_marks[key] = y, url + else: + idx = self._tabbed_browser.currentIndex() + if idx not in self._local_marks: + self._local_marks[idx] = {} + self._local_marks[idx][key] = y + + @cmdutils.register(instance='command-dispatcher', scope='window') + def jump_mark(self, key): + """Jump to the mark named by `key` + + Args: + key: mark identifier; capital indicates a global mark + """ + idx = self._tabbed_browser.currentIndex() + + if key.isupper() and key in self._global_marks: + # y is a pixel position relative to the top of the page + y, url = self._global_marks[key] + + def callback(ok): + if ok: + self._tabbed_browser.cur_load_finished.disconnect(callback) + self.scroll('top') + self.scroll_px(0, y) + + self.openurl(url.toString()) + self._tabbed_browser.cur_load_finished.connect(callback) + + elif idx in self._local_marks and key in self._local_marks[idx]: + self.scroll('top') + self.scroll_px(0, self._local_marks[idx][key]) + else: + message.error(self._win_id, "Mark {} is not set".format(key)) From 9062f5925e7afad6051ff1e31b7226bab7daa206 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Mon, 11 Apr 2016 22:36:37 -0400 Subject: [PATCH 03/25] Set the ' mark on a jump. Automatically set the special "'" mark when jumping. jump-mark "'" will jump to the last position before the previous jump. A jump could be navigating via a link, jumping to another mark, or scrolling by percentage (e.g. gg or G). --- qutebrowser/browser/commands.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index f0973802e..55c351f33 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -476,6 +476,9 @@ class CommandDispatcher: bg: Open in a background tab. window: Open in a new window. """ + # save the pre-jump position in the special ' mark + self.set_mark("'") + cmdutils.check_exclusive((tab, bg, window), 'tbw') widget = self._current_widget() frame = widget.page().currentFrame() @@ -586,6 +589,9 @@ class CommandDispatcher: horizontal: Scroll horizontally instead of vertically. count: Percentage to scroll. """ + # save the pre-jump position in the special ' mark + self.set_mark("'") + if perc is None and count is None: perc = 100 elif perc is None: @@ -1889,7 +1895,7 @@ class CommandDispatcher: Args: key: mark identifier; capital indicates a global mark """ - y = self._current_widget().page().currentFrame().scrollPosition().y() + y = self._current_y_px() if key.isupper(): # this is a global mark, so store the position and url @@ -1919,14 +1925,28 @@ class CommandDispatcher: def callback(ok): if ok: self._tabbed_browser.cur_load_finished.disconnect(callback) - self.scroll('top') - self.scroll_px(0, y) + self._scroll_px_absolute(y) self.openurl(url.toString()) self._tabbed_browser.cur_load_finished.connect(callback) elif idx in self._local_marks and key in self._local_marks[idx]: - self.scroll('top') - self.scroll_px(0, self._local_marks[idx][key]) + y = self._local_marks[idx][key] + + # save the pre-jump position in the special ' mark + # this has to happen after we read the mark, otherwise jump_mark "'" + # would just jump to the current position every time + self.set_mark("'") + + self._scroll_px_absolute(y) else: message.error(self._win_id, "Mark {} is not set".format(key)) + + def _current_y_px(self): + """Return the current y scroll position in pixels from the top""" + return self._current_widget().page().currentFrame().scrollPosition().y() + + def _scroll_px_absolute(self, y): + """Scroll to the position y pixels from the top of the page""" + self.scroll('top') + self.scroll_px(0, y) From 7bfea773db46a87835741d536b0edc39008c9547 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Mon, 11 Apr 2016 23:18:09 -0400 Subject: [PATCH 04/25] Add set_mark and jump_mark modes. These modes use a custom handler to pass whatever the next keypress is to either set_mark or jump_mark. --- qutebrowser/keyinput/modeman.py | 2 ++ qutebrowser/keyinput/modeparsers.py | 47 +++++++++++++++++++++++++++-- qutebrowser/utils/usertypes.py | 3 +- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index fb948ec18..11156e4e4 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -78,6 +78,8 @@ def init(win_id, parent): warn=False), KM.yesno: modeparsers.PromptKeyParser(win_id, modeman), KM.caret: modeparsers.CaretKeyParser(win_id, modeman), + KM.set_mark: modeparsers.MarkKeyParser(win_id, KM.set_mark, modeman), + KM.jump_mark: modeparsers.MarkKeyParser(win_id, KM.jump_mark, modeman), } objreg.register('keyparsers', keyparsers, scope='window', window=win_id) modeman.destroyed.connect( diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index 7ad622114..73abf2110 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -25,9 +25,9 @@ Module attributes: from PyQt5.QtCore import pyqtSlot, Qt -from qutebrowser.utils import message +from qutebrowser.utils import utils, message from qutebrowser.config import config -from qutebrowser.keyinput import keyparser +from qutebrowser.keyinput import keyparser, modeman from qutebrowser.utils import usertypes, log, objreg, utils @@ -230,3 +230,46 @@ class CaretKeyParser(keyparser.CommandKeyParser): super().__init__(win_id, parent, supports_count=True, supports_chains=True) self.read_config('caret') + +class MarkKeyParser(keyparser.CommandKeyParser): + + """KeyParser for set_mark and jump_mark mode. + + Attributes: + _mode: Either KeyMode.set_mark or KeyMode.jump_mark. + """ + + def __init__(self, win_id, mode, parent=None): + super().__init__(win_id, parent, supports_count=False, + supports_chains=False) + self._mode = mode + + def handle(self, e): + """Override handle to always match the next key and create a mark + + Args: + e: the KeyPressEvent from Qt. + + Return: + True if event has been handled, False otherwise. + """ + + if utils.keyevent_to_string(e) is None: + # this is a modifier key, let it pass and keep going + return True; + + if not e.text().isalpha() and e.text() != "'": + # only valid mark names are [a-zA-Z'] + message.error(self._win_id, e.text() + " isn't a valid mark") + return True + + if self._mode == usertypes.KeyMode.set_mark: + self._commandrunner.run('set-mark "{}"'.format(e.text())) + elif self._mode == usertypes.KeyMode.jump_mark: + self._commandrunner.run('jump-mark "{}"'.format(e.text())) + else: + raise ValueError("{} is not a valid mark mode".format(mode)) + + modeman.leave(self._win_id, self._mode, "valid mark key") + + return True diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 5e29ee535..0af42b291 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -231,7 +231,8 @@ ClickTarget = enum('ClickTarget', ['normal', 'tab', 'tab_bg', 'window']) # Key input modes KeyMode = enum('KeyMode', ['normal', 'hint', 'command', 'yesno', 'prompt', - 'insert', 'passthrough', 'caret']) + 'insert', 'passthrough', 'caret', 'set_mark', + 'jump_mark']) # Available command completions From 4037719a7813bb41bb3c4a5f2b53db1114fe8e8b Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Mon, 11 Apr 2016 23:18:49 -0400 Subject: [PATCH 05/25] Add default bindings for set-mark and jump-mark. Bind set-mark to ` and jump-mark to '. --- qutebrowser/config/configdata.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qutebrowser/config/configdata.py b/qutebrowser/config/configdata.py index 10dad1ce9..a77e0571b 100644 --- a/qutebrowser/config/configdata.py +++ b/qutebrowser/config/configdata.py @@ -1437,6 +1437,8 @@ KEY_DATA = collections.OrderedDict([ ('search-prev', ['N']), ('enter-mode insert', ['i']), ('enter-mode caret', ['v']), + ('enter-mode set_mark', ['`']), + ('enter-mode jump_mark', ["'"]), ('yank', ['yy']), ('yank -s', ['yY']), ('yank -t', ['yt']), From 84eb30bc9a6fc1520a8b202795adbe5af784c024 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 12 Apr 2016 21:37:12 -0400 Subject: [PATCH 06/25] Marks are local to urls, not tabs. Rather than binding each set of local marks to a tab, bind them to a url. Strip the fragment from the url, as two pages that differ only in fragment are likely the same page. --- qutebrowser/browser/commands.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 55c351f33..338c8cb13 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -62,8 +62,8 @@ class CommandDispatcher: _editor: The ExternalEditor object. _win_id: The window ID the CommandDispatcher is associated with. _tabbed_browser: The TabbedBrowser used. - _local_marks: Jump markers local to each tab - _global_marks: Jump markers used across tabs + _local_marks: Jump markers local to each page + _global_marks: Jump markers used across all pages """ def __init__(self, win_id, tabbed_browser): @@ -1895,19 +1895,19 @@ class CommandDispatcher: Args: key: mark identifier; capital indicates a global mark """ + # consider urls that differ only in fragment to be identical + url = self._current_url().adjusted(QUrl.RemoveFragment) y = self._current_y_px() if key.isupper(): # this is a global mark, so store the position and url # since we are already storing the scroll, strip the fragment as it # might interfere with our scrolling - url = self._current_url().adjusted(QUrl.RemoveFragment) self._global_marks[key] = y, url else: - idx = self._tabbed_browser.currentIndex() - if idx not in self._local_marks: - self._local_marks[idx] = {} - self._local_marks[idx][key] = y + if url not in self._local_marks: + self._local_marks[url] = {} + self._local_marks[url][key] = y @cmdutils.register(instance='command-dispatcher', scope='window') def jump_mark(self, key): @@ -1916,7 +1916,8 @@ class CommandDispatcher: Args: key: mark identifier; capital indicates a global mark """ - idx = self._tabbed_browser.currentIndex() + # consider urls that differ only in fragment to be identical + cur_url = self._current_url().adjusted(QUrl.RemoveFragment) if key.isupper() and key in self._global_marks: # y is a pixel position relative to the top of the page @@ -1929,9 +1930,8 @@ class CommandDispatcher: self.openurl(url.toString()) self._tabbed_browser.cur_load_finished.connect(callback) - - elif idx in self._local_marks and key in self._local_marks[idx]: - y = self._local_marks[idx][key] + elif cur_url in self._local_marks and key in self._local_marks[cur_url]: + y = self._local_marks[cur_url][key] # save the pre-jump position in the special ' mark # this has to happen after we read the mark, otherwise jump_mark "'" From c7b830d69dbc6da443ba4a21c077a3b781bdeba0 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 12 Apr 2016 22:00:29 -0400 Subject: [PATCH 07/25] Fix up mistakes caught by pylint. --- qutebrowser/browser/commands.py | 21 +++++++++++---------- qutebrowser/keyinput/modeparsers.py | 9 +++++---- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 338c8cb13..4543448b7 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1890,7 +1890,7 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') def set_mark(self, key): - """Set a mark at the current scroll position in the current tab + """Set a mark at the current scroll position in the current tab. Args: key: mark identifier; capital indicates a global mark @@ -1911,13 +1911,13 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher', scope='window') def jump_mark(self, key): - """Jump to the mark named by `key` + """Jump to the mark named by `key`. Args: key: mark identifier; capital indicates a global mark """ # consider urls that differ only in fragment to be identical - cur_url = self._current_url().adjusted(QUrl.RemoveFragment) + urlkey = self._current_url().adjusted(QUrl.RemoveFragment) if key.isupper() and key in self._global_marks: # y is a pixel position relative to the top of the page @@ -1930,12 +1930,12 @@ class CommandDispatcher: self.openurl(url.toString()) self._tabbed_browser.cur_load_finished.connect(callback) - elif cur_url in self._local_marks and key in self._local_marks[cur_url]: - y = self._local_marks[cur_url][key] + elif urlkey in self._local_marks and key in self._local_marks[urlkey]: + y = self._local_marks[urlkey][key] # save the pre-jump position in the special ' mark - # this has to happen after we read the mark, otherwise jump_mark "'" - # would just jump to the current position every time + # this has to happen after we read the mark, otherwise jump_mark + # "'" would just jump to the current position every time self.set_mark("'") self._scroll_px_absolute(y) @@ -1943,10 +1943,11 @@ class CommandDispatcher: message.error(self._win_id, "Mark {} is not set".format(key)) def _current_y_px(self): - """Return the current y scroll position in pixels from the top""" - return self._current_widget().page().currentFrame().scrollPosition().y() + """Return the current y scroll position in pixels from the top.""" + frame = self._current_widget().page().currentFrame() + return frame.scrollPosition().y() def _scroll_px_absolute(self, y): - """Scroll to the position y pixels from the top of the page""" + """Scroll to the position y pixels from the top of the page.""" self.scroll('top') self.scroll_px(0, y) diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index 73abf2110..2a6fdff95 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -25,7 +25,7 @@ Module attributes: from PyQt5.QtCore import pyqtSlot, Qt -from qutebrowser.utils import utils, message +from qutebrowser.utils import message from qutebrowser.config import config from qutebrowser.keyinput import keyparser, modeman from qutebrowser.utils import usertypes, log, objreg, utils @@ -231,6 +231,7 @@ class CaretKeyParser(keyparser.CommandKeyParser): supports_chains=True) self.read_config('caret') + class MarkKeyParser(keyparser.CommandKeyParser): """KeyParser for set_mark and jump_mark mode. @@ -245,7 +246,7 @@ class MarkKeyParser(keyparser.CommandKeyParser): self._mode = mode def handle(self, e): - """Override handle to always match the next key and create a mark + """Override handle to always match the next key and create a mark. Args: e: the KeyPressEvent from Qt. @@ -256,7 +257,7 @@ class MarkKeyParser(keyparser.CommandKeyParser): if utils.keyevent_to_string(e) is None: # this is a modifier key, let it pass and keep going - return True; + return True if not e.text().isalpha() and e.text() != "'": # only valid mark names are [a-zA-Z'] @@ -268,7 +269,7 @@ class MarkKeyParser(keyparser.CommandKeyParser): elif self._mode == usertypes.KeyMode.jump_mark: self._commandrunner.run('jump-mark "{}"'.format(e.text())) else: - raise ValueError("{} is not a valid mark mode".format(mode)) + raise ValueError("{} is not a valid mark mode".format(self._mode)) modeman.leave(self._win_id, self._mode, "valid mark key") From dc246772e7b014ca9dee43b93f92294db0eb2545 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Wed, 13 Apr 2016 20:55:33 -0400 Subject: [PATCH 08/25] Add integration tests for set-mark and jump-mark. --- tests/integration/data/marks.html | 116 +++++++++++++++++++++++ tests/integration/features/marks.feature | 65 +++++++++++++ tests/integration/features/test_marks.py | 33 +++++++ 3 files changed, 214 insertions(+) create mode 100644 tests/integration/data/marks.html create mode 100644 tests/integration/features/marks.feature create mode 100644 tests/integration/features/test_marks.py diff --git a/tests/integration/data/marks.html b/tests/integration/data/marks.html new file mode 100644 index 000000000..60cb40636 --- /dev/null +++ b/tests/integration/data/marks.html @@ -0,0 +1,116 @@ + + + + + Marks I + + +

Top

+ Top + Bottom +
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+11
+12
+13
+14
+15
+16
+17
+18
+19
+20
+21
+22
+23
+24
+25
+26
+27
+28
+29
+30
+31
+32
+33
+34
+35
+36
+37
+38
+39
+40
+41
+42
+43
+44
+45
+46
+47
+48
+49
+50
+51
+52
+53
+54
+55
+56
+57
+58
+59
+60
+61
+62
+63
+64
+65
+66
+67
+68
+69
+70
+71
+72
+73
+74
+75
+76
+77
+78
+79
+80
+81
+82
+83
+84
+85
+86
+87
+88
+89
+90
+91
+92
+93
+94
+95
+96
+97
+98
+99
+This is a very long line so this page can be scrolled horizontally. Did you think this line would end here already? Nah, it does not. But now it will. Or will it? I think it's not long enough yet.
+        
+

Bottom

+ + diff --git a/tests/integration/features/marks.feature b/tests/integration/features/marks.feature new file mode 100644 index 000000000..74694fd20 --- /dev/null +++ b/tests/integration/features/marks.feature @@ -0,0 +1,65 @@ +Feature: Setting positional marks + + Background: + Given I open data/marks.html + And I run :tab-only + + ## :set-mark, :jump-mark + + Scenario: Setting and jumping to a local mark + When I run :scroll-px 0 10 + And I run :set-mark 'a' + And I run :scroll-px 0 20 + And I run :jump-mark 'a' + Then the page should be scrolled to 10 + + Scenario: Jumping back jumping to a particular percentage + When I run :scroll-px 0 20 + And I run :scroll-perc 100 + And I run :jump-mark "'" + Then the page should be scrolled to 20 + + Scenario: Setting the same local mark on another page + When I run :scroll-px 0 10 + And I run :set-mark 'a' + And I open data/marks.html + And I run :scroll-px 0 20 + And I run :set-mark 'a' + And I run :jump-mark 'a' + Then the page should be scrolled to 20 + + Scenario: Jumping to a local mark after returning to a page + When I run :scroll-px 0 10 + And I run :set-mark 'a' + And I open data/numbers/1.txt + And I run :set-mark 'a' + And I open data/marks.html + And I run :jump-mark 'a' + Then the page should be scrolled to 10 + + Scenario: Setting and jumping to a global mark + When I run :scroll-px 0 20 + And I run :set-mark 'A' + And I open data/numbers/1.txt + And I run :jump-mark 'A' + Then data/marks.html should be loaded + And the page should be scrolled to 20 + + Scenario: Jumping to an unset mark + When I run :jump-mark 'b' + Then the error "Mark b is not set" should be shown + + Scenario: Jumping to a local mark that was set on another page + When I run :set-mark 'b' + And I open data/numbers/1.txt + And I run :jump-mark 'b' + Then the error "Mark b is not set" should be shown + + Scenario: Jumping to a local mark after changing fragments + When I open data/marks.html#top + And I run :scroll 'top' + And I run :scroll-px 0 10 + And I run :set-mark 'a' + When I open data/marks.html#bottom + And I run :jump-mark 'a' + Then the page should be scrolled to 10 diff --git a/tests/integration/features/test_marks.py b/tests/integration/features/test_marks.py new file mode 100644 index 000000000..3553ca6f3 --- /dev/null +++ b/tests/integration/features/test_marks.py @@ -0,0 +1,33 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2016 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see . + +import pytest_bdd as bdd +bdd.scenarios('marks.feature') + + +def _get_scroll_y(quteproc): + data = quteproc.get_session() + pos = data['windows'][0]['tabs'][0]['history'][-1]['scroll-pos'] + return pos['y'] + + +@bdd.then(bdd.parsers.re(r"the page should be scrolled to " + r"(?P\d+)")) +def check_y(quteproc, y): + assert int(y) == _get_scroll_y(quteproc) From 540f4af2259e0d1cb22f98c1a5eed203ea72a944 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Wed, 13 Apr 2016 23:14:53 -0400 Subject: [PATCH 09/25] Set the ' mark after following a link. This moves mark storage from CommandDispatcher to TabbedBrowser, so it can also be accessed by the HintManager. --- qutebrowser/browser/commands.py | 52 +++++++----------------- qutebrowser/browser/hints.py | 4 ++ qutebrowser/mainwindow/tabbedbrowser.py | 47 +++++++++++++++++++++ tests/integration/features/marks.feature | 6 +++ 4 files changed, 71 insertions(+), 38 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 4543448b7..dd80c30b0 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -46,6 +46,7 @@ from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils, from qutebrowser.utils.usertypes import KeyMode from qutebrowser.misc import editor, guiprocess from qutebrowser.completion.models import instances, sortfilter +from qutebrowser.mainwindow.tabbedbrowser import MarkNotSetError class CommandDispatcher: @@ -62,15 +63,11 @@ class CommandDispatcher: _editor: The ExternalEditor object. _win_id: The window ID the CommandDispatcher is associated with. _tabbed_browser: The TabbedBrowser used. - _local_marks: Jump markers local to each page - _global_marks: Jump markers used across all pages """ def __init__(self, win_id, tabbed_browser): self._win_id = win_id self._tabbed_browser = tabbed_browser - self._local_marks = {} - self._global_marks = {} def __repr__(self): return utils.get_repr(self) @@ -1896,18 +1893,7 @@ class CommandDispatcher: key: mark identifier; capital indicates a global mark """ # consider urls that differ only in fragment to be identical - url = self._current_url().adjusted(QUrl.RemoveFragment) - y = self._current_y_px() - - if key.isupper(): - # this is a global mark, so store the position and url - # since we are already storing the scroll, strip the fragment as it - # might interfere with our scrolling - self._global_marks[key] = y, url - else: - if url not in self._local_marks: - self._local_marks[url] = {} - self._local_marks[url][key] = y + self._tabbed_browser.set_mark(key) @cmdutils.register(instance='command-dispatcher', scope='window') def jump_mark(self, key): @@ -1916,13 +1902,19 @@ class CommandDispatcher: Args: key: mark identifier; capital indicates a global mark """ - # consider urls that differ only in fragment to be identical - urlkey = self._current_url().adjusted(QUrl.RemoveFragment) - - if key.isupper() and key in self._global_marks: - # y is a pixel position relative to the top of the page - y, url = self._global_marks[key] + try: + y, url = self._tabbed_browser.get_mark(key) + except MarkNotSetError as e: + message.error(self._win_id, str(e)) + return + if url is None: + # save the pre-jump position in the special ' mark + # this has to happen after we read the mark, otherwise jump_mark + # "'" would just jump to the current position every time + self.set_mark("'") + self._scroll_px_absolute(y) + else: def callback(ok): if ok: self._tabbed_browser.cur_load_finished.disconnect(callback) @@ -1930,22 +1922,6 @@ class CommandDispatcher: self.openurl(url.toString()) self._tabbed_browser.cur_load_finished.connect(callback) - elif urlkey in self._local_marks and key in self._local_marks[urlkey]: - y = self._local_marks[urlkey][key] - - # save the pre-jump position in the special ' mark - # this has to happen after we read the mark, otherwise jump_mark - # "'" would just jump to the current position every time - self.set_mark("'") - - self._scroll_px_absolute(y) - else: - message.error(self._win_id, "Mark {} is not set".format(key)) - - def _current_y_px(self): - """Return the current y scroll position in pixels from the top.""" - frame = self._current_widget().page().currentFrame() - return frame.scrollPosition().y() def _scroll_px_absolute(self, y): """Scroll to the position y pixels from the top of the page.""" diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 3b71c0266..c1ba62ee1 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -921,6 +921,10 @@ class HintManager(QObject): immediately=True) return if self._context.target in elem_handlers: + # Set the pre-jump mark ', so we can jump back here after following + tabbed_browser = objreg.get('tabbed-browser', scope='window', + window=self._win_id) + tabbed_browser.set_mark("'") handler = functools.partial( elem_handlers[self._context.target], elem, self._context) elif self._context.target in url_handlers: diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 7c6c4e9c8..d9efe6824 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -41,6 +41,11 @@ class TabDeletedError(Exception): """Exception raised when _tab_index is called for a deleted tab.""" +class MarkNotSetError(Exception): + + """Exception raised when _tab_index is called for a deleted tab.""" + + class TabbedBrowser(tabwidget.TabWidget): """A TabWidget with QWebViews inside. @@ -64,6 +69,8 @@ class TabbedBrowser(tabwidget.TabWidget): _tab_insert_idx_right: Same as above, for 'right'. _undo_stack: List of UndoEntry namedtuples of closed tabs. shutting_down: Whether we're currently shutting down. + _local_marks: Jump markers local to each page + _global_marks: Jump markers used across all pages Signals: cur_progress: Progress of the current tab changed (loadProgress). @@ -114,6 +121,8 @@ class TabbedBrowser(tabwidget.TabWidget): self._now_focused = None self.search_text = None self.search_flags = 0 + self._local_marks = {} + self._global_marks = {} objreg.get('config').changed.connect(self.update_favicons) objreg.get('config').changed.connect(self.update_window_title) objreg.get('config').changed.connect(self.update_tab_titles) @@ -637,3 +646,41 @@ class TabbedBrowser(tabwidget.TabWidget): self._now_focused.wheelEvent(e) else: e.ignore() + + def set_mark(self, key): + """Set a mark at the current scroll position in the current tab. + + Args: + key: mark identifier; capital indicates a global mark + """ + # strip the fragment as it may interfere with scrolling + url = self.current_url().adjusted(QUrl.RemoveFragment) + frame = self.currentWidget().page().currentFrame() + y = frame.scrollPosition().y() + + if key.isupper(): + self._global_marks[key] = y, url + else: + if url not in self._local_marks: + self._local_marks[url] = {} + self._local_marks[url][key] = y + + def get_mark(self, key): + """Retrieve the mark named by `key` as (y, url). + + For a local mark, url is None + Returns None if the mark is not set. + + Args: + key: mark identifier; capital indicates a global mark + """ + # consider urls that differ only in fragment to be identical + urlkey = self.current_url().adjusted(QUrl.RemoveFragment) + + if key.isupper() and key in self._global_marks: + # y is a pixel position relative to the top of the page + return self._global_marks[key] + elif urlkey in self._local_marks and key in self._local_marks[urlkey]: + return self._local_marks[urlkey][key], None + else: + raise MarkNotSetError("Mark {} is not set".format(key)) diff --git a/tests/integration/features/marks.feature b/tests/integration/features/marks.feature index 74694fd20..5b18a6d05 100644 --- a/tests/integration/features/marks.feature +++ b/tests/integration/features/marks.feature @@ -63,3 +63,9 @@ Feature: Setting positional marks When I open data/marks.html#bottom And I run :jump-mark 'a' Then the page should be scrolled to 10 + + Scenario: Jumping back after following a link + When I run :hint links normal + And I run :follow-hint s + And I run :jump-mark "'" + Then the page should be scrolled to 0 From e684cfa03f2caaad765f3b11dfba1529de972ec7 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Thu, 14 Apr 2016 07:49:41 -0400 Subject: [PATCH 10/25] Trivial fixes for marks based on code review. - Fix a docstring copy-paste - Add own name/copyright date to new file - Simplify a bdd expression (no need for regex) - Scroll to a pixel position in a single operation --- qutebrowser/browser/commands.py | 7 +++---- qutebrowser/mainwindow/tabbedbrowser.py | 2 +- tests/integration/features/test_marks.py | 6 +++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index dd80c30b0..c940e6c38 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -29,7 +29,7 @@ import xml.etree.ElementTree from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWidgets import QApplication, QTabBar -from PyQt5.QtCore import Qt, QUrl, QEvent +from PyQt5.QtCore import Qt, QUrl, QEvent, QPoint from PyQt5.QtGui import QKeyEvent from PyQt5.QtPrintSupport import QPrintDialog, QPrintPreviewDialog from PyQt5.QtWebKitWidgets import QWebPage @@ -1892,7 +1892,6 @@ class CommandDispatcher: Args: key: mark identifier; capital indicates a global mark """ - # consider urls that differ only in fragment to be identical self._tabbed_browser.set_mark(key) @cmdutils.register(instance='command-dispatcher', scope='window') @@ -1925,5 +1924,5 @@ class CommandDispatcher: def _scroll_px_absolute(self, y): """Scroll to the position y pixels from the top of the page.""" - self.scroll('top') - self.scroll_px(0, y) + frame = self._current_widget().page().currentFrame() + frame.setScrollPosition(QPoint(0, y)) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index d9efe6824..3209632d6 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -43,7 +43,7 @@ class TabDeletedError(Exception): class MarkNotSetError(Exception): - """Exception raised when _tab_index is called for a deleted tab.""" + """Exception raised when accessing a tab that is not set.""" class TabbedBrowser(tabwidget.TabWidget): diff --git a/tests/integration/features/test_marks.py b/tests/integration/features/test_marks.py index 3553ca6f3..fd61d9489 100644 --- a/tests/integration/features/test_marks.py +++ b/tests/integration/features/test_marks.py @@ -1,6 +1,6 @@ # vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: -# Copyright 2014-2016 Florian Bruhin (The Compiler) +# Copyright 2016 Ryan Roden-Corrent (rcorre) # # This file is part of qutebrowser. # @@ -27,7 +27,7 @@ def _get_scroll_y(quteproc): return pos['y'] -@bdd.then(bdd.parsers.re(r"the page should be scrolled to " - r"(?P\d+)")) +@bdd.then(bdd.parsers.parse("the page should be scrolled to " + "{y}")) def check_y(quteproc, y): assert int(y) == _get_scroll_y(quteproc) From 49b2f6e96780210e12ac99463fcc47fce690322e Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Thu, 14 Apr 2016 08:39:30 -0400 Subject: [PATCH 11/25] Move jump_mark logic to TabbedBrowser. This simplifies the MarkKeyParser by removing its dependency on the commandrunner. It also removes the need for a new exception type. --- qutebrowser/browser/commands.py | 29 ++---------------- qutebrowser/keyinput/modeparsers.py | 15 ++++++---- qutebrowser/mainwindow/tabbedbrowser.py | 40 +++++++++++++++---------- 3 files changed, 36 insertions(+), 48 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index c940e6c38..fd2c61963 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -29,7 +29,7 @@ import xml.etree.ElementTree from PyQt5.QtWebKit import QWebSettings from PyQt5.QtWidgets import QApplication, QTabBar -from PyQt5.QtCore import Qt, QUrl, QEvent, QPoint +from PyQt5.QtCore import Qt, QUrl, QEvent from PyQt5.QtGui import QKeyEvent from PyQt5.QtPrintSupport import QPrintDialog, QPrintPreviewDialog from PyQt5.QtWebKitWidgets import QWebPage @@ -46,7 +46,6 @@ from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils, from qutebrowser.utils.usertypes import KeyMode from qutebrowser.misc import editor, guiprocess from qutebrowser.completion.models import instances, sortfilter -from qutebrowser.mainwindow.tabbedbrowser import MarkNotSetError class CommandDispatcher: @@ -1901,28 +1900,4 @@ class CommandDispatcher: Args: key: mark identifier; capital indicates a global mark """ - try: - y, url = self._tabbed_browser.get_mark(key) - except MarkNotSetError as e: - message.error(self._win_id, str(e)) - return - - if url is None: - # save the pre-jump position in the special ' mark - # this has to happen after we read the mark, otherwise jump_mark - # "'" would just jump to the current position every time - self.set_mark("'") - self._scroll_px_absolute(y) - else: - def callback(ok): - if ok: - self._tabbed_browser.cur_load_finished.disconnect(callback) - self._scroll_px_absolute(y) - - self.openurl(url.toString()) - self._tabbed_browser.cur_load_finished.connect(callback) - - def _scroll_px_absolute(self, y): - """Scroll to the position y pixels from the top of the page.""" - frame = self._current_widget().page().currentFrame() - frame.setScrollPosition(QPoint(0, y)) + self._tabbed_browser.jump_mark(key) diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index 2a6fdff95..745c848e4 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -232,7 +232,7 @@ class CaretKeyParser(keyparser.CommandKeyParser): self.read_config('caret') -class MarkKeyParser(keyparser.CommandKeyParser): +class MarkKeyParser(keyparser.BaseKeyParser): """KeyParser for set_mark and jump_mark mode. @@ -259,15 +259,20 @@ class MarkKeyParser(keyparser.CommandKeyParser): # this is a modifier key, let it pass and keep going return True - if not e.text().isalpha() and e.text() != "'": + key = e.text() + + if not key.isalpha() and key != "'": # only valid mark names are [a-zA-Z'] - message.error(self._win_id, e.text() + " isn't a valid mark") + message.error(self._win_id, key + " isn't a valid mark") return True + tabbed_browser = objreg.get('tabbed-browser', scope='window', + window=self._win_id) + if self._mode == usertypes.KeyMode.set_mark: - self._commandrunner.run('set-mark "{}"'.format(e.text())) + tabbed_browser.set_mark(key) elif self._mode == usertypes.KeyMode.jump_mark: - self._commandrunner.run('jump-mark "{}"'.format(e.text())) + tabbed_browser.jump_mark(key) else: raise ValueError("{} is not a valid mark mode".format(self._mode)) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 3209632d6..ddc3c61ab 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -23,14 +23,15 @@ import functools import collections from PyQt5.QtWidgets import QSizePolicy -from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl, QPoint from PyQt5.QtGui import QIcon from qutebrowser.config import config from qutebrowser.keyinput import modeman from qutebrowser.mainwindow import tabwidget from qutebrowser.browser import signalfilter, webview -from qutebrowser.utils import log, usertypes, utils, qtutils, objreg, urlutils +from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg, urlutils, + message) UndoEntry = collections.namedtuple('UndoEntry', ['url', 'history']) @@ -41,11 +42,6 @@ class TabDeletedError(Exception): """Exception raised when _tab_index is called for a deleted tab.""" -class MarkNotSetError(Exception): - - """Exception raised when accessing a tab that is not set.""" - - class TabbedBrowser(tabwidget.TabWidget): """A TabWidget with QWebViews inside. @@ -665,22 +661,34 @@ class TabbedBrowser(tabwidget.TabWidget): self._local_marks[url] = {} self._local_marks[url][key] = y - def get_mark(self, key): - """Retrieve the mark named by `key` as (y, url). - - For a local mark, url is None - Returns None if the mark is not set. + def jump_mark(self, key): + """Jump to the mark named by `key`. Args: key: mark identifier; capital indicates a global mark """ # consider urls that differ only in fragment to be identical urlkey = self.current_url().adjusted(QUrl.RemoveFragment) + frame = self.currentWidget().page().currentFrame() if key.isupper() and key in self._global_marks: - # y is a pixel position relative to the top of the page - return self._global_marks[key] + y, url = self._global_marks[key] + + def callback(ok): + if ok: + self.cur_load_finished.disconnect(callback) + frame.setScrollPosition(QPoint(0, y)) + + self.openurl(url, newtab=False) + self.cur_load_finished.connect(callback) elif urlkey in self._local_marks and key in self._local_marks[urlkey]: - return self._local_marks[urlkey][key], None + y = self._local_marks[urlkey][key] + + # save the pre-jump position in the special ' mark + # this has to happen after we read the mark, otherwise jump_mark + # "'" would just jump to the current position every time + self.set_mark("'") + + frame.setScrollPosition(QPoint(0, y)) else: - raise MarkNotSetError("Mark {} is not set".format(key)) + message.error(self._win_id, "Mark {} is not set".format(key)) From f4b957374405bb5fcb067e4b42e7447a946d7af2 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Thu, 14 Apr 2016 17:29:43 -0400 Subject: [PATCH 12/25] Remove cyclic dependency from MarkKeyParser. Rather than calling modeman.leave directly, modeman hooks into a request_leave signal that is fired when MarkKeyParser wants to exit mark mode. --- qutebrowser/keyinput/basekeyparser.py | 4 ++++ qutebrowser/keyinput/modeman.py | 1 + qutebrowser/keyinput/modeparsers.py | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index 5402bce77..f2b87eea6 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -67,9 +67,13 @@ class BaseKeyParser(QObject): Signals: keystring_updated: Emitted when the keystring is updated. arg: New keystring. + request_leave: Emitted to request leaving a mode. + arg 0: Mode to leave. + arg 1: Reason for leaving. """ keystring_updated = pyqtSignal(str) + request_leave = pyqtSignal(usertypes.KeyMode, str) do_log = True passthrough = False diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 11156e4e4..3a352ba19 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -225,6 +225,7 @@ class ModeManager(QObject): assert isinstance(mode, usertypes.KeyMode) assert parser is not None self._parsers[mode] = parser + parser.request_leave.connect(self.leave) def enter(self, mode, reason=None, only_if_normal=False): """Enter a new mode. diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index 745c848e4..dd7ec8b6a 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -27,7 +27,7 @@ from PyQt5.QtCore import pyqtSlot, Qt from qutebrowser.utils import message from qutebrowser.config import config -from qutebrowser.keyinput import keyparser, modeman +from qutebrowser.keyinput import keyparser from qutebrowser.utils import usertypes, log, objreg, utils @@ -276,6 +276,6 @@ class MarkKeyParser(keyparser.BaseKeyParser): else: raise ValueError("{} is not a valid mark mode".format(self._mode)) - modeman.leave(self._win_id, self._mode, "valid mark key") + self.request_leave.emit(self._mode, "valid mark key") return True From be6308534f2e3e9a4b94bdf993c58abded2adb5f Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Thu, 14 Apr 2016 17:30:43 -0400 Subject: [PATCH 13/25] Store both x and y position for marks. Previously only stored/used y. --- qutebrowser/mainwindow/tabbedbrowser.py | 15 +++++++------- tests/integration/features/marks.feature | 26 ++++++++++++------------ tests/integration/features/test_marks.py | 12 ++++------- 3 files changed, 24 insertions(+), 29 deletions(-) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index ddc3c61ab..56537c167 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -651,15 +651,14 @@ class TabbedBrowser(tabwidget.TabWidget): """ # strip the fragment as it may interfere with scrolling url = self.current_url().adjusted(QUrl.RemoveFragment) - frame = self.currentWidget().page().currentFrame() - y = frame.scrollPosition().y() + point = self.currentWidget().page().currentFrame().scrollPosition() if key.isupper(): - self._global_marks[key] = y, url + self._global_marks[key] = point, url else: if url not in self._local_marks: self._local_marks[url] = {} - self._local_marks[url][key] = y + self._local_marks[url][key] = point def jump_mark(self, key): """Jump to the mark named by `key`. @@ -672,23 +671,23 @@ class TabbedBrowser(tabwidget.TabWidget): frame = self.currentWidget().page().currentFrame() if key.isupper() and key in self._global_marks: - y, url = self._global_marks[key] + point, url = self._global_marks[key] def callback(ok): if ok: self.cur_load_finished.disconnect(callback) - frame.setScrollPosition(QPoint(0, y)) + frame.setScrollPosition(point) self.openurl(url, newtab=False) self.cur_load_finished.connect(callback) elif urlkey in self._local_marks and key in self._local_marks[urlkey]: - y = self._local_marks[urlkey][key] + point = self._local_marks[urlkey][key] # save the pre-jump position in the special ' mark # this has to happen after we read the mark, otherwise jump_mark # "'" would just jump to the current position every time self.set_mark("'") - frame.setScrollPosition(QPoint(0, y)) + frame.setScrollPosition(point) else: message.error(self._win_id, "Mark {} is not set".format(key)) diff --git a/tests/integration/features/marks.feature b/tests/integration/features/marks.feature index 5b18a6d05..14ab84bfc 100644 --- a/tests/integration/features/marks.feature +++ b/tests/integration/features/marks.feature @@ -7,43 +7,43 @@ Feature: Setting positional marks ## :set-mark, :jump-mark Scenario: Setting and jumping to a local mark - When I run :scroll-px 0 10 + When I run :scroll-px 5 10 And I run :set-mark 'a' And I run :scroll-px 0 20 And I run :jump-mark 'a' - Then the page should be scrolled to 10 + Then the page should be scrolled to 5 10 Scenario: Jumping back jumping to a particular percentage - When I run :scroll-px 0 20 + When I run :scroll-px 10 20 And I run :scroll-perc 100 And I run :jump-mark "'" - Then the page should be scrolled to 20 + Then the page should be scrolled to 10 20 Scenario: Setting the same local mark on another page - When I run :scroll-px 0 10 + When I run :scroll-px 5 10 And I run :set-mark 'a' And I open data/marks.html And I run :scroll-px 0 20 And I run :set-mark 'a' And I run :jump-mark 'a' - Then the page should be scrolled to 20 + Then the page should be scrolled to 0 20 Scenario: Jumping to a local mark after returning to a page - When I run :scroll-px 0 10 + When I run :scroll-px 5 10 And I run :set-mark 'a' And I open data/numbers/1.txt And I run :set-mark 'a' And I open data/marks.html And I run :jump-mark 'a' - Then the page should be scrolled to 10 + Then the page should be scrolled to 5 10 Scenario: Setting and jumping to a global mark - When I run :scroll-px 0 20 + When I run :scroll-px 5 20 And I run :set-mark 'A' And I open data/numbers/1.txt And I run :jump-mark 'A' Then data/marks.html should be loaded - And the page should be scrolled to 20 + And the page should be scrolled to 5 20 Scenario: Jumping to an unset mark When I run :jump-mark 'b' @@ -58,14 +58,14 @@ Feature: Setting positional marks Scenario: Jumping to a local mark after changing fragments When I open data/marks.html#top And I run :scroll 'top' - And I run :scroll-px 0 10 + And I run :scroll-px 10 10 And I run :set-mark 'a' When I open data/marks.html#bottom And I run :jump-mark 'a' - Then the page should be scrolled to 10 + Then the page should be scrolled to 10 10 Scenario: Jumping back after following a link When I run :hint links normal And I run :follow-hint s And I run :jump-mark "'" - Then the page should be scrolled to 0 + Then the page should be scrolled to 0 0 diff --git a/tests/integration/features/test_marks.py b/tests/integration/features/test_marks.py index fd61d9489..b2777cbe4 100644 --- a/tests/integration/features/test_marks.py +++ b/tests/integration/features/test_marks.py @@ -21,13 +21,9 @@ import pytest_bdd as bdd bdd.scenarios('marks.feature') -def _get_scroll_y(quteproc): +@bdd.then(bdd.parsers.parse("the page should be scrolled to {x} {y}")) +def check_y(quteproc, x, y): data = quteproc.get_session() pos = data['windows'][0]['tabs'][0]['history'][-1]['scroll-pos'] - return pos['y'] - - -@bdd.then(bdd.parsers.parse("the page should be scrolled to " - "{y}")) -def check_y(quteproc, y): - assert int(y) == _get_scroll_y(quteproc) + assert int(x) == pos['x'] + assert int(y) == pos['y'] From 4b00a17d7116f5cbf67553b3bc0ccec87e00ab25 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Fri, 15 Apr 2016 17:20:37 -0400 Subject: [PATCH 14/25] Fix MarkKeyParser crash when changing keyconfig. Ensure MarkKeyParser implements on_keyconfig_changed, so it doesn't fail when rebinding a key. It doesn't have keybindings, so the implementation is just `pass`. This also fixes a few flake8 style errors. --- qutebrowser/keyinput/modeparsers.py | 13 ++++++++++--- qutebrowser/mainwindow/tabbedbrowser.py | 6 +++--- tests/unit/keyinput/test_modeman.py | 7 +++++-- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index dd7ec8b6a..f56ad6ca2 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -236,8 +236,8 @@ class MarkKeyParser(keyparser.BaseKeyParser): """KeyParser for set_mark and jump_mark mode. - Attributes: - _mode: Either KeyMode.set_mark or KeyMode.jump_mark. + Attributes: + _mode: Either KeyMode.set_mark or KeyMode.jump_mark. """ def __init__(self, win_id, mode, parent=None): @@ -254,7 +254,6 @@ class MarkKeyParser(keyparser.BaseKeyParser): Return: True if event has been handled, False otherwise. """ - if utils.keyevent_to_string(e) is None: # this is a modifier key, let it pass and keep going return True @@ -279,3 +278,11 @@ class MarkKeyParser(keyparser.BaseKeyParser): self.request_leave.emit(self._mode, "valid mark key") return True + + def on_keyconfig_changed(self, mode): + """MarkKeyParser has no config section (no bindable keys).""" + pass + + def execute(self, cmdstr, _keytype, count=None): + """Should never be called on MarkKeyParser.""" + raise NotImplementedError diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 56537c167..a95fcd2b4 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -23,15 +23,15 @@ import functools import collections from PyQt5.QtWidgets import QSizePolicy -from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl, QPoint +from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl from PyQt5.QtGui import QIcon from qutebrowser.config import config from qutebrowser.keyinput import modeman from qutebrowser.mainwindow import tabwidget from qutebrowser.browser import signalfilter, webview -from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg, urlutils, - message) +from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg, + urlutils, message) UndoEntry = collections.namedtuple('UndoEntry', ['url', 'history']) diff --git a/tests/unit/keyinput/test_modeman.py b/tests/unit/keyinput/test_modeman.py index d0569f7de..b2cd90963 100644 --- a/tests/unit/keyinput/test_modeman.py +++ b/tests/unit/keyinput/test_modeman.py @@ -22,14 +22,17 @@ import pytest from qutebrowser.keyinput import modeman as modeman_module from qutebrowser.utils import usertypes -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, QObject, pyqtSignal -class FakeKeyparser: +class FakeKeyparser(QObject): """A fake BaseKeyParser which doesn't handle anything.""" + request_leave = pyqtSignal(usertypes.KeyMode, str) + def __init__(self): + super().__init__() self.passthrough = False def handle(self, evt): From a924144d9a1efea8e230c0a8e9235077174ce319 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Sat, 16 Apr 2016 00:12:14 -0400 Subject: [PATCH 15/25] Set ' mark after searching. Allow jumping back to the previous position after a search jumps you around. --- qutebrowser/browser/commands.py | 3 +++ tests/integration/features/marks.feature | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index fd2c61963..cf431c99c 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -1415,6 +1415,7 @@ class CommandDispatcher: text: The text to search for. reverse: Reverse search direction. """ + self.set_mark("'") view = self._current_widget() self._clear_search(view, text) flags = 0 @@ -1445,6 +1446,7 @@ class CommandDispatcher: Args: count: How many elements to ignore. """ + self.set_mark("'") view = self._current_widget() self._clear_search(view, self._tabbed_browser.search_text) @@ -1465,6 +1467,7 @@ class CommandDispatcher: Args: count: How many elements to ignore. """ + self.set_mark("'") view = self._current_widget() self._clear_search(view, self._tabbed_browser.search_text) diff --git a/tests/integration/features/marks.feature b/tests/integration/features/marks.feature index 14ab84bfc..85fc76708 100644 --- a/tests/integration/features/marks.feature +++ b/tests/integration/features/marks.feature @@ -69,3 +69,18 @@ Feature: Setting positional marks And I run :follow-hint s And I run :jump-mark "'" Then the page should be scrolled to 0 0 + + Scenario: Jumping back after searching + When I run :hint links normal + And I run :search 48 + And I run :jump-mark "'" + Then the page should be scrolled to 0 0 + + Scenario: Jumping back after search-next + When I run :hint links normal + And I run :search 9 + And I run :search-next + And I run :search-next + And I run :search-next + And I run :jump-mark "'" + Then the page should be scrolled to 0 0 From 2b68aaa3118090667f99762b5a4492ec9fa6e04e Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Mon, 18 Apr 2016 12:26:01 -0400 Subject: [PATCH 16/25] Make set_mark searching tests less flaky. Replace all the html numbers with a few widely spaced divs to guarantee the search will move the viewport on almost and reasonable screen size. --- tests/integration/data/marks.html | 106 +---------------------- tests/integration/features/marks.feature | 11 +-- 2 files changed, 7 insertions(+), 110 deletions(-) diff --git a/tests/integration/data/marks.html b/tests/integration/data/marks.html index 60cb40636..574e2cdd1 100644 --- a/tests/integration/data/marks.html +++ b/tests/integration/data/marks.html @@ -8,109 +8,9 @@

Top

Top Bottom -
-0
-1
-2
-3
-4
-5
-6
-7
-8
-9
-10
-11
-12
-13
-14
-15
-16
-17
-18
-19
-20
-21
-22
-23
-24
-25
-26
-27
-28
-29
-30
-31
-32
-33
-34
-35
-36
-37
-38
-39
-40
-41
-42
-43
-44
-45
-46
-47
-48
-49
-50
-51
-52
-53
-54
-55
-56
-57
-58
-59
-60
-61
-62
-63
-64
-65
-66
-67
-68
-69
-70
-71
-72
-73
-74
-75
-76
-77
-78
-79
-80
-81
-82
-83
-84
-85
-86
-87
-88
-89
-90
-91
-92
-93
-94
-95
-96
-97
-98
-99
-This is a very long line so this page can be scrolled horizontally. Did you think this line would end here already? Nah, it does not. But now it will. Or will it? I think it's not long enough yet.
-        
+
Holy Grail
+
Waldo
+
Holy Grail

Bottom

diff --git a/tests/integration/features/marks.feature b/tests/integration/features/marks.feature index 85fc76708..c0d557236 100644 --- a/tests/integration/features/marks.feature +++ b/tests/integration/features/marks.feature @@ -71,16 +71,13 @@ Feature: Setting positional marks Then the page should be scrolled to 0 0 Scenario: Jumping back after searching - When I run :hint links normal - And I run :search 48 + When I run :scroll-px 20 15 + And I run :search Waldo And I run :jump-mark "'" - Then the page should be scrolled to 0 0 + Then the page should be scrolled to 20 15 Scenario: Jumping back after search-next - When I run :hint links normal - And I run :search 9 - And I run :search-next - And I run :search-next + When I run :search Grail And I run :search-next And I run :jump-mark "'" Then the page should be scrolled to 0 0 From c7ee7a970259da15934bc48c52b456f2f0b97728 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Mon, 18 Apr 2016 18:15:11 -0400 Subject: [PATCH 17/25] Update docs for jump-mark and set-mark --- README.asciidoc | 2 +- doc/help/commands.asciidoc | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/README.asciidoc b/README.asciidoc index 8c54204e9..ed52807f1 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -160,6 +160,7 @@ Contributors, sorted by the number of commits in descending order: * Claude * meles5 * Tarcisio Fedrizzi +* Ryan Roden-Corrent * Artur Shaik * Nathan Isom * Thorsten Wißmann @@ -183,7 +184,6 @@ Contributors, sorted by the number of commits in descending order: * Zach-Button * Halfwit * rikn00 -* Ryan Roden-Corrent * Michael Ilsaas * Martin Zimmermann * Brian Jackson diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 5d4786bbe..47f5907d1 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -29,6 +29,7 @@ |<>|Open main startpage in current tab. |<>|Toggle the web inspector. |<>|Evaluate a JavaScript string. +|<>|Jump to the mark named by `key`. |<>|Execute a command after some time. |<>|Open typical prev/next links or navigate using the URL path. |<>|Open a URL in the current/[count]th tab. @@ -50,6 +51,7 @@ |<>|Save a session. |<>|Set an option. |<>|Preset the statusbar to some text. +|<>|Set a mark at the current scroll position in the current tab. |<>|Spawn a command in a shell. |<>|Stop loading in the current/[count]th tab. |<>|Duplicate the current tab. @@ -377,6 +379,15 @@ Evaluate a JavaScript string. * This command does not split arguments after the last argument and handles quotes literally. * With this command, +;;+ is interpreted literally instead of splitting off a second command. +[[jump-mark]] +=== jump-mark +Syntax: +:jump-mark 'key'+ + +Jump to the mark named by `key`. + +==== positional arguments +* +'key'+: mark identifier; capital indicates a global mark + [[later]] === later Syntax: +:later 'ms' 'command'+ @@ -649,6 +660,15 @@ Preset the statusbar to some text. * This command does not split arguments after the last argument and handles quotes literally. * With this command, +;;+ is interpreted literally instead of splitting off a second command. +[[set-mark]] +=== set-mark +Syntax: +:set-mark 'key'+ + +Set a mark at the current scroll position in the current tab. + +==== positional arguments +* +'key'+: mark identifier; capital indicates a global mark + [[spawn]] === spawn Syntax: +:spawn [*--userscript*] [*--verbose*] [*--detach*] 'cmdline'+ @@ -1243,7 +1263,7 @@ Scroll the current tab by 'count * dx/dy' pixels. ==== positional arguments * +'dx'+: How much to scroll in x-direction. -* +'dy'+: How much to scroll in x-direction. +* +'dy'+: How much to scroll in y-direction. ==== count multiplier From ae267c466fa9f40c790e56ba3e6d0bb9c1d0fd76 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 19 Apr 2016 07:50:59 -0400 Subject: [PATCH 18/25] Small fixes for marks. Mark two callbacks with pyqtSlot as appropriate. Return False instead of raising NotImplementedException to avoid pylint identifying MarkKeyParser as abstract. --- qutebrowser/keyinput/modeparsers.py | 3 ++- qutebrowser/mainwindow/tabbedbrowser.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index f56ad6ca2..c6ede2692 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -279,10 +279,11 @@ class MarkKeyParser(keyparser.BaseKeyParser): return True + @pyqtSlot(str) def on_keyconfig_changed(self, mode): """MarkKeyParser has no config section (no bindable keys).""" pass def execute(self, cmdstr, _keytype, count=None): """Should never be called on MarkKeyParser.""" - raise NotImplementedError + assert False diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index a95fcd2b4..fb5e86db5 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -673,6 +673,7 @@ class TabbedBrowser(tabwidget.TabWidget): if key.isupper() and key in self._global_marks: point, url = self._global_marks[key] + @pyqtSlot(bool) def callback(ok): if ok: self.cur_load_finished.disconnect(callback) From 6466ff919a778d97c773d8998aeea0e88edf068b Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 19 Apr 2016 07:53:39 -0400 Subject: [PATCH 19/25] Don't limit marks to alpha characters. --- qutebrowser/keyinput/modeparsers.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index c6ede2692..2967180da 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -260,11 +260,6 @@ class MarkKeyParser(keyparser.BaseKeyParser): key = e.text() - if not key.isalpha() and key != "'": - # only valid mark names are [a-zA-Z'] - message.error(self._win_id, key + " isn't a valid mark") - return True - tabbed_browser = objreg.get('tabbed-browser', scope='window', window=self._win_id) From 268d490e2514cfc6d9675b6641aeac26778604ba Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 19 Apr 2016 17:30:57 -0400 Subject: [PATCH 20/25] Return False when MarkKeyParser gets special key. --- qutebrowser/keyinput/modeparsers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index 2967180da..f8c54d79a 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -256,7 +256,7 @@ class MarkKeyParser(keyparser.BaseKeyParser): """ if utils.keyevent_to_string(e) is None: # this is a modifier key, let it pass and keep going - return True + return False key = e.text() From 0755e163bb383f0f4d97d05da5904b8602b24879 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 19 Apr 2016 20:46:45 -0400 Subject: [PATCH 21/25] Only set the ' mark on normal/current hinting. Don't set ' for downloading, hovering, or opening a link in a tab. --- qutebrowser/browser/hints.py | 11 +++++++---- tests/integration/features/marks.feature | 8 ++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index c1ba62ee1..9ec14388f 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -466,6 +466,13 @@ class HintManager(QObject): QMouseEvent(QEvent.MouseButtonRelease, pos, Qt.LeftButton, Qt.NoButton, modifiers), ] + + if context.target in [Target.normal, Target.current]: + # Set the pre-jump mark ', so we can jump back here after following + tabbed_browser = objreg.get('tabbed-browser', scope='window', + window=self._win_id) + tabbed_browser.set_mark("'") + if context.target == Target.current: elem.remove_blank_target() for evt in events: @@ -921,10 +928,6 @@ class HintManager(QObject): immediately=True) return if self._context.target in elem_handlers: - # Set the pre-jump mark ', so we can jump back here after following - tabbed_browser = objreg.get('tabbed-browser', scope='window', - window=self._win_id) - tabbed_browser.set_mark("'") handler = functools.partial( elem_handlers[self._context.target], elem, self._context) elif self._context.target in url_handlers: diff --git a/tests/integration/features/marks.feature b/tests/integration/features/marks.feature index c0d557236..585168367 100644 --- a/tests/integration/features/marks.feature +++ b/tests/integration/features/marks.feature @@ -81,3 +81,11 @@ Feature: Setting positional marks And I run :search-next And I run :jump-mark "'" Then the page should be scrolled to 0 0 + + Scenario: Hovering a hint does not set the ' mark + When I run :scroll-px 30 20 + And I run :scroll-perc 0 + And I run :hint links hover + And I run :follow-hint s + And I run :jump-mark "'" + Then the page should be scrolled to 30 20 From 600b5082a90db8d76d5ab31c7b7ec237ca1b3019 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Wed, 20 Apr 2016 21:53:12 -0400 Subject: [PATCH 22/25] Touch up tests and add pyqtSlot for marks. Small code review changes. --- qutebrowser/keyinput/modeman.py | 3 ++- tests/integration/features/marks.feature | 32 ++++++++++++------------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 3a352ba19..b21f2fda9 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -21,7 +21,7 @@ import functools -from PyQt5.QtCore import pyqtSignal, Qt, QObject, QEvent +from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QObject, QEvent from PyQt5.QtWidgets import QApplication from qutebrowser.keyinput import modeparsers, keyparser @@ -271,6 +271,7 @@ class ModeManager(QObject): raise cmdexc.CommandError("Mode {} does not exist!".format(mode)) self.enter(m, 'command') + @pyqtSlot(str, str) def leave(self, mode, reason=None): """Leave a key mode. diff --git a/tests/integration/features/marks.feature b/tests/integration/features/marks.feature index 585168367..3a35791b8 100644 --- a/tests/integration/features/marks.feature +++ b/tests/integration/features/marks.feature @@ -8,12 +8,12 @@ Feature: Setting positional marks Scenario: Setting and jumping to a local mark When I run :scroll-px 5 10 - And I run :set-mark 'a' + And I run :set-mark a And I run :scroll-px 0 20 - And I run :jump-mark 'a' + And I run :jump-mark a Then the page should be scrolled to 5 10 - Scenario: Jumping back jumping to a particular percentage + Scenario: Jumping back after jumping to a particular percentage When I run :scroll-px 10 20 And I run :scroll-perc 100 And I run :jump-mark "'" @@ -21,47 +21,47 @@ Feature: Setting positional marks Scenario: Setting the same local mark on another page When I run :scroll-px 5 10 - And I run :set-mark 'a' + And I run :set-mark a And I open data/marks.html And I run :scroll-px 0 20 - And I run :set-mark 'a' - And I run :jump-mark 'a' + And I run :set-mark a + And I run :jump-mark a Then the page should be scrolled to 0 20 Scenario: Jumping to a local mark after returning to a page When I run :scroll-px 5 10 - And I run :set-mark 'a' + And I run :set-mark a And I open data/numbers/1.txt - And I run :set-mark 'a' + And I run :set-mark a And I open data/marks.html - And I run :jump-mark 'a' + And I run :jump-mark a Then the page should be scrolled to 5 10 Scenario: Setting and jumping to a global mark When I run :scroll-px 5 20 - And I run :set-mark 'A' + And I run :set-mark A And I open data/numbers/1.txt - And I run :jump-mark 'A' + And I run :jump-mark A Then data/marks.html should be loaded And the page should be scrolled to 5 20 Scenario: Jumping to an unset mark - When I run :jump-mark 'b' + When I run :jump-mark b Then the error "Mark b is not set" should be shown Scenario: Jumping to a local mark that was set on another page - When I run :set-mark 'b' + When I run :set-mark b And I open data/numbers/1.txt - And I run :jump-mark 'b' + And I run :jump-mark b Then the error "Mark b is not set" should be shown Scenario: Jumping to a local mark after changing fragments When I open data/marks.html#top And I run :scroll 'top' And I run :scroll-px 10 10 - And I run :set-mark 'a' + And I run :set-mark a When I open data/marks.html#bottom - And I run :jump-mark 'a' + And I run :jump-mark a Then the page should be scrolled to 10 10 Scenario: Jumping back after following a link From e0aa35b05ae41f07759c57893445c703bbd9ef4d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 21 Apr 2016 20:10:58 +0200 Subject: [PATCH 23/25] Fix type in @pyqtSlot for ModeManager.leave For some reason it worked with str as well, but the signal emits a KeyMode member. --- qutebrowser/keyinput/modeman.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index b21f2fda9..4f04e7fd5 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -27,7 +27,7 @@ from PyQt5.QtWidgets import QApplication from qutebrowser.keyinput import modeparsers, keyparser from qutebrowser.config import config from qutebrowser.commands import cmdexc, cmdutils -from qutebrowser.utils import usertypes, log, objreg, utils +from qutebrowser.utils import usertypes, log, objreg, utils, usertypes class KeyEvent: @@ -271,7 +271,7 @@ class ModeManager(QObject): raise cmdexc.CommandError("Mode {} does not exist!".format(mode)) self.enter(m, 'command') - @pyqtSlot(str, str) + @pyqtSlot(usertypes.KeyMode, str) def leave(self, mode, reason=None): """Leave a key mode. From 5b34f1b429a070f9d2db1597864e47db1db646ae Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 21 Apr 2016 20:11:47 +0200 Subject: [PATCH 24/25] Fix ModeManager.leave docstring --- qutebrowser/keyinput/modeman.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 4f04e7fd5..5a581b97e 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -276,7 +276,7 @@ class ModeManager(QObject): """Leave a key mode. Args: - mode: The name of the mode to leave. + mode: The mode to leave as a usertypes.KeyMode member. reason: Why the mode was left. """ if self.mode != mode: From 0955ed49a743d1fd7578a21ea106414949428d1b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 21 Apr 2016 22:55:36 +0200 Subject: [PATCH 25/25] Update docs --- CHANGELOG.asciidoc | 4 ++++ README.asciidoc | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 2dafe1e88..2e8f071a3 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -23,6 +23,10 @@ Added - New `:edit-url` command to edit the URL in an external editor. - New `network -> custom-headers` setting to send custom headers with every request. - New `{url:pretty}` commandline replacement which gets replaced by the decoded URL. +- New marks to remember a scroll position: + - New `:jump-mark` command to jump to a mark, bound to `'` + - New `:set-mark` command to set a mark, bound to ```(backtick) + - The `'` mark gets set when moving away (hinting link with anchor, searching, etc.) so you can move back with `''` Changed ~~~~~~~ diff --git a/README.asciidoc b/README.asciidoc index 203de967e..f02f71a73 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -158,9 +158,9 @@ Contributors, sorted by the number of commits in descending order: * Joel Torstensson * Patric Schmitz * Claude +* Ryan Roden-Corrent * meles5 * Tarcisio Fedrizzi -* Ryan Roden-Corrent * Artur Shaik * Nathan Isom * Thorsten Wißmann