diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 2ffa647a3..3728ebac4 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -22,7 +22,6 @@ import os import os.path import shlex -import posixpath import functools from PyQt5.QtWidgets import QApplication, QTabBar @@ -40,7 +39,7 @@ import pygments.formatters from qutebrowser.commands import userscripts, cmdexc, cmdutils, runners from qutebrowser.config import config, configexc -from qutebrowser.browser import urlmarks, browsertab, inspector +from qutebrowser.browser import urlmarks, browsertab, inspector, navigate from qutebrowser.browser.webkit import webelem, downloads, mhtml from qutebrowser.keyinput import modeman from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils, @@ -440,40 +439,6 @@ class CommandDispatcher: """ self._back_forward(tab, bg, window, count, forward=True) - def _navigate_incdec(self, url, incdec, tab, background, window): - """Helper method for :navigate when `where' is increment/decrement. - - Args: - url: The current url. - incdec: Either 'increment' or 'decrement'. - tab: Whether to open the link in a new tab. - background: Open the link in a new background tab. - window: Open the link in a new window. - """ - segments = set(config.get('general', 'url-incdec-segments')) - try: - new_url = urlutils.incdec_number(url, incdec, segments=segments) - except urlutils.IncDecError as error: - raise cmdexc.CommandError(error.msg) - - self._open(new_url, tab, background, window) - - def _navigate_up(self, url, tab, background, window): - """Helper method for :navigate when `where' is up. - - Args: - url: The current url. - tab: Whether to open the link in a new tab. - background: Open the link in a new background tab. - window: Open the link in a new window. - """ - path = url.path() - if not path or path == '/': - raise cmdexc.CommandError("Can't go up!") - new_path = posixpath.join(path, posixpath.pardir) - url.setPath(new_path) - self._open(url, tab, background, window) - @cmdutils.register(instance='command-dispatcher', scope='window', backend=usertypes.Backend.QtWebKit) @cmdutils.argument('where', choices=['prev', 'next', 'up', 'increment', @@ -506,26 +471,29 @@ class CommandDispatcher: widget = self._current_widget() url = self._current_url().adjusted(QUrl.RemoveFragment) - if where in ['prev', 'next']: - # FIXME:qtwebengine have a proper API for this - if widget.backend == usertypes.Backend.QtWebEngine: - raise cmdexc.CommandError(":navigate prev/next is not " - "supported yet with QtWebEngine") + handlers = { + 'prev': functools.partial(navigate.prevnext, prev=True), + 'next': functools.partial(navigate.prevnext, prev=False), + 'up': navigate.path_up, + 'decrement': functools.partial(navigate.incdec, + inc_or_dec='decrement'), + 'increment': functools.partial(navigate.incdec, + inc_or_dec='increment'), + } - hintmanager = objreg.get('hintmanager', scope='tab', tab='current') - if where == 'prev': - hintmanager.follow_prevnext(widget, url, prev=True, tab=tab, - background=bg, window=window) - elif where == 'next': - hintmanager.follow_prevnext(widget, url, prev=False, tab=tab, - background=bg, window=window) - elif where == 'up': - self._navigate_up(url, tab, bg, window) - elif where in ['decrement', 'increment']: - self._navigate_incdec(url, where, tab, bg, window) - else: # pragma: no cover - raise ValueError("Got called with invalid value {} for " - "`where'.".format(where)) + try: + if where in ['prev', 'next']: + handler = handlers[where] + 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) + self._open(new_url, tab, bg, window) + else: # pragma: no cover + raise ValueError("Got called with invalid value {} for " + "`where'.".format(where)) + except navigate.Error as e: + raise cmdexc.CommandError(e) @cmdutils.register(instance='command-dispatcher', hide=True, scope='window') diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 3f879ce28..a886915b5 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -630,83 +630,6 @@ class HintManager(QObject): # Do multi-word matching return all(word in elemstr for word in filterstr.split()) - def _find_prevnext(self, prev, elems): - """Find a prev/next element in the given list of elements.""" - # First check for - rel_values = ('prev', 'previous') if prev else ('next') - for e in elems: - if e.tag_name() != 'link' or 'rel' not in e: - continue - if e['rel'] in rel_values: - log.hints.debug("Found '{}' with rel={}".format( - e.debug_text(), e['rel'])) - return e - - # Then check for regular links/buttons. - filterfunc = webelem.FILTERS[webelem.Group.prevnext] - elems = [e for e in elems if e.tag_name() != 'link' and filterfunc(e)] - option = 'prev-regexes' if prev else 'next-regexes' - if not elems: - return None - for regex in config.get('hints', option): - log.hints.vdebug("== Checking regex '{}'.".format(regex.pattern)) - for e in elems: - text = str(e) - if not text: - continue - if regex.search(text): - log.hints.debug("Regex '{}' matched on '{}'.".format( - regex.pattern, text)) - return e - else: - log.hints.vdebug("No match on '{}'!".format(text)) - return None - - def follow_prevnext(self, browsertab, baseurl, prev=False, tab=False, - background=False, window=False): - """Click a "previous"/"next" element on the page. - - Args: - browsertab: The WebKitTab/WebEngineTab of the page. - baseurl: The base URL of the current tab. - prev: True to open a "previous" link, False to open a "next" link. - tab: True to open in a new tab, False for the current tab. - background: True to open in a background tab. - window: True to open in a new window, False for the current one. - """ - def _follow_prevnext_cb(elems): - elem = self._find_prevnext(prev, elems) - word = 'prev' if prev else 'forward' - - if elem is None: - message.error(self._win_id, "No {} links found!".format(word)) - return - url = _resolve_url(elem, baseurl) - if url is None: - message.error(self._win_id, "No {} links found!".format(word)) - return - qtutils.ensure_valid(url) - - if window: - from qutebrowser.mainwindow import mainwindow - new_window = mainwindow.MainWindow() - new_window.show() - tabbed_browser = objreg.get('tabbed-browser', scope='window', - window=new_window.win_id) - tabbed_browser.tabopen(url, background=False) - elif tab: - tabbed_browser = objreg.get('tabbed-browser', scope='window', - window=self._win_id) - tabbed_browser.tabopen(url, background=background) - else: - browsertab = objreg.get('tab', scope='tab', - window=self._win_id, tab=self._tab_id) - browsertab.openurl(url) - - selector = ', '.join([webelem.SELECTORS[webelem.Group.links], - webelem.SELECTORS[webelem.Group.prevnext]]) - browsertab.find_all_elements(selector, _follow_prevnext_cb) - def _start_cb(self, elems): """Initialize the elements and labels based on the context set.""" filterfunc = webelem.FILTERS.get(self._context.group, lambda e: True) diff --git a/qutebrowser/browser/navigate.py b/qutebrowser/browser/navigate.py new file mode 100644 index 000000000..9689105f3 --- /dev/null +++ b/qutebrowser/browser/navigate.py @@ -0,0 +1,146 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 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 . + +"""Implementation of :navigate""" + +import posixpath + +from qutebrowser.browser.webkit import webelem +from qutebrowser.config import config +from qutebrowser.utils import (usertypes, objreg, urlutils, log, message, + qtutils) + + +class Error(Exception): + + """Raised when the navigation can't be done.""" + + +def incdec(url, inc_or_dec): + """Helper method for :navigate when `where' is increment/decrement. + + Args: + url: The current url. + 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. + window: Open the link in a new window. + """ + segments = set(config.get('general', 'url-incdec-segments')) + try: + new_url = urlutils.incdec_number(url, inc_or_dec, segments=segments) + except urlutils.IncDecError as error: + raise Error(error.msg) + return new_url + + +def path_up(url): + """Helper method for :navigate when `where' is up. + + Args: + url: The current 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) + return url + + +def _find_prevnext(prev, elems): + """Find a prev/next element in the given list of elements.""" + # First check for + rel_values = ('prev', 'previous') if prev else ('next') + for e in elems: + if e.tag_name() != 'link' or 'rel' not in e: + continue + if e['rel'] in rel_values: + log.hints.debug("Found '{}' with rel={}".format( + e.debug_text(), e['rel'])) + return e + + # Then check for regular links/buttons. + filterfunc = webelem.FILTERS[webelem.Group.prevnext] + elems = [e for e in elems if e.tag_name() != 'link' and filterfunc(e)] + option = 'prev-regexes' if prev else 'next-regexes' + if not elems: + return None + for regex in config.get('hints', option): + log.hints.vdebug("== Checking regex '{}'.".format(regex.pattern)) + for e in elems: + text = str(e) + if not text: + continue + if regex.search(text): + log.hints.debug("Regex '{}' matched on '{}'.".format( + regex.pattern, text)) + return e + else: + log.hints.vdebug("No match on '{}'!".format(text)) + return None + + +def prevnext(*, browsertab, win_id, baseurl, prev=False, + tab=False, background=False, window=False): + """Click a "previous"/"next" element on the page. + + Args: + browsertab: The WebKitTab/WebEngineTab of the page. + baseurl: The base URL of the current tab. + prev: True to open a "previous" link, False to open a "next" link. + tab: True to open in a new tab, False for the current tab. + background: True to open in a background tab. + window: True to open in a new window, False for the current one. + """ + # FIXME:qtwebengine have a proper API for this + if browsertab.backend == usertypes.Backend.QtWebEngine: + raise Error(":navigate prev/next is not supported yet with " + "QtWebEngine") + + def _prevnext_cb(elems): + elem = _find_prevnext(prev, elems) + word = 'prev' if prev else 'forward' + + if elem is None: + message.error(win_id, "No {} links found!".format(word)) + return + url = elem.resolve_url(baseurl) + if url is None: + message.error(win_id, "No {} links found!".format(word)) + return + qtutils.ensure_valid(url) + + if window: + from qutebrowser.mainwindow import mainwindow + new_window = mainwindow.MainWindow() + new_window.show() + tabbed_browser = objreg.get('tabbed-browser', scope='window', + window=new_window.win_id) + tabbed_browser.tabopen(url, background=False) + elif tab: + tabbed_browser = objreg.get('tabbed-browser', scope='window', + window=win_id) + tabbed_browser.tabopen(url, background=background) + else: + browsertab.openurl(url) + + selector = ', '.join([webelem.SELECTORS[webelem.Group.links], + webelem.SELECTORS[webelem.Group.prevnext]]) + browsertab.find_all_elements(selector, _prevnext_cb)