From 71a2dad5704d4ed3381e24f54905f53662f82e3a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Sat, 1 Sep 2018 16:29:38 +0200 Subject: [PATCH] Add a history.exclude setting This allows to exclude URL patterns from being displayed in the completion or in qute://history. --- doc/help/settings.asciidoc | 12 ++++++++++ qutebrowser/browser/history.py | 32 +++++++++++++++++++++------ qutebrowser/browser/qutescheme.py | 5 ++++- qutebrowser/config/configdata.yml | 16 ++++++++++++++ tests/unit/browser/test_history.py | 23 +++++++++++++++++++ tests/unit/browser/test_qutescheme.py | 16 ++++++++++++-- 6 files changed, 94 insertions(+), 10 deletions(-) diff --git a/doc/help/settings.asciidoc b/doc/help/settings.asciidoc index d68cd3949..34b55b5df 100644 --- a/doc/help/settings.asciidoc +++ b/doc/help/settings.asciidoc @@ -205,6 +205,7 @@ |<>|Comma-separated list of regular expressions to use for 'prev' links. |<>|Scatter hint key chains (like Vimium) or not (like dwb). |<>|Make characters in hint strings uppercase. +|<>|A list of patterns which should not be shown in the history. |<>|Maximum time (in minutes) between two history items for them to be considered being from the same browsing session. |<>|Allow Escape to quit the crash reporter. |<>|Which unbound keys to forward to the webview in normal mode. @@ -2509,6 +2510,17 @@ Type: <> Default: +pass:[false]+ +[[history.exclude]] +=== history.exclude +A list of patterns which should not be shown in the history. +This only affects the completion and qute://history page. Matching URLs are still saved in the history, but hidden. +Changing this setting will cause the completion history to be regenerated on the next start, which will take a short while. +This setting requires a restart. + +Type: <> + +Default: empty + [[history.gap_interval]] === history.gap_interval Maximum time (in minutes) between two history items for them to be considered being from the same browsing session. diff --git a/qutebrowser/browser/history.py b/qutebrowser/browser/history.py index 52d6e6cf5..6b34a92b6 100644 --- a/qutebrowser/browser/history.py +++ b/qutebrowser/browser/history.py @@ -25,6 +25,7 @@ import contextlib from PyQt5.QtCore import pyqtSlot, QUrl, QTimer, pyqtSignal +from qutebrowser.config import config from qutebrowser.commands import cmdutils, cmdexc from qutebrowser.utils import (utils, objreg, log, usertypes, message, debug, standarddir, qtutils) @@ -128,12 +129,18 @@ class WebHistory(sql.SqlTable): 'ORDER BY atime desc ' 'limit :limit offset :offset') + config.instance.changed.connect(self._on_config_changed) + def __repr__(self): return utils.get_repr(self, length=len(self)) def __contains__(self, url): return self._contains_query.run(val=url).value() + @config.change_filter('history.exclude') + def _on_config_changed(self): + self.metainfo['force_rebuild'] = True + @contextlib.contextmanager def _handle_sql_errors(self): try: @@ -151,9 +158,14 @@ class WebHistory(sql.SqlTable): 'WHERE NOT redirect and url NOT LIKE "qute://back%" ' 'GROUP BY url ORDER BY atime asc') for entry in q.run(): - data['url'].append(self._format_completion_url(QUrl(entry.url))) + url = QUrl(entry.url) + if any(pattern.matches(url) + for pattern in config.val.history.exclude): + continue + data['url'].append(self._format_completion_url(url)) data['title'].append(entry.title) data['last_atime'].append(entry.atime) + self.completion.insert_batch(data, replace=True) sql.Query('pragma user_version = {}'.format(_USER_VERSION)).run() @@ -259,12 +271,18 @@ class WebHistory(sql.SqlTable): 'title': title, 'atime': atime, 'redirect': redirect}) - if not redirect: - self.completion.insert({ - 'url': self._format_completion_url(url), - 'title': title, - 'last_atime': atime - }, replace=True) + + if any(pattern.matches(url) + for pattern in config.val.history.exclude): + return + if redirect: + return + + self.completion.insert({ + 'url': self._format_completion_url(url), + 'title': title, + 'last_atime': atime + }, replace=True) def _parse_entry(self, line): """Parse a history line like '12345 http://example.com title'.""" diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py index 401eb2bb9..f43212557 100644 --- a/qutebrowser/browser/qutescheme.py +++ b/qutebrowser/browser/qutescheme.py @@ -251,7 +251,10 @@ def history_data(start_time, offset=None): return [{"url": e.url, "title": html.escape(e.title) or html.escape(e.url), - "time": e.atime} for e in entries] + "time": e.atime} + for e in entries + if not any(pattern.matches(QUrl(e.url)) + for pattern in config.val.history.exclude)] @add_handler('history') diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml index e7696dc93..15743f7f0 100644 --- a/qutebrowser/config/configdata.yml +++ b/qutebrowser/config/configdata.yml @@ -1055,6 +1055,22 @@ history.gap_interval: Items with less time between them are grouped when being displayed in `:history`. Use -1 to disable separation. +history.exclude: + type: + name: List + valtype: UrlPattern + none_ok: true + default: [] + restart: true + desc: >- + A list of patterns which should not be shown in the history. + + This only affects the completion and qute://history page. Matching URLs are + still saved in the history, but hidden. + + Changing this setting will cause the completion history to be regenerated + on the next start, which will take a short while. + ## input input.escape_quits_reporter: diff --git a/tests/unit/browser/test_history.py b/tests/unit/browser/test_history.py index 0f71b0488..6eaa399e7 100644 --- a/tests/unit/browser/test_history.py +++ b/tests/unit/browser/test_history.py @@ -221,6 +221,14 @@ class TestAdd: hist.add_from_tab(QUrl(url), QUrl(req_url), 'title') assert set(hist) == set(expected) + def test_exclude(self, hist, config_stub): + """Excluded URLs should be in the history but not completion.""" + config_stub.set_obj('history.exclude', ['*.example.org']) + url = QUrl('http://www.example.org/') + hist.add_from_tab(url, url, 'title') + assert list(hist) + assert not list(hist.completion) + class TestHistoryInterface: @@ -472,6 +480,21 @@ class TestRebuild: ] assert not hist3.metainfo['force_rebuild'] + def test_exclude(self, config_stub, hist): + """Ensure that patterns in history.exclude are ignored.""" + config_stub.set_obj('history.exclude', ['*.example.org']) + assert hist.metainfo['force_rebuild'] + + hist.add_url(QUrl('http://example.com'), redirect=False, atime=1) + hist.add_url(QUrl('http://example.org'), redirect=False, atime=2) + + hist2 = history.WebHistory() + assert list(hist2.completion) == [('http://example.com', '', 1)] + + def test_unrelated_config_change(self, config_stub, hist): + config_stub.set_obj('history.gap_interval', 1234) + assert not hist.metainfo['force_rebuild'] + class TestCompletionMetaInfo: diff --git a/tests/unit/browser/test_qutescheme.py b/tests/unit/browser/test_qutescheme.py index 39df01389..2db98a933 100644 --- a/tests/unit/browser/test_qutescheme.py +++ b/tests/unit/browser/test_qutescheme.py @@ -90,14 +90,15 @@ class TestHistoryHandler: for i in range(entry_count): entry_atime = now - i * interval entry = {"atime": str(entry_atime), - "url": QUrl("www.x.com/" + str(i)), + "url": QUrl("http://www.x.com/" + str(i)), "title": "Page " + str(i)} items.insert(0, entry) return items @pytest.fixture - def fake_web_history(self, fake_save_manager, tmpdir, init_sql): + def fake_web_history(self, fake_save_manager, tmpdir, init_sql, + config_stub): """Create a fake web-history and register it into objreg.""" web_history = history.WebHistory() objreg.register('web-history', web_history) @@ -133,6 +134,17 @@ class TestHistoryHandler: assert item['time'] <= start_time assert item['time'] > end_time + def test_exclude(self, fake_web_history, now, config_stub): + config_stub.set_obj('history.exclude', ['www.x.com']) + url = QUrl('http://www.example.org/') + fake_web_history.add_from_tab(url, url, 'title') + + url = QUrl("qute://history/data?start_time={}".format(now)) + _mimetype, data = qutescheme.qute_history(url) + items = json.loads(data) + assert len(items) == 1 + assert items[0]['url'] == 'http://www.example.org/' + def test_qute_history_benchmark(self, fake_web_history, benchmark, now): r = range(100000) entries = {