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)