Merge branch 'master' of https://github.com/The-Compiler/qutebrowser into macros
Conflicts: qutebrowser/commands/runners.py
This commit is contained in:
commit
2a304d7a6b
11
.travis.yml
11
.travis.yml
@ -19,7 +19,12 @@ matrix:
|
||||
env: DOCKER=ubuntu-xenial
|
||||
services: docker
|
||||
- os: osx
|
||||
env: TESTENV=py35
|
||||
env: TESTENV=py35 OSX=elcapitan
|
||||
osx_image: xcode7.3
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/2013
|
||||
# - os: osx
|
||||
# env: TESTENV=py35 OSX=yosemite
|
||||
# osx_image: xcode6.4
|
||||
- os: linux
|
||||
env: TESTENV=pylint
|
||||
- os: linux
|
||||
@ -36,6 +41,10 @@ matrix:
|
||||
env: TESTENV=check-manifest
|
||||
- os: linux
|
||||
env: TESTENV=eslint
|
||||
allow_failures:
|
||||
- os: osx
|
||||
env: TESTENV=py35 OSX=elcapitan
|
||||
osx_image: xcode7.3
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
@ -47,6 +47,8 @@ Added
|
||||
https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API[HTML5 page visibility API]
|
||||
- New `readability` userscript which shows a readable version of a page (using
|
||||
the `readability-lxml` python package)
|
||||
- New `cast` userscript to show a video on a Google Chromecast
|
||||
- New `:run-with-count` command which replaces the (undocumented) `:count:command` syntax.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
@ -179,6 +181,7 @@ Fixed
|
||||
- Fixed hang when using multiple spaces in a row with the URL completion
|
||||
- Fixed crash when closing a window without focusing it
|
||||
- Userscripts now can access QUTE_FIFO correctly on Windows
|
||||
- Compatibility with pdfjs v1.6.210
|
||||
|
||||
v0.8.3 (unreleased)
|
||||
-------------------
|
||||
|
@ -652,4 +652,4 @@ as closed.
|
||||
* OS X: Run `python3 scripts/dev/build_release.py --upload v0.X.Y` (replace X/Y by hand)
|
||||
* On server: Run `python3 scripts/dev/download_release.py v0.X.Y` (replace X/Y by hand)
|
||||
* Update `qutebrowser-git` PKGBUILD if dependencies/install changed
|
||||
* Announce to qutebrowser mailinglist
|
||||
* Announce to qutebrowser and qutebrowser-announce mailinglist
|
||||
|
@ -70,6 +70,9 @@ message to the
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[mailinglist] at
|
||||
mailto:qutebrowser@lists.qutebrowser.org[].
|
||||
|
||||
There's also a https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[announce-only mailinglist]
|
||||
at mailto:qutebrowser-announce@lists.qutebrowser.org[].
|
||||
|
||||
Contributions / Bugs
|
||||
--------------------
|
||||
|
||||
@ -159,10 +162,10 @@ Contributors, sorted by the number of commits in descending order:
|
||||
* Claude
|
||||
* Corentin Julé
|
||||
* meles5
|
||||
* Kevin Velghe
|
||||
* Philipp Hansch
|
||||
* Daniel Karbach
|
||||
* Panagiotis Ktistakis
|
||||
* Kevin Velghe
|
||||
* Artur Shaik
|
||||
* Nathan Isom
|
||||
* Thorsten Wißmann
|
||||
@ -180,6 +183,7 @@ Contributors, sorted by the number of commits in descending order:
|
||||
* knaggita
|
||||
* Oliver Caldwell
|
||||
* Julian Weigt
|
||||
* Sebastian Frysztak
|
||||
* Jonas Schürmann
|
||||
* error800
|
||||
* Michael Hoang
|
||||
@ -244,6 +248,7 @@ Contributors, sorted by the number of commits in descending order:
|
||||
* Tim Harder
|
||||
* Thiago Barroso Perrotta
|
||||
* Sorokin Alexei
|
||||
* Simon Désaulniers
|
||||
* Rok Mandeljc
|
||||
* Noah Huesser
|
||||
* Moez Bouhlel
|
||||
|
@ -1017,6 +1017,7 @@ How many steps to zoom out.
|
||||
|<<rl-unix-line-discard,rl-unix-line-discard>>|Remove chars backward from the cursor to the beginning of the line.
|
||||
|<<rl-unix-word-rubout,rl-unix-word-rubout>>|Remove chars from the cursor to the beginning of the word.
|
||||
|<<rl-yank,rl-yank>>|Paste the most recently deleted text.
|
||||
|<<run-with-count,run-with-count>>|Run a command with the given count.
|
||||
|<<scroll,scroll>>|Scroll the current tab in the given direction.
|
||||
|<<scroll-page,scroll-page>>|Scroll the frame page-wise.
|
||||
|<<scroll-perc,scroll-perc>>|Scroll to a specific percentage of the page.
|
||||
@ -1365,6 +1366,26 @@ Paste the most recently deleted text.
|
||||
|
||||
This acts like readline's yank.
|
||||
|
||||
[[run-with-count]]
|
||||
=== run-with-count
|
||||
Syntax: +:run-with-count 'count-arg' 'command'+
|
||||
|
||||
Run a command with the given count.
|
||||
|
||||
If run_with_count itself is run with a count, it multiplies count_arg.
|
||||
|
||||
==== positional arguments
|
||||
* +'count-arg'+: The count to pass to the command.
|
||||
* +'command'+: The command to run, with optional args.
|
||||
|
||||
==== count
|
||||
The count that run_with_count itself received.
|
||||
|
||||
==== note
|
||||
* 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.
|
||||
* This command does not replace variables like +\{url\}+.
|
||||
|
||||
[[scroll]]
|
||||
=== scroll
|
||||
Syntax: +:scroll 'direction'+
|
||||
|
@ -69,6 +69,7 @@
|
||||
|<<network-ssl-strict,ssl-strict>>|Whether to validate SSL handshakes.
|
||||
|<<network-dns-prefetch,dns-prefetch>>|Whether to try to pre-fetch DNS entries to speed up browsing.
|
||||
|<<network-custom-headers,custom-headers>>|Set custom headers for qutebrowser HTTP requests.
|
||||
|<<network-netrc-file,netrc-file>>|Set location of a netrc-file for HTTP authentication. If empty, ~/.netrc is used.
|
||||
|==============
|
||||
|
||||
.Quick reference for section ``completion''
|
||||
@ -808,6 +809,12 @@ Set custom headers for qutebrowser HTTP requests.
|
||||
|
||||
Default: empty
|
||||
|
||||
[[network-netrc-file]]
|
||||
=== netrc-file
|
||||
Set location of a netrc-file for HTTP authentication. If empty, ~/.netrc is used.
|
||||
|
||||
Default: empty
|
||||
|
||||
== completion
|
||||
Options related to completion and command history.
|
||||
|
||||
|
@ -32,7 +32,8 @@ image:http://qutebrowser.org/img/cheatsheet-small.png["qutebrowser key binding c
|
||||
`scripts/asciidoc2html.py` to generate the documentation.
|
||||
* Go to the link:qute://settings[settings page] to set up qutebrowser the way you want it.
|
||||
* Subscribe to
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[the mailinglist]
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser[the mailinglist] or
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce[the announce-only mailinglist].
|
||||
* Let me know what features you are missing or things that need (even small!)
|
||||
improvements.
|
||||
|
||||
|
@ -162,6 +162,8 @@ this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* Website: http://www.qutebrowser.org/
|
||||
* Mailinglist: mailto:qutebrowser@lists.qutebrowser.org[] /
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser
|
||||
* Announce-only mailinglist: mailto:qutebrowser-announce@lists.qutebrowser.org[] /
|
||||
https://lists.schokokeks.org/mailman/listinfo.cgi/qutebrowser-announce
|
||||
* IRC: irc://irc.freenode.org/#qutebrowser[`#qutebrowser`] on
|
||||
http://freenode.net/[Freenode]
|
||||
* Github: https://github.com/The-Compiler/qutebrowser
|
||||
|
@ -19,7 +19,7 @@ pbr==1.10.0
|
||||
pep8==1.7.0
|
||||
pep8-naming==0.4.1
|
||||
pycodestyle==2.0.0
|
||||
pydocstyle==1.0.0 # rq.filter: != 1.1.0
|
||||
pydocstyle==1.1.1
|
||||
pyflakes==1.3.0
|
||||
pyparsing==2.1.9
|
||||
pyparsing==2.1.10
|
||||
six==1.10.0
|
||||
|
@ -12,7 +12,7 @@ flake8-tidy-imports
|
||||
flake8-tuple
|
||||
hacking
|
||||
pep8-naming
|
||||
pydocstyle!=1.1.0
|
||||
pydocstyle
|
||||
pyflakes
|
||||
|
||||
# Pinned to 1.5.7 by hacking otherwise
|
||||
@ -23,6 +23,3 @@ pep8==1.7.0
|
||||
|
||||
# https://github.com/JBKahn/flake8-debugger/issues/5
|
||||
#@ filter: flake8-debugger != 2.0.0
|
||||
|
||||
# https://gitlab.com/pycqa/flake8-docstrings/issues/16
|
||||
#@ filter: pydocstyle != 1.1.0
|
||||
|
@ -1,2 +1,2 @@
|
||||
pip==8.1.2
|
||||
setuptools==28.2.0
|
||||
setuptools==28.6.0
|
||||
|
@ -8,7 +8,7 @@ decorator==4.0.10
|
||||
Flask==0.11.1
|
||||
glob2==0.4.1
|
||||
httpbin==0.5.0
|
||||
hypothesis==3.5.2
|
||||
hypothesis==3.5.3
|
||||
itsdangerous==0.24
|
||||
# Jinja2==2.8
|
||||
Mako==1.0.4
|
||||
@ -19,7 +19,7 @@ py==1.4.31
|
||||
pytest==3.0.3
|
||||
pytest-bdd==2.18.0
|
||||
pytest-catchlog==1.2.2
|
||||
pytest-cov==2.3.1
|
||||
pytest-cov==2.4.0
|
||||
pytest-faulthandler==1.3.0
|
||||
pytest-instafail==0.3.0
|
||||
pytest-mock==1.2
|
||||
@ -31,7 +31,7 @@ pytest-warnings==0.1.0
|
||||
pytest-xvfb==0.3.0
|
||||
six==1.10.0
|
||||
spark-parser==1.4.0
|
||||
uncompyle6==2.8.3
|
||||
uncompyle6==2.9.2
|
||||
vulture==0.10
|
||||
Werkzeug==0.11.11
|
||||
xdis==2.3.1
|
||||
xdis==3.1.0
|
||||
|
@ -2,5 +2,5 @@
|
||||
|
||||
pluggy==0.4.0
|
||||
py==1.4.31
|
||||
tox==2.3.1
|
||||
tox==2.4.1
|
||||
virtualenv==15.0.3
|
||||
|
150
misc/userscripts/cast
Executable file
150
misc/userscripts/cast
Executable file
@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Behaviour
|
||||
# Userscript for qutebrowser which casts the url passed in $1 to the default
|
||||
# ChromeCast device in the network using the program `castnow`
|
||||
#
|
||||
# Usage
|
||||
# You can launch the script from qutebrowser as follows:
|
||||
# spawn --userscript ${PATH_TO_FILE} {url}
|
||||
#
|
||||
# Then, you can control the chromecast by launching the simple command
|
||||
# `castnow` in a shell which will connect to the running castnow instance.
|
||||
#
|
||||
# For stopping the script, issue the command `pkill -f castnow` which would
|
||||
# then let the rest of the userscript execute for cleaning temporary file.
|
||||
#
|
||||
# Thanks
|
||||
# This userscript borrows Thorsten Wißmann's javascript code from his `mpv`
|
||||
# userscript.
|
||||
#
|
||||
# Dependencies
|
||||
# - castnow, https://github.com/xat/castnow
|
||||
#
|
||||
# Author
|
||||
# Simon Désaulniers <sim.desaulniers@gmail.com>
|
||||
|
||||
if [ -z "$QUTE_FIFO" ] ; then
|
||||
cat 1>&2 <<EOF
|
||||
Error: $0 can not be run as a standalone script.
|
||||
|
||||
It is a qutebrowser userscript. In order to use it, call it using
|
||||
'spawn --userscript' as described in qute://help/userscripts.html
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msg() {
|
||||
local cmd="$1"
|
||||
shift
|
||||
local msg="$*"
|
||||
if [ -z "$QUTE_FIFO" ] ; then
|
||||
echo "$cmd: $msg" >&2
|
||||
else
|
||||
echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
|
||||
fi
|
||||
}
|
||||
|
||||
js() {
|
||||
cat <<EOF
|
||||
|
||||
function descendantOfTagName(child, ancestorTagName) {
|
||||
// tells whether child has some (proper) ancestor
|
||||
// with the tag name ancestorTagName
|
||||
while (child.parentNode != null) {
|
||||
child = child.parentNode;
|
||||
if (typeof child.tagName === 'undefined') break;
|
||||
if (child.tagName.toUpperCase() == ancestorTagName.toUpperCase()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var App = {};
|
||||
|
||||
var all_videos = [];
|
||||
all_videos.push.apply(all_videos, document.getElementsByTagName("video"));
|
||||
all_videos.push.apply(all_videos, document.getElementsByTagName("object"));
|
||||
all_videos.push.apply(all_videos, document.getElementsByTagName("embed"));
|
||||
App.backup_videos = Array();
|
||||
App.all_replacements = Array();
|
||||
for (i = 0; i < all_videos.length; i++) {
|
||||
var video = all_videos[i];
|
||||
if (descendantOfTagName(video, "object")) {
|
||||
// skip tags that are contained in an object, because we hide
|
||||
// the object anyway.
|
||||
continue;
|
||||
}
|
||||
var replacement = document.createElement("div");
|
||||
replacement.innerHTML = "
|
||||
<p style=\\"margin-bottom: 0.5em\\">
|
||||
The video is being cast on your ChromeCast device.
|
||||
</p>
|
||||
<p>
|
||||
In order to restore this particular video
|
||||
<a style=\\"font-weight: bold;
|
||||
color: white;
|
||||
background: transparent;
|
||||
\\"
|
||||
onClick=\\"restore_video(this, " + i + ");\\"
|
||||
href=\\"javascript: restore_video(this, " + i + ")\\"
|
||||
>click here</a>.
|
||||
</p>
|
||||
";
|
||||
replacement.style.position = "relative";
|
||||
replacement.style.zIndex = "100003000000";
|
||||
replacement.style.fontSize = "1rem";
|
||||
replacement.style.textAlign = "center";
|
||||
replacement.style.verticalAlign = "middle";
|
||||
replacement.style.height = "100%";
|
||||
replacement.style.background = "#101010";
|
||||
replacement.style.color = "white";
|
||||
replacement.style.border = "4px dashed #545454";
|
||||
replacement.style.padding = "2em";
|
||||
replacement.style.margin = "auto";
|
||||
App.all_replacements[i] = replacement;
|
||||
App.backup_videos[i] = video;
|
||||
video.parentNode.replaceChild(replacement, video);
|
||||
}
|
||||
|
||||
function restore_video(obj, index) {
|
||||
obj = App.all_replacements[index];
|
||||
video = App.backup_videos[index];
|
||||
console.log(video);
|
||||
obj.parentNode.replaceChild(video, obj);
|
||||
}
|
||||
|
||||
/** force repainting the video, thanks to:
|
||||
* http://martinwolf.org/2014/06/10/force-repaint-of-an-element-with-javascript/
|
||||
*/
|
||||
var siteHeader = document.getElementById('header');
|
||||
siteHeader.style.display='none';
|
||||
siteHeader.offsetHeight; // no need to store this anywhere, the reference is enough
|
||||
siteHeader.style.display='block';
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
printjs() {
|
||||
js | sed 's,//.*$,,' | tr '\n' ' '
|
||||
}
|
||||
echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
|
||||
|
||||
tmpdir=$(mktemp -d)
|
||||
file_to_cast=${tmpdir}/qutecast
|
||||
|
||||
# kill any running instance of castnow
|
||||
pkill -f /usr/bin/castnow
|
||||
|
||||
# start youtube download in stream mode (-o -) into temporary file
|
||||
youtube-dl -qo - "$1" > ${file_to_cast} &
|
||||
ytdl_pid=$!
|
||||
|
||||
msg info "Casting $1" >> "$QUTE_FIFO"
|
||||
# start castnow in stream mode to cast on ChromeCast
|
||||
tail -F "${file_to_cast}" | castnow -
|
||||
|
||||
# cleanup remaining background process and file on disk
|
||||
kill ${ytdl_pid}
|
||||
rm -rf ${tmpdir}
|
@ -24,9 +24,9 @@
|
||||
# Caveat: Does not use authentication of any kind. Add it in if you want it to.
|
||||
#
|
||||
|
||||
path=/tmp/qutebrowser_$(mktemp XXXXXXXX).html
|
||||
path=$(mktemp --tmpdir qutebrowser_XXXXXXXX.html)
|
||||
|
||||
curl "$QUTE_URL" > $path
|
||||
curl "$QUTE_URL" > "$path"
|
||||
urxvt -e vim "$path"
|
||||
|
||||
rm "$path"
|
||||
|
@ -30,6 +30,9 @@ qt_log_ignore =
|
||||
^QProcess: Destroyed while process .* is still running\.
|
||||
^"Method "GetAll" with signature "s" on interface "org\.freedesktop\.DBus\.Properties" doesn't exist
|
||||
^"Method \\"GetAll\\" with signature \\"s\\" on interface \\"org\.freedesktop\.DBus\.Properties\\" doesn't exist\\n"
|
||||
^propsReply "Method \\"GetAll\\" with signature \\"s\\" on interface \\"org\.freedesktop\.DBus\.Properties\\" doesn't exist\\n"
|
||||
^nmReply "Method \\"GetDevices\\" with signature \\"\\" on interface \\"org\.freedesktop\.NetworkManager\\" doesn't exist\\n"
|
||||
^"Object path cannot be empty"
|
||||
^virtual void QSslSocketBackendPrivate::transmit\(\) SSL write failed with error: -9805
|
||||
^virtual void QSslSocketBackendPrivate::transmit\(\) SSLRead failed with: -9805
|
||||
^Type conversion already registered from type .*
|
||||
|
@ -50,7 +50,8 @@ from qutebrowser.browser.webkit import cookies, cache, downloads
|
||||
from qutebrowser.browser.webkit.network import (webkitqutescheme, proxy,
|
||||
networkmanager)
|
||||
from qutebrowser.mainwindow import mainwindow
|
||||
from qutebrowser.misc import readline, ipc, savemanager, sessions, crashsignal
|
||||
from qutebrowser.misc import (readline, ipc, savemanager, sessions,
|
||||
crashsignal, earlyinit)
|
||||
from qutebrowser.misc import utilcmds # pylint: disable=unused-import
|
||||
from qutebrowser.utils import (log, version, message, utils, qtutils, urlutils,
|
||||
objreg, usertypes, standarddir, error, debug)
|
||||
@ -198,6 +199,9 @@ def _process_args(args):
|
||||
_open_startpage()
|
||||
_open_quickstart(args)
|
||||
|
||||
delta = datetime.datetime.now() - earlyinit.START_TIME
|
||||
log.init.debug("Init finished after {}s".format(delta.total_seconds()))
|
||||
|
||||
|
||||
def _load_session(name):
|
||||
"""Load the default session.
|
||||
|
@ -62,8 +62,10 @@ def _generate_pdfjs_script(url):
|
||||
url: The url of the pdf page as QUrl.
|
||||
"""
|
||||
return (
|
||||
'PDFJS.verbosity = PDFJS.VERBOSITY_LEVELS.info;\n'
|
||||
'PDFView.open("{url}");\n'
|
||||
'document.addEventListener("DOMContentLoaded", function() {{\n'
|
||||
' PDFJS.verbosity = PDFJS.VERBOSITY_LEVELS.info;\n'
|
||||
' (window.PDFView || window.PDFViewerApplication).open("{url}");\n'
|
||||
'}});\n'
|
||||
).format(url=javascript.string_escape(url.toString(QUrl.FullyEncoded)))
|
||||
|
||||
|
||||
|
@ -37,14 +37,6 @@ from PyQt5.QtCore import QUrl
|
||||
from qutebrowser.browser.webkit import webkitelem, downloads
|
||||
from qutebrowser.utils import log, objreg, message, usertypes, utils, urlutils
|
||||
|
||||
try:
|
||||
import cssutils
|
||||
except (ImportError, re.error):
|
||||
# Catching re.error because cssutils in earlier releases (<= 1.0) is broken
|
||||
# on Python 3.5
|
||||
# See https://bitbucket.org/cthedot/cssutils/issues/52
|
||||
cssutils = None
|
||||
|
||||
_File = collections.namedtuple('_File',
|
||||
['content', 'content_type', 'content_location',
|
||||
'transfer_encoding'])
|
||||
@ -85,6 +77,14 @@ def _get_css_imports_cssutils(data, inline=False):
|
||||
data: The content of the stylesheet to scan as string.
|
||||
inline: True if the argument is an inline HTML style attribute.
|
||||
"""
|
||||
try:
|
||||
import cssutils
|
||||
except (ImportError, re.error):
|
||||
# Catching re.error because cssutils in earlier releases (<= 1.0) is
|
||||
# broken on Python 3.5
|
||||
# See https://bitbucket.org/cthedot/cssutils/issues/52
|
||||
return None
|
||||
|
||||
# We don't care about invalid CSS data, this will only litter the log
|
||||
# output with CSS errors
|
||||
parser = cssutils.CSSParser(loglevel=100,
|
||||
@ -114,10 +114,10 @@ def _get_css_imports(data, inline=False):
|
||||
data: The content of the stylesheet to scan as string.
|
||||
inline: True if the argument is an inline HTML style attribute.
|
||||
"""
|
||||
if cssutils is None:
|
||||
return _get_css_imports_regex(data)
|
||||
else:
|
||||
return _get_css_imports_cssutils(data, inline)
|
||||
imports = _get_css_imports_cssutils(data, inline)
|
||||
if imports is None:
|
||||
imports = _get_css_imports_regex(data)
|
||||
return imports
|
||||
|
||||
|
||||
def _check_rel(element):
|
||||
|
@ -330,7 +330,7 @@ class NetworkManager(QNetworkAccessManager):
|
||||
# altogether.
|
||||
reply.netrc_used = True
|
||||
try:
|
||||
net = netrc.netrc()
|
||||
net = netrc.netrc(config.get('network', 'netrc-file'))
|
||||
authenticators = net.authenticators(reply.url().host())
|
||||
if authenticators is not None:
|
||||
(user, _account, password) = authenticators
|
||||
|
@ -31,8 +31,7 @@ from qutebrowser.utils import message, objreg, qtutils, usertypes, utils
|
||||
from qutebrowser.misc import split
|
||||
|
||||
|
||||
ParseResult = collections.namedtuple('ParseResult', ['cmd', 'args', 'cmdline',
|
||||
'count'])
|
||||
ParseResult = collections.namedtuple('ParseResult', ['cmd', 'args', 'cmdline'])
|
||||
last_command = {}
|
||||
|
||||
macro = {}
|
||||
@ -158,26 +157,6 @@ class CommandRunner(QObject):
|
||||
for sub in sub_texts:
|
||||
yield self.parse(sub, *args, **kwargs)
|
||||
|
||||
def _parse_count(self, cmdstr):
|
||||
"""Split a count prefix off from a command for parse().
|
||||
|
||||
Args:
|
||||
cmdstr: The command/args including the count.
|
||||
|
||||
Return:
|
||||
A (count, cmdstr) tuple, with count being None or int.
|
||||
"""
|
||||
if ':' not in cmdstr:
|
||||
return (None, cmdstr)
|
||||
|
||||
count, cmdstr = cmdstr.split(':', maxsplit=1)
|
||||
try:
|
||||
count = int(count)
|
||||
except ValueError:
|
||||
# We just ignore invalid prefixes
|
||||
count = None
|
||||
return (count, cmdstr)
|
||||
|
||||
def parse(self, text, *, fallback=False, keep=False):
|
||||
"""Split the commandline text into command and arguments.
|
||||
|
||||
@ -191,7 +170,6 @@ class CommandRunner(QObject):
|
||||
A ParseResult tuple.
|
||||
"""
|
||||
cmdstr, sep, argstr = text.partition(' ')
|
||||
count, cmdstr = self._parse_count(cmdstr)
|
||||
|
||||
if not cmdstr and not fallback:
|
||||
raise cmdexc.NoSuchCommandError("No command given")
|
||||
@ -206,8 +184,7 @@ class CommandRunner(QObject):
|
||||
raise cmdexc.NoSuchCommandError(
|
||||
'{}: no such command'.format(cmdstr))
|
||||
cmdline = split.split(text, keep=keep)
|
||||
return ParseResult(cmd=None, args=None, cmdline=cmdline,
|
||||
count=count)
|
||||
return ParseResult(cmd=None, args=None, cmdline=cmdline)
|
||||
|
||||
args = self._split_args(cmd, argstr, keep)
|
||||
if keep and args:
|
||||
@ -217,7 +194,7 @@ class CommandRunner(QObject):
|
||||
else:
|
||||
cmdline = [cmdstr] + args[:]
|
||||
|
||||
return ParseResult(cmd=cmd, args=args, cmdline=cmdline, count=count)
|
||||
return ParseResult(cmd=cmd, args=args, cmdline=cmdline)
|
||||
|
||||
def _completion_match(self, cmdstr):
|
||||
"""Replace cmdstr with a matching completion if there's only one match.
|
||||
@ -295,18 +272,9 @@ class CommandRunner(QObject):
|
||||
args = result.args
|
||||
else:
|
||||
args = replace_variables(self._win_id, result.args)
|
||||
if count is not None:
|
||||
if result.count is not None:
|
||||
raise cmdexc.CommandMetaError("Got count via command and "
|
||||
"prefix!")
|
||||
result.cmd.run(self._win_id, args, count=count)
|
||||
elif result.count is not None:
|
||||
result.cmd.run(self._win_id, args, count=result.count)
|
||||
else:
|
||||
result.cmd.run(self._win_id, args)
|
||||
result.cmd.run(self._win_id, args, count=count)
|
||||
|
||||
parsed_command = (self._parse_count(text)[1],
|
||||
count if count is not None else result.count)
|
||||
parsed_command = (text, count)
|
||||
|
||||
if result.cmdline[0] != 'repeat-command':
|
||||
last_command[cur_mode] = parsed_command
|
||||
|
@ -157,6 +157,7 @@ class Completer(QObject):
|
||||
result = runner.parse(text, fallback=True, keep=True)
|
||||
parts = [x for x in result.cmdline if x]
|
||||
pos = self._cmd.cursorPosition() - len(self._cmd.prefix())
|
||||
pos = min(pos, len(text)) # Qt treats 2-byte UTF-16 chars as 2 chars
|
||||
log.completion.debug('partitioning {} around position {}'.format(parts,
|
||||
pos))
|
||||
for i, part in enumerate(parts):
|
||||
|
@ -438,6 +438,11 @@ def data(readonly=False):
|
||||
SettingValue(typ.HeaderDict(none_ok=True), ''),
|
||||
"Set custom headers for qutebrowser HTTP requests."),
|
||||
|
||||
('netrc-file',
|
||||
SettingValue(typ.File(none_ok=True), ''),
|
||||
"Set location of a netrc-file for HTTP authentication. If empty, "
|
||||
"~/.netrc is used."),
|
||||
|
||||
readonly=readonly
|
||||
)),
|
||||
|
||||
|
@ -503,8 +503,8 @@ class MainWindow(QWidget):
|
||||
# Ask if multiple downloads running
|
||||
if 'downloads' in confirm_quit and download_count > 0:
|
||||
quit_texts.append("{} {} running.".format(
|
||||
tab_count,
|
||||
"download is" if tab_count == 1 else "downloads are"))
|
||||
download_count,
|
||||
"download is" if download_count == 1 else "downloads are"))
|
||||
# Process all quit messages that user must confirm
|
||||
if quit_texts or 'always' in confirm_quit:
|
||||
text = '\n'.join(['Really quit?'] + quit_texts)
|
||||
|
@ -703,13 +703,10 @@ class TabBarStyle(QCommonStyle):
|
||||
text_rect.adjust(indicator_width + indicator_padding.left +
|
||||
indicator_padding.right, 0, 0, 0)
|
||||
|
||||
if opt.icon.isNull():
|
||||
icon_rect = QRect()
|
||||
else:
|
||||
icon_rect = self._get_icon_rect(opt, text_rect)
|
||||
if icon_rect.isValid():
|
||||
icon_padding = self.pixelMetric(PixelMetrics.icon_padding, opt)
|
||||
icon_rect = self._get_icon_rect(opt, text_rect)
|
||||
if icon_rect.isValid():
|
||||
text_rect.adjust(icon_rect.width() + icon_padding, 0, 0, 0)
|
||||
text_rect.adjust(icon_rect.width() + icon_padding, 0, 0, 0)
|
||||
|
||||
text_rect = self._style.visualRect(opt.direction, opt.rect, text_rect)
|
||||
return Layouts(text=text_rect, icon=icon_rect,
|
||||
@ -733,9 +730,17 @@ class TabBarStyle(QCommonStyle):
|
||||
else QIcon.Disabled)
|
||||
icon_state = (QIcon.On if opt.state & QStyle.State_Selected
|
||||
else QIcon.Off)
|
||||
tab_icon_size = opt.icon.actualSize(icon_size, icon_mode, icon_state)
|
||||
tab_icon_size = QSize(min(tab_icon_size.width(), icon_size.width()),
|
||||
min(tab_icon_size.height(), icon_size.height()))
|
||||
# reserve space for favicon when tab bar is vertical (issue #1968)
|
||||
position = config.get('tabs', 'position')
|
||||
if (opt.icon.isNull() and
|
||||
position in [QTabWidget.East, QTabWidget.West] and
|
||||
config.get('tabs', 'show-favicons')):
|
||||
tab_icon_size = icon_size
|
||||
else:
|
||||
actual_size = opt.icon.actualSize(icon_size, icon_mode, icon_state)
|
||||
tab_icon_size = QSize(
|
||||
min(actual_size.width(), icon_size.width()),
|
||||
min(actual_size.height(), icon_size.height()))
|
||||
icon_rect = QRect(text_rect.left(), text_rect.top() + 1,
|
||||
tab_icon_size.width(), tab_icon_size.height())
|
||||
icon_rect = self._style.visualRect(opt.direction, opt.rect, icon_rect)
|
||||
|
@ -37,6 +37,7 @@ import signal
|
||||
import operator
|
||||
import importlib
|
||||
import pkg_resources
|
||||
import datetime
|
||||
try:
|
||||
import tkinter
|
||||
except ImportError:
|
||||
@ -45,6 +46,9 @@ except ImportError:
|
||||
# initialization needs to take place before that!
|
||||
|
||||
|
||||
START_TIME = datetime.datetime.now()
|
||||
|
||||
|
||||
def _missing_str(name, *, windows=None, pip=None, webengine=False):
|
||||
"""Get an error string for missing packages.
|
||||
|
||||
|
@ -481,3 +481,5 @@ class SessionManager(QObject):
|
||||
log.sessions.exception("Error while deleting session!")
|
||||
raise cmdexc.CommandError("Error while deleting session: {}"
|
||||
.format(e))
|
||||
else:
|
||||
log.sessions.debug("Deleted session {}.".format(name))
|
||||
|
@ -86,6 +86,23 @@ def repeat(times: int, command, win_id):
|
||||
commandrunner.run_safely(command)
|
||||
|
||||
|
||||
@cmdutils.register(maxsplit=1, hide=True, no_cmd_split=True,
|
||||
no_replace_variables=True)
|
||||
@cmdutils.argument('win_id', win_id=True)
|
||||
@cmdutils.argument('count', count=True)
|
||||
def run_with_count(count_arg: int, command, win_id, count=1):
|
||||
"""Run a command with the given count.
|
||||
|
||||
If run_with_count itself is run with a count, it multiplies count_arg.
|
||||
|
||||
Args:
|
||||
count_arg: The count to pass to the command.
|
||||
command: The command to run, with optional args.
|
||||
count: The count that run_with_count itself received.
|
||||
"""
|
||||
runners.CommandRunner(win_id).run(command, count_arg * count)
|
||||
|
||||
|
||||
@cmdutils.register(hide=True)
|
||||
def message_error(text):
|
||||
"""Show an error message in the statusbar.
|
||||
|
@ -148,7 +148,11 @@ console_filter = None
|
||||
|
||||
def stub(suffix=''):
|
||||
"""Show a STUB: message for the calling function."""
|
||||
function = inspect.stack()[1][3]
|
||||
try:
|
||||
function = inspect.stack()[1][3]
|
||||
except IndexError: # pragma: no cover
|
||||
misc.exception("Failed to get stack")
|
||||
function = '<unknown>'
|
||||
text = "STUB: {}".format(function)
|
||||
if suffix:
|
||||
text = '{} ({})'.format(text, suffix)
|
||||
|
@ -88,11 +88,13 @@ if [[ $DOCKER ]]; then
|
||||
elif [[ $TRAVIS_OS_NAME == osx ]]; then
|
||||
# Disable App Nap
|
||||
defaults write NSGlobalDomain NSAppSleepDisabled -bool YES
|
||||
curl -LO https://github.com/The-Compiler/homebrew-qt5-webkit/releases/download/v5.6.0-1/pyqt5-5.6.el_capitan.bottle.1.tar.gz
|
||||
curl -LO https://github.com/The-Compiler/homebrew-qt5-webkit/releases/download/v5.6.1_1-1/qt5-5.6.1-1.yosemite.bottle.1.tar.gz
|
||||
|
||||
curl -LO https://bootstrap.pypa.io/get-pip.py
|
||||
sudo -H python get-pip.py
|
||||
|
||||
brew --version
|
||||
brew_install python3 {qt5,pyqt5}-*.bottle.1.tar.gz
|
||||
pip_install pip
|
||||
brew_install python3 qt5 pyqt5
|
||||
|
||||
pip_install tox
|
||||
pip --version
|
||||
tox --version
|
||||
|
@ -3,8 +3,17 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Inputs Autofocus</title>
|
||||
<script type="text/javascript">
|
||||
function setup_event_listener() {
|
||||
var elem = document.getElementById('qute-input-autofocus');
|
||||
console.log(elem);
|
||||
elem.addEventListener('input', function() {
|
||||
console.log("contents: " + elem.value);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<body onload="setup_event_listener()">
|
||||
<input id="qute-input-autofocus" type="text" autofocus value=""/>
|
||||
<input id="qute-input-disabled" disabled="disabled" href="#"/>
|
||||
</body>
|
||||
|
@ -3,8 +3,17 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Input</title>
|
||||
<script type="text/javascript">
|
||||
function setup_event_listener() {
|
||||
var elem = document.getElementById('qute-input');
|
||||
console.log(elem);
|
||||
elem.addEventListener('input', function() {
|
||||
console.log("contents: " + elem.value);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<body onload="setup_event_listener()">
|
||||
<input id="qute-input" type="text" value=""/>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -3,8 +3,16 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Textarea</title>
|
||||
<script type="text/javascript">
|
||||
function setup_event_listener() {
|
||||
var elem = document.getElementById('qute-textarea');
|
||||
elem.addEventListener('input', function() {
|
||||
console.log("contents: " + elem.value);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<body onload="setup_event_listener()">
|
||||
<textarea id="qute-textarea"></textarea>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -3,7 +3,7 @@ Feature: Caret mode
|
||||
|
||||
Background:
|
||||
Given I open data/caret.html
|
||||
And I run :tab-only ;; :enter-mode caret
|
||||
And I run :tab-only ;; enter-mode caret
|
||||
|
||||
# document
|
||||
|
||||
|
@ -22,3 +22,8 @@ Feature: Command bar completion
|
||||
# Make sure qutebrowser doesn't hang
|
||||
And I run :message-info "Still alive!"
|
||||
Then the message "Still alive!" should be shown
|
||||
|
||||
Scenario: Crash when pasting emoji into the command line (#2007)
|
||||
Given I open about:blank
|
||||
When I run :set-cmd-text -s :🌀
|
||||
Then no crash should happen
|
||||
|
@ -182,10 +182,11 @@ Feature: Using hints
|
||||
|
||||
### hints -> auto-follow-timeout
|
||||
|
||||
@not_osx
|
||||
Scenario: Ignoring key presses after auto-following hints
|
||||
When I set hints -> auto-follow-timeout to 500
|
||||
When I set hints -> auto-follow-timeout to 1000
|
||||
And I set hints -> mode to number
|
||||
And I run :bind --force , message-error "This should not happen"
|
||||
And I run :bind --force , message-error "This error message was triggered via a keybinding which should have been inhibited"
|
||||
And I open data/hints/html/simple.html
|
||||
And I hint with args "all"
|
||||
And I press the key "f"
|
||||
|
@ -165,7 +165,7 @@ Feature: Keyboard input
|
||||
Then the javascript message "key press: 88" should be logged
|
||||
And the javascript message "key release: 88" should be logged
|
||||
|
||||
@no_xvfb @posix
|
||||
@no_xvfb @posix @qtwebengine_skip
|
||||
Scenario: :fake-key sending key to the website with other window focused
|
||||
When I open data/keyinput/log.html
|
||||
And I set general -> developer-extras to true
|
||||
|
@ -149,7 +149,7 @@ Feature: Various utility commands.
|
||||
And I run :inspector
|
||||
Then the error "Please enable developer-extras before using the webinspector!" should be shown
|
||||
|
||||
@no_xvfb @posix
|
||||
@no_xvfb @posix @qtwebengine_skip
|
||||
Scenario: Inspector smoke test
|
||||
When I set general -> developer-extras to true
|
||||
And I run :inspector
|
||||
@ -165,7 +165,7 @@ Feature: Various utility commands.
|
||||
Then the error "Please enable developer-extras before using the webinspector!" should be shown
|
||||
|
||||
# Different code path as an inspector got created now
|
||||
@no_xvfb @posix
|
||||
@no_xvfb @posix @qtwebengine_skip
|
||||
Scenario: Inspector smoke test 2
|
||||
When I set general -> developer-extras to true
|
||||
And I run :inspector
|
||||
@ -499,7 +499,7 @@ Feature: Various utility commands.
|
||||
Scenario: Using :debug-log-capacity
|
||||
When I run :debug-log-capacity 100
|
||||
And I run :message-info oldstuff
|
||||
And I run :repeat 20 :message-info otherstuff
|
||||
And I run :repeat 20 message-info otherstuff
|
||||
And I run :message-info newstuff
|
||||
And I open qute:log
|
||||
Then the page should contain the plaintext "newstuff"
|
||||
@ -549,6 +549,7 @@ Feature: Various utility commands.
|
||||
And I open cookies/set?qute-test=42 without waiting
|
||||
And I wait until cookies is loaded
|
||||
And I open cookies in a new tab
|
||||
And I set general -> private-browsing to false
|
||||
Then the cookie qute-test should be set to 42
|
||||
|
||||
## https://github.com/The-Compiler/qutebrowser/issues/1742
|
||||
@ -557,6 +558,7 @@ Feature: Various utility commands.
|
||||
Scenario: Private browsing is activated in QtWebKit without restart
|
||||
When I set general -> private-browsing to true
|
||||
And I open data/javascript/localstorage.html
|
||||
And I set general -> private-browsing to false
|
||||
Then the page should contain the plaintext "Local storage status: not working"
|
||||
|
||||
Scenario: :repeat-command
|
||||
@ -695,3 +697,63 @@ Feature: Various utility commands.
|
||||
And I wait until the scroll position changed
|
||||
And I run :click-element id link
|
||||
Then the error "Element position is out of view!" should be shown
|
||||
|
||||
## :command-history-{prev,next}
|
||||
|
||||
Scenario: Calling previous command
|
||||
When I run :set-cmd-text :message-info blah
|
||||
And I run :command-accept
|
||||
And I wait for "blah" in the log
|
||||
And I run :set-cmd-text :
|
||||
And I run :command-history-prev
|
||||
And I run :command-accept
|
||||
Then the message "blah" should be shown
|
||||
|
||||
Scenario: Browsing through commands
|
||||
When I run :set-cmd-text :message-info blarg
|
||||
And I run :command-accept
|
||||
And I wait for "blarg" in the log
|
||||
And I run :set-cmd-text :
|
||||
And I run :command-history-prev
|
||||
And I run :command-history-prev
|
||||
And I run :command-history-next
|
||||
And I run :command-history-next
|
||||
And I run :command-accept
|
||||
Then the message "blarg" should be shown
|
||||
|
||||
Scenario: Calling previous command when history is empty
|
||||
Given I have a fresh instance
|
||||
When I run :set-cmd-text :
|
||||
And I run :command-history-prev
|
||||
And I run :command-accept
|
||||
Then the error "No command given" should be shown
|
||||
|
||||
Scenario: Calling next command when there's no next command
|
||||
When I run :set-cmd-text :
|
||||
And I run :command-history-next
|
||||
And I run :command-accept
|
||||
Then the error "No command given" should be shown
|
||||
|
||||
@qtwebengine_todo: private browsing is not implemented yet
|
||||
Scenario: Calling previous command with private-browsing mode
|
||||
When I run :set-cmd-text :message-info blah
|
||||
And I run :command-accept
|
||||
And I set general -> private-browsing to true
|
||||
And I run :set-cmd-text :message-error "This should only be shown once"
|
||||
And I run :command-accept
|
||||
And I wait for the error "This should only be shown once"
|
||||
And I run :set-cmd-text :
|
||||
And I run :command-history-prev
|
||||
And I run :command-accept
|
||||
And I set general -> private-browsing to false
|
||||
Then the message "blah" should be shown
|
||||
|
||||
## :run-with-count
|
||||
|
||||
Scenario: :run-with-count
|
||||
When I run :run-with-count 2 scroll down
|
||||
Then "command called: scroll ['down'] (count=2)" should be logged
|
||||
|
||||
Scenario: :run-with-count with count
|
||||
When I run :run-with-count 2 scroll down with count 3
|
||||
Then "command called: scroll ['down'] (count=6)" should be logged
|
||||
|
@ -294,11 +294,13 @@ Feature: Saving and loading sessions
|
||||
Scenario: Deleting internal session with --force
|
||||
When I run :session-save --force _internal
|
||||
And I run :session-delete --force _internal
|
||||
And I wait for "Deleted session _internal." in the log
|
||||
Then the session _internal should not exist
|
||||
|
||||
Scenario: Normally deleting a session
|
||||
When I run :session-save deleted_session
|
||||
And I run :session-delete deleted_session
|
||||
And I wait for "Deleted session deleted_session." in the log
|
||||
Then the session deleted_session should not exist
|
||||
|
||||
Scenario: Deleting a session which doesn't exist
|
||||
|
@ -987,13 +987,13 @@ Feature: Tab management
|
||||
Scenario: Using :tab-next after closing last tab (#1448)
|
||||
When I set tabs -> last-close to close
|
||||
And I run :tab-only
|
||||
And I run :tab-close ;; :tab-next
|
||||
And I run :tab-close ;; tab-next
|
||||
Then qutebrowser should quit
|
||||
And no crash should happen
|
||||
|
||||
Scenario: Using :tab-prev after closing last tab (#1448)
|
||||
When I set tabs -> last-close to close
|
||||
And I run :tab-only
|
||||
And I run :tab-close ;; :tab-prev
|
||||
And I run :tab-close ;; tab-prev
|
||||
Then qutebrowser should quit
|
||||
And no crash should happen
|
||||
|
@ -437,7 +437,8 @@ class QuteProc(testprocess.Process):
|
||||
command = command.replace('\\', r'\\')
|
||||
|
||||
if count is not None:
|
||||
command = ':{}:{}'.format(count, command.lstrip(':'))
|
||||
command = ':run-with-count {} {}'.format(count,
|
||||
command.lstrip(':'))
|
||||
|
||||
self.send_ipc([command])
|
||||
if not invalid:
|
||||
|
@ -80,8 +80,11 @@ def redirect_later():
|
||||
@app.route('/custom/redirect-later-continue')
|
||||
def redirect_later_continue():
|
||||
"""Continue a redirect-later request."""
|
||||
_redirect_later_event.set()
|
||||
return flask.Response(b'Continued redirect.')
|
||||
if _redirect_later_event is None:
|
||||
return flask.Response(b'Timed out or no redirect pending.')
|
||||
else:
|
||||
_redirect_later_event.set()
|
||||
return flask.Response(b'Continued redirect.')
|
||||
|
||||
|
||||
@app.route('/custom/content-size')
|
||||
|
@ -56,6 +56,10 @@ def test_insert_mode(file_name, elem_id, source, input_text, auto_insert,
|
||||
# second time.
|
||||
quteproc.send_cmd(':debug-set-fake-clipboard "{}"'.format(input_text))
|
||||
quteproc.send_cmd(':insert-text {clipboard}')
|
||||
else:
|
||||
raise ValueError("Invalid source {!r}".format(source))
|
||||
|
||||
quteproc.wait_for_js('contents: {}'.format(input_text))
|
||||
|
||||
quteproc.send_cmd(':leave-mode')
|
||||
quteproc.send_cmd(':hint all')
|
||||
|
@ -36,11 +36,11 @@ from qutebrowser.browser import pdfjs
|
||||
'http://foobar/%22);alert(%22attack!%22);'),
|
||||
])
|
||||
def test_generate_pdfjs_script(url, expected):
|
||||
expected_code = ('PDFJS.verbosity = PDFJS.VERBOSITY_LEVELS.info;\n'
|
||||
'PDFView.open("{}");\n'.format(expected))
|
||||
expected_open = 'open("{}");'.format(expected)
|
||||
url = QUrl(url)
|
||||
actual = pdfjs._generate_pdfjs_script(url)
|
||||
assert actual == expected_code
|
||||
assert expected_open in actual
|
||||
assert 'PDFView' in actual
|
||||
|
||||
|
||||
def test_fix_urls():
|
||||
|
@ -26,6 +26,15 @@ import pytest
|
||||
mhtml = pytest.importorskip('qutebrowser.browser.webkit.mhtml')
|
||||
|
||||
|
||||
try:
|
||||
import cssutils
|
||||
except (ImportError, re.error):
|
||||
# Catching re.error because cssutils in earlier releases (<= 1.0) is
|
||||
# broken on Python 3.5
|
||||
# See https://bitbucket.org/cthedot/cssutils/issues/52
|
||||
cssutils = None
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def patch_uuid(monkeypatch):
|
||||
monkeypatch.setattr("uuid.uuid4", lambda: "UUID")
|
||||
@ -248,8 +257,7 @@ def test_empty_content_type(checker):
|
||||
|
||||
|
||||
@pytest.mark.parametrize('has_cssutils', [
|
||||
pytest.mark.skipif(mhtml.cssutils is None,
|
||||
reason="requires cssutils")(True),
|
||||
pytest.mark.skipif(cssutils is None, reason="requires cssutils")(True),
|
||||
False,
|
||||
], ids=['with_cssutils', 'no_cssutils'])
|
||||
@pytest.mark.parametrize('inline, style, expected_urls', [
|
||||
@ -267,7 +275,8 @@ def test_empty_content_type(checker):
|
||||
def test_css_url_scanner(monkeypatch, has_cssutils, inline, style,
|
||||
expected_urls):
|
||||
if not has_cssutils:
|
||||
monkeypatch.setattr('qutebrowser.browser.webkit.mhtml.cssutils', None)
|
||||
monkeypatch.setattr(mhtml, '_get_css_imports_cssutils',
|
||||
lambda data, inline=False: None)
|
||||
expected_urls.sort()
|
||||
urls = mhtml._get_css_imports(style, inline=inline)
|
||||
urls.sort()
|
||||
|
@ -64,15 +64,6 @@ class TestCommandRunner:
|
||||
with pytest.raises(cmdexc.NoSuchCommandError):
|
||||
list(cr.parse_all(command))
|
||||
|
||||
def test_parse_with_count(self):
|
||||
"""Test parsing of commands with a count."""
|
||||
cr = runners.CommandRunner(0)
|
||||
result = cr.parse('20:scroll down')
|
||||
assert result.cmd.name == 'scroll'
|
||||
assert result.count == 20
|
||||
assert result.args == ['down']
|
||||
assert result.cmdline == ['scroll', 'down']
|
||||
|
||||
def test_partial_parsing(self):
|
||||
"""Test partial parsing with a runner where it's enabled.
|
||||
|
||||
|
@ -181,6 +181,7 @@ def _set_cmd_prompt(cmd, txt):
|
||||
(':open -- |', None, ''),
|
||||
(':gibberish nonesense |', None, ''),
|
||||
('/:help|', None, ''),
|
||||
('::bind|', usertypes.Completion.command, ':bind'),
|
||||
])
|
||||
def test_update_completion(txt, kind, pattern, status_command_stub,
|
||||
completer_obj, completion_widget_stub):
|
||||
|
@ -41,6 +41,7 @@ class TestTabWidget:
|
||||
'position': 0,
|
||||
'select-on-remove': 1,
|
||||
'show': 'always',
|
||||
'show-favicons': True,
|
||||
'padding': configtypes.PaddingValues(0, 0, 5, 5),
|
||||
'indicator-width': 3,
|
||||
'indicator-padding': configtypes.PaddingValues(2, 2, 0, 4),
|
||||
|
Loading…
Reference in New Issue
Block a user