Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
174a0f8abf | ||
|
|
7972addd82 |
@@ -1,17 +0,0 @@
|
||||
shallow_clone: true
|
||||
version: '{branch}-{build}'
|
||||
cache:
|
||||
- C:\projects\qutebrowser\.cache
|
||||
build: off
|
||||
environment:
|
||||
PYTHONUNBUFFERED: 1
|
||||
matrix:
|
||||
- TESTENV: py34
|
||||
- TESTENV: unittests-frozen
|
||||
- TESTENV: pylint
|
||||
|
||||
install:
|
||||
- C:\Python27\python -u scripts\dev\ci\appveyor_install.py
|
||||
|
||||
test_script:
|
||||
- C:\Python34\Scripts\tox -e %TESTENV%
|
||||
@@ -1,10 +1,8 @@
|
||||
[run]
|
||||
source = qutebrowser
|
||||
branch = true
|
||||
omit =
|
||||
qutebrowser/__main__.py
|
||||
*/__init__.py
|
||||
qutebrowser/resources.py
|
||||
|
||||
[report]
|
||||
exclude_lines =
|
||||
@@ -13,6 +11,3 @@ exclude_lines =
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
if __name__ == ["']__main__["']:
|
||||
|
||||
[xml]
|
||||
output=coverage.xml
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
charset = utf-8
|
||||
|
||||
max_line_length = 79
|
||||
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[*.feature]
|
||||
max_line_length = 9999
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
qutebrowser/3rdparty/pdfjs/*
|
||||
49
.eslintrc
@@ -1,49 +0,0 @@
|
||||
# vim: ft=yaml
|
||||
|
||||
env:
|
||||
browser: true
|
||||
|
||||
rules:
|
||||
block-scoped-var: 2
|
||||
dot-location: 2
|
||||
default-case: 2
|
||||
guard-for-in: 2
|
||||
no-div-regex: 2
|
||||
no-param-reassign: 2
|
||||
no-eq-null: 2
|
||||
no-floating-decimal: 2
|
||||
no-self-compare: 2
|
||||
no-throw-literal: 2
|
||||
no-void: 2
|
||||
radix: 2
|
||||
wrap-iife: [2, "inside"]
|
||||
brace-style: [2, "1tbs", {"allowSingleLine": true}]
|
||||
comma-style: [2, "last"]
|
||||
consistent-this: [2, "self"]
|
||||
func-style: [2, "declaration"]
|
||||
indent: [2, 4, {"SwitchCase": 1}]
|
||||
linebreak-style: [2, "unix"]
|
||||
max-nested-callbacks: [2, 3]
|
||||
no-lonely-if: 2
|
||||
no-multiple-empty-lines: [2, {"max": 2}]
|
||||
no-nested-ternary: 2
|
||||
no-unneeded-ternary: 2
|
||||
operator-assignment: [2, "always"]
|
||||
operator-linebreak: [2, "after"]
|
||||
keyword-spacing: 2
|
||||
space-before-blocks: [2, "always"]
|
||||
space-before-function-paren: [2, {"anonymous": "never", "named": "never"}]
|
||||
object-curly-spacing: [2, "never"]
|
||||
array-bracket-spacing: [2, "never"]
|
||||
computed-property-spacing: [2, "never"]
|
||||
space-in-parens: [2, "never"]
|
||||
space-unary-ops: [2, {"words": true, "nonwords": false}]
|
||||
spaced-comment: [2, "always"]
|
||||
max-depth: [2, 5]
|
||||
max-len: [2, 79, 4]
|
||||
max-params: [2, 5]
|
||||
max-statements: [2, 30]
|
||||
no-bitwise: 2
|
||||
quote-props: [2, "always"]
|
||||
global-strict: 0
|
||||
quotes: 0
|
||||
51
.flake8
@@ -1,48 +1,13 @@
|
||||
# vim: ft=dosini fileencoding=utf-8:
|
||||
|
||||
[flake8]
|
||||
exclude = .*,__pycache__,resources.py
|
||||
# E128: continuation line under-indented for visual indent
|
||||
# E226: missing whitespace around arithmetic operator
|
||||
# E265: Block comment should start with '#'
|
||||
# E501: Line too long
|
||||
# F841: unused variable
|
||||
# F401: Unused import
|
||||
# E402: module level import not at top of file
|
||||
# E266: too many leading '#' for block comment
|
||||
# F401: Unused import
|
||||
# N802: function name should be lowercase
|
||||
# P101: format string does contain unindexed parameters
|
||||
# P102: docstring does contain unindexed parameters
|
||||
# P103: other string does contain unindexed parameters
|
||||
# D102: Missing docstring in public method (will be handled by others)
|
||||
# D103: Missing docstring in public function (will be handled by others)
|
||||
# D104: Missing docstring in public package (will be handled by others)
|
||||
# D105: Missing docstring in magic method (will be handled by others)
|
||||
# D209: Blank line before closing """ (removed from PEP257)
|
||||
# D211: No blank lines allowed before class docstring
|
||||
# (PEP257 got changed, but let's stick to the old standard)
|
||||
# D402: First line should not be function's signature (false-positives)
|
||||
# D403: First word of the first line should be properly capitalized
|
||||
# (false-positives)
|
||||
# H101: Use TODO(NAME)
|
||||
# H201: bare except
|
||||
# H238: Use new-stule classes
|
||||
# H301: one import per line
|
||||
# H306: imports not in alphabetical order
|
||||
ignore =
|
||||
E128,E226,E265,E501,E402,E266,
|
||||
F401,
|
||||
N802,
|
||||
P101,P102,P103,
|
||||
D102,D103,D104,D105,D209,D211,D402,D403,
|
||||
H101,H201,H238,H301,H306
|
||||
min-version = 3.4.0
|
||||
max-complexity = 12
|
||||
putty-auto-ignore = True
|
||||
putty-ignore =
|
||||
/# pylint: disable=invalid-name/ : +N801,N806
|
||||
/# pylint: disable=wildcard-import/ : +F403
|
||||
/# pragma: no mccabe/ : +C901
|
||||
tests/*/test_*.py : +D100,D101,D401
|
||||
tests/unit/browser/webkit/http/test_content_disposition.py : +D400
|
||||
scripts/dev/ci/appveyor_install.py : +FI53
|
||||
copyright-check = True
|
||||
copyright-regexp = # Copyright [\d-]+ .*
|
||||
copyright-min-file-size = 110
|
||||
# W503: line break before binary operator
|
||||
ignore=E265,E501,F841,F401,E402,E266,W503
|
||||
max_complexity = 12
|
||||
exclude=resources.py
|
||||
|
||||
4
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,4 +0,0 @@
|
||||
Please remember to mention your version info (qutebrowser, Qt, PyQt,
|
||||
OS/distribution) from the `qute:version` page or `qutebrowser --version`
|
||||
|
||||
---
|
||||
16
.gitignore
vendored
@@ -1,7 +1,5 @@
|
||||
__pycache__
|
||||
*.py~
|
||||
*.pyc
|
||||
*.swp
|
||||
/build
|
||||
/dist
|
||||
/qutebrowser.egg-info
|
||||
@@ -12,7 +10,6 @@ __pycache__
|
||||
/setuptools-*.egg
|
||||
/setuptools-*.zip
|
||||
/qutebrowser/git-commit-id
|
||||
/qutebrowser/3rdparty
|
||||
/doc/*.html
|
||||
/README.html
|
||||
/CHANGELOG.html
|
||||
@@ -23,17 +20,4 @@ __pycache__
|
||||
/.venv
|
||||
/.coverage
|
||||
/htmlcov
|
||||
/coverage.xml
|
||||
/.coverage.*
|
||||
/.tox
|
||||
/testresults.html
|
||||
/.cache
|
||||
/.testmondata
|
||||
/.hypothesis
|
||||
/prof
|
||||
/venv
|
||||
TODO
|
||||
/scripts/testbrowser_cpp/Makefile
|
||||
/scripts/testbrowser_cpp/main.o
|
||||
/scripts/testbrowser_cpp/testbrowser
|
||||
/scripts/dev/pylint_checkers/qute_pylint.egg-info
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
[pydocstyle]
|
||||
# Disabled checks:
|
||||
# D102: Missing docstring in public method (will be handled by others)
|
||||
# D103: Missing docstring in public function (will be handled by others)
|
||||
# D104: Missing docstring in public package (will be handled by others)
|
||||
# D105: Missing docstring in magic method (will be handled by others)
|
||||
# D209: Blank line before closing """ (removed from PEP257)
|
||||
# D211: No blank lines allowed before class docstring
|
||||
# (PEP257 got changed, but let's stick to the old standard)
|
||||
# D402: First line should not be function's signature (false-positives)
|
||||
ignore = D102,D103,D104,D105,D209,D211,D402
|
||||
match = (?!resources|test_*).*\.py
|
||||
inherit = false
|
||||
52
.pylintrc
@@ -3,21 +3,18 @@
|
||||
[MASTER]
|
||||
ignore=resources.py
|
||||
extension-pkg-whitelist=PyQt5,sip
|
||||
load-plugins=qute_pylint.config,
|
||||
qute_pylint.modeline,
|
||||
qute_pylint.openencoding,
|
||||
qute_pylint.settrace,
|
||||
pylint.extensions.bad_builtin,
|
||||
pylint.extensions.docstyle
|
||||
persistent=n
|
||||
load-plugins=pylint_checkers.config,
|
||||
pylint_checkers.crlf,
|
||||
pylint_checkers.modeline,
|
||||
pylint_checkers.openencoding,
|
||||
pylint_checkers.settrace
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
enable=all
|
||||
disable=no-self-use,
|
||||
bad-builtin,
|
||||
fixme,
|
||||
global-statement,
|
||||
locally-disabled,
|
||||
locally-enabled,
|
||||
too-many-ancestors,
|
||||
too-few-public-methods,
|
||||
too-many-public-methods,
|
||||
@@ -27,33 +24,27 @@ disable=no-self-use,
|
||||
blacklisted-name,
|
||||
too-many-lines,
|
||||
logging-format-interpolation,
|
||||
interface-not-implemented,
|
||||
broad-except,
|
||||
bare-except,
|
||||
eval-used,
|
||||
exec-used,
|
||||
file-ignored,
|
||||
wrong-import-order,
|
||||
ungrouped-imports,
|
||||
redefined-variable-type,
|
||||
suppressed-message,
|
||||
too-many-return-statements,
|
||||
duplicate-code,
|
||||
wrong-import-position
|
||||
exec-used
|
||||
|
||||
[BASIC]
|
||||
function-rgx=[a-z_][a-z0-9_]{2,50}$
|
||||
module-rgx=(__)?[a-z][a-z0-9_]*(__)?$
|
||||
function-rgx=([a-z_][a-z0-9_]{2,30}|setUpModule|tearDownModule)$
|
||||
const-rgx=[A-Za-z_][A-Za-z0-9_]{0,30}$
|
||||
method-rgx=[a-z_][A-Za-z0-9_]{1,50}$
|
||||
method-rgx=[a-z_][A-Za-z0-9_]{2,40}$
|
||||
attr-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
argument-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
variable-rgx=[a-z_][a-z0-9_]{0,30}$
|
||||
docstring-min-length=3
|
||||
no-docstring-rgx=(^_|^main$)
|
||||
class-attribute-rgx=[A-Za-z_][A-Za-z0-9_]{1,30}$
|
||||
inlinevar-rgx=[a-z_][a-z0-9_]*$
|
||||
docstring-min-length=2
|
||||
|
||||
[FORMAT]
|
||||
max-line-length=79
|
||||
ignore-long-lines=(<?https?://|^# Copyright 201\d|# (pylint|flake8): disable=)
|
||||
expected-line-ending-format=LF
|
||||
ignore-long-lines=<?https?://
|
||||
|
||||
[SIMILARITIES]
|
||||
min-similarity-lines=8
|
||||
@@ -61,16 +52,11 @@ min-similarity-lines=8
|
||||
[VARIABLES]
|
||||
dummy-variables-rgx=_.*
|
||||
|
||||
[CLASSES]
|
||||
defining-attr-methods=__init__,__new__,setUp
|
||||
|
||||
[DESIGN]
|
||||
max-args=10
|
||||
|
||||
[CLASSES]
|
||||
valid-metaclass-classmethod-first-arg=cls
|
||||
|
||||
[TYPECHECK]
|
||||
# MsgType added as WORKAROUND for
|
||||
# https://bitbucket.org/logilab/pylint/issues/690/
|
||||
# UnsetObject because pylint infers any objreg.get(...) as UnsetObject.
|
||||
ignored-classes=qutebrowser.utils.objreg.UnsetObject,
|
||||
qutebrowser.browser.webkit.webelem.WebElementWrapper,
|
||||
scripts.dev.check_coverage.MsgType
|
||||
ignored-classes=WebElementWrapper,AnsiCodes,UnsetObject
|
||||
|
||||
69
.travis.yml
@@ -1,69 +0,0 @@
|
||||
sudo: required
|
||||
dist: trusty
|
||||
language: generic
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
env: TESTENV=py34-cov
|
||||
- os: linux
|
||||
env: DOCKER=debian-jessie
|
||||
services: docker
|
||||
- os: linux
|
||||
env: DOCKER=archlinux
|
||||
services: docker
|
||||
- os: linux
|
||||
env: DOCKER=ubuntu-xenial
|
||||
services: docker
|
||||
- os: osx
|
||||
env: TESTENV=py35
|
||||
- os: linux
|
||||
env: TESTENV=pylint
|
||||
- os: linux
|
||||
env: TESTENV=flake8
|
||||
- os: linux
|
||||
env: TESTENV=docs
|
||||
- os: linux
|
||||
env: TESTENV=vulture
|
||||
- os: linux
|
||||
env: TESTENV=misc
|
||||
- os: linux
|
||||
env: TESTENV=pyroma
|
||||
- os: linux
|
||||
env: TESTENV=check-manifest
|
||||
- os: linux
|
||||
env: TESTENV=eslint
|
||||
allow_failures:
|
||||
- os: osx
|
||||
env: TESTENV=py35
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.cache/pip
|
||||
- $HOME/build/The-Compiler/qutebrowser/.cache
|
||||
|
||||
before_install:
|
||||
# We need to do this so we pick up the system-wide python properly
|
||||
- 'export PATH="/usr/bin:$PATH"'
|
||||
|
||||
install:
|
||||
- bash scripts/dev/ci/travis_install.sh
|
||||
|
||||
script:
|
||||
- bash scripts/dev/ci/travis_run.sh
|
||||
|
||||
after_success:
|
||||
- '[[ $TESTENV == *-cov ]] && codecov -e TESTENV -X gcov'
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
- https://buildtimetrend.herokuapp.com/travis
|
||||
irc:
|
||||
channels:
|
||||
- "chat.freenode.net#qutebrowser-dev"
|
||||
on_success: always
|
||||
on_failure: always
|
||||
skip_join: true
|
||||
template:
|
||||
- "%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}"
|
||||
- "%{compare_url} - %{build_url}"
|
||||
@@ -14,603 +14,6 @@ This project adheres to http://semver.org/[Semantic Versioning].
|
||||
// `Fixed` for any bug fixes.
|
||||
// `Security` to invite users to upgrade in case of vulnerabilities.
|
||||
|
||||
v0.8.2
|
||||
------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed `general -> private-browsing` not being set correctly until a restart
|
||||
(which caused e.g. local storage to be enabled).
|
||||
- Fixed crash when using hints with JS disabled in some rare circumstances.
|
||||
- When hinting input fields (`:t`), also consider input elements without a type.
|
||||
- Fixed crash when opening an invalid URL with a percent-encoded and a real @ in it
|
||||
- Fixed default `;o` and `;O` bindings
|
||||
- Fixed local storage not working (and possible other bugs) when using a
|
||||
relative path with `--basedir`.
|
||||
- Fixed crash when deleting a quickmark with Ctrl-D
|
||||
- Fixed HTML5 video playback on Windows
|
||||
- Fixed crash when using `:prompt-open-download` with a file with chars not
|
||||
encodable with the OS' filesystem encoding (e.g. with `LC_ALL=C`)
|
||||
- Fixed `:prompt-open-download` with a too long filename (< 255 bytes)
|
||||
- Fixed crash when cancelling a download after doing `:prompt-open-download`
|
||||
- Fixed crash when writing a download to disk fails with
|
||||
`:prompt-open-download`.
|
||||
- Fixed HTML5 video playback on Windows
|
||||
|
||||
v0.8.1
|
||||
------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fix crash when pressing enter without a command
|
||||
- Adjust error message to point out QtWebEngine is unsupported with the OS
|
||||
X .app currently.
|
||||
- Hide Harfbuzz warning with the OS X .app
|
||||
|
||||
v0.8.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New `:repeat-command` command (mapped to `.`) to repeat the last command.
|
||||
Note that two former default bundings conflict with that binding, unbinding
|
||||
them via `:unbind .i` and `:unbind .o` is recommended.
|
||||
- New `qute:bookmarks` page which displays all bookmarks and quickmarks.
|
||||
- New `:prompt-open-download` (bound to `Ctrl-X`) which can be used to open a
|
||||
download directly when getting the filename prompt.
|
||||
- New `{host}` replacement for tab- and window titles which evaluates
|
||||
to the current host.
|
||||
- New default binding `;t` for `:hint input`.
|
||||
- New variables `$QUTE_CONFIG_DIR`, `$QUTE_DATA_DIR` and
|
||||
`$QUTE_DOWNLOAD_DIR` available for userscripts.
|
||||
- New option `ui` -> `status-position` to configure the position of the
|
||||
status bar (top/bottom).
|
||||
- New `--pdf <filename>` argument for `:print` WHICH can be used to generate a
|
||||
PDF without a dialog.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- `:scroll-perc` now prefers a count over the argument given to it, which means
|
||||
`gg` can be used with a count.
|
||||
- Aliases can now use `;;` to have an alias which executed multiple commands.
|
||||
- `:edit-url` now does nothing if the URL isn't changed in the spawned editor.
|
||||
- `:bookmark-add` can now be passed a URL and title to add that as a bookmark
|
||||
rather than the current page.
|
||||
- New `taskadd` userscript to add a taskwarrior task annotated with the
|
||||
current URL.
|
||||
- `:bookmark-del` and `:quickmark-del` now delete the current page's URL if none
|
||||
is given.
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
- Compatibility with PyQt 5.7
|
||||
- Fixed some configuration values being lost when a config option gets removed
|
||||
from qutebrowser's code.
|
||||
- Fix crash when downloading with a full disk
|
||||
- Using `:jump-mark` (e.g. `''`) when the current URL is invalid doesn't crash
|
||||
anymore.
|
||||
|
||||
Removed
|
||||
-------
|
||||
|
||||
- The ability to display status messages from webpages, as well as the related
|
||||
`ui -> display-statusbar-messages` setting.
|
||||
- The `general -> wrap-search` setting as searches now always wrap.
|
||||
According to a quick straw poll and prior crash logs, almost nobody is using
|
||||
`wrap-search = false`, and turning off wrapping is not possible with
|
||||
QtWebEngine.
|
||||
- `:edit-url` now doesn't accept a count anymore as its behavior was confusing
|
||||
and it doesn't make much sense to add a count.
|
||||
|
||||
v0.7.0
|
||||
------
|
||||
|
||||
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 `''`
|
||||
- New `--force-color` argument to force colored logging even if stdout is not a
|
||||
terminal
|
||||
- New `:messages` command to show error messages
|
||||
- New pop-up showing possible keybinding when the first key of a keychain is
|
||||
pressed. This can be turned off using `:set ui keyhint-blacklist *`.
|
||||
- New `hints -> auto-follow-timeout` setting to ignore keypresses after
|
||||
following a hint when filtering in number mode.
|
||||
- New `:history-clear` command to clear the entire history
|
||||
- New `hints -> find-implementation` to select which implementation (JS/Python)
|
||||
should be used to find hints on a page. The `javascript` implementation is
|
||||
better, but slower.
|
||||
- New `inputs` group for `:hint` to hint text input fields.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- qutebrowser got a new (slightly updated) logo
|
||||
- `:tab-focus` can now take a negative index to focus the nth tab counted from
|
||||
the right.
|
||||
- `:yank` can now yank the pretty/decoded URL by adding `--pretty`
|
||||
- `:navigate` now clears the URL fragment
|
||||
- `:completion-item-del` (`Ctrl-D`) can now be used in `:buffer` completion to
|
||||
close a tab
|
||||
- Counts can now be used with special keybindings (e.g. with modifiers)
|
||||
- Various SSL ciphers are now disabled by default. With recent Qt/OpenSSL
|
||||
versions those already all are disabled, but with older versions they might
|
||||
not be.
|
||||
- Show favicons as window icon with `tabs-are-windows` set.
|
||||
- `:bind <key>` without a command now shows the existing binding
|
||||
- The optional `colorlog` dependency got removed, as qutebrowser now displays
|
||||
colored logs without it.
|
||||
- URLs are now shown decoded when hovering.
|
||||
- Keybindings are now shown in the command completion
|
||||
- Improved behavior when pasting multiple lines
|
||||
- Rapid hints can now also be used for the `normal` hint target, which can be
|
||||
useful with javascript click handlers or checkboxes which don't actually open
|
||||
a new page.
|
||||
- `:zoom-in` or `:zoom-out` (`+`/`-`) with a too large count now zooms to the
|
||||
smallest/largest zoom instead of doing nothing.
|
||||
- The commandline now accepts partially typed commands if they're unique.
|
||||
- Number hints are now kept filtered after following a hint in rapid mode.
|
||||
- Number hints are now renumbered after filtering
|
||||
- Number hints can now be filtered with multiple space-separated search terms
|
||||
- `hints -> scatter` is now ignored for number hints
|
||||
- Better history implementation which also stores titles.
|
||||
As a consequence, URLs which redirect to another URL are now added to the
|
||||
history too, marked with a `-r` suffix to the timestamp field.
|
||||
|
||||
Fixed
|
||||
-----
|
||||
|
||||
- Fixed using `:hint links spawn` with flags - you can now use things like the
|
||||
`-v` argument for `:spawn` or pass flags to the spawned commands.
|
||||
- Various fixes for hinting corner-cases where following a link didn't work or
|
||||
the hint was drawn at the wrong position.
|
||||
- Fixed crash when downloading from a URL with SSL errors
|
||||
- Close file handles correctly when a download failed
|
||||
- Fixed crash when using `;Y` (`:hint links yank-primary`) on a system without
|
||||
primary selection
|
||||
- Don't display quit confirmation with finished downloads
|
||||
- Fixed updating the tab index in the statusbar when opening a background tab
|
||||
- Fixed a crash when entering `:-- ` in the commandline
|
||||
- Fixed `:debug-console` with PyQt 5.6
|
||||
- Fixed qutebrowser not starting when `sys.stderr` is `None`
|
||||
- Fixed crash when cancelling a download which belongs to an MHTML download
|
||||
- Fixed rebinding of keybindings being case-sensitive
|
||||
- Fix for tab indicators getting lost when moving tabs
|
||||
- Fixed handling of backspace in number hinting mode
|
||||
- Fixed `FileNotFoundError` when starting in some cases on old Qt versions
|
||||
- Fixed sharing of cookies between tabs when `private-browsing` is enabled
|
||||
- Toggling values with `:set` now uses lower-case values
|
||||
- Hints now work with (non-standard) links with spaces around the URL
|
||||
- Strip off trailing spaces for history entries with no title
|
||||
|
||||
v0.6.2
|
||||
------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed crash when using `:tab-{prev,next,focus}` right after closing the last
|
||||
tab with `last-close` set to `close`.
|
||||
- Fixed crash when doing `:undo` in a new instance with `tabs -> last-close` set
|
||||
to `default-page`.
|
||||
- Fixed crash when starting with --cachedir=""
|
||||
- Fixed crash in some circumstances when using dictionary hints
|
||||
- Fixed various crashes related to PyQt 5.6
|
||||
|
||||
v0.6.1
|
||||
-----
|
||||
|
||||
Fixed
|
||||
~~~~~~
|
||||
|
||||
- Fixed broken cheatsheet image which was missing from package
|
||||
- Fixed occasional crash when switching/disconnecting monitors
|
||||
- Fixed crash when downloading non-ascii files with a broken locale (`LC_ALL=C`)
|
||||
- Added workaround for a Qt/PyQt bug which is too weird to describe here
|
||||
|
||||
v0.6.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New `:buffer` command to easily switch tabs by name. This is not bound to a
|
||||
key by default for existing users due to a conflict with the `gt`/`gT`
|
||||
bindings (which are now removed from the default bindings).
|
||||
You can bind it by hand by running `:bind -f gt set-cmd-text -s :buffer`.
|
||||
- New `--quiet` argument for the `:debug-pyeval` command to not open a tab with
|
||||
the results. Note `:debug-pyeval` is still only intended for debugging.
|
||||
- The completion now matches each entered word separately.
|
||||
- A new command `:paste-primary` got added to paste the primary selection, and
|
||||
`<Shift-Insert>` got added as a binding so it pastes primary rather than
|
||||
clipboard.
|
||||
- New mode `word` for `hints -> mode` which uses a dictionary and link-texts
|
||||
for hints instead of single characters.
|
||||
- New `--all` argument for `:download-cancel` to cancel all running downloads.
|
||||
- New `password_fill` userscript to fill passwords using the `pass` executable.
|
||||
- New `current` hinting mode which forces opening hints in the current tab
|
||||
(even with `target="_blank"`)
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- Pasting multiple lines via `:paste` now opens each line in a new tab.
|
||||
- `:navigate increment/decrement` now preserves leading zeroes in URLs.
|
||||
- `general -> editor` can now also handle `{}` inside another argument (e.g. to open `vim` via `termite`)
|
||||
- Improved performance when scrolling with many tabs open.
|
||||
- Shift-Insert now also pastes primary selection for prompts.
|
||||
- `:download-remove --all` got un-deprecated to provide symmetry with
|
||||
`:download-cancel --all`. It does the same as `:download-clear`.
|
||||
- Improved detection of URLs/search terms when pasting multiple lines.
|
||||
- Don't remove `qutebrowser-editor-*` temporary file if editor subprocess crashed
|
||||
- Userscripts are also searched in `/usr/share/qutebrowser/userscripts`.
|
||||
- Blocked hosts are now also read from a `blocked-hosts` file in the config dir
|
||||
(e.g. `~/.config/qutebrowser/blocked-hosts`).
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed starting with -c "".
|
||||
- Fixed crash when a tab is closed twice via javascript (e.g. Dropbox
|
||||
authentication dialogs)
|
||||
- Fixed crash when a notification/geolocation prompt is answered after closing
|
||||
the tab it belongs to.
|
||||
- Fixed crash when downloading a file without any path information (e.g a
|
||||
magnet link).
|
||||
- Fixed crashes when opening an empty URL (e.g. via pasting).
|
||||
- Fixed validation of duplicate values in `hints -> chars`.
|
||||
- Fixed crash when PDF.js was partially installed.
|
||||
- Fixed crash when XDG_DOWNLOAD_DIR was not an absolute path.
|
||||
- Fixed very long filenames when downloading `data://`-URLs.
|
||||
- Fixed ugly UI fonts on Windows when Liberation Mono is installed
|
||||
- Fixed crash when unbinding key from a section which doesn't exist in the config
|
||||
- Fixed report window after a segfault
|
||||
- Fixed some directory browser issues on Windows
|
||||
- Fixed crash when closing a window with a finished download and delayed
|
||||
`remove-finished-downloads` setting.
|
||||
- Fixed crash when hitting `<Tab>` then `<Ctrl-C>` on pages without keyboard
|
||||
focus.
|
||||
- Fixed "Frame load interrupted by policy change" error showing up when
|
||||
downloading files with Qt 5.6.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- The `gt`/`gT` bindings (luakit-like alternatives to `J`/`K`) were removed
|
||||
(except for existing configs) to make room for the `gt` binding to show
|
||||
buffers.
|
||||
|
||||
v0.5.1
|
||||
------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed completion for various config values when using `:set`.
|
||||
- Fixed config validation for various config values.
|
||||
- Prevented an error being logged when a website with HTTP authentication was
|
||||
opened on Windows.
|
||||
|
||||
v0.5.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- Ability to preview PDFs using pdf.js in the browser if it's installed. This
|
||||
is disabled by default and can be enabled using the
|
||||
`content -> pdfjs-enabled` setting.
|
||||
- New setting `ui -> hide-wayland-decoration` to hide the window decoration
|
||||
when using wayland.
|
||||
- New userscripts in `misc/userscripts`:
|
||||
- `open_download` to easily open a file in your downloads folder.
|
||||
- `view_in_mpv` to open a video in mpv and remove it from the page.
|
||||
- `qutedmenu` and `dmenu_qutebrowser` to select URLs via dmenu
|
||||
- New setting `content -> host-blocking-whitelist` to whitelist certain domains
|
||||
from the adblocker.
|
||||
- `{scroll_pos}` can now be used in `ui -> window-title-format` and
|
||||
`tabs -> title-format`.
|
||||
- New setting `general -> url-incdec-segments` to configure which segments of
|
||||
the URL should be affected by `:navigate increment/decrement`.
|
||||
- New `--target` argument to specify how URLs should be opened in an existing
|
||||
instance.
|
||||
- New setting `statusbar.url.fg.success.https` to set the foreground color for
|
||||
the URL when a page was loaded via HTTPS.
|
||||
- The scrollbar in the completion is now styled, and the following new options
|
||||
got added:
|
||||
* `completion -> scrollbar-width`
|
||||
* `completion -> scrollbar-padding`
|
||||
* `colors -> completion.scrollbar.fg`
|
||||
* `colors -> completion.scrollbar.bg`
|
||||
- New value `none` for options taking a color system so they don't display a
|
||||
gradient:
|
||||
* `colors -> tabs.indicator.system`
|
||||
* `colors -> downloads.fg.system`
|
||||
* `colors -> downloads.bg.system`
|
||||
- New command `:download-retry` to retry a failed download.
|
||||
- New command `:download-clear` which replaces `:download-remove --all`.
|
||||
- `:set-cmd-text` has a new `--append` argument to append to the current
|
||||
statusbar text.
|
||||
- qutebrowser now uses `~/.netrc` if available to authenticate via HTTP.
|
||||
- New `:fake-key` command to send a fake keypress to a website or to
|
||||
qutebrowser.
|
||||
- New `--mhtml` argument for `:download` to download a page including all
|
||||
ressources as MHTML file.
|
||||
- New option `tabs -> title-alignment` to change the alignment of tab titles.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- The `colors -> tabs.bg/fg.selected` option got split into
|
||||
`tabs.bg/fg.selected.odd/even`.
|
||||
- `:spawn --userscript` and `:hint` with the `userscript` target now look up
|
||||
relative paths in `~/.local/share/qutebrowser/userscripts` or
|
||||
`$XDG_DATA_DIR`. Using a binary in `$PATH` won't work anymore with
|
||||
`--userscript`.
|
||||
- New design for error pages
|
||||
- Link filtering for hints now checks if the text is contained anywhere in
|
||||
the link, and matches case-insensitively.
|
||||
- The `ui -> remove-finished-downloads` option got changed to an integer and
|
||||
now takes a time (in milliseconds) to keep the download around after it's
|
||||
finished. When set to `-1`, downloads are never removed.
|
||||
- The `:follow-hint` command now optionally takes the keystring of a hint to
|
||||
follow.
|
||||
- `:scroll-px` now doesn't take floats anymore, which made little sense.
|
||||
- Updated the user agent list for the `:set network user-agent` completion.
|
||||
- Starting with `--debug` doesn't log `VDEBUG` messages anymore (add
|
||||
`--loglevel VDEBUG` to get them).
|
||||
- `:debug-console` now hides the console if it's already shown.
|
||||
- `:yank-selected` now doesn't log the selected text anymore.
|
||||
- `general -> log-javascript-console` got changed from a boolean to an option
|
||||
taking a loglevel (`none`, `info`, `debug`).
|
||||
- `:tab-move +/-` now wraps around if `tabs -> wrap` is `true`.
|
||||
- When a subprocess (like launched by `:spawn`) fails, its stdout/stderr is now
|
||||
logged to the console.
|
||||
- A search engine name can now contain any non-space character, like dashes.
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- `:download-remove --all` is now deprecated and `:download-clear` should be
|
||||
used instead.
|
||||
- `:download <url> <destination>` is now deprecated and
|
||||
`:download --dest <destination> <url>` should be used instead.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- `:scroll` with two pixel-arguments (deprecated in v0.3.0)
|
||||
- The `:run-userscript` command (deprecated in v0.2.0)
|
||||
- The `rapid` and `rapid-win` targets for `:hint` (deprecated in v0.2.0)
|
||||
- The `:cancel-download` command (deprecated in v0.2.0)
|
||||
- The `:download-page` command (deprecated in v0.2.0)
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed retrying of downloads which were started in a now closed tab.
|
||||
- Fixed displaying of web history if `web-history-max-items` is set to -1.
|
||||
- Cloned tabs now don't display favicons anymore if show-favicons is False.
|
||||
- Fixed a crash when clicking a bookmark name and pressing `Ctrl-D`.
|
||||
- Fixed a crash when a website presents a very small favicon.
|
||||
- Fixed prompting for download directory when
|
||||
`storage -> prompt-download-directory` was unset.
|
||||
- Fixed crash when using `:follow-hint` outside of hint mode.
|
||||
- Fixed crash when using `:set foo bar?` with invalid section/option.
|
||||
- Fixed scrolling to the very left/right with `:scroll-perc`.
|
||||
- Using an external editor should now work correctly with some funny chars
|
||||
(U+2028/U+2029/BOM).
|
||||
- Movements in caret mode now should work correctly on OS X and Windows.
|
||||
- Fixed upgrade from earlier config versions.
|
||||
- Fixed crash when killing a running userscript.
|
||||
- Fixed characters being passed through when shifted with
|
||||
`forward-unbound-keys` set to `auto`.
|
||||
- Fixed restarting after a crash is reported.
|
||||
- Removed `.pyc` files accidentally contained in source releases.
|
||||
|
||||
v0.4.1
|
||||
------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Adjusted AppArmor config for the IPC changes in v0.4.0.
|
||||
- Fixed atime update frequency for IPC file.
|
||||
- Worked around a Qt issue where middle-clicking caused scrolling with a
|
||||
touchpad to restart at the beginning of the page.
|
||||
- The `completion -> web-history-max-items` setting is now also respected for
|
||||
items added after starting qutebrowser.
|
||||
- Search terms are now shared between different tabs again
|
||||
- Tests (a reduced subset of them) now run correctly again when DISPLAY is not
|
||||
set.
|
||||
- Fixed an issue causing qutebrowser to crash with Python 3.5 as soon as an ad
|
||||
was blocked.
|
||||
- Fixed an issue causing qutebrowser to not start with more recent Python 3.4
|
||||
versions (e.g. on Debian experimental).
|
||||
- Fixed various `PendingDeprecationWarnings` shown with Python 3.5.
|
||||
|
||||
v0.4.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New bookmark functionality (similar to quickmarks without a name).
|
||||
* New command `:bookmark-add` to bookmark the current page (bound to `M`).
|
||||
* New command `:bookmark-load` to load a bookmark (bound to `gb`/`gB`/`wB`).
|
||||
- New (hidden) command `:completion-item-del` (bound to `<Ctrl-D>`) to delete
|
||||
the current item in the completion (for quickmarks/bookmarks).
|
||||
- New settings `tabs -> padding` and `tabs -> indicator-tabbing` to control the
|
||||
size/padding of the tabbar.
|
||||
- New setting `ui -> statusbar-padding` to control the size/padding of the
|
||||
status bar.
|
||||
- New setting `network -> referer-header` to configure when the referer should
|
||||
be sent (by default it's only sent while on the same domain).
|
||||
- New setting `tabs -> show` which supersedes the old `tabs -> hide-*` options
|
||||
and has an additional `switching` option which shows tab while switching
|
||||
them. There's also a new `show-switching` option to configure the timeout.
|
||||
- New setting `storage -> remember-download-directory` to remember the last
|
||||
used download directory.
|
||||
- New setting `storage -> prompt-download-directory` to download all downloads
|
||||
without asking.
|
||||
- Rapid hinting is now also possible for downloads.
|
||||
- Directory browsing via `file://` is now supported.
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- Some developer scripts got moved to `scripts/dev/`
|
||||
- When downloading to a FIFO or special file, a confirmation is displayed as
|
||||
this might cause qutebrowser to hang.
|
||||
- The `:yank-selected` command now works in all modes instead of just caret
|
||||
mode and is not hidden anymore.
|
||||
- `minimal_webkit_testbrowser.py` now has a `--webengine` switch to test
|
||||
QtWebEngine if it's installed.
|
||||
- The column width percentages for the completion view now depend on the
|
||||
completion model.
|
||||
- The values for `tabs -> position` and `ui -> downloads-position` got changed
|
||||
from `north`/`south`/`west/`east` to `top`/`bottom`/`left`/`right`. Existing
|
||||
configs should be adjusted automatically.
|
||||
- `:tab-focus`/`gt` now behaves like `:tab-next` if no count/index is given.
|
||||
- The completion widget doesn't show a border anymore.
|
||||
- The tabbar doesn't display ugly arrows anymore if there isn't enough space
|
||||
for all tabs.
|
||||
- Some insignificant Qt warnings which were printed on OS X are now hidden.
|
||||
- Better support for Qt 5.5 and Python 3.5.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Fixed a bug where cookies were saved despite qutebrowser being started in
|
||||
private browsing mode.
|
||||
- The local socket used for inter-process communication (opening new instances)
|
||||
is now ensured to only be accessible by the user on all operating systems.
|
||||
- Various corner cases for inter-process communication issues got fixed.
|
||||
- `link_pyqt.py` now should work better on untested distributions.
|
||||
- Fixed various corner-cases with crashes when reading invalid config values
|
||||
and the history file.
|
||||
- Fixed various corner-cases when setting text via an external editor.
|
||||
- Fixed potential crash when hinting a text field.
|
||||
- Fixed entering of insert mode when certain disabled text fields were clicked.
|
||||
- Fixed a crash when using `:set` with `-p` and `!` (invert value)
|
||||
- Downloads with unknown size are now handled correctly.
|
||||
- `:navigate increment/decrement` (`<Ctrl-A>`/`<Ctrl-X>`) now handles some
|
||||
corner-cases better.
|
||||
- Fixed a bug where the completion got affected by another window's completion
|
||||
if it was open in both windows.
|
||||
- Fixed a performance issue with large histories when opening previously
|
||||
unvisited websites.
|
||||
- The progress bar now doesn't cause the statusbar to change it's height
|
||||
anymore.
|
||||
- `~` is now always expanded when spawning a script.
|
||||
- Fixed various corner cases when opening links in an existing instance.
|
||||
- Fixed a race-condition causing an exception when starting qutebrowser.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- The `tabs -> indicator-space` setting got removed as the new padding settings
|
||||
should be used instead.
|
||||
- The `tabs -> hide-always` and `tabs -> hide-auto` settings got merged into
|
||||
the new `tabs -> show` setting.
|
||||
|
||||
v0.3.0
|
||||
------
|
||||
|
||||
Added
|
||||
~~~~~
|
||||
|
||||
- New commands `:message-info`, `:message-error` and `:message-warning` to show messages in the statusbar, e.g. from a userscript.
|
||||
- New command `:scroll-px` which replaces `:scroll` for pixel-exact scrolling.
|
||||
- New command `:jseval` to run a javascript snippet on the current page.
|
||||
- New (hidden) command `:follow-selected` (bound to `Enter`/`Ctrl-Enter` by default) to follow the link which is currently selected (e.g. after searching via `/`).
|
||||
- New (hidden) command `:clear-keychain` to clear a partially entered keychain (bound to `<Escape>` by default, in addition to clearing search).
|
||||
- New setting `ui -> smooth-scrolling`.
|
||||
- New setting `content -> webgl` to enable/disable https://www.khronos.org/webgl/[WebGL].
|
||||
- New setting `content -> css-regions` to enable/disable support for http://dev.w3.org/csswg/css-regions/[CSS Regions].
|
||||
- New setting `content -> hyperlink-auditing` to enable/disable support for https://html.spec.whatwg.org/multipage/semantics.html#hyperlink-auditing[hyperlink auditing].
|
||||
- New setting `tabs -> mousewheel-tab-switching` to control mousewheel behavior on the tab bar.
|
||||
- New arguments `--datadir` and `--cachedir` to set the data/cache location.
|
||||
- New arguments `--basedir` and `--temp-basedir` (intended for debugging) to set a different base directory for all data, which allows multiple invocations.
|
||||
- New argument `--no-err-windows` to suppress all error windows.
|
||||
- New arguments `--top-navigate` and `--bottom-navigate` (`-t`/`-b`) for `:scroll-page` to specify a navigation action (e.g. automatically go to the next page when arriving at the bottom).
|
||||
- New flag `-d`/`--detach` for `:spawn` to detach the spawned process so it's not closed when qutebrowser is.
|
||||
- New flag `-v`/`--verbose` for `:spawn` to print informations when the process started/exited successfully.
|
||||
- Many new color settings (foreground setting for every background setting).
|
||||
- New setting `ui -> modal-js-dialog` to use the standard modal dialogs for javascript questions instead of using the statusbar.
|
||||
- New setting `colors -> webpage.bg` to set the background color to use for websites which don't set one.
|
||||
- New setting `completion -> auto-open` to only open the completion when tab is pressed (if set to false).
|
||||
- New visual/caret mode (bound to `v`) to select text by keyboard.
|
||||
- There are now some example userscripts in `misc/userscripts`.
|
||||
- Support for Qt 5.5 and tox 2.0
|
||||
|
||||
Changed
|
||||
~~~~~~~
|
||||
|
||||
- *Breaking change for userscripts:* `QUTE_HTML` and `QUTE_TEXT` for userscripts now don't store the contents directly, and instead contain a filename.
|
||||
- The `content -> geolocation` and `notifications` settings now support a `true` value to always allow those. However, this is *not recommended*.
|
||||
- New bindings `<Ctrl-R>` (rapid), `<Ctrl-F>` (foreground) and `<Ctrl-B>` (background) to switch hint modes while hinting.
|
||||
- `<Ctrl-M>` and numpad-enter are now bound by default for bindings where `<Return>` was bound.
|
||||
- `:hint tab` and `F` now respect the `background-tabs` setting. To enforce a foreground tab (what `F` did before), use `:hint tab-fg` or `;f`.
|
||||
- `:scroll` now takes a direction argument (`up`/`down`/`left`/`right`/`top`/`bottom`/`page-up`/`page-down`) instead of two pixel arguments (`dx`/`dy`). The old form still works but is deprecated.
|
||||
- The `ui -> user-stylesheet` setting now also takes file paths relative to the config directory.
|
||||
- The `content -> cookies-accept` setting now has new `no-3rdparty` (default) and `no-unknown-3rdparty` values to block third-party cookies. The `default` value got renamed to `all`.
|
||||
- Improved startup time by reading the webpage history while qutebrowser is open.
|
||||
- The way `:spawn` splits its commandline has been changed slightly to allow commands with flags.
|
||||
- The default for the `new-instance-open-target` setting has been changed to `tab`.
|
||||
- Sessions now store zoom/scroll-position separately for each entry.
|
||||
|
||||
Deprecated
|
||||
~~~~~~~~~~
|
||||
|
||||
- `:scroll` with two pixel-arguments is now deprecated - `:scroll-px` should be used instead.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
||||
- The `--no-crash-dialog` argument which was intended for debugging only was removed as it's replaced by `--no-err-windows` which suppresses all error windows.
|
||||
- Support for Qt installations without SSL support was dropped.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Scrolling should now work more reliably on some pages where arrow keys worked but `hjkl` didn't.
|
||||
- Small improvements when checking if an input is a URL or not.
|
||||
- Fixed wrong cursor position when completing the first item in the completion.
|
||||
- Fixed exception when using search engines with {foo} in their name.
|
||||
- Fixed a bug where the same title was shown for all tabs on some systems.
|
||||
- Don't install the scripts package when installing qutebrowser.
|
||||
- Fixed searching for terms starting with a hyphen (e.g. `/-foo`)
|
||||
- Proxy authentication credentials are now remembered between different tabs.
|
||||
- Fixed updating of the tab title on pages without title.
|
||||
- Fixed AssertionError when closing many windows quickly.
|
||||
- Various fixes for deprecated key bindings and auto-migrations.
|
||||
- Workaround for qutebrowser not starting when there are NUL-bytes in the history (because of a currently unknown bug).
|
||||
- Fixed handling of keybindings containing Ctrl/Meta on OS X.
|
||||
- Fixed crash when downloading a URL without filename (e.g. magnet links) via "Save as...".
|
||||
- Fixed exception when starting qutebrowser with `:set` as argument.
|
||||
- Fixed horrible completion performance when the `shrink` option was set.
|
||||
- Sessions now store zoom/scroll-position correctly.
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/releases/tag/v0.2.1[v0.2.1]
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
- Added missing manpage (doc/qutebrowser.1.asciidoc) to archive.
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/releases/tag/v0.2.0[v0.2.0]
|
||||
-----------------------------------------------------------------------
|
||||
|
||||
@@ -749,7 +152,7 @@ Fixed
|
||||
- Scroll completion to top when showing it.
|
||||
- Handle unencodable file paths in config types correctly.
|
||||
- Fix for crash when executing a delayed command (because of a shadowed keybinding) and then unfocusing the window.
|
||||
- Fix for crash when hinting on a page which doesn't have a URL yet.
|
||||
- Fix for crash when hinting on a page which doesn't have an URL yet.
|
||||
- Fix exception when using `:set-cmd-text` with an empty argument.
|
||||
- Add a timeout to pastebin HTTP replies.
|
||||
- Various other fixes for small/rare bugs.
|
||||
@@ -849,7 +252,7 @@ Removed
|
||||
Fixed
|
||||
~~~~~
|
||||
|
||||
* Fix rare exception when a key is pressed shortly after opening a window
|
||||
* Fix rare exception when a key is pressed shorly after opening a window
|
||||
* Fix exception with certain invalid URLs like `http:foo:0`
|
||||
* Work around Qt bug which renders checkboxes on OS X unusable
|
||||
* Fix exception when a local files can't be read in `:adblock-update`
|
||||
|
||||
@@ -86,42 +86,23 @@ Useful utilities
|
||||
Checkers
|
||||
~~~~~~~~
|
||||
|
||||
qutebrowser uses http://tox.readthedocs.org/en/latest/[tox] to run its
|
||||
qutbebrowser uses http://tox.readthedocs.org/en/latest/[tox] to run its
|
||||
unittests and several linters/checkers.
|
||||
|
||||
Currently, following tox environments are available:
|
||||
Currently, the following tools will be invoked when you run `tox`:
|
||||
|
||||
* Tests using https://www.pytest.org[pytest]:
|
||||
- `py34`: Run pytest for python-3.4.
|
||||
- `py35`: Run pytest for python-3.5.
|
||||
- `py34-cov`: Run pytest for python-3.4 with code coverage report.
|
||||
- `py35-cov`: Run pytest for python-3.5 with code coverage report.
|
||||
* `flake8`: Run https://pypi.python.org/pypi/flake8[flake8] checks:
|
||||
https://pypi.python.org/pypi/pyflakes[pyflakes],
|
||||
https://pypi.python.org/pypi/pep8[pep8],
|
||||
https://pypi.python.org/pypi/mccabe[mccabe]
|
||||
* `vulture`: Run https://pypi.python.org/pypi/vulture[vulture] to find
|
||||
unused code portions.
|
||||
* `pylint`: Run http://pylint.org/[pylint] static code analysis.
|
||||
* `pydocstyle`: Check
|
||||
https://www.python.org/dev/peps/pep-0257/[PEP257] compliance with
|
||||
https://github.com/PyCQA/pydocstyle[pydocstyle]
|
||||
* `pyroma`: Check packaging practices with
|
||||
https://pypi.python.org/pypi/pyroma/[pyroma]
|
||||
* `eslint`: Run http://eslint.org/[ESLint] javascript checker.
|
||||
* `check-manifest`: Check MANIFEST.in completeness with
|
||||
https://github.com/mgedmin/check-manifest[check-manifest]
|
||||
* `mkvenv`: Bootstrap a virtualenv for testing.
|
||||
* `misc`: Run `scripts/misc_checks.py` to check for:
|
||||
* Unit tests using the Python
|
||||
https://docs.python.org/3.4/library/unittest.html[unittest] framework
|
||||
* https://pypi.python.org/pypi/flake8/[flake8]
|
||||
* https://github.com/GreenSteam/pep257/[pep257]
|
||||
* http://pylint.org/[pylint]
|
||||
* https://pypi.python.org/pypi/pyroma/[pyroma]
|
||||
* https://github.com/mgedmin/check-manifest[check-manifest]
|
||||
* `scripts/misc_checks.py` which checks for the following things:
|
||||
- untracked git files
|
||||
- VCS conflict markers
|
||||
- common spelling mistakes
|
||||
|
||||
The default test suite is run with `tox`, the list of default
|
||||
environments is obtained with `tox -l` .
|
||||
|
||||
Please make sure the checks run without any warnings on your new contributions.
|
||||
|
||||
There's of course the possibility of false-positives, and the following
|
||||
techniques are useful to handle these:
|
||||
|
||||
@@ -140,48 +121,16 @@ smallest scope which makes sense. Most of the time, this will be line scope.
|
||||
* If you really think a check shouldn't be done globally as it yields a lot of
|
||||
false-positives, let me know! I'm still tweaking the parameters.
|
||||
|
||||
|
||||
Running Specific Tests
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
While you are developing you often don't want to run the full test
|
||||
suite each time.
|
||||
|
||||
Specific test environments can be run with `tox -e <envlist>`.
|
||||
|
||||
Additional parameters can be passed to the test scripts by separating
|
||||
them from `tox` arguments with `--`.
|
||||
|
||||
Examples:
|
||||
|
||||
----
|
||||
# run only pytest tests which failed in last run:
|
||||
tox -e py35 -- --lf
|
||||
|
||||
# run only the end2end feature tests:
|
||||
tox -e py35 -- tests/end2end/features
|
||||
|
||||
# run everything with undo in the generated name, based on the scenario text
|
||||
tox -e py35 -- tests/end2end/features/test_tabs_bdd.py -k undo
|
||||
|
||||
# run coverage test for specific file (updates htmlcov/index.html)
|
||||
tox -e py35-cov -- tests/unit/browser/test_webelem.py
|
||||
----
|
||||
|
||||
Profiling
|
||||
~~~~~~~~~
|
||||
|
||||
In the _scripts/_ subfolder there's a `run_profile.py` which profiles the code
|
||||
and shows a graphical representation of what takes how much time.
|
||||
|
||||
It uses the built-in Python
|
||||
https://docs.python.org/3.4/library/profile.html[cProfile] module and can show
|
||||
the output in four different ways:
|
||||
|
||||
* Raw profile file (`--profile-tool=none`)
|
||||
* https://pypi.python.org/pypi/pyprof2calltree/[pyprof2calltree] and http://kcachegrind.sourceforge.net/html/Home.html[KCacheGrind] (`--profile-tool=kcachegrind`)
|
||||
* https://jiffyclub.github.io/snakeviz/[SnakeViz] (`--profile-tool=snakeviz`)
|
||||
* https://github.com/jrfonseca/gprof2dot[gprof2dot] (needs `dot` from http://graphviz.org/[Graphviz] and http://feh.finalrewind.org/[feh])
|
||||
It needs https://pypi.python.org/pypi/pyprof2calltree/[pyprof2calltree] and
|
||||
http://kcachegrind.sourceforge.net/html/Home.html[KCacheGrind]. It uses the
|
||||
built-in Python https://docs.python.org/3.4/library/profile.html[cProfile]
|
||||
module.
|
||||
|
||||
Debugging
|
||||
~~~~~~~~~
|
||||
@@ -203,7 +152,7 @@ Useful websites
|
||||
|
||||
Some resources which might be handy:
|
||||
|
||||
* http://doc.qt.io/qt-5/classes.html[The Qt5 reference]
|
||||
* http://qt-project.org/doc/qt-5/classes.html[The Qt5 reference]
|
||||
* https://docs.python.org/3/library/index.html[The Python reference]
|
||||
* http://httpbin.org/[httpbin, a test service for HTTP requests/responses]
|
||||
* http://requestb.in/[RequestBin, a service to inspect HTTP requests]
|
||||
@@ -216,6 +165,7 @@ Documentation of used Python libraries:
|
||||
* http://pythonhosted.org/setuptools/[setuptools]
|
||||
* http://cx-freeze.readthedocs.org/en/latest/overview.html[cx_Freeze]
|
||||
* https://pypi.python.org/pypi/colorama[colorama]
|
||||
* https://pypi.python.org/pypi/colorlog[colorlog]
|
||||
|
||||
Related RFCs and standards:
|
||||
|
||||
@@ -260,7 +210,8 @@ Other
|
||||
Languages] (http://www.rfc-editor.org/errata_search.php?rfc=5646[Errata])
|
||||
* http://www.w3.org/TR/CSS2/[Cascading Style Sheets Level 2 Revision 1 (CSS
|
||||
2.1) Specification]
|
||||
* http://doc.qt.io/qt-5/stylesheet-reference.html[Qt Style Sheets Reference]
|
||||
* http://qt-project.org/doc/qt-4.8/stylesheet-reference.html[Qt Style Sheets
|
||||
Reference]
|
||||
* http://mimesniff.spec.whatwg.org/[MIME Sniffing Standard]
|
||||
* http://spec.whatwg.org/[WHATWG specifications]
|
||||
* http://www.w3.org/html/wg/drafts/html/master/Overview.html[HTML 5.1 Nightly]
|
||||
@@ -276,7 +227,7 @@ Hints
|
||||
Python and Qt objects
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
For many tasks, there are solutions in both Qt and the Python standard library
|
||||
For many tasks, there are solutions in both Qt and the Python standard libary
|
||||
available.
|
||||
|
||||
In qutebrowser, the policy is usually using the Python libraries, as they
|
||||
@@ -286,7 +237,9 @@ There are some exceptions to that:
|
||||
|
||||
* `QThread` is used instead of Python threads because it provides signals and
|
||||
slots.
|
||||
* `QProcess` is used instead of Python's `subprocess`
|
||||
* `QProcess` is used instead of Python's `subprocess` if certain actions (e.g.
|
||||
cleanup) when the process finished are desired, as it provides signals for
|
||||
that.
|
||||
* `QUrl` is used instead of storing URLs as string, see the
|
||||
<<handling-urls,handling URLs>> section for details.
|
||||
|
||||
@@ -295,22 +248,22 @@ When using Qt objects, two issues must be taken care of:
|
||||
* Methods of Qt objects report their status by using their return values,
|
||||
instead of using exceptions.
|
||||
+
|
||||
If a function gets or returns a Qt object which has an `.isValid()`
|
||||
method such as `QUrl` or `QModelIndex`, there's a helper function
|
||||
`ensure_valid` in `qutebrowser.utils.qtutils` which should get called
|
||||
on all such objects. It will raise
|
||||
`qutebrowser.utils.qtutils.QtValueError` if the value is not valid.
|
||||
If a function gets or returns a Qt object which
|
||||
has an `.isValid()` method such as `QUrl` or `QModelIndex`, there's a helper
|
||||
function `ensure_valid` in `qutebrowser.utils.qt` which should get called on
|
||||
all such objects. It will raise `qutebrowser.utils.qt.QtValueError` if the
|
||||
value is not valid.
|
||||
+
|
||||
If a function returns something else on error, the return value should
|
||||
carefully be checked.
|
||||
|
||||
* Methods of Qt objects have certain maximum values, based on their
|
||||
underlying C++ types.
|
||||
* Methods of Qt objects have certain maximum values, based on their underlying
|
||||
C++ types.
|
||||
+
|
||||
When passing a numeric parameter to a Qt function, all numbers should
|
||||
be range-checked using `qutebrowser.qtutils.check_overflow`, or
|
||||
passing a value which is too large should be avoided by other means
|
||||
(e.g. by setting a maximum value for a config object).
|
||||
When passing a numeric parameter to a Qt function, all numbers should be
|
||||
range-checked using `qutebrowser.utils.check_overflow`, or passing a value
|
||||
which is too large should be avoided by other means (e.g. by setting a maximum
|
||||
value for a config object).
|
||||
|
||||
[[object-registry]]
|
||||
The object registry
|
||||
@@ -341,8 +294,8 @@ All objects can be printed by starting with the `--debug` flag and using the
|
||||
|
||||
The registry is mainly used for <<commands,command handlers>> but also can be
|
||||
useful in places where using Qt's
|
||||
http://doc.qt.io/qt-5/signalsandslots.html[signals and slots] mechanism would
|
||||
be difficult.
|
||||
http://qt-project.org/doc/qt-5/signalsandslots.html[signals and slots]
|
||||
mechanism would be difficult.
|
||||
|
||||
Logging
|
||||
~~~~~~~
|
||||
@@ -374,7 +327,7 @@ The following logging levels are available for every logger:
|
||||
|
||||
[width="75%",cols="25%,75%"]
|
||||
|=======================================================================
|
||||
|critical |Critical issue, qutebrowser can't continue to run.
|
||||
|criticial |Critical issue, qutebrowser can't continue to run.
|
||||
|error |There was an issue and some kind of operation was abandoned.
|
||||
|warning |There was an issue but the operation can continue running.
|
||||
|info |General informational messages.
|
||||
@@ -412,67 +365,44 @@ then gets passed as the `self` parameter to the handler. The `scope` argument
|
||||
selects which object registry (global, per-tab, etc.) to use. See the
|
||||
<<object-registry,object registry>> section for details.
|
||||
|
||||
There are also other arguments to customize the way the command is
|
||||
registered, see the class documentation for `register` in
|
||||
`qutebrowser.commands.cmdutils` for details.
|
||||
There are also other arguments to customize the way the command is registered,
|
||||
see the class documentation for `register` in `qutebrowser.commands.utils` for
|
||||
details.
|
||||
|
||||
The types of the function arguments are inferred based on their default values,
|
||||
e.g. an argument `foo=True` will be converted to a flag `-f`/`--foo` in
|
||||
qutebrowser's commandline.
|
||||
|
||||
The type can be overridden using Python's
|
||||
http://legacy.python.org/dev/peps/pep-3107/[function annotations]:
|
||||
This behavior can be overridden using Python's
|
||||
http://legacy.python.org/dev/peps/pep-3107/[function annotations]. The
|
||||
annotation should always be a `dict`, like this:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
@cmdutils.register(...)
|
||||
def foo(bar: int, baz=True):
|
||||
def foo(bar: {'type': int}, baz=True):
|
||||
...
|
||||
----
|
||||
|
||||
Possible values:
|
||||
The following keys are supported in the dict:
|
||||
|
||||
* `type`: The type this value should have. The value entered by the user is
|
||||
then automatically checked. Possible values:
|
||||
- A callable (`int`, `float`, etc.): Gets called to validate/convert the
|
||||
value.
|
||||
- A string: The value must match exactly (mainly useful with tuples to get
|
||||
a choice of values, see below).
|
||||
- A python enum type: All members of the enum are possible values.
|
||||
- A `typing.Union` of multiple types above: Any of these types are valid
|
||||
values, e.g. `typing.Union[str, int]`
|
||||
|
||||
You can customize how an argument is handled using the `@cmdutils.argument`
|
||||
decorator *after* `@cmdutils.register`. This can e.g. be used to customize the
|
||||
flag an argument should get:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
@cmdutils.register(...)
|
||||
@cmdutils.argument('bar', flag='c')
|
||||
def foo(bar):
|
||||
...
|
||||
----
|
||||
|
||||
For a `str` argument, you can restrict the allowed strings using `choices`:
|
||||
|
||||
[source,python]
|
||||
----
|
||||
@cmdutils.register(...)
|
||||
@cmdutils.argument('bar', choices=['val1', 'val2'])
|
||||
def foo(bar: str):
|
||||
...
|
||||
----
|
||||
|
||||
For `typing.Union` types, the given `choices` are only checked if other types
|
||||
(like `int`) don't match.
|
||||
|
||||
The following arguments are supported for `@cmdutils.argument`:
|
||||
|
||||
- `flag`: Customize the short flag (`-x`) the argument will get.
|
||||
- `win_id=True`: Mark the argument as special window ID argument
|
||||
- `count=True`: Mark the argument as special count argument
|
||||
- `hide=True`: Hide the argument from the documentation
|
||||
- `completion`: A `usertypes.Completion` member to use as completion.
|
||||
- `choices`: The allowed string choices for the argument.
|
||||
|
||||
The name of an argument will always be the parameter name, with any trailing
|
||||
underscores stripped and underscores replaced by dashes.
|
||||
- A tuple of multiple types above: Any of these types are valid values,
|
||||
e.g. `('foo', 'bar')` or `(int, 'foo')`.
|
||||
* `flag`: The flag to be used, as 1-char string (default: First char of the
|
||||
long name).
|
||||
* `name`: The long name to be used, as string (default: Name of the parameter).
|
||||
* `special`: The string `count` or `win_id` if the parameter should be
|
||||
auto-filled (with the count given by the user and the window ID the command was
|
||||
executed in, respectively).
|
||||
* `nargs`: Gets passed to argparse, see
|
||||
https://docs.python.org/dev/library/argparse.html#nargs[its documentation].
|
||||
|
||||
[[handling-urls]]
|
||||
Handling URLs
|
||||
@@ -595,7 +525,10 @@ Return:
|
||||
* The layout of a class should be like this:
|
||||
- docstring
|
||||
- `__magic__` methods
|
||||
- other methods
|
||||
- properties
|
||||
- _private methods
|
||||
- public methods
|
||||
- `on_*` methods
|
||||
- overrides of Qt methods
|
||||
|
||||
Checklists
|
||||
@@ -608,7 +541,7 @@ New Qt release
|
||||
|
||||
* Run all tests and check nothing is broken.
|
||||
* Check the
|
||||
https://bugreports.qt.io/issues/?jql=reporter%20%3D%20%22The%20Compiler%22%20ORDER%20BY%20fixVersion%20ASC[Qt bugtracker]
|
||||
https://bugreports.qt-project.org/issues/?jql=reporter%20%3D%20%22The%20Compiler%22%20ORDER%20BY%20fixVersion%20ASC[Qt bugtracker]
|
||||
and make sure all bugs marked as resolved are actually fixed.
|
||||
* Update own PKGBUILDs based on upstream Archlinux updates and rebuild.
|
||||
* Update recommended Qt version in `README`
|
||||
@@ -617,57 +550,28 @@ workaround.
|
||||
* Check relevant
|
||||
https://github.com/The-Compiler/qutebrowser/issues?q=is%3Aopen+is%3Aissue+label%3Aqt[qutebrowser
|
||||
bugs] and check if they're fixed.
|
||||
* As soon as Homebrew updated, update the custom OS X bottle:
|
||||
- Update https://github.com/The-Compiler/homebrew-qt5-webkit/blob/master/Formula/qt5.rb[qt5.rb]
|
||||
- `brew install --build-from-source --build-bottle --verbose qt5.rb`
|
||||
- `brew bottle qt5.rb`
|
||||
- `brew install --build-from-source --build-bottle --verbose pyqt5`
|
||||
- `brew bottle pyqt5`
|
||||
- Upload bottles to github
|
||||
- Adjust `scripts/dev/ci/travis_install.sh`
|
||||
|
||||
New PyQt release
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
* See above
|
||||
* Install new PyQt in Windows VM (32- and 64-bit)
|
||||
* Download new installer and update PyQt installer path in `ci_install.py`.
|
||||
|
||||
qutebrowser release
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* Make sure there are no unstaged changes and the tests are green.
|
||||
|
||||
* Add newest config to `tests/unit/config/old_configs` and update `test_upgrade_version`
|
||||
- `python -m qutebrowser --basedir conf :quit`
|
||||
- `sed '/^#/d' conf/config/qutebrowser.conf > tests/unit/config/old_configs/qutebrowser-v0.x.y.conf`
|
||||
- `rm -r conf`
|
||||
- commit
|
||||
* Adjust `__version_info__` in `qutebrowser/__init__.py`.
|
||||
* Remove *(unreleased)* from changelog.
|
||||
* Run tests again
|
||||
* Make sure there are no unstaged changes.
|
||||
* Run `src2asciidoc.py` and commit changes if necessary.
|
||||
* Run `asciidoc2html.py`.
|
||||
* Commit
|
||||
* Adjust `__version_info__` in `qutebrowser/__init__.py`.
|
||||
|
||||
* Run all tests on all supported systems.
|
||||
* Test an upgrade from the previous version (no manual intervention).
|
||||
* Test an upgrade from the first version (no manual intervention).
|
||||
|
||||
* Create annotated git tag (`git tag -s "v0.X.Y" -m "Release v0.X.Y"`)
|
||||
* If committing on minor branch, cherry-pick release commit to master.
|
||||
* `git push origin`; `git push origin v0.X.Y`
|
||||
* If it's a new minor, create git branch `v0.X.x`
|
||||
* `git push`; `git push "v0.X.Y"`
|
||||
* Create release on github
|
||||
* Mark the milestone at https://github.com/The-Compiler/qutebrowser/milestones
|
||||
as closed.
|
||||
|
||||
* Run `scripts/dev/build_release.py` on Linux to build an sdist
|
||||
* Upload to PyPI: `twine upload dist/foo{,.asc}`
|
||||
* Create Windows packages via `C:\Python34_x32\python scripts\dev\build_release.py --asciidoc C:\Python27\python C:\asciidoc-8.6.9\asciidoc.py`
|
||||
* Upload to github
|
||||
|
||||
* Create standalone Windows package (32/64bit) in Windows VM
|
||||
* Upload to PyPI: `python setup.py register sdist upload --sign`
|
||||
* Upload to qutebrowser.org with checksum/GPG
|
||||
- On server: `sudo mkdir -p /srv/http/qutebrowser/releases/v0.X.Y/windows`
|
||||
- `rsync -avPh dist/ tonks:`
|
||||
- On server: `sudo mv qutebrowser-0.X.Y.tar.gz* /srv/http/qutebrowser/releases/v0.X.Y`
|
||||
- Upload windows release:
|
||||
- `scp bb-win8:proj/qutebrowser/qutebrowser-0.X.Y-windows.zip .`
|
||||
- `aunpack qutebrowser-0.X.Y-windows.zip`
|
||||
- `sudo mv qutebrowser-0.X.Y-windows/* /srv/http/qutebrowser/releases/v0.X.Y/windows`
|
||||
* Update `qutebrowser-git` PKGBUILD if dependencies/install changed
|
||||
|
||||
* Announce to qutebrowser mailinglist
|
||||
|
||||
72
FAQ.asciidoc
@@ -1,12 +1,11 @@
|
||||
Frequently asked questions
|
||||
==========================
|
||||
:title: Frequently asked questions
|
||||
The Compiler <mail@qutebrowser.org>
|
||||
|
||||
[qanda]
|
||||
What is qutebrowser based on?::
|
||||
qutebrowser uses http://www.python.org/[Python], http://qt.io/[Qt] and
|
||||
http://www.riverbankcomputing.com/software/pyqt/intro[PyQt].
|
||||
qutebrowser uses http://www.python.org/[Python], http://qt-project.org/[Qt]
|
||||
and http://www.riverbankcomputing.com/software/pyqt/intro[PyQt].
|
||||
+
|
||||
The concept of it is largely inspired by http://portix.bitbucket.org/dwb/[dwb]
|
||||
and http://www.vimperator.org/vimperator[Vimperator]. Many actions and
|
||||
@@ -16,7 +15,7 @@ Why another browser?::
|
||||
It might be hard to believe, but I didn't find any browser which I was
|
||||
happy with, so I started to write my own. Also, I needed a project to get
|
||||
into writing GUI applications with Python and
|
||||
link:http://qt.io/[Qt]/link:http://www.riverbankcomputing.com/software/pyqt/intro[PyQt].
|
||||
link:http://qt-project.org/[Qt]/link:http://www.riverbankcomputing.com/software/pyqt/intro[PyQt].
|
||||
+
|
||||
Read the next few questions to find out why I was unhappy with existing
|
||||
software.
|
||||
@@ -33,11 +32,12 @@ API] seems to lack basic features like proxy support, and almost no projects
|
||||
seem to have started porting to WebKit2 (I only know of
|
||||
http://www.uzbl.org/[uzbl]).
|
||||
+
|
||||
qutebrowser uses http://qt.io/[Qt] and http://wiki.qt.io/QtWebKit[QtWebKit]
|
||||
instead, which suffers from far less such crashes. It might switch to
|
||||
http://wiki.qt.io/QtWebEngine[QtWebEngine] in the future, which is based on
|
||||
Google's https://en.wikipedia.org/wiki/Blink_(layout_engine)[Blink] rendering
|
||||
engine.
|
||||
qutebrowser uses http://qt-project.org/[Qt] and
|
||||
http://qt-project.org/wiki/QtWebKit[QtWebKit] instead, which suffers from far
|
||||
less such crashes. It might switch to
|
||||
http://qt-project.org/wiki/QtWebEngine[QtWebEngine] in the future, which is
|
||||
based on Google's https://en.wikipedia.org/wiki/Blink_(layout_engine)[Blink]
|
||||
rendering engine.
|
||||
|
||||
What's wrong with https://www.mozilla.org/en-US/firefox/new/[Firefox] and link:http://5digits.org/pentadactyl/[Pentadactyl]/link:http://www.vimperator.org/vimperator[Vimperator]?::
|
||||
Firefox likes to break compatibility with addons on each upgrade, gets
|
||||
@@ -54,14 +54,14 @@ What's wrong with http://www.chromium.org/Home[Chromium] and https://vimium.gith
|
||||
|
||||
Why Python?::
|
||||
I enjoy writing Python since 2011, which made it one of the possible
|
||||
choices. I wanted to use http://qt.io/[Qt] because of
|
||||
http://wiki.qt.io/QtWebKit[QtWebKit] so I didn't have
|
||||
http://wiki.qt.io/Category:LanguageBindings[many other choices]. I don't
|
||||
like C++ and can't write it very well, so that wasn't an alternative.
|
||||
choices. I wanted to use http://qt-project.org/[Qt] because of
|
||||
http://qt-project.org/wiki/QtWebKit[QtWebKit] so I didn't have
|
||||
http://qt-project.org/wiki/Category:LanguageBindings[many other choices]. I
|
||||
don't like C++ and can't write it very well, so that wasn't an alternative.
|
||||
|
||||
But isn't Python too slow for a browser?::
|
||||
http://www.infoworld.com/d/application-development/van-rossum-python-not-too-slow-188715[No.]
|
||||
I believe efficiency while coding is a lot more important than efficiency
|
||||
I believe efficency while coding is a lot more important than efficency
|
||||
while running. Also, most of the heavy lifting of qutebrowser is done by Qt
|
||||
and WebKit in C++, with the
|
||||
https://wiki.python.org/moin/GlobalInterpreterLock[GIL] released.
|
||||
@@ -80,36 +80,8 @@ How do I play Youtube videos with mpv?::
|
||||
player - optionally even with hinting for links:
|
||||
+
|
||||
----
|
||||
:bind m spawn mpv {url}
|
||||
:bind M hint links spawn mpv {hint-url}
|
||||
----
|
||||
+
|
||||
Note that you might need an additional package (e.g.
|
||||
https://www.archlinux.org/packages/community/any/youtube-dl/[youtube-dl] on
|
||||
Archlinux) to play web videos with mpv.
|
||||
+
|
||||
There is a very useful script for mpv, which emulates "unique application"
|
||||
functionality. This way you can add links to the mpv playlist instead of
|
||||
playing them all at once.
|
||||
+
|
||||
You can find the script here: https://github.com/mpv-player/mpv/blob/master/TOOLS/umpv
|
||||
+
|
||||
It also works nicely with rapid hints:
|
||||
+
|
||||
----
|
||||
:bind m spawn umpv {url}
|
||||
:bind M hint links spawn umpv {hint-url}
|
||||
:bind ;M hint --rapid links spawn umpv {hint-url}
|
||||
----
|
||||
|
||||
How do I use qutebrowser with mutt?::
|
||||
Due to a Qt limitation, local files without `.html` extensions are
|
||||
"downloaded" instead of displayed, see
|
||||
https://github.com/The-Compiler/qutebrowser/issues/566[#566]. You can work
|
||||
around this by using this in your `mailcap`:
|
||||
+
|
||||
----
|
||||
text/html; mv %s %s.html && qutebrowser %s.html >/dev/null 2>/dev/null; needsterminal;
|
||||
:bind x spawn mpv {url}
|
||||
:bind ;x hint links spawn mpv {hint-url}
|
||||
----
|
||||
|
||||
== Troubleshooting
|
||||
@@ -128,7 +100,7 @@ Unable to view flash content.::
|
||||
Experiencing freezing on sites like duckduckgo and youtube.::
|
||||
This issue could be caused by stale plugin files installed by `mozplugger`
|
||||
if mozplugger was subsequently removed.
|
||||
Try exiting qutebrowser and removing `~/.mozilla/plugins/mozplugger*.so`.
|
||||
Try exiting qutebroser and removing `~/.mozilla/plugins/mozplugger*.so`.
|
||||
See https://github.com/The-Compiler/qutebrowser/issues/357[Issue #357]
|
||||
for more details.
|
||||
|
||||
@@ -140,10 +112,10 @@ Experiencing segfaults (crashes) on Debian systems.::
|
||||
|
||||
Segfaults on Facebook, Medium, Amazon, ...::
|
||||
If you are on a Debian or Ubuntu based system, you might experience some crashes
|
||||
visiting these sites. This is caused by various bugs in Qt which have been
|
||||
visting these sites. This is caused by a known bug in Qt which has been
|
||||
fixed in Qt 5.4. However Debian and Ubuntu are slow to adopt or upgrade
|
||||
some packages. On Debian Jessie, it's recommended to use the experimental
|
||||
repos as described in https://github.com/The-Compiler/qutebrowser/blob/master/INSTALL.asciidoc#on-debian--ubuntu[INSTALL].
|
||||
some packages. There is currently no easy way to manually upgrade to Qt
|
||||
5.4 on those systems.
|
||||
+
|
||||
Since Ubuntu Trusty (using Qt 5.2.1),
|
||||
https://bugreports.qt.io/browse/QTBUG-42417?jql=component%20%3D%20WebKit%20and%20resolution%20%3D%20Done%20and%20fixVersion%20in%20(5.3.0%2C%20%225.3.0%20Alpha%22%2C%20%225.3.0%20Beta1%22%2C%20%225.3.0%20RC1%22%2C%205.3.1%2C%205.3.2%2C%205.4.0%2C%20%225.4.0%20Alpha%22%2C%20%225.4.0%20Beta%22%2C%20%225.4.0%20RC%22)%20and%20priority%20in%20(%22P2%3A%20Important%22%2C%20%22P1%3A%20Critical%22%2C%20%22P0%3A%20Blocker%22)[over
|
||||
@@ -157,5 +129,5 @@ My issue is not listed.::
|
||||
https://github.com/The-Compiler/qutebrowser/issues[the issue tracker] or
|
||||
using the `:report` command.
|
||||
If you are reporting a segfault, make sure you read the
|
||||
link:doc/stacktrace.asciidoc[guide] on how to report them with all needed
|
||||
information.
|
||||
https://github.com/The-Compiler/qutebrowser/blob/master/doc/stacktrace.asciidoc[guide]
|
||||
on how to report them with all needed information.
|
||||
|
||||
267
INSTALL.asciidoc
@@ -8,148 +8,80 @@ qutebrowser should run on these systems:
|
||||
|
||||
* Debian jessie or newer
|
||||
* Ubuntu Trusty (14.04 LTS) or newer
|
||||
* Any other distribution based on these (e.g. Linux Mint 17+)
|
||||
|
||||
Unfortunately there is no Debian package in the official repos yet, but installing qutebrowser is
|
||||
still relatively easy!
|
||||
|
||||
You can use packages that are built for every release or build it yourself from git.
|
||||
|
||||
Using the packages
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
* Any other distribution based on these (e.g. Linux Mint)
|
||||
|
||||
Install the dependencies via apt-get:
|
||||
|
||||
----
|
||||
# apt-get install python3-lxml python-tox python3-pyqt5 python3-pyqt5.qtwebkit python3-sip python3-jinja2 python3-pygments python3-yaml
|
||||
----
|
||||
|
||||
Get the packages from the https://github.com/The-Compiler/qutebrowser/releases[release page]
|
||||
|
||||
Install the packages:
|
||||
|
||||
----
|
||||
# dpkg -i python3-pypeg2_*_all.deb
|
||||
# dpkg -i qutebrowser_*_all.deb
|
||||
----
|
||||
|
||||
Build it from git
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Install the dependencies via apt-get:
|
||||
|
||||
[NOTE]
|
||||
==========================
|
||||
On Debian, it's recommended to install the Qt packages from the
|
||||
https://wiki.debian.org/DebianExperimental[experimental] repository as those
|
||||
are a much newer version of Qt which is more stable.
|
||||
|
||||
Add the following line to your `/etc/apt/sources.list`:
|
||||
|
||||
----
|
||||
deb http://ftp.debian.org/debian experimental main
|
||||
----
|
||||
|
||||
Then install the packages like this:
|
||||
|
||||
----
|
||||
# apt-get update
|
||||
# apt-get install -t experimental python3-pyqt5 python3-pyqt5.qtwebkit python3-sip python3-dev
|
||||
# apt-get install python-tox
|
||||
----
|
||||
|
||||
It's also recommended to pin those packages to receive updates by creating a
|
||||
file `/etc/apt/preferences.d/qutebrowser` with the following contents:
|
||||
|
||||
----
|
||||
Package: python3-pyqt5* libqt5*
|
||||
Pin: release a=experimental
|
||||
Pin-Priority: 800
|
||||
----
|
||||
==========================
|
||||
|
||||
For distributions other than Debian or if you prefer to not use the
|
||||
experimental repo:
|
||||
|
||||
----
|
||||
# apt-get install python3-pyqt5 python3-pyqt5.qtwebkit python-tox python3-sip python3-dev
|
||||
# apt-get install python3-pyqt5 python3-pyqt5.qtwebkit python-tox
|
||||
----
|
||||
|
||||
To generate the documentation for the `:help` command, when using the git
|
||||
repository (rather than a release):
|
||||
|
||||
----
|
||||
# apt-get install asciidoc source-highlight
|
||||
# apt-get install asciidoc
|
||||
$ python3 scripts/asciidoc2html.py
|
||||
----
|
||||
|
||||
If video or sound don't seem to work, try installing the gstreamer plugins:
|
||||
Then run tox like this to set up a
|
||||
https://docs.python.org/3/library/venv.html[virtual environment]:
|
||||
|
||||
----
|
||||
# apt-get install gstreamer1.0-plugins-{bad,base,good,ugly}
|
||||
$ tox -e mkvenv
|
||||
----
|
||||
|
||||
Then <<tox,install qutebrowser via tox>>.
|
||||
This installs all needed Python dependencies in a `.venv` subfolder. The
|
||||
system-wide Qt5/PyQt5 installations are symlinked into the virtual environment.
|
||||
|
||||
On Fedora
|
||||
---------
|
||||
|
||||
qutebrowser is available in the official repositories for Fedora 22 and newer.
|
||||
You can then create a simple wrapper script to start qutebrowser somewhere in
|
||||
your `$PATH` (e.g. `/usr/local/bin/qutebrowser` or `~/bin/qutebrowser`):
|
||||
|
||||
----
|
||||
# dnf install qutebrowser
|
||||
#!/bin/bash
|
||||
~/path/to/qutebrowser/.venv/bin/python3 -m qutebrowser "$@"
|
||||
----
|
||||
|
||||
Please also read about <<updating,updating qutebrowser with tox>>.
|
||||
|
||||
On Archlinux
|
||||
------------
|
||||
|
||||
qutebrowser is available in the official [community] repository.
|
||||
|
||||
----
|
||||
# pacman -S qutebrowser
|
||||
----
|
||||
|
||||
There is also a -git version available in the AUR:
|
||||
There are two Archlinux packages available in the AUR:
|
||||
https://aur.archlinux.org/packages/qutebrowser/[qutebrowser] and
|
||||
https://aur.archlinux.org/packages/qutebrowser-git/[qutebrowser-git].
|
||||
|
||||
You can install it using `makepkg` like this:
|
||||
You can install them like this:
|
||||
|
||||
----
|
||||
$ git clone https://aur.archlinux.org/qutebrowser-git.git
|
||||
$ cd qutebrowser-git
|
||||
$ mkdir qutebrowser
|
||||
$ cd qutebrowser
|
||||
$ wget https://aur.archlinux.org/packages/qu/qutebrowser-git/PKGBUILD
|
||||
$ makepkg -si
|
||||
$ cd ..
|
||||
$ rm -r qutebrowser-git
|
||||
----
|
||||
|
||||
or you could use an AUR helper, e.g. `yaourt -S qutebrowser-git`.
|
||||
|
||||
If video or sound don't seem to work, try installing the gstreamer plugins:
|
||||
|
||||
----
|
||||
# pacman -S gst-plugins-{base,good,bad,ugly} gst-libav
|
||||
----
|
||||
|
||||
On Gentoo
|
||||
---------
|
||||
|
||||
qutebrowser is available in the main repository and can be installed with:
|
||||
A dedicated overlay is available on
|
||||
https://github.com/posativ/qutebrowser-overlay[GitHub]. To install it, add the
|
||||
overlay with http://wiki.gentoo.org/wiki/Layman[layman]:
|
||||
|
||||
----
|
||||
# layman -a qutebrowser
|
||||
----
|
||||
|
||||
Note, that Qt5 is available in the portage tree, but masked. You may need to do
|
||||
a lot of keywording to install qutebrowser. Also make sure you have `python3_4`
|
||||
in your `PYTHON_TARGETS` (`/etc/portage/make.conf`) and rebuild your system
|
||||
(`emerge -uDNav @world`). Afterwards, you can install qutebrowser:
|
||||
|
||||
----
|
||||
# emerge -av qutebrowser
|
||||
----
|
||||
|
||||
Make sure you have `python3_4` in your `PYTHON_TARGETS`
|
||||
(`/etc/portage/make.conf`) and rebuild your system (`emerge -uDNav @world`) if
|
||||
necessary.
|
||||
|
||||
If video or sound don't seem to work, try installing the gstreamer plugins:
|
||||
|
||||
----
|
||||
# emerge -av gst-plugins-{base,good,bad,ugly,libav}
|
||||
----
|
||||
|
||||
|
||||
On Void Linux
|
||||
-------------
|
||||
|
||||
@@ -160,57 +92,12 @@ with:
|
||||
# xbps-install qutebrowser
|
||||
----
|
||||
|
||||
On NixOS
|
||||
--------
|
||||
|
||||
Nixpkgs collection contains `pkgs.qutebrowser` since June 2015. You can install
|
||||
it with:
|
||||
|
||||
----
|
||||
$ nix-env -i qutebrowser
|
||||
----
|
||||
|
||||
On openSUSE
|
||||
-----------
|
||||
|
||||
There are prebuilt RPMs available for Tumbleweed and Leap 42.1:
|
||||
|
||||
http://software.opensuse.org/download.html?project=home%3Aarpraher&package=qutebrowser[One Click Install]
|
||||
|
||||
Or add the repo manually:
|
||||
|
||||
----
|
||||
# zypper addrepo http://download.opensuse.org/repositories/home:arpraher/openSUSE_Tumbleweed/home:arpraher.repo
|
||||
# zypper refresh
|
||||
# zypper install qutebrowser
|
||||
----
|
||||
|
||||
On Windows
|
||||
----------
|
||||
|
||||
There are different ways to install qutebrowser on Windows:
|
||||
|
||||
Prebuilt binaries
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Prebuilt standalone packages and MSI installers
|
||||
https://github.com/The-Compiler/qutebrowser/releases[are built] for every
|
||||
release.
|
||||
|
||||
https://chocolatey.org/packages/qutebrowser[Chocolatey package]
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
* PackageManagement PowerShell module
|
||||
----
|
||||
PS C:\> Install-Package qutebrowser
|
||||
----
|
||||
* Chocolatey's client
|
||||
----
|
||||
C:\> choco install qutebrowser
|
||||
----
|
||||
|
||||
Manual install
|
||||
~~~~~~~~~~~~~~
|
||||
You can either use one of the
|
||||
https://github.com/The-Compiler/qutebrowser/releases[prebuilt standalone
|
||||
packages or MSI installers], or install manually:
|
||||
|
||||
* Use the installer from http://www.python.org/downloads[python.org] to get
|
||||
Python 3 (be sure to install pip).
|
||||
@@ -224,36 +111,32 @@ https://pip.pypa.io/en/latest/[pip]:
|
||||
$ pip install tox
|
||||
----
|
||||
|
||||
Then <<tox,install qutebrowser via tox>>.
|
||||
Then run tox like this to set up a
|
||||
https://docs.python.org/3/library/venv.html[virtual environment]:
|
||||
|
||||
----
|
||||
$ tox -e mkvenv
|
||||
----
|
||||
|
||||
This installs all needed Python dependencies in a `.venv` subfolder. The
|
||||
system-wide Qt5/PyQt5 installations are used in the virtual environment.
|
||||
|
||||
Please also read about <<updating,updating qutebrowser with tox>>.
|
||||
|
||||
On OS X
|
||||
-------
|
||||
|
||||
The easiest way to install qutebrowser on OS X is to use the prebuilt `.app`
|
||||
files from the
|
||||
https://github.com/The-Compiler/qutebrowser/releases[release page].
|
||||
|
||||
Alternatively, you can install the dependencies via a package manager (like
|
||||
http://brew.sh/[Homebrew] or https://www.macports.org/[MacPorts]) and run
|
||||
qutebrowser from source.
|
||||
|
||||
For Homebrew, a few extra steps are necessary since Homebrew dropped QtWebKit
|
||||
from Qt 5.6 - however, some users reported this didn't work for them, so using
|
||||
the `.app` is strongly encouraged.
|
||||
|
||||
This installs a Qt 5.5 and symlinks it so PyQt5 will work with it instead of Qt
|
||||
5.6. This requires that `qt5` is not installed via Homebrew:
|
||||
To install qutebrowser on OS X, you'll want a package manager, e.g.
|
||||
http://brew.sh/[Homebrew] or https://www.macports.org/[MacPorts]. Also make
|
||||
sure, you have https://itunes.apple.com/en/app/xcode/id497799835[XCode]
|
||||
installed to compile PyQt5 in a later step.
|
||||
|
||||
----
|
||||
$ brew install python3 d-bus mysql sip xz
|
||||
$ brew install homebrew/versions/qt55
|
||||
$ brew install --ignore-dependencies pyqt5
|
||||
$ ln -s /usr/local/opt/qt55 /usr/local/opt/qt5
|
||||
|
||||
$ pip3.5 install qutebrowser
|
||||
$ brew install python3 pyqt5
|
||||
$ pip3.4 install qutebrowser
|
||||
----
|
||||
|
||||
For MacPorts, run:
|
||||
if you are using Homebrew. For MacPorts, run:
|
||||
|
||||
----
|
||||
$ sudo port install python34 py34-jinja2 asciidoc py34-pygments py34-pyqt5
|
||||
@@ -274,47 +157,9 @@ standard location for your distro (`/usr/share/applications` and
|
||||
The normal `setup.py install` doesn't install these files, so you'll have to do
|
||||
it as part of the packaging process.
|
||||
|
||||
[[tox]]
|
||||
Installing qutebrowser with tox
|
||||
-------------------------------
|
||||
|
||||
First of all, clone the repository using http://git-scm.org/[git] and switch
|
||||
into the repository folder:
|
||||
|
||||
----
|
||||
$ git clone https://github.com/The-Compiler/qutebrowser.git
|
||||
$ cd qutebrowser
|
||||
----
|
||||
|
||||
|
||||
Then run tox inside the qutebrowser repository to set up a
|
||||
https://docs.python.org/3/library/venv.html[virtual environment]:
|
||||
|
||||
----
|
||||
$ tox -e mkvenv
|
||||
----
|
||||
|
||||
This installs all needed Python dependencies in a `.venv` subfolder. The
|
||||
system-wide Qt5/PyQt5 installations are symlinked into the virtual environment.
|
||||
|
||||
You can then create a simple wrapper script to start qutebrowser somewhere in
|
||||
your `$PATH` (e.g. `/usr/local/bin/qutebrowser` or `~/bin/qutebrowser`):
|
||||
|
||||
----
|
||||
#!/bin/bash
|
||||
~/path/to/qutebrowser/.venv/bin/python3 -m qutebrowser "$@"
|
||||
----
|
||||
|
||||
If you are developing on qutebrowser, you may want to redirect it to a local
|
||||
config:
|
||||
|
||||
----
|
||||
#!/bin/bash
|
||||
~/path/to/qutebrowser/.venv/bin/python3 -m qutebrowser -c .qutebrowser-local "$@"
|
||||
----
|
||||
|
||||
Updating
|
||||
~~~~~~~~
|
||||
[[updating]]
|
||||
Updating qutebrowser with tox
|
||||
-----------------------------
|
||||
|
||||
When you updated your local copy of the code (e.g. by pulling the git repo, or
|
||||
extracting a new version), the virtualenv should automatically use the updated
|
||||
|
||||
81
MANIFEST.in
@@ -1,49 +1,32 @@
|
||||
recursive-include qutebrowser *.py
|
||||
recursive-include qutebrowser/img *.svg *.png
|
||||
recursive-include qutebrowser/test *.py
|
||||
recursive-include qutebrowser/javascript *.js
|
||||
graft qutebrowser/html
|
||||
graft qutebrowser/3rdparty
|
||||
graft icons
|
||||
graft doc/img
|
||||
graft misc/apparmor
|
||||
graft misc/userscripts
|
||||
recursive-include scripts *.py
|
||||
include qutebrowser/utils/testfile
|
||||
include qutebrowser/git-commit-id
|
||||
include COPYING doc/* README.asciidoc CONTRIBUTING.asciidoc FAQ.asciidoc INSTALL.asciidoc CHANGELOG.asciidoc
|
||||
include qutebrowser.desktop
|
||||
include requirements.txt
|
||||
include tox.ini
|
||||
include qutebrowser.py
|
||||
include misc/cheatsheet.svg
|
||||
|
||||
prune www
|
||||
prune scripts/dev
|
||||
prune scripts/testbrowser_cpp
|
||||
prune .github
|
||||
exclude scripts/asciidoc2html.py
|
||||
exclude doc/notes
|
||||
recursive-exclude doc *.asciidoc
|
||||
include doc/qutebrowser.1.asciidoc
|
||||
prune tests
|
||||
prune qutebrowser/3rdparty
|
||||
prune misc/requirements
|
||||
prune misc/docker
|
||||
exclude .editorconfig
|
||||
exclude pytest.ini
|
||||
exclude qutebrowser.rcc
|
||||
exclude .coveragerc
|
||||
exclude .pylintrc
|
||||
exclude .eslintrc
|
||||
exclude .eslintignore
|
||||
exclude doc/help
|
||||
exclude .appveyor.yml
|
||||
exclude .travis.yml
|
||||
exclude codecov.yml
|
||||
exclude .pydocstylerc
|
||||
exclude misc/appveyor_install.py
|
||||
exclude misc/qutebrowser.spec
|
||||
exclude .flake8
|
||||
|
||||
global-exclude __pycache__ *.pyc *.pyo
|
||||
global-exclude __pycache__ *.pyc *.pyo
|
||||
|
||||
recursive-include qutebrowser/html *.html
|
||||
recursive-include qutebrowser/test *.py
|
||||
graft icons
|
||||
graft scripts/pylint_checkers
|
||||
graft doc/img
|
||||
graft misc
|
||||
include qutebrowser/utils/testfile
|
||||
include qutebrowser/git-commit-id
|
||||
include COPYING doc/* README.asciidoc CONTRIBUTING.asciidoc FAQ.asciidoc INSTALL.asciidoc CHANGELOG.asciidoc
|
||||
include qutebrowser.desktop
|
||||
include requirements.txt
|
||||
include tox.ini
|
||||
include qutebrowser.py
|
||||
|
||||
exclude scripts/cleanup.py
|
||||
exclude scripts/minimal_webkit_testbrowser.py
|
||||
exclude scripts/run_profile.py
|
||||
exclude scripts/src2asciidoc.sh
|
||||
exclude scripts/gen_resources.sh
|
||||
exclude scripts/quit_segfault_test.sh
|
||||
exclude scripts/segfault_test.sh
|
||||
exclude doc/notes
|
||||
recursive-exclude doc *.asciidoc
|
||||
include doc/qutebrowser.1.asciidoc
|
||||
prune tests
|
||||
exclude qutebrowser.rcc
|
||||
exclude .coveragerc
|
||||
exclude .flake8
|
||||
exclude .pylintrc
|
||||
exclude doc/help
|
||||
|
||||
143
README.asciidoc
@@ -6,20 +6,15 @@
|
||||
qutebrowser
|
||||
===========
|
||||
|
||||
// QUTE_WEB_HIDE
|
||||
image:icons/qutebrowser-64x64.png[qutebrowser logo] *A keyboard-driven, vim-like browser based on PyQt5 and QtWebKit.*
|
||||
|
||||
image:https://img.shields.io/pypi/l/qutebrowser.svg?style=flat["license badge",link="https://github.com/The-Compiler/qutebrowser/blob/master/COPYING"]
|
||||
image:https://img.shields.io/pypi/v/qutebrowser.svg?style=flat["version badge",link="https://pypi.python.org/pypi/qutebrowser/"]
|
||||
image:https://img.shields.io/github/issues/The-Compiler/qutebrowser.svg?style=flat["issues badge",link="https://github.com/The-Compiler/qutebrowser/issues"]
|
||||
image:https://requires.io/github/The-Compiler/qutebrowser/requirements.svg?branch=master["requirements badge",link="https://requires.io/github/The-Compiler/qutebrowser/requirements/?branch=master"]
|
||||
image:https://travis-ci.org/The-Compiler/qutebrowser.svg?branch=master["Build Status", link="https://travis-ci.org/The-Compiler/qutebrowser"]
|
||||
image:https://ci.appveyor.com/api/projects/status/9gmnuip6i1oq7046?svg=true["AppVeyor build status", link="https://ci.appveyor.com/project/The-Compiler/qutebrowser"]
|
||||
image:https://codecov.io/github/The-Compiler/qutebrowser/coverage.svg?branch=master["coverage badge",link="https://codecov.io/github/The-Compiler/qutebrowser?branch=master"]
|
||||
image:http://qutebrowser.org:8010/png?builder=archlinux["build badge",link="http://qutebrowser.org:8010/waterfall"]
|
||||
|
||||
link:http://www.qutebrowser.org[website] | link:http://blog.qutebrowser.org[blog] | link:https://github.com/The-Compiler/qutebrowser/releases[releases]
|
||||
// QUTE_WEB_HIDE_END
|
||||
|
||||
qutebrowser is a keyboard-focused browser with a minimal GUI. It's based
|
||||
qutebrowser is a keyboard-focused browser with with a minimal GUI. It's based
|
||||
on Python, PyQt5 and QtWebKit and free software, licensed under the GPL.
|
||||
|
||||
It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl.
|
||||
@@ -73,7 +68,7 @@ Contributions / Bugs
|
||||
--------------------
|
||||
|
||||
You want to contribute to qutebrowser? Awesome! Please read
|
||||
link:CONTRIBUTING.asciidoc[the contribution guidelines] for details and
|
||||
link:doc/CONTRIBUTING.asciidoc[the contribution guidelines] for details and
|
||||
useful hints.
|
||||
|
||||
If you found a bug or have a feature request, you can report it in several
|
||||
@@ -93,26 +88,25 @@ Requirements
|
||||
|
||||
The following software and libraries are required to run qutebrowser:
|
||||
|
||||
* http://www.python.org/[Python] 3.4 or newer
|
||||
* http://qt.io/[Qt] 5.2.0 or newer (5.5.1 recommended)
|
||||
* http://www.python.org/[Python] 3.4
|
||||
* http://qt-project.org/[Qt] 5.2.0 or newer (5.4.1 recommended)
|
||||
* QtWebKit
|
||||
* http://www.riverbankcomputing.com/software/pyqt/intro[PyQt] 5.2.0 or newer
|
||||
(5.5.1 recommended) for Python 3
|
||||
(5.4.1 recommended) for Python 3
|
||||
* https://pypi.python.org/pypi/setuptools/[pkg_resources/setuptools]
|
||||
* http://fdik.org/pyPEG/[pyPEG2]
|
||||
* http://jinja.pocoo.org/[jinja2]
|
||||
* http://pygments.org/[pygments]
|
||||
* http://pyyaml.org/wiki/PyYAML[PyYAML]
|
||||
|
||||
The following libraries are optional and provide a better user experience:
|
||||
|
||||
* http://cthedot.de/cssutils/[cssutils]
|
||||
|
||||
To generate the documentation for the `:help` command, when using the git
|
||||
repository (rather than a release), http://asciidoc.org/[asciidoc] is needed.
|
||||
|
||||
On Windows, https://pypi.python.org/pypi/colorama/[colorama] is needed to
|
||||
display colored log output.
|
||||
The following libraries are optional and provide colored logging in the
|
||||
console:
|
||||
|
||||
* https://pypi.python.org/pypi/colorlog/[colorlog]
|
||||
* On Windows: https://pypi.python.org/pypi/colorama/[colorama]
|
||||
|
||||
See link:INSTALL.asciidoc[INSTALL] for directions on how to install qutebrowser
|
||||
and its dependencies.
|
||||
@@ -140,119 +134,44 @@ Contributors, sorted by the number of commits in descending order:
|
||||
|
||||
// QUTE_AUTHORS_START
|
||||
* Florian Bruhin
|
||||
* Daniel Schadt
|
||||
* Ryan Roden-Corrent
|
||||
* Antoni Boucher
|
||||
* Lamar Pavel
|
||||
* Bruno Oliveira
|
||||
* Alexander Cogneau
|
||||
* Felix Van der Jeugt
|
||||
* Martin Tournoij
|
||||
* Jakub Klinkovský
|
||||
* Raphael Pierzina
|
||||
* Joel Torstensson
|
||||
* Jan Verbeek
|
||||
* Tarcisio Fedrizzi
|
||||
* Patric Schmitz
|
||||
* Raphael Pierzina
|
||||
* Claude
|
||||
* Corentin Julé
|
||||
* meles5
|
||||
* Philipp Hansch
|
||||
* Panagiotis Ktistakis
|
||||
* Artur Shaik
|
||||
* Nathan Isom
|
||||
* Thorsten Wißmann
|
||||
* Kevin Velghe
|
||||
* Austin Anderson
|
||||
* Jimmy
|
||||
* Marshall Lochbaum
|
||||
* Alexey "Averrin" Nabrodov
|
||||
* avk
|
||||
* ZDarian
|
||||
* Milan Svoboda
|
||||
* John ShaggyTwoDope Jenkins
|
||||
* Peter Vilim
|
||||
* Clayton Craft
|
||||
* Oliver Caldwell
|
||||
* Jonas Schürmann
|
||||
* error800
|
||||
* Liam BEGUIN
|
||||
* skinnay
|
||||
* Zach-Button
|
||||
* Tomasz Kramkowski
|
||||
* Ismail S
|
||||
* Halfwit
|
||||
* David Vogt
|
||||
* John ShaggyTwoDope Jenkins
|
||||
* Jimmy
|
||||
* rikn00
|
||||
* kanikaa1234
|
||||
* haitaka
|
||||
* Nick Ginther
|
||||
* Michał Góral
|
||||
* Michael Ilsaas
|
||||
* Patric Schmitz
|
||||
* Martin Zimmermann
|
||||
* Fritz Reichwald
|
||||
* Error 800
|
||||
* Brian Jackson
|
||||
* sbinix
|
||||
* neeasade
|
||||
* jnphilipp
|
||||
* Tobias Patzl
|
||||
* Stefan Tatschner
|
||||
* Samuel Loury
|
||||
* Peter Michely
|
||||
* Panashe M. Fundira
|
||||
* Link
|
||||
* Larry Hynes
|
||||
* Johannes Altmanninger
|
||||
* Jeremy Kaplan
|
||||
* Ismail
|
||||
* Edgar Hipp
|
||||
* Daryl Finlay
|
||||
* adam
|
||||
* Samir Benmendil
|
||||
* Regina Hug
|
||||
* Mathias Fussenegger
|
||||
* Marcelo Santos
|
||||
* Jean-Louis Fuchs
|
||||
* Fritz V155 Reichwald
|
||||
* Franz Fellner
|
||||
* zwarag
|
||||
* xd1le
|
||||
* oniondreams
|
||||
* issue
|
||||
* haxwithaxe
|
||||
* evan
|
||||
* dylan araps
|
||||
* Xitian9
|
||||
* Tomas Orsava
|
||||
* Tobias Werth
|
||||
* Tim Harder
|
||||
* Larry Hynes
|
||||
* error800
|
||||
* Thorsten Wißmann
|
||||
* Thiago Barroso Perrotta
|
||||
* Sorokin Alexei
|
||||
* Noah Huesser
|
||||
* Matthias Lisin
|
||||
* Marcel Schilling
|
||||
* Johannes Martinsson
|
||||
* Jean-Christophe Petkovich
|
||||
* Jay Kamat
|
||||
* Helen Sherwood-Taylor
|
||||
* HalosGhost
|
||||
* Gregor Pohl
|
||||
* Franz Fellner
|
||||
* Eivind Uggedal
|
||||
* Dietrich Daroch
|
||||
* Daniel Lu
|
||||
* Arseniy Seroka
|
||||
* Andy Balaam
|
||||
* Andreas Fischer
|
||||
// QUTE_AUTHORS_END
|
||||
|
||||
The following people have contributed graphics:
|
||||
|
||||
* Jad/link:http://yelostudio.com[yelo] (new icon)
|
||||
* WOFall (original icon)
|
||||
* WOFall (icon)
|
||||
* regines (key binding cheatsheet)
|
||||
|
||||
Thanks / Similar projects
|
||||
-------------------------
|
||||
Thanks / Similiar projects
|
||||
--------------------------
|
||||
|
||||
Many projects with a similar goal as qutebrowser exist:
|
||||
|
||||
@@ -293,10 +212,9 @@ problems and helpful hints:
|
||||
|
||||
Also, thanks to:
|
||||
|
||||
* Everyone contributing to the link:doc/backers.asciidoc[crowdfunding].
|
||||
* Everyone who had the patience to test qutebrowser before v0.1.
|
||||
* Everyone triaging/fixing my bugs in the
|
||||
https://bugreports.qt.io/secure/Dashboard.jspa[Qt bugtracker]
|
||||
https://bugreports.qt-project.org/secure/Dashboard.jspa[Qt bugtracker]
|
||||
* Everyone answering my questions on http://stackoverflow.com/[Stack Overflow]
|
||||
and in IRC.
|
||||
* All the projects which were a great help while developing qutebrowser.
|
||||
@@ -316,14 +234,3 @@ GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pdf.js
|
||||
------
|
||||
|
||||
qutebrowser optionally uses https://github.com/mozilla/pdf.js/[pdf.js] to
|
||||
display PDF files in the browser. Windows releases come with a bundled pdf.js.
|
||||
|
||||
pdf.js is distributed under the terms of the Apache License. You can
|
||||
find a copy of the license in `qutebrowser/3rdparty/pdfjs/LICENSE` (in the
|
||||
Windows release or after running `scripts/dev/update_3rdparty.py`), or online
|
||||
http://www.apache.org/licenses/LICENSE-2.0.html[here].
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
status:
|
||||
project:
|
||||
enabled: no
|
||||
patch:
|
||||
enabled: no
|
||||
changes:
|
||||
enabled: no
|
||||
|
||||
comment: off
|
||||
@@ -1,172 +0,0 @@
|
||||
Crowdfunding backers
|
||||
====================
|
||||
|
||||
Mid-2016, qutebrowser did run a http://igg.me/at/qutebrowser[crowdfunding] for
|
||||
QtWebEngine support in qutebrowser.
|
||||
|
||||
Thanks a lot to the following people who contributed to it:
|
||||
|
||||
Gold sponsors
|
||||
-------------
|
||||
|
||||
- Chris Salzberg
|
||||
- Clayton Craft
|
||||
- Jean-Louis Fuchs
|
||||
- Matthias Lisin
|
||||
- 1 Anonymous
|
||||
|
||||
Day sponsors
|
||||
------------
|
||||
|
||||
- Agent 42
|
||||
- Iggy Jackson
|
||||
- James B
|
||||
- Rudi Seitz
|
||||
- Tim „Das MooL“ Wegener
|
||||
- amd1212
|
||||
- gavtroy
|
||||
- 4 Anonymous
|
||||
|
||||
Other sponsors
|
||||
--------------
|
||||
|
||||
- AP M
|
||||
- Alessandro Balzano
|
||||
- Allan Nordhøy
|
||||
- Andor Uhlar
|
||||
- Andreas Leppert
|
||||
- Andreas Saga Romsdal
|
||||
- Andrew Rogers / tuxlovesyou
|
||||
- André Glüpker
|
||||
- Arian Sanusi
|
||||
- Arin Lares
|
||||
- Assaf Lavie
|
||||
- Baptiste Wicht
|
||||
- Benjamin Richter
|
||||
- Benjamin Schnitzler
|
||||
- Bernardo Kuri
|
||||
- Boris Kourtoukov
|
||||
- Brian Buccola
|
||||
- Bruno Oliveira
|
||||
- Bryan Gilbert
|
||||
- Cassandra Rebecca Ruppen
|
||||
- Charles Saternos
|
||||
- Chris H
|
||||
- Christian Karl
|
||||
- Christian Lange
|
||||
- Christian Strasser
|
||||
- Colin O'Brien
|
||||
- Corsin Pfister
|
||||
- Cosmin Popescu
|
||||
- Daniel Andersson
|
||||
- David Wilson
|
||||
- Demure Demeanor
|
||||
- Doug Stone-Weaver
|
||||
- Eero Kari
|
||||
- Enric Morales
|
||||
- Eric Krohn
|
||||
- Eskild Hustvedt
|
||||
- Federico Panico
|
||||
- Felix Van der Jeugt
|
||||
- Francis Tseng
|
||||
- Geir Isene
|
||||
- George Voronin
|
||||
- German Correa
|
||||
- Grady Martin
|
||||
- Gregor Böhl
|
||||
- Guilherme Stein
|
||||
- Hannes Doyle
|
||||
- Hasan Soydabas
|
||||
- Ian Scott
|
||||
- Jacob Boldman
|
||||
- Jacob Wikmark
|
||||
- Jan Verbeek
|
||||
- Jarrod Seccombe
|
||||
- Joel Bradshaw
|
||||
- Johannes Martinsson
|
||||
- Jonas Schürmann
|
||||
- Josh Medeiros
|
||||
- José Alberto Orejuela García
|
||||
- Julie Engel
|
||||
- Jörg Behrmann
|
||||
- Jørgen Skancke
|
||||
- Kevin Velghe
|
||||
- Konstantin Shmelkov
|
||||
- Kyle Frazer
|
||||
- Lukas Gierth
|
||||
- Mar v Leeuwaarde
|
||||
- Marek Roszman
|
||||
- Marius Betz
|
||||
- Marius Krämer
|
||||
- Markus Schmidinger
|
||||
- Martin Gabelmann
|
||||
- Martin Zimmermann
|
||||
- Mathias Fußenegger
|
||||
- Maxime Wack
|
||||
- Michał Góral
|
||||
- Nathan Isom
|
||||
- Nathanael Philipp
|
||||
- Nils Stål
|
||||
- Oliver Hope
|
||||
- Oskar Nyberg
|
||||
- Pablo Navarro
|
||||
- Panashe M. Fundira
|
||||
- Patric Schmitz
|
||||
- Pete M
|
||||
- Peter Smith
|
||||
- Phil Collins
|
||||
- Philipp Hansch
|
||||
- Philipp Kuhnz
|
||||
- Raphael Khaiat
|
||||
- Raphael Pierzina
|
||||
- Renan Guilherme
|
||||
- Rick Losie
|
||||
- Robert Cross
|
||||
- Roy Van Ginneken
|
||||
- Rupus Reinefjord
|
||||
- Ryan Roden-Corrent
|
||||
- Samir Benmendil
|
||||
- Simon Giotta
|
||||
- Stephen England
|
||||
- Sverrir H Steindorsson
|
||||
- Tarcisio Fedrizzi
|
||||
- Thorsten Wißmann
|
||||
- Timon Stampfli
|
||||
- Tjelvar Olsson
|
||||
- Tomasz Kramkowski
|
||||
- Tsukiko Tsutsukakushi
|
||||
- Vasilij Schneidermann
|
||||
- Vinney Cavallo
|
||||
- Wesly Grefrath
|
||||
- Will Ware
|
||||
- Yousaf Khurshid
|
||||
- Zach Schultz
|
||||
- averrin
|
||||
- ben hengst
|
||||
- colin
|
||||
- craigtski47
|
||||
- dag.robole
|
||||
- daniel.m.kao
|
||||
- diepfann3
|
||||
- eamonn oneil
|
||||
- esakaforever
|
||||
- francois47
|
||||
- glspisso
|
||||
- gmccoy4242
|
||||
- gtcee3
|
||||
- jonathf
|
||||
- lapinski.maciej
|
||||
- lauri.hakko
|
||||
- ljanzen
|
||||
- mutilx9
|
||||
- nussgipfel
|
||||
- oed
|
||||
- p p
|
||||
- r.c.bruno.andre
|
||||
- robert.perce
|
||||
- sghctoma
|
||||
- targy
|
||||
- freelancer
|
||||
- pupu
|
||||
- regines
|
||||
- 37 Anonymous
|
||||
@@ -8,31 +8,19 @@
|
||||
|<<adblock-update,adblock-update>>|Update the adblock block lists.
|
||||
|<<back,back>>|Go back in the history of the current tab.
|
||||
|<<bind,bind>>|Bind a key to a command.
|
||||
|<<bookmark-add,bookmark-add>>|Save the current page as a bookmark, or a specific url.
|
||||
|<<bookmark-del,bookmark-del>>|Delete a bookmark.
|
||||
|<<bookmark-load,bookmark-load>>|Load a bookmark.
|
||||
|<<buffer,buffer>>|Select tab by index or url/title best match.
|
||||
|<<close,close>>|Close the current window.
|
||||
|<<download,download>>|Download a given URL, or current page if no URL given.
|
||||
|<<download-cancel,download-cancel>>|Cancel the last/[count]th download.
|
||||
|<<download-clear,download-clear>>|Remove all finished downloads from the list.
|
||||
|<<download-delete,download-delete>>|Delete the last/[count]th download from disk.
|
||||
|<<download-open,download-open>>|Open the last/[count]th download.
|
||||
|<<download-remove,download-remove>>|Remove the last/[count]th download from the list.
|
||||
|<<download-retry,download-retry>>|Retry the first failed/[count]th download.
|
||||
|<<edit-url,edit-url>>|Navigate to a url formed in an external editor.
|
||||
|<<fake-key,fake-key>>|Send a fake keypress or key string to the website or qutebrowser.
|
||||
|<<forward,forward>>|Go forward in the history of the current tab.
|
||||
|<<fullscreen,fullscreen>>|Toggle fullscreen mode.
|
||||
|<<help,help>>|Show help about a command or setting.
|
||||
|<<hint,hint>>|Start hinting.
|
||||
|<<history-clear,history-clear>>|Clear all browsing history.
|
||||
|<<home,home>>|Open main startpage in current tab.
|
||||
|<<inspector,inspector>>|Toggle the web inspector.
|
||||
|<<jseval,jseval>>|Evaluate a JavaScript string.
|
||||
|<<jump-mark,jump-mark>>|Jump to the mark named by `key`.
|
||||
|<<later,later>>|Execute a command after some time.
|
||||
|<<messages,messages>>|Show a log of past messages.
|
||||
|<<navigate,navigate>>|Open typical prev/next links or navigate using the URL path.
|
||||
|<<open,open>>|Open a URL in the current/[count]th tab.
|
||||
|<<paste,paste>>|Open a page from the clipboard.
|
||||
@@ -53,7 +41,6 @@
|
||||
|<<session-save,session-save>>|Save a session.
|
||||
|<<set,set>>|Set an option.
|
||||
|<<set-cmd-text,set-cmd-text>>|Preset the statusbar to some text.
|
||||
|<<set-mark,set-mark>>|Set a mark at the current scroll position in the current tab.
|
||||
|<<spawn,spawn>>|Spawn a command in a shell.
|
||||
|<<stop,stop>>|Stop loading in the current/[count]th tab.
|
||||
|<<tab-clone,tab-clone>>|Duplicate the current tab.
|
||||
@@ -69,7 +56,6 @@
|
||||
|<<view-source,view-source>>|Show the source of the current page.
|
||||
|<<wq,wq>>|Save open pages and quit.
|
||||
|<<yank,yank>>|Yank the current URL/title to the clipboard or primary selection.
|
||||
|<<yank-selected,yank-selected>>|Yank the selected text to the clipboard or primary selection.
|
||||
|<<zoom,zoom>>|Set the zoom level for the current tab.
|
||||
|<<zoom-in,zoom-in>>|Increase the zoom level for the current tab.
|
||||
|<<zoom-out,zoom-out>>|Decrease the zoom level for the current tab.
|
||||
@@ -78,8 +64,6 @@
|
||||
=== adblock-update
|
||||
Update the adblock block lists.
|
||||
|
||||
This updates ~/.local/share/qutebrowser/blocked-hosts with downloaded host lists and re-reads ~/.config/qutebrowser/blocked-hosts.
|
||||
|
||||
[[back]]
|
||||
=== back
|
||||
Syntax: +:back [*--tab*] [*--bg*] [*--window*]+
|
||||
@@ -96,14 +80,13 @@ How many pages to go back.
|
||||
|
||||
[[bind]]
|
||||
=== bind
|
||||
Syntax: +:bind [*--mode* 'mode'] [*--force*] 'key' ['command']+
|
||||
Syntax: +:bind [*--mode* 'MODE'] [*--force*] 'key' 'command'+
|
||||
|
||||
Bind a key to a command.
|
||||
|
||||
==== positional arguments
|
||||
* +'key'+: The keychain or special key (inside `<...>`) to bind.
|
||||
* +'command'+: The command to execute, with optional args, or not given to print the current binding.
|
||||
|
||||
* +'command'+: The command to execute, with optional args.
|
||||
|
||||
==== optional arguments
|
||||
* +*-m*+, +*--mode*+: A comma-separated list of modes to bind the key in (default: `normal`).
|
||||
@@ -114,108 +97,40 @@ Bind a key to a command.
|
||||
* 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.
|
||||
|
||||
[[bookmark-add]]
|
||||
=== bookmark-add
|
||||
Syntax: +:bookmark-add ['url'] ['title']+
|
||||
|
||||
Save the current page as a bookmark, or a specific url.
|
||||
|
||||
If no url and title are provided, then save the current page as a bookmark. If a url and title have been provided, then save the given url as a bookmark with the provided title. You can view all saved bookmarks on the link:qute://bookmarks[bookmarks page].
|
||||
|
||||
==== positional arguments
|
||||
* +'url'+: url to save as a bookmark. If None, use url of current page.
|
||||
* +'title'+: title of the new bookmark.
|
||||
|
||||
[[bookmark-del]]
|
||||
=== bookmark-del
|
||||
Syntax: +:bookmark-del ['url']+
|
||||
|
||||
Delete a bookmark.
|
||||
|
||||
==== positional arguments
|
||||
* +'url'+: The url of the bookmark to delete. If not given, use the current page's url.
|
||||
|
||||
|
||||
==== note
|
||||
* This command does not split arguments after the last argument and handles quotes literally.
|
||||
|
||||
[[bookmark-load]]
|
||||
=== bookmark-load
|
||||
Syntax: +:bookmark-load [*--tab*] [*--bg*] [*--window*] 'url'+
|
||||
|
||||
Load a bookmark.
|
||||
|
||||
==== positional arguments
|
||||
* +'url'+: The url of the bookmark to load.
|
||||
|
||||
==== optional arguments
|
||||
* +*-t*+, +*--tab*+: Load the bookmark in a new tab.
|
||||
* +*-b*+, +*--bg*+: Load the bookmark in a new background tab.
|
||||
* +*-w*+, +*--window*+: Load the bookmark in a new window.
|
||||
|
||||
==== note
|
||||
* This command does not split arguments after the last argument and handles quotes literally.
|
||||
|
||||
[[buffer]]
|
||||
=== buffer
|
||||
Syntax: +:buffer 'index'+
|
||||
|
||||
Select tab by index or url/title best match.
|
||||
|
||||
Focuses window if necessary.
|
||||
|
||||
==== positional arguments
|
||||
* +'index'+: The [win_id/]index of the tab to focus. Or a substring in which case the closest match will be focused.
|
||||
|
||||
|
||||
[[close]]
|
||||
=== close
|
||||
Close the current window.
|
||||
|
||||
[[download]]
|
||||
=== download
|
||||
Syntax: +:download [*--mhtml*] [*--dest* 'dest'] ['url'] ['dest-old']+
|
||||
Syntax: +:download ['url'] ['dest']+
|
||||
|
||||
Download a given URL, or current page if no URL given.
|
||||
|
||||
The form `:download [url] [dest]` is deprecated, use `:download --dest [dest] [url]` instead.
|
||||
|
||||
==== positional arguments
|
||||
* +'url'+: The URL to download. If not given, download the current page.
|
||||
|
||||
==== optional arguments
|
||||
* +*-m*+, +*--mhtml*+: Download the current page and all assets as mhtml file.
|
||||
* +*-d*+, +*--dest*+: The file path to write the download to, or not given to ask.
|
||||
* +'dest'+: The file path to write the download to, or not given to ask.
|
||||
|
||||
[[download-cancel]]
|
||||
=== download-cancel
|
||||
Syntax: +:download-cancel [*--all*]+
|
||||
|
||||
Cancel the last/[count]th download.
|
||||
|
||||
==== optional arguments
|
||||
* +*-a*+, +*--all*+: Cancel all running downloads
|
||||
|
||||
==== count
|
||||
The index of the download to cancel.
|
||||
|
||||
[[download-clear]]
|
||||
=== download-clear
|
||||
Remove all finished downloads from the list.
|
||||
|
||||
[[download-delete]]
|
||||
=== download-delete
|
||||
Delete the last/[count]th download from disk.
|
||||
|
||||
==== count
|
||||
The index of the download to delete.
|
||||
The index of the download to cancel.
|
||||
|
||||
[[download-open]]
|
||||
=== download-open
|
||||
Open the last/[count]th download.
|
||||
|
||||
==== count
|
||||
The index of the download to open.
|
||||
The index of the download to cancel.
|
||||
|
||||
[[download-remove]]
|
||||
=== download-remove
|
||||
@@ -224,47 +139,10 @@ Syntax: +:download-remove [*--all*]+
|
||||
Remove the last/[count]th download from the list.
|
||||
|
||||
==== optional arguments
|
||||
* +*-a*+, +*--all*+: Remove all finished downloads.
|
||||
* +*-a*+, +*--all*+: If given removes all finished downloads.
|
||||
|
||||
==== count
|
||||
The index of the download to remove.
|
||||
|
||||
[[download-retry]]
|
||||
=== download-retry
|
||||
Retry the first failed/[count]th download.
|
||||
|
||||
==== count
|
||||
The index of the download to retry.
|
||||
|
||||
[[edit-url]]
|
||||
=== edit-url
|
||||
Syntax: +:edit-url [*--bg*] [*--tab*] [*--window*] ['url']+
|
||||
|
||||
Navigate to a url formed in an external editor.
|
||||
|
||||
The editor which should be launched can be configured via the `general -> editor` config option.
|
||||
|
||||
==== positional arguments
|
||||
* +'url'+: URL to edit; defaults to the current page url.
|
||||
|
||||
==== optional arguments
|
||||
* +*-b*+, +*--bg*+: Open in a new background tab.
|
||||
* +*-t*+, +*--tab*+: Open in a new tab.
|
||||
* +*-w*+, +*--window*+: Open in a new window.
|
||||
|
||||
[[fake-key]]
|
||||
=== fake-key
|
||||
Syntax: +:fake-key [*--global*] 'keystring'+
|
||||
|
||||
Send a fake keypress or key string to the website or qutebrowser.
|
||||
|
||||
:fake-key xy - sends the keychain 'xy' :fake-key <Ctrl-x> - sends Ctrl-x :fake-key <Escape> - sends the escape key
|
||||
|
||||
==== positional arguments
|
||||
* +'keystring'+: The keystring to send.
|
||||
|
||||
==== optional arguments
|
||||
* +*-g*+, +*--global*+: If given, the keys are sent to the qutebrowser UI.
|
||||
The index of the download to cancel.
|
||||
|
||||
[[forward]]
|
||||
=== forward
|
||||
@@ -314,17 +192,13 @@ Start hinting.
|
||||
- `all`: All clickable elements.
|
||||
- `links`: Only links.
|
||||
- `images`: Only images.
|
||||
- `inputs`: Only input fields.
|
||||
|
||||
|
||||
|
||||
* +'target'+: What to do with the selected element.
|
||||
|
||||
- `normal`: Open the link.
|
||||
- `current`: Open the link in the current tab.
|
||||
- `tab`: Open the link in a new tab (honoring the
|
||||
background-tabs setting).
|
||||
- `tab-fg`: Open the link in a new foreground tab.
|
||||
- `normal`: Open the link in the current tab.
|
||||
- `tab`: Open the link in a new tab.
|
||||
- `tab-bg`: Open the link in a new background tab.
|
||||
- `window`: Open the link in a new window.
|
||||
- `hover` : Hover over the link.
|
||||
@@ -334,7 +208,7 @@ Start hinting.
|
||||
- `fill`: Fill the commandline with the command given as
|
||||
argument.
|
||||
- `download`: Download the link.
|
||||
- `userscript`: Call a userscript with `$QUTE_URL` set to the
|
||||
- `userscript`: Call an userscript with `$QUTE_URL` set to the
|
||||
link.
|
||||
- `spawn`: Spawn a command.
|
||||
|
||||
@@ -345,11 +219,7 @@ Start hinting.
|
||||
- With `spawn`: The executable and arguments to spawn.
|
||||
`{hint-url}` will get replaced by the selected
|
||||
URL.
|
||||
- With `userscript`: The userscript to execute. Either store
|
||||
the userscript in
|
||||
`~/.local/share/qutebrowser/userscripts`
|
||||
(or `$XDG_DATA_DIR`), or use an absolute
|
||||
path.
|
||||
- With `userscript`: The userscript to execute.
|
||||
- With `fill`: The command to fill the statusbar with.
|
||||
`{hint-url}` will get replaced by the selected
|
||||
URL.
|
||||
@@ -357,19 +227,10 @@ Start hinting.
|
||||
|
||||
|
||||
==== optional arguments
|
||||
* +*-r*+, +*--rapid*+: Whether to do rapid hinting. This is only possible with targets `tab` (with background-tabs=true), `tab-bg`,
|
||||
`window`, `run`, `hover`, `userscript` and `spawn`.
|
||||
* +*-r*+, +*--rapid*+: Whether to do rapid hinting. This is only possible with targets `tab-bg`, `window`, `run`, `hover`, `userscript` and
|
||||
`spawn`.
|
||||
|
||||
|
||||
==== note
|
||||
* This command does not split arguments after the last argument and handles quotes literally.
|
||||
|
||||
[[history-clear]]
|
||||
=== history-clear
|
||||
Clear all browsing history.
|
||||
|
||||
Note this only clears the global history (e.g. `~/.local/share/qutebrowser/history` on Linux) but not cookies, the back/forward history of a tab, cache or other persistent data.
|
||||
|
||||
[[home]]
|
||||
=== home
|
||||
Open main startpage in current tab.
|
||||
@@ -378,33 +239,6 @@ Open main startpage in current tab.
|
||||
=== inspector
|
||||
Toggle the web inspector.
|
||||
|
||||
Note: Due a bug in Qt, the inspector will show incorrect request headers in the network tab.
|
||||
|
||||
[[jseval]]
|
||||
=== jseval
|
||||
Syntax: +:jseval [*--quiet*] 'js-code'+
|
||||
|
||||
Evaluate a JavaScript string.
|
||||
|
||||
==== positional arguments
|
||||
* +'js-code'+: The string to evaluate.
|
||||
|
||||
==== optional arguments
|
||||
* +*-q*+, +*--quiet*+: Don't show resulting JS object.
|
||||
|
||||
==== 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.
|
||||
|
||||
[[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'+
|
||||
@@ -419,22 +253,6 @@ Execute a command after some time.
|
||||
* 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.
|
||||
|
||||
[[messages]]
|
||||
=== messages
|
||||
Syntax: +:messages [*--plain*] [*--tab*] [*--bg*] [*--window*] ['level']+
|
||||
|
||||
Show a log of past messages.
|
||||
|
||||
==== positional arguments
|
||||
* +'level'+: Include messages with `level` or higher severity. Valid values: vdebug, debug, info, warning, error, critical.
|
||||
|
||||
|
||||
==== optional arguments
|
||||
* +*-p*+, +*--plain*+: Whether to show plaintext (as opposed to html).
|
||||
* +*-t*+, +*--tab*+: Open in a new tab.
|
||||
* +*-b*+, +*--bg*+: Open in a background tab.
|
||||
* +*-w*+, +*--window*+: Open in a new window.
|
||||
|
||||
[[navigate]]
|
||||
=== navigate
|
||||
Syntax: +:navigate [*--tab*] [*--bg*] [*--window*] 'where'+
|
||||
@@ -479,6 +297,7 @@ The tab index to open the URL in.
|
||||
|
||||
==== 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.
|
||||
|
||||
[[paste]]
|
||||
=== paste
|
||||
@@ -486,8 +305,6 @@ Syntax: +:paste [*--sel*] [*--tab*] [*--bg*] [*--window*]+
|
||||
|
||||
Open a page from the clipboard.
|
||||
|
||||
If the pasted text contains newlines, each line gets opened in its own tab.
|
||||
|
||||
==== optional arguments
|
||||
* +*-s*+, +*--sel*+: Use the primary selection instead of the clipboard.
|
||||
* +*-t*+, +*--tab*+: Open in a new tab.
|
||||
@@ -496,13 +313,12 @@ If the pasted text contains newlines, each line gets opened in its own tab.
|
||||
|
||||
[[print]]
|
||||
=== print
|
||||
Syntax: +:print [*--preview*] [*--pdf* 'file']+
|
||||
Syntax: +:print [*--preview*]+
|
||||
|
||||
Print the current/[count]th tab.
|
||||
|
||||
==== optional arguments
|
||||
* +*-p*+, +*--preview*+: Show preview instead of printing.
|
||||
* +*-f*+, +*--pdf*+: The file path to write the PDF to.
|
||||
|
||||
==== count
|
||||
The tab index to print.
|
||||
@@ -513,25 +329,22 @@ Syntax: +:quickmark-add 'url' 'name'+
|
||||
|
||||
Add a new quickmark.
|
||||
|
||||
You can view all saved quickmarks on the link:qute://bookmarks[bookmarks page].
|
||||
|
||||
==== positional arguments
|
||||
* +'url'+: The url to add as quickmark.
|
||||
* +'name'+: The name for the new quickmark.
|
||||
|
||||
[[quickmark-del]]
|
||||
=== quickmark-del
|
||||
Syntax: +:quickmark-del ['name']+
|
||||
Syntax: +:quickmark-del 'name'+
|
||||
|
||||
Delete a quickmark.
|
||||
|
||||
==== positional arguments
|
||||
* +'name'+: The name of the quickmark to delete. If not given, delete the quickmark for the current page (choosing one arbitrarily
|
||||
if there are more than one).
|
||||
|
||||
* +'name'+: The name of the quickmark to delete.
|
||||
|
||||
==== 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.
|
||||
|
||||
[[quickmark-load]]
|
||||
=== quickmark-load
|
||||
@@ -549,6 +362,7 @@ Load a quickmark.
|
||||
|
||||
==== 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.
|
||||
|
||||
[[quickmark-save]]
|
||||
=== quickmark-save
|
||||
@@ -616,6 +430,7 @@ Search for a text on the current page. With no text, clear results.
|
||||
|
||||
==== 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.
|
||||
|
||||
[[session-delete]]
|
||||
=== session-delete
|
||||
@@ -679,54 +494,33 @@ If the option name ends with '?', the value of the option is shown instead. If t
|
||||
|
||||
[[set-cmd-text]]
|
||||
=== set-cmd-text
|
||||
Syntax: +:set-cmd-text [*--space*] [*--append*] 'text'+
|
||||
Syntax: +:set-cmd-text [*--space*] 'text'+
|
||||
|
||||
Preset the statusbar to some text.
|
||||
|
||||
You can use the `{url}` and `{url:pretty}` variables here which will get replaced by the encoded/decoded URL.
|
||||
|
||||
==== positional arguments
|
||||
* +'text'+: The commandline to set.
|
||||
|
||||
==== optional arguments
|
||||
* +*-s*+, +*--space*+: If given, a space is added to the end.
|
||||
* +*-a*+, +*--append*+: If given, the text is appended to the current text.
|
||||
|
||||
==== note
|
||||
* This command does not split arguments after the last argument and handles quotes literally.
|
||||
|
||||
[[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
|
||||
* With this command, +;;+ is interpreted literally instead of splitting off a second command.
|
||||
|
||||
[[spawn]]
|
||||
=== spawn
|
||||
Syntax: +:spawn [*--userscript*] [*--verbose*] [*--detach*] 'cmdline'+
|
||||
Syntax: +:spawn [*--userscript*] 'args' ['args' ...]+
|
||||
|
||||
Spawn a command in a shell.
|
||||
|
||||
Note the `{url}` and `{url:pretty}` variables might be useful here. `{url}` gets replaced by the URL in fully encoded format and `{url:pretty}` uses a "pretty form" with most percent-encoded characters decoded.
|
||||
Note the {url} variable which gets replaced by the current URL might be useful here.
|
||||
|
||||
==== positional arguments
|
||||
* +'cmdline'+: The commandline to execute.
|
||||
* +'args'+: The commandline to execute.
|
||||
|
||||
==== optional arguments
|
||||
* +*-u*+, +*--userscript*+: Run the command as a userscript. You can use an absolute path, or store the userscript in one of those
|
||||
locations:
|
||||
- `~/.local/share/qutebrowser/userscripts`
|
||||
(or `$XDG_DATA_DIR`)
|
||||
- `/usr/share/qutebrowser/userscripts`
|
||||
|
||||
* +*-v*+, +*--verbose*+: Show notifications when the command started/exited.
|
||||
* +*-d*+, +*--detach*+: Whether the command should be detached from qutebrowser.
|
||||
|
||||
==== note
|
||||
* This command does not split arguments after the last argument and handles quotes literally.
|
||||
* +*-u*+, +*--userscript*+: Run the command as an userscript.
|
||||
|
||||
[[stop]]
|
||||
=== stop
|
||||
@@ -770,11 +564,8 @@ Syntax: +:tab-focus ['index']+
|
||||
|
||||
Select the tab given as argument/[count].
|
||||
|
||||
If neither count nor index are given, it behaves like tab-next.
|
||||
|
||||
==== positional arguments
|
||||
* +'index'+: The tab index to focus, starting with 1. The special value `last` focuses the last focused tab. Negative indexes
|
||||
counts from the end, such that -1 is the last tab.
|
||||
* +'index'+: The tab index to focus, starting with 1. The special value `last` focuses the last focused tab.
|
||||
|
||||
|
||||
==== count
|
||||
@@ -848,25 +639,13 @@ Save open pages and quit.
|
||||
|
||||
[[yank]]
|
||||
=== yank
|
||||
Syntax: +:yank [*--title*] [*--sel*] [*--domain*] [*--pretty*]+
|
||||
Syntax: +:yank [*--title*] [*--sel*]+
|
||||
|
||||
Yank the current URL/title to the clipboard or primary selection.
|
||||
|
||||
==== optional arguments
|
||||
* +*-t*+, +*--title*+: Yank the title instead of the URL.
|
||||
* +*-s*+, +*--sel*+: Use the primary selection instead of the clipboard.
|
||||
* +*-d*+, +*--domain*+: Yank only the scheme, domain, and port number.
|
||||
* +*-p*+, +*--pretty*+: Yank the URL in pretty decoded form.
|
||||
|
||||
[[yank-selected]]
|
||||
=== yank-selected
|
||||
Syntax: +:yank-selected [*--sel*] [*--keep*]+
|
||||
|
||||
Yank the selected text to the clipboard or primary selection.
|
||||
|
||||
==== optional arguments
|
||||
* +*-s*+, +*--sel*+: Use the primary selection instead of the clipboard.
|
||||
* +*-k*+, +*--keep*+: If given, stay in visual mode after yanking.
|
||||
|
||||
[[zoom]]
|
||||
=== zoom
|
||||
@@ -902,43 +681,18 @@ How many steps to zoom out.
|
||||
[options="header",width="75%",cols="25%,75%"]
|
||||
|==============
|
||||
|Command|Description
|
||||
|<<clear-keychain,clear-keychain>>|Clear the currently entered key chain.
|
||||
|<<command-accept,command-accept>>|Execute the command currently in the commandline.
|
||||
|<<command-history-next,command-history-next>>|Go forward in the commandline history.
|
||||
|<<command-history-prev,command-history-prev>>|Go back in the commandline history.
|
||||
|<<completion-item-del,completion-item-del>>|Delete the current completion item.
|
||||
|<<completion-item-next,completion-item-next>>|Select the next completion item.
|
||||
|<<completion-item-prev,completion-item-prev>>|Select the previous completion item.
|
||||
|<<drop-selection,drop-selection>>|Drop selection and keep selection mode enabled.
|
||||
|<<enter-mode,enter-mode>>|Enter a key mode.
|
||||
|<<follow-hint,follow-hint>>|Follow a hint.
|
||||
|<<follow-selected,follow-selected>>|Follow the selected text.
|
||||
|<<follow-hint,follow-hint>>|Follow the currently selected hint.
|
||||
|<<leave-mode,leave-mode>>|Leave the mode we're currently in.
|
||||
|<<message-error,message-error>>|Show an error message in the statusbar.
|
||||
|<<message-info,message-info>>|Show an info message in the statusbar.
|
||||
|<<message-warning,message-warning>>|Show a warning message in the statusbar.
|
||||
|<<move-to-end-of-document,move-to-end-of-document>>|Move the cursor or selection to the end of the document.
|
||||
|<<move-to-end-of-line,move-to-end-of-line>>|Move the cursor or selection to the end of line.
|
||||
|<<move-to-end-of-next-block,move-to-end-of-next-block>>|Move the cursor or selection to the end of next block.
|
||||
|<<move-to-end-of-prev-block,move-to-end-of-prev-block>>|Move the cursor or selection to the end of previous block.
|
||||
|<<move-to-end-of-word,move-to-end-of-word>>|Move the cursor or selection to the end of the word.
|
||||
|<<move-to-next-char,move-to-next-char>>|Move the cursor or selection to the next char.
|
||||
|<<move-to-next-line,move-to-next-line>>|Move the cursor or selection to the next line.
|
||||
|<<move-to-next-word,move-to-next-word>>|Move the cursor or selection to the next word.
|
||||
|<<move-to-prev-char,move-to-prev-char>>|Move the cursor or selection to the previous char.
|
||||
|<<move-to-prev-line,move-to-prev-line>>|Move the cursor or selection to the prev line.
|
||||
|<<move-to-prev-word,move-to-prev-word>>|Move the cursor or selection to the previous word.
|
||||
|<<move-to-start-of-document,move-to-start-of-document>>|Move the cursor or selection to the start of the document.
|
||||
|<<move-to-start-of-line,move-to-start-of-line>>|Move the cursor or selection to the start of the line.
|
||||
|<<move-to-start-of-next-block,move-to-start-of-next-block>>|Move the cursor or selection to the start of next block.
|
||||
|<<move-to-start-of-prev-block,move-to-start-of-prev-block>>|Move the cursor or selection to the start of previous block.
|
||||
|<<open-editor,open-editor>>|Open an external editor with the currently selected form field.
|
||||
|<<paste-primary,paste-primary>>|Paste the primary selection at cursor position.
|
||||
|<<prompt-accept,prompt-accept>>|Accept the current prompt.
|
||||
|<<prompt-no,prompt-no>>|Answer no to a yes/no prompt.
|
||||
|<<prompt-open-download,prompt-open-download>>|Immediately open a download.
|
||||
|<<prompt-yes,prompt-yes>>|Answer yes to a yes/no prompt.
|
||||
|<<repeat-command,repeat-command>>|Repeat the last executed command.
|
||||
|<<rl-backward-char,rl-backward-char>>|Move back a character.
|
||||
|<<rl-backward-delete-char,rl-backward-delete-char>>|Delete the character before the cursor.
|
||||
|<<rl-backward-word,rl-backward-word>>|Move back to the start of the current or previous word.
|
||||
@@ -952,18 +706,12 @@ 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.
|
||||
|<<scroll,scroll>>|Scroll the current tab in the given direction.
|
||||
|<<scroll,scroll>>|Scroll the current tab by 'count * dx/dy'.
|
||||
|<<scroll-page,scroll-page>>|Scroll the frame page-wise.
|
||||
|<<scroll-perc,scroll-perc>>|Scroll to a specific percentage of the page.
|
||||
|<<scroll-px,scroll-px>>|Scroll the current tab by 'count * dx/dy' pixels.
|
||||
|<<search-next,search-next>>|Continue the search to the ([count]th) next term.
|
||||
|<<search-prev,search-prev>>|Continue the search to the ([count]th) previous term.
|
||||
|<<toggle-selection,toggle-selection>>|Toggle caret selection mode.
|
||||
|==============
|
||||
[[clear-keychain]]
|
||||
=== clear-keychain
|
||||
Clear the currently entered key chain.
|
||||
|
||||
[[command-accept]]
|
||||
=== command-accept
|
||||
Execute the command currently in the commandline.
|
||||
@@ -976,10 +724,6 @@ Go forward in the commandline history.
|
||||
=== command-history-prev
|
||||
Go back in the commandline history.
|
||||
|
||||
[[completion-item-del]]
|
||||
=== completion-item-del
|
||||
Delete the current completion item.
|
||||
|
||||
[[completion-item-next]]
|
||||
=== completion-item-next
|
||||
Select the next completion item.
|
||||
@@ -988,10 +732,6 @@ Select the next completion item.
|
||||
=== completion-item-prev
|
||||
Select the previous completion item.
|
||||
|
||||
[[drop-selection]]
|
||||
=== drop-selection
|
||||
Drop selection and keep selection mode enabled.
|
||||
|
||||
[[enter-mode]]
|
||||
=== enter-mode
|
||||
Syntax: +:enter-mode 'mode'+
|
||||
@@ -1003,156 +743,18 @@ Enter a key mode.
|
||||
|
||||
[[follow-hint]]
|
||||
=== follow-hint
|
||||
Syntax: +:follow-hint ['keystring']+
|
||||
|
||||
Follow a hint.
|
||||
|
||||
==== positional arguments
|
||||
* +'keystring'+: The hint to follow.
|
||||
|
||||
[[follow-selected]]
|
||||
=== follow-selected
|
||||
Syntax: +:follow-selected [*--tab*]+
|
||||
|
||||
Follow the selected text.
|
||||
|
||||
==== optional arguments
|
||||
* +*-t*+, +*--tab*+: Load the selected link in a new tab.
|
||||
Follow the currently selected hint.
|
||||
|
||||
[[leave-mode]]
|
||||
=== leave-mode
|
||||
Leave the mode we're currently in.
|
||||
|
||||
[[message-error]]
|
||||
=== message-error
|
||||
Syntax: +:message-error 'text'+
|
||||
|
||||
Show an error message in the statusbar.
|
||||
|
||||
==== positional arguments
|
||||
* +'text'+: The text to show.
|
||||
|
||||
[[message-info]]
|
||||
=== message-info
|
||||
Syntax: +:message-info 'text'+
|
||||
|
||||
Show an info message in the statusbar.
|
||||
|
||||
==== positional arguments
|
||||
* +'text'+: The text to show.
|
||||
|
||||
[[message-warning]]
|
||||
=== message-warning
|
||||
Syntax: +:message-warning 'text'+
|
||||
|
||||
Show a warning message in the statusbar.
|
||||
|
||||
==== positional arguments
|
||||
* +'text'+: The text to show.
|
||||
|
||||
[[move-to-end-of-document]]
|
||||
=== move-to-end-of-document
|
||||
Move the cursor or selection to the end of the document.
|
||||
|
||||
[[move-to-end-of-line]]
|
||||
=== move-to-end-of-line
|
||||
Move the cursor or selection to the end of line.
|
||||
|
||||
[[move-to-end-of-next-block]]
|
||||
=== move-to-end-of-next-block
|
||||
Move the cursor or selection to the end of next block.
|
||||
|
||||
==== count
|
||||
How many blocks to move.
|
||||
|
||||
[[move-to-end-of-prev-block]]
|
||||
=== move-to-end-of-prev-block
|
||||
Move the cursor or selection to the end of previous block.
|
||||
|
||||
==== count
|
||||
How many blocks to move.
|
||||
|
||||
[[move-to-end-of-word]]
|
||||
=== move-to-end-of-word
|
||||
Move the cursor or selection to the end of the word.
|
||||
|
||||
==== count
|
||||
How many words to move.
|
||||
|
||||
[[move-to-next-char]]
|
||||
=== move-to-next-char
|
||||
Move the cursor or selection to the next char.
|
||||
|
||||
==== count
|
||||
How many lines to move.
|
||||
|
||||
[[move-to-next-line]]
|
||||
=== move-to-next-line
|
||||
Move the cursor or selection to the next line.
|
||||
|
||||
==== count
|
||||
How many lines to move.
|
||||
|
||||
[[move-to-next-word]]
|
||||
=== move-to-next-word
|
||||
Move the cursor or selection to the next word.
|
||||
|
||||
==== count
|
||||
How many words to move.
|
||||
|
||||
[[move-to-prev-char]]
|
||||
=== move-to-prev-char
|
||||
Move the cursor or selection to the previous char.
|
||||
|
||||
==== count
|
||||
How many chars to move.
|
||||
|
||||
[[move-to-prev-line]]
|
||||
=== move-to-prev-line
|
||||
Move the cursor or selection to the prev line.
|
||||
|
||||
==== count
|
||||
How many lines to move.
|
||||
|
||||
[[move-to-prev-word]]
|
||||
=== move-to-prev-word
|
||||
Move the cursor or selection to the previous word.
|
||||
|
||||
==== count
|
||||
How many words to move.
|
||||
|
||||
[[move-to-start-of-document]]
|
||||
=== move-to-start-of-document
|
||||
Move the cursor or selection to the start of the document.
|
||||
|
||||
[[move-to-start-of-line]]
|
||||
=== move-to-start-of-line
|
||||
Move the cursor or selection to the start of the line.
|
||||
|
||||
[[move-to-start-of-next-block]]
|
||||
=== move-to-start-of-next-block
|
||||
Move the cursor or selection to the start of next block.
|
||||
|
||||
==== count
|
||||
How many blocks to move.
|
||||
|
||||
[[move-to-start-of-prev-block]]
|
||||
=== move-to-start-of-prev-block
|
||||
Move the cursor or selection to the start of previous block.
|
||||
|
||||
==== count
|
||||
How many blocks to move.
|
||||
|
||||
[[open-editor]]
|
||||
=== open-editor
|
||||
Open an external editor with the currently selected form field.
|
||||
|
||||
The editor which should be launched can be configured via the `general -> editor` config option.
|
||||
|
||||
[[paste-primary]]
|
||||
=== paste-primary
|
||||
Paste the primary selection at cursor position.
|
||||
|
||||
[[prompt-accept]]
|
||||
=== prompt-accept
|
||||
Accept the current prompt.
|
||||
@@ -1161,21 +763,10 @@ Accept the current prompt.
|
||||
=== prompt-no
|
||||
Answer no to a yes/no prompt.
|
||||
|
||||
[[prompt-open-download]]
|
||||
=== prompt-open-download
|
||||
Immediately open a download.
|
||||
|
||||
[[prompt-yes]]
|
||||
=== prompt-yes
|
||||
Answer yes to a yes/no prompt.
|
||||
|
||||
[[repeat-command]]
|
||||
=== repeat-command
|
||||
Repeat the last executed command.
|
||||
|
||||
==== count
|
||||
Which count to pass the command.
|
||||
|
||||
[[rl-backward-char]]
|
||||
=== rl-backward-char
|
||||
Move back a character.
|
||||
@@ -1256,20 +847,20 @@ This acts like readline's yank.
|
||||
|
||||
[[scroll]]
|
||||
=== scroll
|
||||
Syntax: +:scroll 'direction'+
|
||||
Syntax: +:scroll 'dx' 'dy'+
|
||||
|
||||
Scroll the current tab in the given direction.
|
||||
Scroll the current tab by 'count * dx/dy'.
|
||||
|
||||
==== positional arguments
|
||||
* +'direction'+: In which direction to scroll (up/down/left/right/top/bottom).
|
||||
|
||||
* +'dx'+: How much to scroll in x-direction.
|
||||
* +'dy'+: How much to scroll in x-direction.
|
||||
|
||||
==== count
|
||||
multiplier
|
||||
|
||||
[[scroll-page]]
|
||||
=== scroll-page
|
||||
Syntax: +:scroll-page [*--top-navigate* 'ACTION'] [*--bottom-navigate* 'ACTION'] 'x' 'y'+
|
||||
Syntax: +:scroll-page 'x' 'y'+
|
||||
|
||||
Scroll the frame page-wise.
|
||||
|
||||
@@ -1277,12 +868,6 @@ Scroll the frame page-wise.
|
||||
* +'x'+: How many pages to scroll to the right.
|
||||
* +'y'+: How many pages to scroll down.
|
||||
|
||||
==== optional arguments
|
||||
* +*-t*+, +*--top-navigate*+: :navigate action (prev, decrement) to run when scrolling up at the top of the page.
|
||||
|
||||
* +*-b*+, +*--bottom-navigate*+: :navigate action (next, increment) to run when scrolling down at the bottom of the page.
|
||||
|
||||
|
||||
==== count
|
||||
multiplier
|
||||
|
||||
@@ -1303,19 +888,6 @@ The percentage can be given either as argument or as count. If no percentage is
|
||||
==== count
|
||||
Percentage to scroll.
|
||||
|
||||
[[scroll-px]]
|
||||
=== scroll-px
|
||||
Syntax: +:scroll-px 'dx' 'dy'+
|
||||
|
||||
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 y-direction.
|
||||
|
||||
==== count
|
||||
multiplier
|
||||
|
||||
[[search-next]]
|
||||
=== search-next
|
||||
Continue the search to the ([count]th) next term.
|
||||
@@ -1330,10 +902,6 @@ Continue the search to the ([count]th) previous term.
|
||||
==== count
|
||||
How many elements to ignore.
|
||||
|
||||
[[toggle-selection]]
|
||||
=== toggle-selection
|
||||
Toggle caret selection mode.
|
||||
|
||||
|
||||
== Debugging commands
|
||||
These commands are mainly intended for debugging. They are hidden if qutebrowser was started without the `--debug`-flag.
|
||||
@@ -1344,14 +912,10 @@ These commands are mainly intended for debugging. They are hidden if qutebrowser
|
||||
|Command|Description
|
||||
|<<debug-all-objects,debug-all-objects>>|Print a list of all objects to the debug log.
|
||||
|<<debug-cache-stats,debug-cache-stats>>|Print LRU cache stats.
|
||||
|<<debug-clear-ssl-errors,debug-clear-ssl-errors>>|Clear remembered SSL error answers.
|
||||
|<<debug-console,debug-console>>|Show the debugging console.
|
||||
|<<debug-crash,debug-crash>>|Crash for debugging purposes.
|
||||
|<<debug-dump-page,debug-dump-page>>|Dump the current page's content to a file.
|
||||
|<<debug-pyeval,debug-pyeval>>|Evaluate a python string and display the results as a web page.
|
||||
|<<debug-set-fake-clipboard,debug-set-fake-clipboard>>|Put data into the fake clipboard and enable logging, used for tests.
|
||||
|<<debug-trace,debug-trace>>|Trace executed code via hunter.
|
||||
|<<debug-webaction,debug-webaction>>|Execute a webaction.
|
||||
|==============
|
||||
[[debug-all-objects]]
|
||||
=== debug-all-objects
|
||||
@@ -1361,10 +925,6 @@ Print a list of all objects to the debug log.
|
||||
=== debug-cache-stats
|
||||
Print LRU cache stats.
|
||||
|
||||
[[debug-clear-ssl-errors]]
|
||||
=== debug-clear-ssl-errors
|
||||
Clear remembered SSL error answers.
|
||||
|
||||
[[debug-console]]
|
||||
=== debug-console
|
||||
Show the debugging console.
|
||||
@@ -1378,43 +938,19 @@ Crash for debugging purposes.
|
||||
==== positional arguments
|
||||
* +'typ'+: either 'exception' or 'segfault'.
|
||||
|
||||
[[debug-dump-page]]
|
||||
=== debug-dump-page
|
||||
Syntax: +:debug-dump-page [*--plain*] 'dest'+
|
||||
|
||||
Dump the current page's content to a file.
|
||||
|
||||
==== positional arguments
|
||||
* +'dest'+: Where to write the file to.
|
||||
|
||||
==== optional arguments
|
||||
* +*-p*+, +*--plain*+: Write plain text instead of HTML.
|
||||
|
||||
[[debug-pyeval]]
|
||||
=== debug-pyeval
|
||||
Syntax: +:debug-pyeval [*--quiet*] 's'+
|
||||
Syntax: +:debug-pyeval 's'+
|
||||
|
||||
Evaluate a python string and display the results as a web page.
|
||||
|
||||
==== positional arguments
|
||||
* +'s'+: The string to evaluate.
|
||||
|
||||
==== optional arguments
|
||||
* +*-q*+, +*--quiet*+: Don't show the output in a new tab.
|
||||
|
||||
==== 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.
|
||||
|
||||
[[debug-set-fake-clipboard]]
|
||||
=== debug-set-fake-clipboard
|
||||
Syntax: +:debug-set-fake-clipboard ['s']+
|
||||
|
||||
Put data into the fake clipboard and enable logging, used for tests.
|
||||
|
||||
==== positional arguments
|
||||
* +'s'+: The text to put into the fake clipboard, or unset to enable logging.
|
||||
|
||||
[[debug-trace]]
|
||||
=== debug-trace
|
||||
Syntax: +:debug-trace ['expr']+
|
||||
@@ -1428,17 +964,3 @@ Trace executed code via hunter.
|
||||
* 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.
|
||||
|
||||
[[debug-webaction]]
|
||||
=== debug-webaction
|
||||
Syntax: +:debug-webaction 'action'+
|
||||
|
||||
Execute a webaction.
|
||||
|
||||
See http://doc.qt.io/qt-5/qwebpage.html#WebAction-enum for the available actions.
|
||||
|
||||
==== positional arguments
|
||||
* +'action'+: The action to execute, e.g. MoveToNextChar.
|
||||
|
||||
==== count
|
||||
How many times to repeat the action.
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ The following help pages are currently available:
|
||||
* link:commands.html[Documentation of commands]
|
||||
* link:settings.html[Documentation of settings]
|
||||
* link:userscripts.html[How to write userscripts]
|
||||
* link:CONTRIBUTING.html[Contributing to qutebrowser]
|
||||
|
||||
Getting help
|
||||
------------
|
||||
|
||||
|
Before Width: | Height: | Size: 989 KiB |
|
Before Width: | Height: | Size: 43 KiB |
@@ -5,26 +5,12 @@ The Compiler <mail@qutebrowser.org>
|
||||
NOTE: This page will only appear on the first start. To view it at a later
|
||||
time, use the `:help` command.
|
||||
|
||||
Basic keybindings to get you started
|
||||
------------------------------------
|
||||
|
||||
* Use the arrow keys or `hjkl` to move around a webpage (vim-like syntax is used in quite a few places)
|
||||
* To go to a new webpage, press `o`, then type a url, then press Enter (Use `O` to open the url in a new tab). If what you've typed isn't a url, then a search engine will be used instead (DuckDuckGo, by default)
|
||||
* To switch between tabs, use `J` (next tab) and `K` (previous tab), or press `<Alt-num>`, where `num` is the position of the tab to switch to
|
||||
* To close the current tab, press `d` (and press `u` to undo closing a tab)
|
||||
* Use `H` and `L` to go back and forth in the history
|
||||
* To click on something without using the mouse, press `f` to show the hints, then type the keys next to what you want to click on (if that sounds weird, then just try pressing `f` and see what happens)
|
||||
* Press `:` to show the commandline
|
||||
* To search in a page, press `/`, type the phrase to search for, then press Enter. Use `n` and `N` to go back and forth through the matches, and press Esc to stop doing the search.
|
||||
* To close qutebrowser, press `Alt-F4`, or `:q`, or `:wq` to save the currently open tabs and quit (note that in the settings you can make qutebrowser always save the currently open tabs)
|
||||
|
||||
What to do now
|
||||
--------------
|
||||
|
||||
* View the link:http://qutebrowser.org/img/cheatsheet-big.png[key binding cheatsheet]
|
||||
* View the http://qutebrowser.org/img/cheatsheet-big.png[key binding cheatsheet]
|
||||
to make yourself familiar with the key bindings: +
|
||||
image:http://qutebrowser.org/img/cheatsheet-small.png["qutebrowser key binding cheatsheet",link="http://qutebrowser.org/img/cheatsheet-big.png"]
|
||||
* Run `:adblock-update` to download adblock lists and activate adblocking.
|
||||
* If you just cloned the repository, you'll need to run
|
||||
`scripts/asciidoc2html.py` to generate the documentation.
|
||||
* Go to the link:qute://settings[settings page] to set up qutebrowser the way you want it.
|
||||
|
||||
@@ -16,7 +16,7 @@ qutebrowser - a keyboard-driven, vim-like browser based on PyQt5 and QtWebKit.
|
||||
*qutebrowser* ['-OPTION' ['...']] [':COMMAND' ['...']] ['URL' ['...']]
|
||||
|
||||
== DESCRIPTION
|
||||
qutebrowser is a keyboard-focused browser with a minimal GUI. It's based
|
||||
qutebrowser is a keyboard-focused browser with with a minimal GUI. It's based
|
||||
on Python, PyQt5 and QtWebKit and free software, licensed under the GPL.
|
||||
|
||||
It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl.
|
||||
@@ -41,15 +41,6 @@ show it.
|
||||
*-c* 'CONFDIR', *--confdir* 'CONFDIR'::
|
||||
Set config directory (empty for no config storage).
|
||||
|
||||
*--datadir* 'DATADIR'::
|
||||
Set data directory (empty for no data storage).
|
||||
|
||||
*--cachedir* 'CACHEDIR'::
|
||||
Set cache directory (empty for no cache storage).
|
||||
|
||||
*--basedir* 'BASEDIR'::
|
||||
Base directory for all storage. Other --*dir arguments are ignored if this is given.
|
||||
|
||||
*-V*, *--version*::
|
||||
Show version and quit.
|
||||
|
||||
@@ -62,14 +53,8 @@ show it.
|
||||
*-R*, *--override-restore*::
|
||||
Don't restore a session even if one would be restored.
|
||||
|
||||
*--target* '{auto,tab,tab-bg,tab-silent,tab-bg-silent,window}'::
|
||||
How URLs should be opened if there is already a qutebrowser instance running.
|
||||
|
||||
*--backend* '{webkit,webengine}'::
|
||||
Which backend to use (webengine backend is EXPERIMENTAL!).
|
||||
|
||||
=== debug arguments
|
||||
*-l* '{critical,error,warning,info,debug,vdebug}', *--loglevel* '{critical,error,warning,info,debug,vdebug}'::
|
||||
*-l* 'LOGLEVEL', *--loglevel* 'LOGLEVEL'::
|
||||
Set loglevel
|
||||
|
||||
*--logfilter* 'LOGFILTER'::
|
||||
@@ -81,15 +66,9 @@ show it.
|
||||
*--debug*::
|
||||
Turn on debugging options.
|
||||
|
||||
*--json-logging*::
|
||||
Output log lines in JSON format (one object per line).
|
||||
|
||||
*--nocolor*::
|
||||
Turn off colored logging.
|
||||
|
||||
*--force-color*::
|
||||
Force colored logging
|
||||
|
||||
*--harfbuzz* '{old,new,system,auto}'::
|
||||
HarfBuzz engine version to use. Default: auto.
|
||||
|
||||
@@ -102,15 +81,12 @@ show it.
|
||||
*--debug-exit*::
|
||||
Turn on debugging of late exit.
|
||||
|
||||
*--no-crash-dialog*::
|
||||
Don't show a crash dialog.
|
||||
|
||||
*--pdb-postmortem*::
|
||||
Drop into pdb on exceptions.
|
||||
|
||||
*--temp-basedir*::
|
||||
Use a temporary basedir.
|
||||
|
||||
*--no-err-windows*::
|
||||
Don't show any error windows (used for tests/smoke.py).
|
||||
|
||||
*--qt-name* 'NAME'::
|
||||
Set the window name.
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ $ git checkout symbols
|
||||
$ export DEBUG_CFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
|
||||
$ export DEBUG_CXXFLAGS='-ggdb3 -fvar-tracking-assignments -Og'
|
||||
$ cd qt5
|
||||
$ makepkg -si --pkg qt5-base-debug,qt5-webkit-debug,qt5-webengine-debug
|
||||
$ makepkg -si --pkg qt5-base-debug,qt5-webkit-debug
|
||||
$ cd ../pyqt5
|
||||
$ makepkg -si --pkg pyqt5-common-debug,python-pyqt5-debug
|
||||
----
|
||||
@@ -76,7 +76,7 @@ Server = http://qutebrowser.org/qt-debug/$arch
|
||||
Then install the packages:
|
||||
|
||||
----
|
||||
# pacman -Suy pyqt5-common-debug python-pyqt5-debug qt5-base-debug qt5-webkit-debug,qt5-webengine-debug
|
||||
# pacman -Sy pyqt5-common-debug python-pyqt5-debug qt5-base-debug qt5-webkit-debug
|
||||
----
|
||||
|
||||
The `-debug` packages conflict with the non-debug variants - it's safe to
|
||||
|
||||
@@ -5,12 +5,9 @@ The Compiler <mail@qutebrowser.org>
|
||||
qutebrowser is extensible by writing userscripts which can be called via the
|
||||
`:spawn --userscript` command, or via a key binding.
|
||||
|
||||
You can also call a userscript via hints so they get the selected hint URL by
|
||||
calling `:hint links userscript ...`.
|
||||
|
||||
These userscripts are similar to the (non-javascript) dwb userscripts. They can
|
||||
be written in any language which can read environment variables and write to a
|
||||
FIFO. Note they are *not* related to Greasemonkey userscripts.
|
||||
These userscripts are similiar to the (non-javascript) dwb userscripts. They
|
||||
can be written in any language which can read environment variables and write
|
||||
to a FIFO. Note they are *not* related to Greasemonkey userscripts.
|
||||
|
||||
Note for simple things such as opening the current page with another browser or
|
||||
mpv, a simple key binding to something like `:spawn mpv {url}` should suffice.
|
||||
@@ -18,24 +15,17 @@ mpv, a simple key binding to something like `:spawn mpv {url}` should suffice.
|
||||
Also note userscripts need to have the executable bit set (`chmod +x`) for
|
||||
qutebrowser to run them.
|
||||
|
||||
To call a userscript, it needs to be stored in your data directory under
|
||||
`userscripts` (for example: `~/.local/share/qutebrowser/userscripts/myscript`),
|
||||
or just use an absolute path.
|
||||
|
||||
Getting information
|
||||
-------------------
|
||||
|
||||
The following environment variables will be set when a userscript is launched:
|
||||
The following environment variables will be set when an userscript is launched:
|
||||
|
||||
- `QUTE_MODE`: Either `hints` (started via hints) or `command` (started via
|
||||
command or key binding).
|
||||
- `QUTE_USER_AGENT`: The currently set user agent.
|
||||
- `QUTE_FIFO`: The FIFO or file to write commands to.
|
||||
- `QUTE_HTML`: Path of a file containing the HTML source of the current page.
|
||||
- `QUTE_TEXT`: Path of a file containing the plaintext of the current page.
|
||||
- `QUTE_CONFIG_DIR`: Path of the directory containing qutebrowser's configuration.
|
||||
- `QUTE_DATA_DIR`: Path of the directory containing qutebrowser's data.
|
||||
- `QUTE_DOWNLOAD_DIR`: Path of the downloads directory.
|
||||
- `QUTE_HTML`: The HTML source of the current page.
|
||||
- `QUTE_TEXT`: The plaintext of the current page.
|
||||
|
||||
In `command` mode:
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 9.3 KiB |
|
Before Width: | Height: | Size: 945 B After Width: | Height: | Size: 872 B |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 21 KiB |
@@ -1,207 +0,0 @@
|
||||
/* XPM */
|
||||
static char *qutebrowser[] = {
|
||||
/* columns rows colors chars-per-pixel */
|
||||
"32 32 169 2 ",
|
||||
" c #0A396E",
|
||||
". c #0B3C72",
|
||||
"X c #0B4077",
|
||||
"o c #0C437B",
|
||||
"O c #134175",
|
||||
"+ c #15467C",
|
||||
"@ c #18477B",
|
||||
"# c #1A497D",
|
||||
"$ c #0D4B86",
|
||||
"% c #0F4E8D",
|
||||
"& c #124A80",
|
||||
"* c #1F4F83",
|
||||
"= c #0E518C",
|
||||
"- c #1F5084",
|
||||
"; c #11508C",
|
||||
": c #0F5193",
|
||||
"> c #115799",
|
||||
", c #115B9C",
|
||||
"< c #204F83",
|
||||
"1 c #245287",
|
||||
"2 c #2A598C",
|
||||
"3 c #325E8F",
|
||||
"4 c #11609F",
|
||||
"5 c #346496",
|
||||
"6 c #3B6898",
|
||||
"7 c #115CA1",
|
||||
"8 c #115EAC",
|
||||
"9 c #1263A3",
|
||||
"0 c #1260AD",
|
||||
"q c #136BAC",
|
||||
"w c #136BB2",
|
||||
"e c #1366BA",
|
||||
"r c #196BB2",
|
||||
"t c #157ABB",
|
||||
"y c #1577BB",
|
||||
"u c #2E6DB0",
|
||||
"i c #387FB1",
|
||||
"p c #456E9A",
|
||||
"a c #4873A1",
|
||||
"s c #4375AA",
|
||||
"d c #507AA6",
|
||||
"f c #597EA4",
|
||||
"g c #4D7EB3",
|
||||
"h c #156FCB",
|
||||
"j c #167AC5",
|
||||
"k c #1675CA",
|
||||
"l c #177BCE",
|
||||
"z c #1777D8",
|
||||
"x c #1476E4",
|
||||
"c c #167BE6",
|
||||
"v c #167DE8",
|
||||
"b c #197EEF",
|
||||
"n c #1A7FF0",
|
||||
"m c #1A80BE",
|
||||
"M c #5F87AF",
|
||||
"N c #5D8BBA",
|
||||
"B c #5A84B1",
|
||||
"V c #6C8FB3",
|
||||
"C c #6F96BE",
|
||||
"Z c #1886CC",
|
||||
"A c #1883D7",
|
||||
"S c #198DD5",
|
||||
"D c #1987D9",
|
||||
"F c #198ADC",
|
||||
"G c #1A96DC",
|
||||
"H c #3090D9",
|
||||
"J c #1682E9",
|
||||
"K c #1983ED",
|
||||
"L c #1689E9",
|
||||
"P c #1A8DEE",
|
||||
"I c #1B95ED",
|
||||
"U c #1C9EEA",
|
||||
"Y c #1B97E4",
|
||||
"T c #1A84F2",
|
||||
"R c #1A8BF2",
|
||||
"E c #1C94F4",
|
||||
"W c #1D9CF5",
|
||||
"Q c #3388E6",
|
||||
"! c #3D90E9",
|
||||
"~ c #228EF3",
|
||||
"^ c #229FF6",
|
||||
"/ c #3294F4",
|
||||
"( c #3D9FF6",
|
||||
") c #339CF4",
|
||||
"_ c #1CA2E5",
|
||||
"` c #1DABEE",
|
||||
"' c #1DA4F6",
|
||||
"] c #1EA9F7",
|
||||
"[ c #1EADF8",
|
||||
"{ c #1FB4F9",
|
||||
"} c #1FB9FA",
|
||||
"| c #20ACF8",
|
||||
" . c #27A4F6",
|
||||
".. c #3DA9F6",
|
||||
"X. c #20B9FA",
|
||||
"o. c #2EB6F9",
|
||||
"O. c #458DC9",
|
||||
"+. c #5C8DC1",
|
||||
"@. c #5795C6",
|
||||
"#. c #709DCB",
|
||||
"$. c #74A8DD",
|
||||
"%. c #4A97EA",
|
||||
"&. c #4896EA",
|
||||
"*. c #559EEA",
|
||||
"=. c #439AF5",
|
||||
"-. c #46A3F6",
|
||||
";. c #5FA9F6",
|
||||
":. c #5EA6F3",
|
||||
">. c #47BCF9",
|
||||
",. c #51B5F8",
|
||||
"<. c #58BDF8",
|
||||
"1. c #68ABEF",
|
||||
"2. c #7DB9E7",
|
||||
"3. c #63AEF7",
|
||||
"4. c #6FB1F7",
|
||||
"5. c #66B9F8",
|
||||
"6. c #61B2F6",
|
||||
"7. c #71B4F7",
|
||||
"8. c #78B7F4",
|
||||
"9. c #72BFF9",
|
||||
"0. c #3BC0FA",
|
||||
"q. c #6FCEFB",
|
||||
"w. c #6CC5FA",
|
||||
"e. c #7BCAF9",
|
||||
"r. c #89A7C3",
|
||||
"t. c #83A2C1",
|
||||
"y. c #98B6D3",
|
||||
"u. c #9DB9D3",
|
||||
"i. c #89B6E4",
|
||||
"p. c #83B6E9",
|
||||
"a. c #81BDF7",
|
||||
"s. c #83BFF8",
|
||||
"d. c #9EC4E9",
|
||||
"f. c #8CC2F9",
|
||||
"g. c #85CDFB",
|
||||
"h. c #87C4F9",
|
||||
"j. c #92C6F9",
|
||||
"k. c #95CAFA",
|
||||
"l. c #9CCBFA",
|
||||
"z. c #89D7FC",
|
||||
"x. c #91D9FC",
|
||||
"c. c #9CDEFD",
|
||||
"v. c #9ED2FB",
|
||||
"b. c #A7CAEC",
|
||||
"n. c #B5CEE3",
|
||||
"m. c #A1CEFA",
|
||||
"M. c #AED0F0",
|
||||
"N. c #ACD6FA",
|
||||
"B. c #A0DFFC",
|
||||
"V. c #AFD8FC",
|
||||
"C. c #B5D9FB",
|
||||
"Z. c #BCDDFC",
|
||||
"A. c #BFDCF5",
|
||||
"S. c #ACE3FD",
|
||||
"D. c #B5E5FE",
|
||||
"F. c #BBE2FC",
|
||||
"G. c #CFE5F5",
|
||||
"H. c #C3E1FC",
|
||||
"J. c #CAE6FD",
|
||||
"K. c #CCEBFD",
|
||||
"L. c #C4EBFE",
|
||||
"P. c #D6EDFE",
|
||||
"I. c #DAEEFD",
|
||||
"U. c #DEF1FE",
|
||||
"Y. c #D6F2FE",
|
||||
"T. c #E4F4FE",
|
||||
"R. c #E9F6FE",
|
||||
"E. c #EBF8FF",
|
||||
"W. c None",
|
||||
/* pixels */
|
||||
"W.W.W.W.W.W.W.W.W.W.W.c.S.L.Y.E.E.S.X.} W.W.W.W.W.W.W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.W.W.W.D.T.E.E.T.L.D.c.z.} } X.} } W.W.W.W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.W.B.T.T.R.T.R.U.0.X.z.S.} } } } { { X.W.W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.x.x.K.T.T.T.L.P.q.o.{ } } ` _ { { { { { W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.c.P.D.G.u.r.i 9 Z _ { { G 4 X t { { { { { { W.W.W.W.W.",
|
||||
"W.W.W.W.K.U.n.f O { = t { { { { [ { { W.W.W.W.",
|
||||
"W.W.W.F.I.t.. ' t { { [ [ [ [ [ >.W.W.W.",
|
||||
"W.W.x.P.V ' X t ` [ [ [ [ [ [ o.e.W.W.",
|
||||
"W.W.J.y. X t S Y Z $ ' . y [ [ [ ] [ [ | Z.J.W.W.",
|
||||
"W.<.e.& , _ ] ] [ ] U . ' . y [ ' [ ] ] ] w.K.J.g.W.",
|
||||
"W.' S o ' ' [ ' [ ' ] o ' . y Y 9 = = 9 @.J.J.J.F.W.",
|
||||
"W.| , j ' ' ' ' ' ' ' o ' . $ p A.J.J.g.",
|
||||
"' .. G ' ' ' ' ' ' ' o ' . M H.H.h.",
|
||||
",.2. . W ' W ' ' ' ' W . ' . M.A.x.",
|
||||
"N.M.. . W W W ' W W W W .w 9 I U 0 #.Z.m.",
|
||||
" .9.O D W W W W ' W j $ % F W W W .5 d Z.C.",
|
||||
"W W ; 9 9.h.5...Q % o j W W W W W W O. 3 C.N.",
|
||||
"E W 7 B b.d.a . w E E W W W E W E A @ C.l.",
|
||||
"I E l u W E W E W E E E E A . - k.6.",
|
||||
"P E E 7 m.o E E E E E E E E l . = E P ",
|
||||
"L E E E > . O s.o E E E E E E E E 7 , E L ",
|
||||
"W.R E R ) #.5 1 6 N i.2 s.+ E E E E E E R L . k R W.",
|
||||
"W.L R E -.m.m.m.m.m.m.2 m.@ N m.m.s.( R R % X E J W.",
|
||||
"W.W.K R ~ a.m.l.l.l.l.2 s.+ < i.l.m.j.h % e K W.W.",
|
||||
"W.W.J R R / l.l.l.l.k.2 s.+ * 5 + 8 R J W.W.",
|
||||
"W.W.W.v T R 3.k.k.j.k.2 2 j.& . 8 R v W.W.W.",
|
||||
"W.W.W.W.J T ~ 7.j.j.j.g +.p.j.s.+. . . : z T v W.W.W.W.",
|
||||
"W.W.W.W.W.c T T =.f.j.j.s.j.j.j.j.$.g s u e h b T T v W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.c b n 4.f.f.s.m.s.s.s.j.s.j./ T n T b c W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.W.c x 1.s.s.s.s.s.s.s.s.4.=.n T n c c W.W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.W.W.W.&.*.1.a.s.s.s.s.3.n n v x x W.W.W.W.W.W.W.W.W.",
|
||||
"W.W.W.W.W.W.W.W.W.W.W.%.%.%.%.*.*.Q x x x W.W.W.W.W.W.W.W.W.W.W."
|
||||
};
|
||||
@@ -29,8 +29,6 @@ profile qutebrowser /usr/{local/,}bin/qutebrowser {
|
||||
|
||||
/proc/*/mounts r,
|
||||
owner /tmp/** rwkl,
|
||||
owner /run/user/*/ rw,
|
||||
owner /run/user/*/** krw,
|
||||
|
||||
@{HOME}/.config/qutebrowser/** krw,
|
||||
@{HOME}/.local/share/qutebrowser/** krw,
|
||||
|
||||
@@ -32,18 +32,18 @@
|
||||
objecttolerance="10"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1.7582312"
|
||||
inkscape:cx="875.18895"
|
||||
inkscape:cy="136.8726"
|
||||
inkscape:zoom="0.8791156"
|
||||
inkscape:cx="327.65084"
|
||||
inkscape:cy="233.0095"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
width="1024px"
|
||||
height="640px"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1362"
|
||||
inkscape:window-height="740"
|
||||
inkscape:window-width="1366"
|
||||
inkscape:window-height="768"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="24"
|
||||
inkscape:window-y="0"
|
||||
showguides="true"
|
||||
inkscape:guide-bbox="true"
|
||||
inkscape:window-maximized="0"
|
||||
@@ -1939,7 +1939,7 @@
|
||||
x="542.06946"
|
||||
sodipodi:role="line"
|
||||
id="tspan4938"
|
||||
style="font-size:8px">scroll</tspan><tspan
|
||||
style="font-size:8px">scoll</tspan><tspan
|
||||
y="276.1955"
|
||||
x="542.06946"
|
||||
sodipodi:role="line"
|
||||
@@ -2999,8 +2999,6 @@
|
||||
style="font-size:10px;fill:#000000"
|
||||
id="flowPara3626-73">;b - open hint in background tab</flowPara><flowPara
|
||||
style="font-size:10px;fill:#000000"
|
||||
id="flowPara4051">;f - open hint in foreground tab</flowPara><flowPara
|
||||
style="font-size:10px;fill:#000000"
|
||||
id="flowPara3788">;h - hover over hint (mouse-over)</flowPara><flowPara
|
||||
style="font-size:10px;fill:#000000"
|
||||
id="flowPara3790">;i - hint images</flowPara><flowPara
|
||||
@@ -3040,13 +3038,17 @@
|
||||
style="font-weight:bold;-inkscape-font-specification:'Sans Bold';fill:#ff0000"
|
||||
id="flowSpan3852">(10)</flowSpan> misc. commands:</flowPara><flowPara
|
||||
style="font-size:10px;fill:#000000"
|
||||
id="flowPara3725-0">gt - switch tabs by name</flowPara><flowPara
|
||||
style="font-size:10px;fill:#000000"
|
||||
id="flowPara4052"><flowSpan
|
||||
id="flowPara3725-0"><flowSpan
|
||||
style="fill:#0000ff"
|
||||
id="flowSpan4054">gm/gl/lr</flowSpan> - move tab</flowPara><flowPara
|
||||
id="flowSpan5471">gm</flowSpan> - move tab</flowPara><flowPara
|
||||
style="font-size:10px;fill:#000000"
|
||||
id="flowPara4056"> (to index/left/right)</flowPara><flowPara
|
||||
id="flowPara3854"><flowSpan
|
||||
style="fill:#0000ff"
|
||||
id="flowSpan5473">gl</flowSpan> - move tab to left</flowPara><flowPara
|
||||
style="font-size:10px;fill:#000000"
|
||||
id="flowPara3856"><flowSpan
|
||||
style="fill:#0000ff"
|
||||
id="flowSpan5475">gr</flowSpan> - move tab to right</flowPara><flowPara
|
||||
style="font-size:10px;fill:#000000"
|
||||
id="flowPara3858">gC - clone tab</flowPara><flowPara
|
||||
style="font-size:10px;fill:#000000"
|
||||
@@ -3322,15 +3324,27 @@
|
||||
style="font-size:8px">tab</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8px;line-height:89.99999762%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ff0000;fill-opacity:1;stroke:none"
|
||||
x="274.21381"
|
||||
y="343.17578"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8px;line-height:89.99999762%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
||||
x="267.67316"
|
||||
y="326.20523"
|
||||
id="text10547-23-6-7"
|
||||
sodipodi:linespacing="89.999998%"><tspan
|
||||
sodipodi:role="line"
|
||||
x="274.21381"
|
||||
y="343.17578"
|
||||
id="tspan4052">(10)</tspan></text>
|
||||
x="267.67316"
|
||||
y="326.20523"
|
||||
id="tspan10560-1-3-1" /><tspan
|
||||
sodipodi:role="line"
|
||||
x="267.67316"
|
||||
y="333.40524"
|
||||
id="tspan5325">co: close</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="267.67316"
|
||||
y="340.60522"
|
||||
id="tspan10562-12-5-98">other tabs</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
x="267.67316"
|
||||
y="347.80524"
|
||||
id="tspan4045">cd: clea</tspan></text>
|
||||
<text
|
||||
sodipodi:linespacing="89.999998%"
|
||||
id="text10564-6-7-8-0"
|
||||
@@ -3455,20 +3469,5 @@
|
||||
y="177.63554"
|
||||
style="font-size:8px"
|
||||
id="tspan3719">cache)</tspan></text>
|
||||
<text
|
||||
sodipodi:linespacing="89.999998%"
|
||||
id="text9514-60-7-7-0-8"
|
||||
y="338.04874"
|
||||
x="342.42523"
|
||||
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:8px;line-height:89.99999762%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none"
|
||||
xml:space="preserve"><tspan
|
||||
y="338.04874"
|
||||
x="342.42523"
|
||||
sodipodi:role="line"
|
||||
id="tspan5689-6">visual</tspan><tspan
|
||||
y="345.24875"
|
||||
x="342.42523"
|
||||
sodipodi:role="line"
|
||||
id="tspan4112">mode</tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 137 KiB After Width: | Height: | Size: 137 KiB |
@@ -1,35 +0,0 @@
|
||||
FROM base/archlinux
|
||||
MAINTAINER Florian Bruhin <me@the-compiler.org>
|
||||
|
||||
RUN echo 'Server = http://mirror.de.leaseweb.net/archlinux/$repo/os/$arch' > /etc/pacman.d/mirrorlist
|
||||
RUN pacman-key --init && pacman-key --populate archlinux && pacman -Sy --noconfirm archlinux-keyring
|
||||
|
||||
RUN pacman -Suyy --noconfirm
|
||||
RUN pacman-db-upgrade
|
||||
|
||||
RUN pacman -S --noconfirm \
|
||||
git \
|
||||
python-tox \
|
||||
qt5-base \
|
||||
qt5-webkit \
|
||||
python-pyqt5 \
|
||||
xorg-xinit \
|
||||
herbstluftwm \
|
||||
xorg-server-xvfb
|
||||
|
||||
RUN echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen && locale-gen
|
||||
|
||||
RUN useradd user && mkdir /home/user && chown -R user:users /home/user
|
||||
USER user
|
||||
WORKDIR /home/user
|
||||
|
||||
ENV DISPLAY=:0
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
CMD Xvfb -screen 0 800x600x24 :0 & \
|
||||
sleep 2 && \
|
||||
herbstluftwm & \
|
||||
git clone /outside qutebrowser.git && \
|
||||
cd qutebrowser.git && \
|
||||
tox -e py35
|
||||
@@ -1,35 +0,0 @@
|
||||
FROM debian:jessie
|
||||
MAINTAINER Florian Bruhin <me@the-compiler.org>
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get -y update && \
|
||||
apt-get -y dist-upgrade && \
|
||||
apt-get -y install --no-install-recommends \
|
||||
python3-pyqt5 \
|
||||
python3-pyqt5.qtwebkit \
|
||||
python-tox \
|
||||
python3-sip \
|
||||
xvfb \
|
||||
git \
|
||||
python3-setuptools \
|
||||
wget \
|
||||
herbstluftwm \
|
||||
locales \
|
||||
libjs-pdf
|
||||
RUN echo 'en_US.UTF-8 UTF-8' > /etc/locale.gen && locale-gen
|
||||
|
||||
RUN useradd user && mkdir /home/user && chown -R user:users /home/user
|
||||
USER user
|
||||
WORKDIR /home/user
|
||||
|
||||
ENV DISPLAY=:0
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
CMD Xvfb -screen 0 800x600x24 :0 & \
|
||||
sleep 2 && \
|
||||
herbstluftwm & \
|
||||
git clone /outside qutebrowser.git && \
|
||||
cd qutebrowser.git && \
|
||||
tox -e py34
|
||||
@@ -1,37 +0,0 @@
|
||||
FROM ubuntu:xenial
|
||||
MAINTAINER Florian Bruhin <me@the-compiler.org>
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get -y update && \
|
||||
apt-get -y dist-upgrade && \
|
||||
apt-get -y install --no-install-recommends \
|
||||
python3-pyqt5 \
|
||||
python3-pyqt5.qtwebkit \
|
||||
python-tox \
|
||||
python3-sip \
|
||||
xvfb \
|
||||
git \
|
||||
python3-setuptools \
|
||||
wget \
|
||||
herbstluftwm \
|
||||
language-pack-en \
|
||||
libjs-pdf \
|
||||
dbus
|
||||
|
||||
RUN dbus-uuidgen --ensure
|
||||
|
||||
RUN useradd user && mkdir /home/user && chown -R user:users /home/user
|
||||
USER user
|
||||
WORKDIR /home/user
|
||||
|
||||
ENV DISPLAY=:0
|
||||
ENV LC_ALL=en_US.UTF-8
|
||||
ENV LANG=en_US.UTF-8
|
||||
|
||||
CMD Xvfb -screen 0 800x600x24 :0 & \
|
||||
sleep 2 && \
|
||||
herbstluftwm & \
|
||||
git clone /outside qutebrowser.git && \
|
||||
cd qutebrowser.git && \
|
||||
tox -e py35
|
||||
7
misc/qt_menu.nib/README
Normal file
@@ -0,0 +1,7 @@
|
||||
These files are copied from Qt's source tree in
|
||||
src/plugins/platforms/cocoa/qt_menu.nib at revision
|
||||
b8246f08e49eb672974fd3d3d972a5ff13c1524d.
|
||||
|
||||
http://code.qt.io/cgit/qt/qtbase.git/tree/src/plugins/platforms/cocoa/qt_menu.nib
|
||||
|
||||
They are needed for cx_Freeze and don't seem to be bundled with Qt anymore.
|
||||
59
misc/qt_menu.nib/classes.nib
generated
Normal file
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBClasses</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>ACTIONS</key>
|
||||
<dict>
|
||||
<key>hide</key>
|
||||
<string>id</string>
|
||||
<key>hideOtherApplications</key>
|
||||
<string>id</string>
|
||||
<key>orderFrontStandardAboutPanel</key>
|
||||
<string>id</string>
|
||||
<key>qtDispatcherToQPAMenuItem</key>
|
||||
<string>id</string>
|
||||
<key>terminate</key>
|
||||
<string>id</string>
|
||||
<key>unhideAllApplications</key>
|
||||
<string>id</string>
|
||||
</dict>
|
||||
<key>CLASS</key>
|
||||
<string>QCocoaMenuLoader</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>OUTLETS</key>
|
||||
<dict>
|
||||
<key>aboutItem</key>
|
||||
<string>NSMenuItem</string>
|
||||
<key>aboutQtItem</key>
|
||||
<string>NSMenuItem</string>
|
||||
<key>appMenu</key>
|
||||
<string>NSMenu</string>
|
||||
<key>hideItem</key>
|
||||
<string>NSMenuItem</string>
|
||||
<key>preferencesItem</key>
|
||||
<string>NSMenuItem</string>
|
||||
<key>quitItem</key>
|
||||
<string>NSMenuItem</string>
|
||||
<key>theMenu</key>
|
||||
<string>NSMenu</string>
|
||||
</dict>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSResponder</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CLASS</key>
|
||||
<string>FirstResponder</string>
|
||||
<key>LANGUAGE</key>
|
||||
<string>ObjC</string>
|
||||
<key>SUPERCLASS</key>
|
||||
<string>NSObject</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>IBVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
||||
18
misc/qt_menu.nib/info.nib
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>IBFramework Version</key>
|
||||
<string>672</string>
|
||||
<key>IBOldestOS</key>
|
||||
<integer>5</integer>
|
||||
<key>IBOpenObjects</key>
|
||||
<array>
|
||||
<integer>57</integer>
|
||||
</array>
|
||||
<key>IBSystem Version</key>
|
||||
<string>9L31a</string>
|
||||
<key>targetFramework</key>
|
||||
<string>IBCocoaFramework</string>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
misc/qt_menu.nib/keyedobjects.nib
generated
Normal file
@@ -1,74 +0,0 @@
|
||||
# -*- mode: python -*-
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.getcwd())
|
||||
from scripts import setupcommon
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
def get_data_files():
|
||||
data_files = [
|
||||
('../qutebrowser/html', 'html'),
|
||||
('../qutebrowser/img', 'img'),
|
||||
('../qutebrowser/javascript', 'javascript'),
|
||||
('../qutebrowser/html/doc', 'html/doc'),
|
||||
('../qutebrowser/git-commit-id', '')
|
||||
]
|
||||
|
||||
if os.path.exists(os.path.join('qutebrowser', '3rdparty', 'pdfjs')):
|
||||
data_files.append(('../qutebrowser/3rdparty/pdfjs', '3rdparty/pdfjs'))
|
||||
else:
|
||||
print("Warning: excluding pdfjs as it's not present!")
|
||||
|
||||
return data_files
|
||||
|
||||
|
||||
setupcommon.write_git_file()
|
||||
|
||||
|
||||
if os.name == 'nt':
|
||||
icon = 'icons/qutebrowser.ico'
|
||||
elif sys.platform == 'darwin':
|
||||
icon = 'icons/qutebrowser.icns'
|
||||
else:
|
||||
icon = None
|
||||
|
||||
|
||||
a = Analysis(['../qutebrowser/__main__.py'],
|
||||
pathex=['misc'],
|
||||
binaries=None,
|
||||
datas=get_data_files(),
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher)
|
||||
pyz = PYZ(a.pure, a.zipped_data,
|
||||
cipher=block_cipher)
|
||||
exe = EXE(pyz,
|
||||
a.scripts,
|
||||
exclude_binaries=True,
|
||||
name='qutebrowser',
|
||||
icon=icon,
|
||||
debug=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False )
|
||||
coll = COLLECT(exe,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
name='qutebrowser')
|
||||
|
||||
app = BUNDLE(coll,
|
||||
name='qutebrowser.app',
|
||||
icon=icon,
|
||||
info_plist={'NSHighResolutionCapable': 'True'},
|
||||
bundle_identifier=None)
|
||||
@@ -1,20 +0,0 @@
|
||||
This directory contains various `requirements` files which are used by `tox` to
|
||||
have reproducable tests with pinned versions.
|
||||
|
||||
The files are generated based on unpinned requirements in `*.txt-raw` files.
|
||||
|
||||
Those files can also contain some special commands:
|
||||
|
||||
- Add an additional comment to a line: `#@ comment: <package> <comment here>`
|
||||
- Filter a line for requirements.io: `#@ filter: <package> <filter>`
|
||||
- Don't include a package in the output: `#@ ignore: <package>` (or multiple packages)
|
||||
- Replace a part of a frozen package specification with another: `#@ replace <regex> <replacement>`
|
||||
|
||||
Some examples:
|
||||
|
||||
```
|
||||
#@ comment: mypkg blah blub
|
||||
#@ filter: mypkg != 1.0.0
|
||||
#@ ignore: mypkg, otherpkg
|
||||
#@ replace: foo bar
|
||||
```
|
||||
@@ -1,3 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
check-manifest==0.31
|
||||
@@ -1 +0,0 @@
|
||||
check-manifest
|
||||
@@ -1,5 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
codecov==2.0.5
|
||||
coverage==4.1
|
||||
requests==2.10.0
|
||||
@@ -1 +0,0 @@
|
||||
codecov
|
||||
@@ -1,3 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
cx-Freeze==4.3.4
|
||||
@@ -1 +0,0 @@
|
||||
cx_Freeze
|
||||
@@ -1,26 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
flake8==2.6.2 # rq.filter: < 3.0.0
|
||||
flake8-copyright==0.2.0
|
||||
flake8-debugger==1.4.0
|
||||
flake8-deprecated==1.0
|
||||
flake8-docstrings==0.2.8
|
||||
flake8-future-import==0.4.3
|
||||
flake8-mock==0.2
|
||||
flake8-pep3101==0.4
|
||||
flake8-putty==0.4.0
|
||||
flake8-string-format==0.2.2
|
||||
flake8-tidy-imports==1.0.2
|
||||
flake8-tuple==0.2.12
|
||||
hacking==0.11.0
|
||||
mccabe==0.5.0
|
||||
packaging==16.7
|
||||
pbr==1.10.0
|
||||
pep257==0.7.0 # still needed by flake8-docstrings but ignored
|
||||
pep8==1.7.0
|
||||
pep8-naming==0.4.1
|
||||
pycodestyle==2.0.0
|
||||
pydocstyle==1.0.0
|
||||
pyflakes==1.2.3
|
||||
pyparsing==2.1.5
|
||||
six==1.10.0
|
||||
@@ -1,23 +0,0 @@
|
||||
flake8<3.0.0
|
||||
flake8-copyright
|
||||
flake8-debugger
|
||||
flake8-deprecated
|
||||
flake8-docstrings
|
||||
flake8-future-import
|
||||
flake8-mock
|
||||
flake8-pep3101
|
||||
flake8-putty
|
||||
flake8-string-format
|
||||
flake8-tidy-imports
|
||||
flake8-tuple
|
||||
hacking
|
||||
pep8-naming
|
||||
pydocstyle
|
||||
pyflakes
|
||||
|
||||
pep8==1.7.0
|
||||
|
||||
#@ comment: pep257 still needed by flake8-docstrings but ignored
|
||||
|
||||
# Waiting until hacking/flake8-tuple are updated
|
||||
#@ filter: flake8 < 3.0.0
|
||||
@@ -1 +0,0 @@
|
||||
pip==8.1.2
|
||||
@@ -1,3 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
PyInstaller==3.2
|
||||
@@ -1 +0,0 @@
|
||||
PyInstaller
|
||||
@@ -1,11 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
-e git+https://github.com/PyCQA/astroid.git#egg=astroid
|
||||
isort==4.2.5
|
||||
lazy-object-proxy==1.2.2
|
||||
mccabe==0.5.0
|
||||
-e git+https://github.com/PyCQA/pylint.git#egg=pylint
|
||||
./scripts/dev/pylint_checkers
|
||||
requests==2.10.0
|
||||
six==1.10.0
|
||||
wrapt==1.10.8
|
||||
@@ -1,13 +0,0 @@
|
||||
-e git+https://github.com/PyCQA/astroid.git#egg=astroid
|
||||
-e git+https://github.com/PyCQA/pylint.git#egg=pylint
|
||||
./scripts/dev/pylint_checkers
|
||||
requests
|
||||
|
||||
# https://github.com/PyCQA/pylint/issues/932
|
||||
mccabe==0.5.0
|
||||
|
||||
# remove @commit-id for scm installs
|
||||
#@ replace: @.*# #
|
||||
|
||||
# fix qute-pylint location
|
||||
#@ replace: qute-pylint==.* ./scripts/dev/pylint_checkers
|
||||
@@ -1,11 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
astroid==1.4.7
|
||||
isort==4.2.5
|
||||
lazy-object-proxy==1.2.2
|
||||
mccabe==0.5.0
|
||||
pylint==1.6.4
|
||||
./scripts/dev/pylint_checkers
|
||||
requests==2.10.0
|
||||
six==1.10.0
|
||||
wrapt==1.10.8
|
||||
@@ -1,6 +0,0 @@
|
||||
pylint
|
||||
./scripts/dev/pylint_checkers
|
||||
requests
|
||||
|
||||
# fix qute-pylint location
|
||||
#@ replace: qute-pylint==.* ./scripts/dev/pylint_checkers
|
||||
@@ -1,4 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
docutils==0.12
|
||||
pyroma==2.0.2
|
||||
@@ -1 +0,0 @@
|
||||
pyroma
|
||||
@@ -1,6 +0,0 @@
|
||||
Jinja2
|
||||
Pygments
|
||||
pyPEG2
|
||||
PyYAML
|
||||
colorama
|
||||
cssutils
|
||||
@@ -1,32 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
beautifulsoup4==4.5.0
|
||||
CherryPy==7.1.0
|
||||
coverage==4.1
|
||||
decorator==4.0.10
|
||||
Flask==0.10.1 # rq.filter: < 0.11.0
|
||||
glob2==0.4.1
|
||||
httpbin==0.4.1
|
||||
hypothesis==3.4.2
|
||||
itsdangerous==0.24
|
||||
# Jinja2==2.8
|
||||
Mako==1.0.4
|
||||
# MarkupSafe==0.23
|
||||
parse==1.6.6
|
||||
parse-type==0.3.4
|
||||
py==1.4.31
|
||||
pytest==2.9.2
|
||||
pytest-bdd==2.17.0
|
||||
pytest-catchlog==1.2.2
|
||||
pytest-cov==2.3.0
|
||||
pytest-faulthandler==1.3.0
|
||||
pytest-instafail==0.3.0
|
||||
pytest-mock==1.1
|
||||
pytest-qt==1.11.0
|
||||
pytest-repeat==0.3.0
|
||||
pytest-rerunfailures==2.0.0
|
||||
pytest-travis-fold==1.2.0
|
||||
pytest-xvfb==0.2.0
|
||||
six==1.10.0
|
||||
vulture==0.10
|
||||
Werkzeug==0.11.10
|
||||
@@ -1,22 +0,0 @@
|
||||
beautifulsoup4
|
||||
CherryPy
|
||||
coverage
|
||||
Flask==0.10.1
|
||||
httpbin
|
||||
hypothesis
|
||||
pytest
|
||||
pytest-bdd
|
||||
pytest-catchlog
|
||||
pytest-cov
|
||||
pytest-faulthandler
|
||||
pytest-instafail
|
||||
pytest-mock
|
||||
pytest-qt
|
||||
pytest-repeat
|
||||
pytest-rerunfailures
|
||||
pytest-travis-fold
|
||||
pytest-xvfb
|
||||
vulture
|
||||
|
||||
#@ filter: Flask < 0.11.0
|
||||
#@ ignore: Jinja2, MarkupSafe
|
||||
@@ -1,6 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
pluggy==0.3.1
|
||||
py==1.4.31
|
||||
tox==2.3.1
|
||||
virtualenv==15.0.2
|
||||
@@ -1 +0,0 @@
|
||||
tox
|
||||
@@ -1,3 +0,0 @@
|
||||
# This file is automatically generated by scripts/dev/recompile_requirements.py
|
||||
|
||||
vulture==0.10
|
||||
@@ -1 +0,0 @@
|
||||
vulture
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2015 Zach-Button <zachrey.button@gmail.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Pipes history, quickmarks, and URL into dmenu.
|
||||
#
|
||||
# If run from qutebrowser as a userscript, it runs :open on the URL
|
||||
# If not, it opens a new qutebrowser window at the URL
|
||||
#
|
||||
# Ideal for use with tabs-are-windows. Set a hotkey to launch this script, then:
|
||||
# :bind o spawn --userscript dmenu_qutebrowser
|
||||
#
|
||||
# Use the hotkey to open in new tab/window, press 'o' to open URL in current tab/window
|
||||
# You can simulate "go" by pressing "o<tab>", as the current URL is always first in the list
|
||||
#
|
||||
# I personally use "<Mod4>o" to launch this script. For me, my workflow is:
|
||||
# Default keys Keys with this script
|
||||
# O <Mod4>o
|
||||
# o o
|
||||
# go o<Tab>
|
||||
# gO gC, then o<Tab>
|
||||
# (This is unnecessarily long. I use this rarely, feel free to make this script accept parameters.)
|
||||
#
|
||||
|
||||
[ -z "$QUTE_URL" ] && QUTE_URL='http://google.com'
|
||||
|
||||
url=$(echo "$QUTE_URL" | cat - "$QUTE_CONFIG_DIR/quickmarks" "$QUTE_DATA_DIR/history" | dmenu -l 15 -p qutebrowser)
|
||||
url=$(echo "$url" | sed -E 's/[^ ]+ +//g' | egrep "https?:" || echo "$url")
|
||||
|
||||
[ -z "${url// }" ] && exit
|
||||
|
||||
echo "open $url" >> "$QUTE_FIFO" || qutebrowser "$url"
|
||||
@@ -1,114 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Both standalone script and qutebrowser userscript that opens a rofi menu with
|
||||
# all files from the download director and opens the selected file. It works
|
||||
# both as a userscript and a standalone script that is called from outside of
|
||||
# qutebrowser.
|
||||
#
|
||||
# Suggested keybinding (for "show downloads"):
|
||||
# spawn --userscript ~/.config/qutebrowser/open_download
|
||||
# sd
|
||||
#
|
||||
# Requirements:
|
||||
# - rofi (in a recent version)
|
||||
# - xdg-open and xdg-mime
|
||||
# - You should configure qutebrowser to download files to a single directory
|
||||
# - It comes in handy if you enable remove-finished-downloads. If you want to
|
||||
# see the recent downloads, just press "sd".
|
||||
#
|
||||
# Thorsten Wißmann, 2015 (thorsten` on freenode)
|
||||
# Any feedback is welcome!
|
||||
|
||||
set -e
|
||||
|
||||
# open a file from the download directory using rofi
|
||||
DOWNLOAD_DIR=${DOWNLOAD_DIR:-$QUTE_DOWNLOAD_DIR}
|
||||
DOWNLOAD_DIR=${DOWNLOAD_DIR:-$HOME/Downloads}
|
||||
# the name of the rofi command
|
||||
ROFI_CMD=${ROFI_CMD:-rofi}
|
||||
ROFI_ARGS=${ROFI_ARGS:-}
|
||||
|
||||
msg() {
|
||||
local cmd="$1"
|
||||
shift
|
||||
local msg="$*"
|
||||
if [ -z "$QUTE_FIFO" ] ; then
|
||||
echo "$cmd: $msg" >&2
|
||||
else
|
||||
echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
|
||||
fi
|
||||
}
|
||||
die() {
|
||||
msg error "$*"
|
||||
if [ -n "$QUTE_FIFO" ] ; then
|
||||
# when run as a userscript, the above error message already informs the
|
||||
# user about the failure, and no additional "userscript exited with status
|
||||
# 1" is needed.
|
||||
exit 0;
|
||||
else
|
||||
exit 1;
|
||||
fi
|
||||
}
|
||||
|
||||
if ! [ -d "$DOWNLOAD_DIR" ] ; then
|
||||
die "Download directory »$DOWNLOAD_DIR« not found!"
|
||||
fi
|
||||
if ! which "${ROFI_CMD}" > /dev/null ; then
|
||||
die "Rofi command »${ROFI_CMD}« not found in PATH!"
|
||||
fi
|
||||
|
||||
rofi_default_args=(
|
||||
-monitor -2 # place above window
|
||||
-location 6 # aligned at the bottom
|
||||
-width 100 # use full window width
|
||||
-i
|
||||
-no-custom
|
||||
-format i # make rofi return the index
|
||||
-l 10
|
||||
-p 'Open download:' -dmenu
|
||||
)
|
||||
|
||||
crop-first-column() {
|
||||
local maxlength=${1:-40}
|
||||
local expression='s|^\([^\t]\{0,'"$maxlength"'\}\)[^\t]*\t|\1\t|'
|
||||
sed "$expression"
|
||||
}
|
||||
|
||||
ls-files() {
|
||||
# add the slash at the end of the download dir enforces to follow the
|
||||
# symlink, if the DOWNLOAD_DIR itself is a symlink
|
||||
ls -Q --quoting-style escape -h -o -1 -A -t "${DOWNLOAD_DIR}/" \
|
||||
| grep '^[-]' \
|
||||
| cut -d' ' -f3- \
|
||||
| sed 's,^\(.*[^\]\) \(.*\)$,\2\t\1,' \
|
||||
| sed 's,\\\(.\),\1,g'
|
||||
}
|
||||
|
||||
mapfile -t entries < <(ls-files)
|
||||
|
||||
# we need to manually check that there are items, because rofi doesn't show up
|
||||
# if there are no items and -no-custom is passed to rofi.
|
||||
if [ "${#entries[@]}" -eq 0 ] ; then
|
||||
die "Download directory »${DOWNLOAD_DIR}« empty"
|
||||
fi
|
||||
|
||||
line=$(printf "%s\n" "${entries[@]}" \
|
||||
| crop-first-column 55 \
|
||||
| column -s $'\t' -t \
|
||||
| $ROFI_CMD "${rofi_default_args[@]}" $ROFI_ARGS) || true
|
||||
if [ -z "$line" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
file="${entries[$line]}"
|
||||
file="${file%%$'\t'*}"
|
||||
path="$DOWNLOAD_DIR/$file"
|
||||
filetype=$(xdg-mime query filetype "$path")
|
||||
application=$(xdg-mime query default "$filetype")
|
||||
|
||||
if [ -z "$application" ] ; then
|
||||
die "Do not know how to open »$file« of type $filetype"
|
||||
fi
|
||||
|
||||
msg info "Opening »$file« (of type $filetype) with ${application%.desktop}"
|
||||
|
||||
xdg-open "$path" &
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright 2015 jnphilipp <me@jnphilipp.org>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
# Opens all links to feeds defined in the head of a site
|
||||
#
|
||||
# Ideal for use with tabs-are-windows. Set a hotkey to launch this script, then:
|
||||
# :bind gF spawn --userscript openfeeds
|
||||
#
|
||||
# Use the hotkey to open the feeds in new tab/window, press 'gF' to open
|
||||
#
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from urllib.parse import urljoin
|
||||
|
||||
with open(os.environ['QUTE_HTML'], 'r') as f:
|
||||
soup = BeautifulSoup(f)
|
||||
with open(os.environ['QUTE_FIFO'], 'w') as f:
|
||||
for link in soup.find_all('link', rel='alternate', type=re.compile(r'application/((rss|rdf|atom)\+)?xml|text/xml')):
|
||||
f.write('open -t %s\n' % urljoin(os.environ['QUTE_URL'], link.get('href')))
|
||||
@@ -1,365 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
help() {
|
||||
blink=$'\e[1;31m' reset=$'\e[0m'
|
||||
cat <<EOF
|
||||
This script can only be used as a userscript for qutebrowser
|
||||
2015, Thorsten Wißmann <edu _at_ thorsten-wissmann _dot_ de>
|
||||
In case of questions or suggestions, do not hesitate to send me an E-Mail or to
|
||||
directly ask me via IRC (nickname thorsten\`) in #qutebrowser on freenode.
|
||||
|
||||
$blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset
|
||||
WARNING: the passwords are stored in qutebrowser's
|
||||
debug log reachable via the url qute:log
|
||||
$blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset
|
||||
|
||||
Usage: run as a userscript form qutebrowser, e.g.:
|
||||
spawn --userscript ~/.config/qutebrowser/password_fill
|
||||
|
||||
Pass backend: (see also passwordstore.org)
|
||||
This script expects pass to store the credentials of each page in an extra
|
||||
file, where the filename (or filepath) contains the domain of the respective
|
||||
page. The first line of the file must contain the password, the login name
|
||||
must be contained in a later line beginning with "user:", "login:", or
|
||||
"username:" (configurable by the user_pattern variable).
|
||||
|
||||
Behavior:
|
||||
It will try to find a username/password entry in the configured backend
|
||||
(currently only pass) for the current website and will load that pair of
|
||||
username and password to any form on the current page that has some password
|
||||
entry field. If multiple entries are found, a zenity menu is offered.
|
||||
|
||||
If no entry is found, then it crops subdomains from the url if at least one
|
||||
entry is found in the backend. (In that case, it always shows a menu)
|
||||
|
||||
Configuration:
|
||||
This script loads the bash script ~/.config/qutebrowser/password_fill_rc (if
|
||||
it exists), so you can change any configuration variable and overwrite any
|
||||
function you like.
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
shopt -s nocasematch # make regexp matching in bash case insensitive
|
||||
|
||||
if [ -z "$QUTE_FIFO" ] ; then
|
||||
help
|
||||
exit
|
||||
fi
|
||||
|
||||
error() {
|
||||
local msg="$*"
|
||||
echo "message-error '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
|
||||
}
|
||||
msg() {
|
||||
local msg="$*"
|
||||
echo "message-info '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
|
||||
}
|
||||
die() {
|
||||
error "$*"
|
||||
exit 0
|
||||
}
|
||||
|
||||
javascript_escape() {
|
||||
# print the first argument in an escaped way, such that it can safely
|
||||
# be used within javascripts double quotes
|
||||
sed "s,[\\\'\"],\\\&,g" <<< "$1"
|
||||
}
|
||||
|
||||
# ======================================================= #
|
||||
# CONFIGURATION
|
||||
# ======================================================= #
|
||||
# The configuration file is per default located in
|
||||
# ~/.config/qutebrowser/password_fill_rc and is a bash script that is loaded
|
||||
# later in the present script. So basically you can replace all of the
|
||||
# following definitions and make them fit your needs.
|
||||
|
||||
# The following simplifies a URL to the domain (e.g. "wiki.qutebrowser.org")
|
||||
# which is later used to search the correct entries in the password backend. If
|
||||
# you e.g. don't want the "www." to be removed or if you want to distinguish
|
||||
# between different paths on the same domain.
|
||||
|
||||
simplify_url() {
|
||||
simple_url="${1##*://}" # remove protocol specification
|
||||
simple_url="${simple_url%%\?*}" # remove GET parameters
|
||||
simple_url="${simple_url%%/*}" # remove directory path
|
||||
simple_url="${simple_url%:*}" # remove port
|
||||
simple_url="${simple_url##www.}" # remove www. subdomain
|
||||
}
|
||||
|
||||
# no_entries_found() is called if the first query_entries() call did not find
|
||||
# any matching entries. Multiple implementations are possible:
|
||||
# The easiest behavior is to quit:
|
||||
#no_entries_found() {
|
||||
# if [ 0 -eq "${#files[@]}" ] ; then
|
||||
# die "No entry found for »$simple_url«"
|
||||
# fi
|
||||
#}
|
||||
# But you could also fill the files array with all entries from your pass db
|
||||
# if the first db query did not find anything
|
||||
# no_entries_found() {
|
||||
# if [ 0 -eq "${#files[@]}" ] ; then
|
||||
# query_entries ""
|
||||
# if [ 0 -eq "${#files[@]}" ] ; then
|
||||
# die "No entry found for »$simple_url«"
|
||||
# fi
|
||||
# fi
|
||||
# }
|
||||
|
||||
# Another behavior is to drop another level of subdomains until search hits
|
||||
# are found:
|
||||
no_entries_found() {
|
||||
while [ 0 -eq "${#files[@]}" ] && [ -n "$simple_url" ]; do
|
||||
shorter_simple_url=$(sed 's,^[^.]*\.,,' <<< "$simple_url")
|
||||
if [ "$shorter_simple_url" = "$simple_url" ] ; then
|
||||
# if no dot, then even remove the top level domain
|
||||
simple_url=""
|
||||
query_entries "$simple_url"
|
||||
break
|
||||
fi
|
||||
simple_url="$shorter_simple_url"
|
||||
query_entries "$simple_url"
|
||||
#die "No entry found for »$simple_url«"
|
||||
# enforce menu if we do "fuzzy" matching
|
||||
menu_if_one_entry=1
|
||||
done
|
||||
if [ 0 -eq "${#files[@]}" ] ; then
|
||||
die "No entry found for »$simple_url«"
|
||||
fi
|
||||
}
|
||||
|
||||
# Backend implementations tell, how the actual password store is accessed.
|
||||
# Right now, there is only one fully functional password backend, namely for
|
||||
# the program "pass".
|
||||
# A password backend consists of three actions:
|
||||
# - init() initializes backend-specific things and does sanity checks.
|
||||
# - query_entries() is called with a simplified url and is expected to fill
|
||||
# the bash array $files with the names of matching password entries. There
|
||||
# are no requirements how these names should look like.
|
||||
# - open_entry() is called with some specific entry of the $files array and is
|
||||
# expected to write the username of that entry to the $username variable and
|
||||
# the corresponding password to $password
|
||||
|
||||
reset_backend() {
|
||||
init() { true ; }
|
||||
query_entries() { true ; }
|
||||
open_entry() { true ; }
|
||||
}
|
||||
|
||||
# choose_entry() is expected to choose one entry from the array $files and
|
||||
# write it to the variable $file.
|
||||
choose_entry() {
|
||||
choose_entry_zenity
|
||||
}
|
||||
|
||||
# The default implementation chooses a random entry from the array. So if there
|
||||
# are multiple matching entries, multiple calls to this userscript will
|
||||
# eventually pick the "correct" entry. I.e. if this userscript is bound to
|
||||
# "zl", the user has to press "zl" until the correct username shows up in the
|
||||
# login form.
|
||||
choose_entry_random() {
|
||||
local nr=${#files[@]}
|
||||
file="${files[$((RANDOM % nr))]}"
|
||||
# Warn user, that there might be other matching password entries
|
||||
if [ "$nr" -gt 1 ] ; then
|
||||
msg "Picked $file out of $nr entries: ${files[*]}"
|
||||
fi
|
||||
}
|
||||
|
||||
# another implementation would be to ask the user via some menu (like rofi or
|
||||
# dmenu or zenity or even qutebrowser completion in future?) which entry to
|
||||
# pick
|
||||
MENU_COMMAND=( head -n 1 )
|
||||
# whether to show the menu if there is only one entry in it
|
||||
menu_if_one_entry=0
|
||||
choose_entry_menu() {
|
||||
local nr=${#files[@]}
|
||||
if [ "$nr" -eq 1 ] && ! ((menu_if_one_entry)) ; then
|
||||
file="${files[0]}"
|
||||
else
|
||||
file=$( printf "%s\n" "${files[@]}" | "${MENU_COMMAND[@]}" )
|
||||
fi
|
||||
}
|
||||
|
||||
choose_entry_rofi() {
|
||||
MENU_COMMAND=( rofi -p "qutebrowser> " -dmenu
|
||||
-mesg $'Pick a password entry for <b>'"${QUTE_URL//&/&}"'</b>' )
|
||||
choose_entry_menu || true
|
||||
}
|
||||
|
||||
choose_entry_zenity() {
|
||||
MENU_COMMAND=( zenity --list --title "Qutebrowser password fill"
|
||||
--text "Pick the password entry:"
|
||||
--column "Name" )
|
||||
choose_entry_menu || true
|
||||
}
|
||||
|
||||
choose_entry_zenity_radio() {
|
||||
zenity_helper() {
|
||||
awk '{ print $0 ; print $0 }' \
|
||||
| zenity --list --radiolist \
|
||||
--title "Qutebrowser password fill" \
|
||||
--text "Pick the password entry:" \
|
||||
--column " " --column "Name"
|
||||
}
|
||||
MENU_COMMAND=( zenity_helper )
|
||||
choose_entry_menu || true
|
||||
}
|
||||
|
||||
# =======================================================
|
||||
# backend: PASS
|
||||
|
||||
# configuration options:
|
||||
match_filename=1 # whether allowing entry match by filepath
|
||||
match_line=0 # whether allowing entry match by URL-Pattern in file
|
||||
# Note: match_line=1 gets very slow, even for small password stores!
|
||||
match_line_pattern='^url: .*' # applied using grep -iE
|
||||
user_pattern='^(user|username|login): '
|
||||
|
||||
GPG_OPTS=( "--quiet" "--yes" "--compress-algo=none" "--no-encrypt-to" )
|
||||
GPG="gpg"
|
||||
export GPG_TTY="${GPG_TTY:-$(tty 2>/dev/null)}"
|
||||
which gpg2 &>/dev/null && GPG="gpg2"
|
||||
[[ -n $GPG_AGENT_INFO || $GPG == "gpg2" ]] && GPG_OPTS+=( "--batch" "--use-agent" )
|
||||
|
||||
pass_backend() {
|
||||
init() {
|
||||
PREFIX="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
|
||||
if ! [ -d "$PREFIX" ] ; then
|
||||
die "Can not open password store dir »$PREFIX«"
|
||||
fi
|
||||
}
|
||||
query_entries() {
|
||||
local url="$1"
|
||||
|
||||
if ((match_line)) ; then
|
||||
# add entries with matching URL-tag
|
||||
while read -r -d "" passfile ; do
|
||||
if $GPG "${GPG_OPTS}" -d "$passfile" \
|
||||
| grep --max-count=1 -iE "${match_line_pattern}${url}" > /dev/null
|
||||
then
|
||||
passfile="${passfile#$PREFIX}"
|
||||
passfile="${passfile#/}"
|
||||
files+=( "${passfile%.gpg}" )
|
||||
fi
|
||||
done < <(find -L "$PREFIX" -iname '*.gpg' -print0)
|
||||
fi
|
||||
if ((match_filename)) ; then
|
||||
# add entries with matching filepath
|
||||
while read -r passfile ; do
|
||||
passfile="${passfile#$PREFIX}"
|
||||
passfile="${passfile#/}"
|
||||
files+=( "${passfile%.gpg}" )
|
||||
done < <(find -L "$PREFIX" -iname '*.gpg' | grep "$url")
|
||||
fi
|
||||
}
|
||||
open_entry() {
|
||||
local path="$PREFIX/${1}.gpg"
|
||||
password=""
|
||||
local firstline=1
|
||||
while read -r line ; do
|
||||
if ((firstline)) ; then
|
||||
password="$line"
|
||||
firstline=0
|
||||
else
|
||||
if [[ $line =~ $user_pattern ]] ; then
|
||||
# remove the matching prefix "user: " from the beginning of the line
|
||||
username=${line#${BASH_REMATCH[0]}}
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done < <($GPG "${GPG_OPTS}" -d "$path" )
|
||||
}
|
||||
}
|
||||
# =======================================================
|
||||
|
||||
# =======================================================
|
||||
# backend: secret
|
||||
secret_backend() {
|
||||
init() {
|
||||
return
|
||||
}
|
||||
query_entries() {
|
||||
local domain="$1"
|
||||
while read -r line ; do
|
||||
if [[ "$line" =~ "attribute.username = " ]] ; then
|
||||
files+=("$domain ${line#${BASH_REMATCH[0]}}")
|
||||
fi
|
||||
done < <( secret-tool search --unlock --all domain "$domain" 2>&1 )
|
||||
}
|
||||
open_entry() {
|
||||
local domain="${1%% *}"
|
||||
username="${1#* }"
|
||||
password=$(secret-tool lookup domain "$domain" username "$username")
|
||||
}
|
||||
}
|
||||
# =======================================================
|
||||
|
||||
# load some sane default backend
|
||||
reset_backend
|
||||
pass_backend
|
||||
# load configuration
|
||||
QUTE_CONFIG_DIR=${QUTE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/qutebrowser/}
|
||||
PWFILL_CONFIG=${PWFILL_CONFIG:-${QUTE_CONFIG_DIR}/password_fill_rc}
|
||||
if [ -f "$PWFILL_CONFIG" ] ; then
|
||||
source "$PWFILL_CONFIG"
|
||||
fi
|
||||
init
|
||||
|
||||
simplify_url "$QUTE_URL"
|
||||
query_entries "${simple_url}"
|
||||
no_entries_found
|
||||
# remove duplicates
|
||||
mapfile -t files < <(printf "%s\n" "${files[@]}" | sort | uniq )
|
||||
choose_entry
|
||||
if [ -z "$file" ] ; then
|
||||
# choose_entry didn't want any of these entries
|
||||
exit 0
|
||||
fi
|
||||
open_entry "$file"
|
||||
#username="$(date)"
|
||||
#password="XYZ"
|
||||
#msg "$username, ${#password}"
|
||||
|
||||
[ -n "$username" ] || die "Username not set in entry $file"
|
||||
[ -n "$password" ] || die "Password not set in entry $file"
|
||||
|
||||
js() {
|
||||
cat <<EOF
|
||||
function hasPasswordField(form) {
|
||||
var inputs = form.getElementsByTagName("input");
|
||||
for (var j = 0; j < inputs.length; j++) {
|
||||
var input = inputs[j];
|
||||
if (input.type == "password") {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
function loadData2Form (form) {
|
||||
var inputs = form.getElementsByTagName("input");
|
||||
for (var j = 0; j < inputs.length; j++) {
|
||||
var input = inputs[j];
|
||||
if (input.type == "text" || input.type == "email") {
|
||||
input.value = "$(javascript_escape "${username}")";
|
||||
}
|
||||
if (input.type == "password") {
|
||||
input.value = "$(javascript_escape "${password}")";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var forms = document.getElementsByTagName("form");
|
||||
for (i = 0; i < forms.length; i++) {
|
||||
if (hasPasswordField(forms[i])) {
|
||||
loadData2Form(forms[i]);
|
||||
}
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
printjs() {
|
||||
js | sed 's,//.*$,,' | tr '\n' ' '
|
||||
}
|
||||
echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Copyright 2015 Zach-Button <zachrey.button@gmail.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#
|
||||
# This script fetches the unprocessed HTML source for a page and opens it in vim.
|
||||
# :bind gf spawn --userscript qutebrowser_viewsource
|
||||
#
|
||||
# Caveat: Does not use authentication of any kind. Add it in if you want it to.
|
||||
#
|
||||
|
||||
path=/tmp/qutebrowser_$(mktemp XXXXXXXX).html
|
||||
|
||||
curl "$QUTE_URL" > $path
|
||||
urxvt -e vim "$path"
|
||||
|
||||
rm "$path"
|
||||
@@ -1,59 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# Handle open -s && open -t with bemenu
|
||||
|
||||
#:bind o spawn --userscript /path/to/userscripts/qutedmenu open
|
||||
#:bind O spawn --userscript /path/to/userscripts/qutedmenu tab
|
||||
|
||||
# If you would like to set a custom colorscheme/font use these dirs.
|
||||
# https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/bemenucolors
|
||||
readonly confdir=${XDG_CONFIG_HOME:-$HOME/.config}
|
||||
|
||||
readonly optsfile=$confdir/dmenu/bemenucolors
|
||||
|
||||
create_menu() {
|
||||
# Check quickmarks
|
||||
while read -r url; do
|
||||
printf -- '%s\n' "$url"
|
||||
done < "$QUTE_CONFIG_DIR"/quickmarks
|
||||
|
||||
# Next bookmarks
|
||||
while read -r url _; do
|
||||
printf -- '%s\n' "$url"
|
||||
done < "$QUTE_CONFIG_DIR"/bookmarks/urls
|
||||
|
||||
# Finally history
|
||||
while read -r _ url; do
|
||||
printf -- '%s\n' "$url"
|
||||
done < "$QUTE_DATA_DIR"/history
|
||||
}
|
||||
|
||||
get_selection() {
|
||||
opts+=(-p qutebrowser)
|
||||
#create_menu | dmenu -l 10 "${opts[@]}"
|
||||
create_menu | bemenu -l 10 "${opts[@]}"
|
||||
}
|
||||
|
||||
# Main
|
||||
# https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/font
|
||||
if [[ -s $confdir/dmenu/font ]]; then
|
||||
read -r font < "$confdir"/dmenu/font
|
||||
fi
|
||||
|
||||
if [[ $font ]]; then
|
||||
opts+=(-fn "$font")
|
||||
fi
|
||||
|
||||
if [[ -s $optsfile ]]; then
|
||||
source "$optsfile"
|
||||
fi
|
||||
|
||||
url=$(get_selection)
|
||||
url=${url/*http/http}
|
||||
|
||||
# If no selection is made, exit (escape pressed, e.g.)
|
||||
[[ ! $url ]] && exit 0
|
||||
|
||||
case $1 in
|
||||
open) printf '%s' "open $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;;
|
||||
tab) printf '%s' "open -t $url" >> "$QUTE_FIFO" || qutebrowser "$url" ;;
|
||||
esac
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Behavior:
|
||||
# Userscript for qutebrowser which adds a task to taskwarrior.
|
||||
# If run as a command (:spawn --userscript taskadd), it creates a new task
|
||||
# with the description equal to the current page title and annotates it with
|
||||
# the current page url. Additional arguments are passed along so you can add
|
||||
# mods to the task (e.g. priority, due date, tags).
|
||||
#
|
||||
# Example:
|
||||
# :spawn --userscript taskadd due:eod pri:H
|
||||
#
|
||||
# To enable passing along extra args, I suggest using a mapping like:
|
||||
# :bind <somekey> set-cmd-text -s :spawn --userscript taskadd
|
||||
#
|
||||
# If run from hint mode, it uses the selected hint text as the description
|
||||
# and the selected hint url as the annotation.
|
||||
#
|
||||
# Ryan Roden-Corrent (rcorre), 2016
|
||||
# Any feedback is welcome!
|
||||
#
|
||||
# For more info on Taskwarrior, see http://taskwarrior.org/
|
||||
|
||||
# use either the current page title or the hint text as the task description
|
||||
[[ $QUTE_MODE == 'hints' ]] && title=$QUTE_SELECTED_TEXT || title=$QUTE_TITLE
|
||||
|
||||
# try to add the task and grab the output
|
||||
msg="$(task add $title $@ 2>&1)"
|
||||
|
||||
if [[ $? == 0 ]]; then
|
||||
# annotate the new task with the url, send the output back to the browser
|
||||
task +LATEST annotate "$QUTE_URL"
|
||||
echo "message-info '$msg'" >> $QUTE_FIFO
|
||||
else
|
||||
echo "message-error '$msg'" >> $QUTE_FIFO
|
||||
fi
|
||||
@@ -1,143 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Behavior:
|
||||
# Userscript for qutebrowser which views the current web page in mpv using
|
||||
# sensible mpv-flags. While viewing the page in MPV, all <video>, <embed>,
|
||||
# and <object> tags in the original page are temporarily removed. Clicking on
|
||||
# such a removed video restores the respective video.
|
||||
#
|
||||
# In order to use this script, just start it using `spawn --userscript` from
|
||||
# qutebrowser. I recommend using an alias, e.g. put this in the
|
||||
# [alias]-section of qutebrowser.conf:
|
||||
#
|
||||
# mpv = spawn --userscript /path/to/view_in_mpv
|
||||
#
|
||||
# Background:
|
||||
# Most of my machines are too slow to play youtube videos using html5, but
|
||||
# they work fine in mpv (and mpv has further advantages like video scaling,
|
||||
# etc). Of course, I don't want the video to be played (or even to be
|
||||
# downloaded) twice — in MPV and in qwebkit. So I often close the tab after
|
||||
# opening it in mpv. However, I actually want to keep the rest of the page
|
||||
# (comments and video suggestions), i.e. only the videos should disappear
|
||||
# when mpv is started. And that's precisely what the present script does.
|
||||
#
|
||||
# Thorsten Wißmann, 2015 (thorsten` on freenode)
|
||||
# Any feedback is welcome!
|
||||
|
||||
set -e
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
MPV_COMMAND=${MPV_COMMAND:-mpv}
|
||||
# Warning: spaces in single flags are not supported
|
||||
MPV_FLAGS=${MPV_FLAGS:- --force-window --no-terminal --keep-open=yes --ytdl }
|
||||
video_command=( "$MPV_COMMAND" $MPV_FLAGS )
|
||||
|
||||
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\\">
|
||||
Opening page with:
|
||||
<span style=\\"font-family: monospace;\\">${video_command[*]}</span>
|
||||
</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"
|
||||
|
||||
msg info "Opening $QUTE_URL with mpv"
|
||||
"${video_command[@]}" "$QUTE_URL"
|
||||
40
pytest.ini
@@ -1,40 +0,0 @@
|
||||
[pytest]
|
||||
addopts = --strict -rfEw --faulthandler-timeout=70 --instafail
|
||||
markers =
|
||||
gui: Tests using the GUI (e.g. spawning widgets)
|
||||
posix: Tests which only can run on a POSIX OS.
|
||||
windows: Tests which only can run on Windows.
|
||||
linux: Tests which only can run on Linux.
|
||||
osx: Tests which only can run on OS X.
|
||||
not_osx: Tests which can not run on OS X.
|
||||
not_frozen: Tests which can't be run if sys.frozen is True.
|
||||
no_xvfb: Tests which can't be run with Xvfb.
|
||||
frozen: Tests which can only be run if sys.frozen is True.
|
||||
integration: Tests which test a bigger portion of code
|
||||
end2end: End to end tests which run qutebrowser as subprocess
|
||||
xfail_norun: xfail the test with out running it
|
||||
ci: Tests which should only run on CI.
|
||||
flaky_once: Try to rerun this test once if it fails
|
||||
qt_log_level_fail = WARNING
|
||||
qt_log_ignore =
|
||||
^SpellCheck: .*
|
||||
^SetProcessDpiAwareness failed: .*
|
||||
^QWindowsWindow::setGeometryDp: Unable to set geometry .*
|
||||
^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"
|
||||
^virtual void QSslSocketBackendPrivate::transmit\(\) SSL write failed with error: -9805
|
||||
^virtual void QSslSocketBackendPrivate::transmit\(\) SSLRead failed with: -9805
|
||||
^Type conversion already registered from type .*
|
||||
^QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once\.
|
||||
^QWaitCondition: Destroyed while threads are still waiting
|
||||
^QXcbXSettings::QXcbXSettings\(QXcbScreen\*\) Failed to get selection owner for XSETTINGS_S atom
|
||||
^QStandardPaths: XDG_RUNTIME_DIR not set, defaulting to .*
|
||||
^QXcbClipboard: SelectionRequest too old
|
||||
^QGeoclueMaster error creating GeoclueMasterClient\.
|
||||
^Geoclue error: Process org\.freedesktop\.Geoclue\.Master exited with status 127
|
||||
^QObject::connect: Cannot connect \(null\)::stateChanged\(QNetworkSession::State\) to QNetworkReplyHttpImpl::_q_networkSessionStateChanged\(QNetworkSession::State\)
|
||||
^QXcbClipboard: Cannot transfer data, no data available
|
||||
^load glyph failed
|
||||
qt_wait_signal_raising = true
|
||||
xfail_strict = true
|
||||
@@ -1,6 +1,6 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -24,12 +24,12 @@
|
||||
import os.path
|
||||
|
||||
__author__ = "Florian Bruhin"
|
||||
__copyright__ = "Copyright 2014-2016 Florian Bruhin (The Compiler)"
|
||||
__copyright__ = "Copyright 2014-2015 Florian Bruhin (The Compiler)"
|
||||
__license__ = "GPL"
|
||||
__maintainer__ = __author__
|
||||
__email__ = "mail@qutebrowser.org"
|
||||
__version_info__ = (0, 8, 2)
|
||||
__version__ = '.'.join(str(e) for e in __version_info__)
|
||||
__version_info__ = (0, 2, 1)
|
||||
__version__ = '.'.join(map(str, __version_info__))
|
||||
__description__ = "A keyboard-driven, vim-like browser based on PyQt5 and QtWebKit."
|
||||
|
||||
basedir = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env python3
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
||||
1288
qutebrowser/app.py
@@ -1,6 +1,6 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -24,11 +24,10 @@ import os.path
|
||||
import functools
|
||||
import posixpath
|
||||
import zipfile
|
||||
import fnmatch
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import objreg, standarddir, log, message, usertypes
|
||||
from qutebrowser.commands import cmdutils, cmdexc
|
||||
from qutebrowser.utils import objreg, standarddir, log, message
|
||||
from qutebrowser.commands import cmdutils
|
||||
|
||||
|
||||
def guess_zip_filename(zf):
|
||||
@@ -48,7 +47,7 @@ def guess_zip_filename(zf):
|
||||
|
||||
|
||||
def get_fileobj(byte_io):
|
||||
"""Get a usable file object to read the hosts file from."""
|
||||
"""Get an usable file object to read the hosts file from."""
|
||||
byte_io.seek(0) # rewind downloaded file
|
||||
if zipfile.is_zipfile(byte_io):
|
||||
byte_io.seek(0) # rewind what zipfile.is_zipfile did
|
||||
@@ -60,22 +59,6 @@ def get_fileobj(byte_io):
|
||||
return io.TextIOWrapper(byte_io, encoding='utf-8')
|
||||
|
||||
|
||||
def is_whitelisted_host(host):
|
||||
"""Check if the given host is on the adblock whitelist.
|
||||
|
||||
Args:
|
||||
host: The host of the request as string.
|
||||
"""
|
||||
whitelist = config.get('content', 'host-blocking-whitelist')
|
||||
if whitelist is None:
|
||||
return False
|
||||
|
||||
for pattern in whitelist:
|
||||
if fnmatch.fnmatch(host, pattern.lower()):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
class FakeDownload:
|
||||
|
||||
"""A download stub to use on_download_finished with local files."""
|
||||
@@ -91,12 +74,10 @@ class HostBlocker:
|
||||
"""Manage blocked hosts based from /etc/hosts-like files.
|
||||
|
||||
Attributes:
|
||||
_blocked_hosts: A set of blocked hosts.
|
||||
_config_blocked_hosts: A set of blocked hosts from ~/.config.
|
||||
blocked_hosts: A set of blocked hosts.
|
||||
_in_progress: The DownloadItems which are currently downloading.
|
||||
_done_count: How many files have been read successfully.
|
||||
_local_hosts_file: The path to the blocked-hosts file.
|
||||
_config_hosts_file: The path to a blocked-hosts in ~/.config
|
||||
_hosts_file: The path to the blocked-hosts file.
|
||||
|
||||
Class attributes:
|
||||
WHITELISTED: Hosts which never should be blocked.
|
||||
@@ -106,90 +87,31 @@ class HostBlocker:
|
||||
'local')
|
||||
|
||||
def __init__(self):
|
||||
self._blocked_hosts = set()
|
||||
self._config_blocked_hosts = set()
|
||||
self.blocked_hosts = set()
|
||||
self._in_progress = []
|
||||
self._done_count = 0
|
||||
|
||||
data_dir = standarddir.data()
|
||||
if data_dir is None:
|
||||
self._local_hosts_file = None
|
||||
else:
|
||||
self._local_hosts_file = os.path.join(data_dir, 'blocked-hosts')
|
||||
self.on_config_changed()
|
||||
|
||||
config_dir = standarddir.config()
|
||||
if config_dir is None:
|
||||
self._config_hosts_file = None
|
||||
else:
|
||||
self._config_hosts_file = os.path.join(config_dir, 'blocked-hosts')
|
||||
|
||||
self._hosts_file = os.path.join(standarddir.data(), 'blocked-hosts')
|
||||
objreg.get('config').changed.connect(self.on_config_changed)
|
||||
|
||||
def is_blocked(self, url):
|
||||
"""Check if the given URL (as QUrl) is blocked."""
|
||||
if not config.get('content', 'host-blocking-enabled'):
|
||||
return False
|
||||
host = url.host()
|
||||
return ((host in self._blocked_hosts or
|
||||
host in self._config_blocked_hosts) and
|
||||
not is_whitelisted_host(host))
|
||||
|
||||
def _read_hosts_file(self, filename, target):
|
||||
"""Read hosts from the given filename.
|
||||
|
||||
Args:
|
||||
filename: The file to read.
|
||||
target: The set to store the hosts in.
|
||||
|
||||
Return:
|
||||
True if a read was attempted, False otherwise
|
||||
"""
|
||||
if filename is None or not os.path.exists(filename):
|
||||
return False
|
||||
|
||||
try:
|
||||
with open(filename, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
target.add(line.strip())
|
||||
except OSError:
|
||||
log.misc.exception("Failed to read host blocklist!")
|
||||
|
||||
return True
|
||||
|
||||
def read_hosts(self):
|
||||
"""Read hosts from the existing blocked-hosts file."""
|
||||
self._blocked_hosts = set()
|
||||
|
||||
if self._local_hosts_file is None:
|
||||
return
|
||||
|
||||
self._read_hosts_file(self._config_hosts_file,
|
||||
self._config_blocked_hosts)
|
||||
|
||||
found = self._read_hosts_file(self._local_hosts_file,
|
||||
self._blocked_hosts)
|
||||
|
||||
if not found:
|
||||
args = objreg.get('args')
|
||||
if (config.get('content', 'host-block-lists') is not None and
|
||||
args.basedir is None):
|
||||
self.blocked_hosts = set()
|
||||
if os.path.exists(self._hosts_file):
|
||||
try:
|
||||
with open(self._hosts_file, 'r', encoding='utf-8') as f:
|
||||
for line in f:
|
||||
self.blocked_hosts.add(line.strip())
|
||||
except OSError:
|
||||
log.misc.exception("Failed to read host blocklist!")
|
||||
else:
|
||||
if config.get('content', 'host-block-lists') is not None:
|
||||
message.info('current',
|
||||
"Run :adblock-update to get adblock lists.")
|
||||
|
||||
@cmdutils.register(instance='host-blocker')
|
||||
@cmdutils.argument('win_id', win_id=True)
|
||||
def adblock_update(self, win_id):
|
||||
"""Update the adblock block lists.
|
||||
|
||||
This updates ~/.local/share/qutebrowser/blocked-hosts with downloaded
|
||||
host lists and re-reads ~/.config/qutebrowser/blocked-hosts.
|
||||
"""
|
||||
self._read_hosts_file(self._config_hosts_file,
|
||||
self._config_blocked_hosts)
|
||||
if self._local_hosts_file is None:
|
||||
raise cmdexc.CommandError("No data storage is configured!")
|
||||
self._blocked_hosts = set()
|
||||
def adblock_update(self, win_id: {'special': 'win_id'}):
|
||||
"""Update the adblock block lists."""
|
||||
self.blocked_hosts = set()
|
||||
self._done_count = 0
|
||||
urls = config.get('content', 'host-block-lists')
|
||||
download_manager = objreg.get('download-manager', scope='window',
|
||||
@@ -210,8 +132,7 @@ class HostBlocker:
|
||||
else:
|
||||
fobj = io.BytesIO()
|
||||
fobj.name = 'adblock: ' + url.host()
|
||||
target = usertypes.FileObjDownloadTarget(fobj)
|
||||
download = download_manager.get(url, target=target,
|
||||
download = download_manager.get(url, fileobj=fobj,
|
||||
auto_remove=True)
|
||||
self._in_progress.append(download)
|
||||
download.finished.connect(
|
||||
@@ -258,7 +179,7 @@ class HostBlocker:
|
||||
error_count += 1
|
||||
continue
|
||||
if host not in self.WHITELISTED:
|
||||
self._blocked_hosts.add(host)
|
||||
self.blocked_hosts.add(host)
|
||||
log.misc.debug("{}: read {} lines".format(byte_io.name, line_count))
|
||||
if error_count > 0:
|
||||
message.error('current', "adblock: {} read errors for {}".format(
|
||||
@@ -266,23 +187,21 @@ class HostBlocker:
|
||||
|
||||
def on_lists_downloaded(self):
|
||||
"""Install block lists after files have been downloaded."""
|
||||
with open(self._local_hosts_file, 'w', encoding='utf-8') as f:
|
||||
for host in sorted(self._blocked_hosts):
|
||||
with open(self._hosts_file, 'w', encoding='utf-8') as f:
|
||||
for host in sorted(self.blocked_hosts):
|
||||
f.write(host + '\n')
|
||||
message.info('current', "adblock: Read {} hosts from {} sources."
|
||||
.format(len(self._blocked_hosts), self._done_count))
|
||||
.format(len(self.blocked_hosts), self._done_count))
|
||||
|
||||
@config.change_filter('content', 'host-block-lists')
|
||||
def on_config_changed(self):
|
||||
"""Update files when the config changed."""
|
||||
urls = config.get('content', 'host-block-lists')
|
||||
if urls is None and self._local_hosts_file is not None:
|
||||
if urls is None:
|
||||
try:
|
||||
os.remove(self._local_hosts_file)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
except OSError as e:
|
||||
log.misc.exception("Failed to delete hosts file: {}".format(e))
|
||||
os.remove(self._hosts_file)
|
||||
except OSError:
|
||||
log.misc.exception("Failed to delete hosts file.")
|
||||
|
||||
def on_download_finished(self, download):
|
||||
"""Check if all downloads are finished and if so, trigger reading.
|
||||
|
||||
@@ -1,641 +0,0 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Base class for a wrapper over QWebView/QWebEngineView."""
|
||||
|
||||
import itertools
|
||||
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QUrl, QObject, QPoint
|
||||
from PyQt5.QtGui import QIcon
|
||||
from PyQt5.QtWidgets import QWidget, QLayout
|
||||
|
||||
from qutebrowser.keyinput import modeman
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import utils, objreg, usertypes, message, log, qtutils
|
||||
|
||||
|
||||
tab_id_gen = itertools.count(0)
|
||||
|
||||
|
||||
def create(win_id, parent=None):
|
||||
"""Get a QtWebKit/QtWebEngine tab object.
|
||||
|
||||
Args:
|
||||
win_id: The window ID where the tab will be shown.
|
||||
parent: The Qt parent to set.
|
||||
"""
|
||||
# Importing modules here so we don't depend on QtWebEngine without the
|
||||
# argument and to avoid circular imports.
|
||||
mode_manager = modeman.instance(win_id)
|
||||
if objreg.get('args').backend == 'webengine':
|
||||
from qutebrowser.browser.webengine import webenginetab
|
||||
tab_class = webenginetab.WebEngineTab
|
||||
else:
|
||||
from qutebrowser.browser.webkit import webkittab
|
||||
tab_class = webkittab.WebKitTab
|
||||
return tab_class(win_id=win_id, mode_manager=mode_manager, parent=parent)
|
||||
|
||||
|
||||
class WebTabError(Exception):
|
||||
|
||||
"""Base class for various errors."""
|
||||
|
||||
|
||||
class WrapperLayout(QLayout):
|
||||
|
||||
"""A Qt layout which simply wraps a single widget.
|
||||
|
||||
This is used so the widget is hidden behind a AbstractTab API and can't
|
||||
easily be accidentally accessed.
|
||||
"""
|
||||
|
||||
def __init__(self, widget, parent=None):
|
||||
super().__init__(parent)
|
||||
self._widget = widget
|
||||
|
||||
def addItem(self, _widget):
|
||||
raise AssertionError("Should never be called!")
|
||||
|
||||
def sizeHint(self):
|
||||
return self._widget.sizeHint()
|
||||
|
||||
def itemAt(self, _index): # pragma: no cover
|
||||
# For some reason this sometimes gets called by Qt.
|
||||
return None
|
||||
|
||||
def takeAt(self, _index):
|
||||
raise AssertionError("Should never be called!")
|
||||
|
||||
def setGeometry(self, rect):
|
||||
self._widget.setGeometry(rect)
|
||||
|
||||
|
||||
class TabData:
|
||||
|
||||
"""A simple namespace with a fixed set of attributes.
|
||||
|
||||
Attributes:
|
||||
keep_icon: Whether the (e.g. cloned) icon should not be cleared on page
|
||||
load.
|
||||
inspector: The QWebInspector used for this webview.
|
||||
viewing_source: Set if we're currently showing a source view.
|
||||
"""
|
||||
|
||||
__slots__ = ['keep_icon', 'viewing_source', 'inspector']
|
||||
|
||||
def __init__(self):
|
||||
self.keep_icon = False
|
||||
self.viewing_source = False
|
||||
self.inspector = None
|
||||
|
||||
|
||||
class AbstractPrinting:
|
||||
|
||||
"""Attribute of AbstractTab for printing the page."""
|
||||
|
||||
def __init__(self):
|
||||
self._widget = None
|
||||
|
||||
def check_pdf_support(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def check_printer_support(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def to_pdf(self, filename):
|
||||
raise NotImplementedError
|
||||
|
||||
def to_printer(self, printer):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AbstractSearch(QObject):
|
||||
|
||||
"""Attribute of AbstractTab for doing searches.
|
||||
|
||||
Attributes:
|
||||
text: The last thing this view was searched for.
|
||||
_flags: The flags of the last search (needs to be set by subclasses).
|
||||
_widget: The underlying WebView widget.
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._widget = None
|
||||
self.text = None
|
||||
|
||||
def search(self, text, *, ignore_case=False, reverse=False,
|
||||
result_cb=None):
|
||||
"""Find the given text on the page.
|
||||
|
||||
Args:
|
||||
text: The text to search for.
|
||||
ignore_case: Search case-insensitively. (True/False/'smart')
|
||||
reverse: Reverse search direction.
|
||||
result_cb: Called with a bool indicating whether a match was found.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def clear(self):
|
||||
"""Clear the current search."""
|
||||
raise NotImplementedError
|
||||
|
||||
def prev_result(self, *, result_cb=None):
|
||||
"""Go to the previous result of the current search.
|
||||
|
||||
Args:
|
||||
result_cb: Called with a bool indicating whether a match was found.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def next_result(self, *, result_cb=None):
|
||||
"""Go to the next result of the current search.
|
||||
|
||||
Args:
|
||||
result_cb: Called with a bool indicating whether a match was found.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AbstractZoom(QObject):
|
||||
|
||||
"""Attribute of AbstractTab for controlling zoom.
|
||||
|
||||
Attributes:
|
||||
_neighborlist: A NeighborList with the zoom levels.
|
||||
_default_zoom_changed: Whether the zoom was changed from the default.
|
||||
"""
|
||||
|
||||
def __init__(self, win_id, parent=None):
|
||||
super().__init__(parent)
|
||||
self._widget = None
|
||||
self._win_id = win_id
|
||||
self._default_zoom_changed = False
|
||||
self._init_neighborlist()
|
||||
objreg.get('config').changed.connect(self._on_config_changed)
|
||||
|
||||
# # FIXME:qtwebengine is this needed?
|
||||
# # For some reason, this signal doesn't get disconnected automatically
|
||||
# # when the WebView is destroyed on older PyQt versions.
|
||||
# # See https://github.com/The-Compiler/qutebrowser/issues/390
|
||||
# self.destroyed.connect(functools.partial(
|
||||
# cfg.changed.disconnect, self.init_neighborlist))
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def _on_config_changed(self, section, option):
|
||||
if section == 'ui' and option in ['zoom-levels', 'default-zoom']:
|
||||
if not self._default_zoom_changed:
|
||||
factor = float(config.get('ui', 'default-zoom')) / 100
|
||||
self._set_factor_internal(factor)
|
||||
self._default_zoom_changed = False
|
||||
self._init_neighborlist()
|
||||
|
||||
def _init_neighborlist(self):
|
||||
"""Initialize self._neighborlist."""
|
||||
levels = config.get('ui', 'zoom-levels')
|
||||
self._neighborlist = usertypes.NeighborList(
|
||||
levels, mode=usertypes.NeighborList.Modes.edge)
|
||||
self._neighborlist.fuzzyval = config.get('ui', 'default-zoom')
|
||||
|
||||
def offset(self, offset):
|
||||
"""Increase/Decrease the zoom level by the given offset.
|
||||
|
||||
Args:
|
||||
offset: The offset in the zoom level list.
|
||||
|
||||
Return:
|
||||
The new zoom percentage.
|
||||
"""
|
||||
level = self._neighborlist.getitem(offset)
|
||||
self.set_factor(float(level) / 100, fuzzyval=False)
|
||||
return level
|
||||
|
||||
def set_factor(self, factor, *, fuzzyval=True):
|
||||
"""Zoom to a given zoom factor.
|
||||
|
||||
Args:
|
||||
factor: The zoom factor as float.
|
||||
fuzzyval: Whether to set the NeighborLists fuzzyval.
|
||||
"""
|
||||
if fuzzyval:
|
||||
self._neighborlist.fuzzyval = int(factor * 100)
|
||||
if factor < 0:
|
||||
raise ValueError("Can't zoom to factor {}!".format(factor))
|
||||
self._default_zoom_changed = True
|
||||
self._set_factor_internal(factor)
|
||||
|
||||
def factor(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_default(self):
|
||||
default_zoom = config.get('ui', 'default-zoom')
|
||||
self._set_factor_internal(float(default_zoom) / 100)
|
||||
|
||||
@pyqtSlot(QPoint)
|
||||
def _on_mouse_wheel_zoom(self, delta):
|
||||
"""Handle zooming via mousewheel requested by the web view."""
|
||||
divider = config.get('input', 'mouse-zoom-divider')
|
||||
factor = self.factor() + delta.y() / divider
|
||||
if factor < 0:
|
||||
return
|
||||
perc = int(100 * factor)
|
||||
message.info(self._win_id, "Zoom level: {}%".format(perc))
|
||||
self._neighborlist.fuzzyval = perc
|
||||
self._set_factor_internal(factor)
|
||||
self._default_zoom_changed = True
|
||||
|
||||
|
||||
class AbstractCaret(QObject):
|
||||
|
||||
"""Attribute of AbstractTab for caret browsing."""
|
||||
|
||||
def __init__(self, win_id, tab, mode_manager, parent=None):
|
||||
super().__init__(parent)
|
||||
self._tab = tab
|
||||
self._win_id = win_id
|
||||
self._widget = None
|
||||
self.selection_enabled = False
|
||||
mode_manager.entered.connect(self._on_mode_entered)
|
||||
mode_manager.left.connect(self._on_mode_left)
|
||||
|
||||
def _on_mode_entered(self, mode):
|
||||
raise NotImplementedError
|
||||
|
||||
def _on_mode_left(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_next_line(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_prev_line(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_next_char(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_prev_char(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_end_of_word(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_next_word(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_prev_word(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_start_of_line(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_end_of_line(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_start_of_next_block(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_start_of_prev_block(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_end_of_next_block(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_end_of_prev_block(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_start_of_document(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def move_to_end_of_document(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def toggle_selection(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def drop_selection(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def has_selection(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def selection(self, html=False):
|
||||
raise NotImplementedError
|
||||
|
||||
def follow_selected(self, *, tab=False):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AbstractScroller(QObject):
|
||||
|
||||
"""Attribute of AbstractTab to manage scroll position."""
|
||||
|
||||
perc_changed = pyqtSignal(int, int)
|
||||
|
||||
def __init__(self, tab, parent=None):
|
||||
super().__init__(parent)
|
||||
self._tab = tab
|
||||
self._widget = None
|
||||
|
||||
def _init_widget(self, widget):
|
||||
self._widget = widget
|
||||
|
||||
def pos_px(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def pos_perc(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def to_perc(self, x=None, y=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def to_point(self, point):
|
||||
raise NotImplementedError
|
||||
|
||||
def delta(self, x=0, y=0):
|
||||
raise NotImplementedError
|
||||
|
||||
def delta_page(self, x=0, y=0):
|
||||
raise NotImplementedError
|
||||
|
||||
def up(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def down(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def left(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def right(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def top(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def bottom(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def page_up(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def page_down(self, count=1):
|
||||
raise NotImplementedError
|
||||
|
||||
def at_top(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def at_bottom(self):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AbstractHistory:
|
||||
|
||||
"""The history attribute of a AbstractTab."""
|
||||
|
||||
def __init__(self, tab):
|
||||
self._tab = tab
|
||||
self._history = None
|
||||
|
||||
def __len__(self):
|
||||
return len(self._history)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._history.items())
|
||||
|
||||
def current_idx(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def back(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def forward(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def can_go_back(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def can_go_forward(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def serialize(self):
|
||||
"""Serialize into an opaque format understood by self.deserialize."""
|
||||
raise NotImplementedError
|
||||
|
||||
def deserialize(self, data):
|
||||
"""Serialize from a format produced by self.serialize."""
|
||||
raise NotImplementedError
|
||||
|
||||
def load_items(self, items):
|
||||
"""Deserialize from a list of WebHistoryItems."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class AbstractTab(QWidget):
|
||||
|
||||
"""A wrapper over the given widget to hide its API and expose another one.
|
||||
|
||||
We use this to unify QWebView and QWebEngineView.
|
||||
|
||||
Attributes:
|
||||
history: The AbstractHistory for the current tab.
|
||||
registry: The ObjectRegistry associated with this tab.
|
||||
|
||||
_load_status: loading status of this page
|
||||
Accessible via load_status() method.
|
||||
_has_ssl_errors: Whether SSL errors happened.
|
||||
Needs to be set by subclasses.
|
||||
|
||||
for properties, see WebView/WebEngineView docs.
|
||||
|
||||
Signals:
|
||||
See related Qt signals.
|
||||
|
||||
new_tab_requested: Emitted when a new tab should be opened with the
|
||||
given URL.
|
||||
load_status_changed: The loading status changed
|
||||
"""
|
||||
|
||||
window_close_requested = pyqtSignal()
|
||||
link_hovered = pyqtSignal(str)
|
||||
load_started = pyqtSignal()
|
||||
load_progress = pyqtSignal(int)
|
||||
load_finished = pyqtSignal(bool)
|
||||
icon_changed = pyqtSignal(QIcon)
|
||||
title_changed = pyqtSignal(str)
|
||||
load_status_changed = pyqtSignal(str)
|
||||
new_tab_requested = pyqtSignal(QUrl)
|
||||
url_changed = pyqtSignal(QUrl)
|
||||
shutting_down = pyqtSignal()
|
||||
|
||||
def __init__(self, win_id, parent=None):
|
||||
self.win_id = win_id
|
||||
self.tab_id = next(tab_id_gen)
|
||||
super().__init__(parent)
|
||||
|
||||
self.registry = objreg.ObjectRegistry()
|
||||
tab_registry = objreg.get('tab-registry', scope='window',
|
||||
window=win_id)
|
||||
tab_registry[self.tab_id] = self
|
||||
objreg.register('tab', self, registry=self.registry)
|
||||
|
||||
# self.history = AbstractHistory(self)
|
||||
# self.scroller = AbstractScroller(self, parent=self)
|
||||
# self.caret = AbstractCaret(win_id=win_id, tab=self, mode_manager=...,
|
||||
# parent=self)
|
||||
# self.zoom = AbstractZoom(win_id=win_id)
|
||||
# self.search = AbstractSearch(parent=self)
|
||||
# self.printing = AbstractPrinting()
|
||||
self.data = TabData()
|
||||
self._layout = None
|
||||
self._widget = None
|
||||
self._progress = 0
|
||||
self._has_ssl_errors = False
|
||||
self._load_status = usertypes.LoadStatus.none
|
||||
self.backend = None
|
||||
|
||||
def _set_widget(self, widget):
|
||||
# pylint: disable=protected-access
|
||||
self._layout = WrapperLayout(widget, self)
|
||||
self._widget = widget
|
||||
self.history._history = widget.history()
|
||||
self.scroller._init_widget(widget)
|
||||
self.caret._widget = widget
|
||||
self.zoom._widget = widget
|
||||
self.search._widget = widget
|
||||
self.printing._widget = widget
|
||||
widget.mouse_wheel_zoom.connect(self.zoom._on_mouse_wheel_zoom)
|
||||
widget.setParent(self)
|
||||
self.setFocusProxy(widget)
|
||||
|
||||
def _set_load_status(self, val):
|
||||
"""Setter for load_status."""
|
||||
if not isinstance(val, usertypes.LoadStatus):
|
||||
raise TypeError("Type {} is no LoadStatus member!".format(val))
|
||||
log.webview.debug("load status for {}: {}".format(repr(self), val))
|
||||
self._load_status = val
|
||||
self.load_status_changed.emit(val.name)
|
||||
|
||||
@pyqtSlot(QUrl)
|
||||
def _on_url_changed(self, url):
|
||||
"""Update title when URL has changed and no title is available."""
|
||||
if url.isValid() and not self.title():
|
||||
self.title_changed.emit(url.toDisplayString())
|
||||
self.url_changed.emit(url)
|
||||
|
||||
@pyqtSlot()
|
||||
def _on_load_started(self):
|
||||
self._progress = 0
|
||||
self._has_ssl_errors = False
|
||||
self.data.viewing_source = False
|
||||
self._set_load_status(usertypes.LoadStatus.loading)
|
||||
self.load_started.emit()
|
||||
|
||||
@pyqtSlot(bool)
|
||||
def _on_load_finished(self, ok):
|
||||
if ok and not self._has_ssl_errors:
|
||||
if self.url().scheme() == 'https':
|
||||
self._set_load_status(usertypes.LoadStatus.success_https)
|
||||
else:
|
||||
self._set_load_status(usertypes.LoadStatus.success)
|
||||
|
||||
elif ok:
|
||||
self._set_load_status(usertypes.LoadStatus.warn)
|
||||
else:
|
||||
self._set_load_status(usertypes.LoadStatus.error)
|
||||
self.load_finished.emit(ok)
|
||||
if not self.title():
|
||||
self.title_changed.emit(self.url().toDisplayString())
|
||||
|
||||
@pyqtSlot(int)
|
||||
def _on_load_progress(self, perc):
|
||||
self._progress = perc
|
||||
self.load_progress.emit(perc)
|
||||
|
||||
@pyqtSlot()
|
||||
def _on_ssl_errors(self):
|
||||
self._has_ssl_errors = True
|
||||
|
||||
def url(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def progress(self):
|
||||
return self._progress
|
||||
|
||||
def load_status(self):
|
||||
return self._load_status
|
||||
|
||||
def _openurl_prepare(self, url):
|
||||
qtutils.ensure_valid(url)
|
||||
self.title_changed.emit(url.toDisplayString())
|
||||
|
||||
def openurl(self, url):
|
||||
raise NotImplementedError
|
||||
|
||||
def reload(self, *, force=False):
|
||||
raise NotImplementedError
|
||||
|
||||
def stop(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def clear_ssl_errors(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def dump_async(self, callback, *, plain=False):
|
||||
"""Dump the current page to a file ascync.
|
||||
|
||||
The given callback will be called with the result when dumping is
|
||||
complete.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def run_js_async(self, code, callback=None):
|
||||
"""Run javascript async.
|
||||
|
||||
The given callback will be called with the result when running JS is
|
||||
complete.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def run_js_blocking(self, code):
|
||||
"""Run javascript and block.
|
||||
|
||||
This returns the result to the caller. Its use should be avoided when
|
||||
possible as it runs a local event loop for QtWebEngine.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def shutdown(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def title(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def icon(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def set_html(self, html, base_url):
|
||||
raise NotImplementedError
|
||||
|
||||
def __repr__(self):
|
||||
try:
|
||||
url = utils.elide(self.url().toDisplayString(QUrl.EncodeUnicode),
|
||||
100)
|
||||
except AttributeError:
|
||||
url = '<AttributeError>'
|
||||
return utils.get_repr(self, tab_id=self.tab_id, url=url)
|
||||
@@ -1,6 +1,6 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -21,60 +21,31 @@
|
||||
|
||||
import os.path
|
||||
|
||||
from PyQt5.QtCore import pyqtSlot
|
||||
from PyQt5.QtNetwork import QNetworkDiskCache, QNetworkCacheMetaData
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.utils import utils, objreg
|
||||
from qutebrowser.utils import utils, standarddir, objreg
|
||||
|
||||
|
||||
class DiskCache(QNetworkDiskCache):
|
||||
|
||||
"""Disk cache which sets correct cache dir and size.
|
||||
"""Disk cache which sets correct cache dir and size."""
|
||||
|
||||
If the cache is deactivated via the command line argument --cachedir="",
|
||||
both attributes _cache_dir and _http_cache_dir are set to None.
|
||||
|
||||
Attributes:
|
||||
_activated: Whether the cache should be used.
|
||||
_cache_dir: The base directory for cache files (standarddir.cache()) or
|
||||
None.
|
||||
_http_cache_dir: the HTTP subfolder in _cache_dir or None.
|
||||
"""
|
||||
|
||||
def __init__(self, cache_dir, parent=None):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self._cache_dir = cache_dir
|
||||
if cache_dir is None:
|
||||
self._http_cache_dir = None
|
||||
else:
|
||||
self._http_cache_dir = os.path.join(cache_dir, 'http')
|
||||
self._maybe_activate()
|
||||
objreg.get('config').changed.connect(self.on_config_changed)
|
||||
self.setCacheDirectory(os.path.join(standarddir.cache(), 'http'))
|
||||
self.setMaximumCacheSize(config.get('storage', 'cache-size'))
|
||||
objreg.get('config').changed.connect(self.cache_size_changed)
|
||||
|
||||
def __repr__(self):
|
||||
return utils.get_repr(self, size=self.cacheSize(),
|
||||
maxsize=self.maximumCacheSize(),
|
||||
path=self.cacheDirectory())
|
||||
|
||||
def _maybe_activate(self):
|
||||
"""Activate/deactivate the cache based on the config."""
|
||||
if (config.get('general', 'private-browsing') or
|
||||
self._cache_dir is None):
|
||||
self._activated = False
|
||||
else:
|
||||
self._activated = True
|
||||
self.setCacheDirectory(self._http_cache_dir)
|
||||
self.setMaximumCacheSize(config.get('storage', 'cache-size'))
|
||||
|
||||
@pyqtSlot(str, str)
|
||||
def on_config_changed(self, section, option):
|
||||
"""Update cache size/activated if the config was changed."""
|
||||
if (section, option) == ('storage', 'cache-size'):
|
||||
self.setMaximumCacheSize(config.get('storage', 'cache-size'))
|
||||
elif (section, option) == ('general', # pragma: no branch
|
||||
'private-browsing'):
|
||||
self._maybe_activate()
|
||||
@config.change_filter('storage', 'cache-size')
|
||||
def cache_size_changed(self):
|
||||
"""Update cache size if the config was changed."""
|
||||
self.setMaximumCacheSize(config.get('storage', 'cache-size'))
|
||||
|
||||
def cacheSize(self):
|
||||
"""Return the current size taken up by the cache.
|
||||
@@ -82,10 +53,10 @@ class DiskCache(QNetworkDiskCache):
|
||||
Return:
|
||||
An int.
|
||||
"""
|
||||
if self._activated:
|
||||
return super().cacheSize()
|
||||
else:
|
||||
if config.get('general', 'private-browsing'):
|
||||
return 0
|
||||
else:
|
||||
return super().cacheSize()
|
||||
|
||||
def fileMetaData(self, filename):
|
||||
"""Return the QNetworkCacheMetaData for the cache file filename.
|
||||
@@ -96,10 +67,10 @@ class DiskCache(QNetworkDiskCache):
|
||||
Return:
|
||||
A QNetworkCacheMetaData object.
|
||||
"""
|
||||
if self._activated:
|
||||
return super().fileMetaData(filename)
|
||||
else:
|
||||
if config.get('general', 'private-browsing'):
|
||||
return QNetworkCacheMetaData()
|
||||
else:
|
||||
return super().fileMetaData(filename)
|
||||
|
||||
def data(self, url):
|
||||
"""Return the data associated with url.
|
||||
@@ -110,10 +81,10 @@ class DiskCache(QNetworkDiskCache):
|
||||
return:
|
||||
A QIODevice or None.
|
||||
"""
|
||||
if self._activated:
|
||||
return super().data(url)
|
||||
else:
|
||||
if config.get('general', 'private-browsing'):
|
||||
return None
|
||||
else:
|
||||
return super().data(url)
|
||||
|
||||
def insert(self, device):
|
||||
"""Insert the data in device and the prepared meta data into the cache.
|
||||
@@ -121,10 +92,10 @@ class DiskCache(QNetworkDiskCache):
|
||||
Args:
|
||||
device: A QIODevice.
|
||||
"""
|
||||
if self._activated:
|
||||
super().insert(device)
|
||||
if config.get('general', 'private-browsing'):
|
||||
return
|
||||
else:
|
||||
return None
|
||||
super().insert(device)
|
||||
|
||||
def metaData(self, url):
|
||||
"""Return the meta data for the url url.
|
||||
@@ -135,10 +106,10 @@ class DiskCache(QNetworkDiskCache):
|
||||
Return:
|
||||
A QNetworkCacheMetaData object.
|
||||
"""
|
||||
if self._activated:
|
||||
return super().metaData(url)
|
||||
else:
|
||||
if config.get('general', 'private-browsing'):
|
||||
return QNetworkCacheMetaData()
|
||||
else:
|
||||
return super().metaData(url)
|
||||
|
||||
def prepare(self, meta_data):
|
||||
"""Return the device that should be populated with the data.
|
||||
@@ -149,10 +120,10 @@ class DiskCache(QNetworkDiskCache):
|
||||
Return:
|
||||
A QIODevice or None.
|
||||
"""
|
||||
if self._activated:
|
||||
return super().prepare(meta_data)
|
||||
else:
|
||||
if config.get('general', 'private-browsing'):
|
||||
return None
|
||||
else:
|
||||
return super().prepare(meta_data)
|
||||
|
||||
def remove(self, url):
|
||||
"""Remove the cache entry for url.
|
||||
@@ -160,10 +131,10 @@ class DiskCache(QNetworkDiskCache):
|
||||
Return:
|
||||
True on success, False otherwise.
|
||||
"""
|
||||
if self._activated:
|
||||
return super().remove(url)
|
||||
else:
|
||||
if config.get('general', 'private-browsing'):
|
||||
return False
|
||||
else:
|
||||
return super().remove(url)
|
||||
|
||||
def updateMetaData(self, meta_data):
|
||||
"""Update the cache meta date for the meta_data's url to meta_data.
|
||||
@@ -171,14 +142,14 @@ class DiskCache(QNetworkDiskCache):
|
||||
Args:
|
||||
meta_data: A QNetworkCacheMetaData object.
|
||||
"""
|
||||
if self._activated:
|
||||
super().updateMetaData(meta_data)
|
||||
else:
|
||||
if config.get('general', 'private-browsing'):
|
||||
return
|
||||
else:
|
||||
super().updateMetaData(meta_data)
|
||||
|
||||
def clear(self):
|
||||
"""Remove all items from the cache."""
|
||||
if self._activated:
|
||||
super().clear()
|
||||
else:
|
||||
if config.get('general', 'private-browsing'):
|
||||
return
|
||||
else:
|
||||
super().clear()
|
||||
@@ -1,6 +1,6 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -37,6 +37,9 @@ class RAMCookieJar(QNetworkCookieJar):
|
||||
|
||||
changed = pyqtSignal()
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
def __repr__(self):
|
||||
return utils.get_repr(self, count=len(self.allCookies()))
|
||||
|
||||
@@ -65,31 +68,23 @@ class CookieJar(RAMCookieJar):
|
||||
_lineparser: The LineParser managing the cookies file.
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None, *, line_parser=None):
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
if line_parser:
|
||||
self._lineparser = line_parser
|
||||
else:
|
||||
self._lineparser = lineparser.LineParser(
|
||||
standarddir.data(), 'cookies', binary=True, parent=self)
|
||||
self.parse_cookies()
|
||||
self._lineparser = lineparser.LineParser(
|
||||
standarddir.data(), 'cookies', binary=True, parent=self)
|
||||
cookies = []
|
||||
for line in self._lineparser:
|
||||
cookies += QNetworkCookie.parseCookies(line)
|
||||
self.setAllCookies(cookies)
|
||||
objreg.get('config').changed.connect(self.cookies_store_changed)
|
||||
objreg.get('save-manager').add_saveable(
|
||||
'cookies', self.save, self.changed,
|
||||
config_opt=('content', 'cookies-store'))
|
||||
|
||||
def parse_cookies(self):
|
||||
"""Parse cookies from lineparser and store them."""
|
||||
cookies = []
|
||||
for line in self._lineparser:
|
||||
cookies += QNetworkCookie.parseCookies(line)
|
||||
self.setAllCookies(cookies)
|
||||
|
||||
def purge_old_cookies(self):
|
||||
"""Purge expired cookies from the cookie jar."""
|
||||
# Based on:
|
||||
# http://doc.qt.io/qt-5/qtwebkitexamples-webkitwidgets-browser-cookiejar-cpp.html
|
||||
# http://qt-project.org/doc/qt-5/qtwebkitexamples-webkitwidgets-browser-cookiejar-cpp.html
|
||||
now = QDateTime.currentDateTime()
|
||||
cookies = [c for c in self.allCookies()
|
||||
if c.isSessionCookie() or c.expirationDate() >= now]
|
||||
@@ -1,6 +1,6 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -25,7 +25,7 @@ import sip
|
||||
from PyQt5.QtCore import pyqtSlot, QSize, Qt, QTimer
|
||||
from PyQt5.QtWidgets import QListView, QSizePolicy, QMenu
|
||||
|
||||
from qutebrowser.browser.webkit import downloads
|
||||
from qutebrowser.browser import downloads
|
||||
from qutebrowser.config import style
|
||||
from qutebrowser.utils import qtutils, utils, objreg
|
||||
|
||||
@@ -64,8 +64,8 @@ class DownloadView(QListView):
|
||||
|
||||
STYLESHEET = """
|
||||
QListView {
|
||||
background-color: {{ color['downloads.bg.bar'] }};
|
||||
font: {{ font['downloads'] }};
|
||||
{{ color['downloads.bg.bar'] }}
|
||||
{{ font['downloads'] }}
|
||||
}
|
||||
|
||||
QListView::item {
|
||||
@@ -79,7 +79,6 @@ class DownloadView(QListView):
|
||||
self.setResizeMode(QListView.Adjust)
|
||||
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
||||
self.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed)
|
||||
self.setFocusPolicy(Qt.NoFocus)
|
||||
self.setFlow(QListView.LeftToRight)
|
||||
self.setSpacing(1)
|
||||
self._menu = None
|
||||
@@ -126,7 +125,6 @@ class DownloadView(QListView):
|
||||
- (QAction, callable) tuples.
|
||||
- (None, None) for a separator
|
||||
"""
|
||||
model = self.model()
|
||||
actions = []
|
||||
if item is None:
|
||||
pass
|
||||
@@ -136,12 +134,13 @@ class DownloadView(QListView):
|
||||
else:
|
||||
actions.append(("Retry", item.retry))
|
||||
actions.append(("Remove",
|
||||
functools.partial(model.remove_item, item)))
|
||||
functools.partial(self.model().remove_item, item)))
|
||||
else:
|
||||
actions.append(("Cancel", item.cancel))
|
||||
if model.can_clear():
|
||||
if self.model().can_clear():
|
||||
actions.append((None, None))
|
||||
actions.append(("Remove all finished", model.download_clear))
|
||||
actions.append(("Remove all finished", functools.partial(
|
||||
self.model().download_remove, True)))
|
||||
return actions
|
||||
|
||||
@pyqtSlot('QPoint')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2014-2016 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
@@ -19,39 +19,35 @@
|
||||
|
||||
"""A HintManager to draw hints over links."""
|
||||
|
||||
import collections
|
||||
import functools
|
||||
import math
|
||||
import re
|
||||
from string import ascii_lowercase
|
||||
import functools
|
||||
import subprocess
|
||||
import collections
|
||||
|
||||
from PyQt5.QtCore import (pyqtSignal, pyqtSlot, QObject, QEvent, Qt, QUrl,
|
||||
QTimer)
|
||||
from PyQt5.QtGui import QMouseEvent
|
||||
from PyQt5.QtGui import QMouseEvent, QClipboard
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5.QtWebKit import QWebElement
|
||||
from PyQt5.QtWebKitWidgets import QWebPage
|
||||
|
||||
from qutebrowser.config import config
|
||||
from qutebrowser.keyinput import modeman, modeparsers
|
||||
from qutebrowser.browser.webkit import webelem
|
||||
from qutebrowser.browser import webelem
|
||||
from qutebrowser.commands import userscripts, cmdexc, cmdutils, runners
|
||||
from qutebrowser.utils import usertypes, log, qtutils, message, objreg, utils
|
||||
from qutebrowser.utils import usertypes, log, qtutils, message, objreg
|
||||
|
||||
|
||||
ElemTuple = collections.namedtuple('ElemTuple', ['elem', 'label'])
|
||||
|
||||
|
||||
Target = usertypes.enum('Target', ['normal', 'current', 'tab', 'tab_fg',
|
||||
'tab_bg', 'window', 'yank', 'yank_primary',
|
||||
'run', 'fill', 'hover', 'download',
|
||||
Target = usertypes.enum('Target', ['normal', 'tab', 'tab_bg', 'window', 'yank',
|
||||
'yank_primary', 'run', 'fill', 'hover',
|
||||
'rapid', 'rapid_win', 'download',
|
||||
'userscript', 'spawn'])
|
||||
|
||||
|
||||
class WordHintingError(Exception):
|
||||
|
||||
"""Exception raised on errors during word hinting."""
|
||||
|
||||
|
||||
@pyqtSlot(usertypes.KeyMode)
|
||||
def on_mode_entered(mode, win_id):
|
||||
"""Stop hinting when insert mode was entered."""
|
||||
if mode == usertypes.KeyMode.insert:
|
||||
@@ -66,16 +62,14 @@ class HintContext:
|
||||
frames: The QWebFrames to use.
|
||||
destroyed_frames: id()'s of QWebFrames which have been destroyed.
|
||||
(Workaround for https://github.com/The-Compiler/qutebrowser/issues/152)
|
||||
all_elems: A list of all (elem, label) namedtuples ever created.
|
||||
elems: A mapping from key strings to (elem, label) namedtuples.
|
||||
May contain less elements than `all_elems` due to filtering.
|
||||
baseurl: The URL of the current page.
|
||||
target: What to do with the opened links.
|
||||
normal/current/tab/tab_fg/tab_bg/window: Get passed to
|
||||
BrowserTab.
|
||||
normal/tab/tab_bg/window: Get passed to BrowserTab.
|
||||
yank/yank_primary: Yank to clipboard/primary selection.
|
||||
run: Run a command.
|
||||
fill: Fill commandline with link.
|
||||
rapid: Rapid mode with background tabs
|
||||
download: Download the link.
|
||||
userscript: Call a custom userscript.
|
||||
spawn: Spawn a simple command.
|
||||
@@ -83,12 +77,10 @@ class HintContext:
|
||||
args: Custom arguments for userscript/spawn
|
||||
rapid: Whether to do rapid hinting.
|
||||
mainframe: The main QWebFrame where we started hinting in.
|
||||
tab: The WebTab object we started hinting in.
|
||||
group: The group of web elements to hint.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.all_elems = []
|
||||
self.elems = {}
|
||||
self.target = None
|
||||
self.baseurl = None
|
||||
@@ -98,7 +90,6 @@ class HintContext:
|
||||
self.destroyed_frames = []
|
||||
self.args = []
|
||||
self.mainframe = None
|
||||
self.tab = None
|
||||
self.group = None
|
||||
|
||||
def get_args(self, urlstr):
|
||||
@@ -121,21 +112,18 @@ class HintManager(QObject):
|
||||
_context: The HintContext for the current invocation.
|
||||
_win_id: The window ID this HintManager is associated with.
|
||||
_tab_id: The tab ID this HintManager is associated with.
|
||||
_filterstr: Used to save the filter string for restoring in rapid mode.
|
||||
|
||||
Signals:
|
||||
mouse_event: Mouse event to be posted in the web view.
|
||||
arg: A QMouseEvent
|
||||
start_hinting: Emitted when hinting starts, before a link is clicked.
|
||||
arg: The ClickTarget to use.
|
||||
arg: The hinting target name.
|
||||
stop_hinting: Emitted after a link was clicked.
|
||||
"""
|
||||
|
||||
HINT_TEXTS = {
|
||||
Target.normal: "Follow hint",
|
||||
Target.current: "Follow hint in current tab",
|
||||
Target.tab: "Follow hint in new tab",
|
||||
Target.tab_fg: "Follow hint in foreground tab",
|
||||
Target.tab_bg: "Follow hint in background tab",
|
||||
Target.window: "Follow hint in new window",
|
||||
Target.yank: "Yank hint to clipboard",
|
||||
@@ -149,7 +137,7 @@ class HintManager(QObject):
|
||||
}
|
||||
|
||||
mouse_event = pyqtSignal('QMouseEvent')
|
||||
start_hinting = pyqtSignal(usertypes.ClickTarget)
|
||||
start_hinting = pyqtSignal(str)
|
||||
stop_hinting = pyqtSignal()
|
||||
|
||||
def __init__(self, win_id, tab_id, parent=None):
|
||||
@@ -158,8 +146,6 @@ class HintManager(QObject):
|
||||
self._win_id = win_id
|
||||
self._tab_id = tab_id
|
||||
self._context = None
|
||||
self._filterstr = None
|
||||
self._word_hinter = WordHinter()
|
||||
mode_manager = objreg.get('mode-manager', scope='window',
|
||||
window=win_id)
|
||||
mode_manager.left.connect(self.on_mode_left)
|
||||
@@ -174,7 +160,7 @@ class HintManager(QObject):
|
||||
|
||||
def _cleanup(self):
|
||||
"""Clean up after hinting."""
|
||||
for elem in self._context.all_elems:
|
||||
for elem in self._context.elems.values():
|
||||
try:
|
||||
elem.label.removeFromDocument()
|
||||
except webelem.IsNullError:
|
||||
@@ -200,7 +186,6 @@ class HintManager(QObject):
|
||||
window=self._win_id)
|
||||
message_bridge.maybe_reset_text(text)
|
||||
self._context = None
|
||||
self._filterstr = None
|
||||
|
||||
def _hint_strings(self, elems):
|
||||
"""Calculate the hint strings for elems.
|
||||
@@ -213,19 +198,12 @@ class HintManager(QObject):
|
||||
Return:
|
||||
A list of hint strings, in the same order as the elements.
|
||||
"""
|
||||
hint_mode = config.get('hints', 'mode')
|
||||
if hint_mode == 'word':
|
||||
try:
|
||||
return self._word_hinter.hint(elems)
|
||||
except WordHintingError as e:
|
||||
message.error(self._win_id, str(e), immediately=True)
|
||||
# falls back on letter hints
|
||||
if hint_mode == 'number':
|
||||
if config.get('hints', 'mode') == 'number':
|
||||
chars = '0123456789'
|
||||
else:
|
||||
chars = config.get('hints', 'chars')
|
||||
min_chars = config.get('hints', 'min-chars')
|
||||
if config.get('hints', 'scatter') and hint_mode != 'number':
|
||||
if config.get('hints', 'scatter'):
|
||||
return self._hint_scattered(min_chars, chars, elems)
|
||||
else:
|
||||
return self._hint_linear(min_chars, chars, elems)
|
||||
@@ -357,7 +335,7 @@ class HintManager(QObject):
|
||||
('display', 'inline !important'),
|
||||
('z-index', '{} !important'.format(int(2 ** 32 / 2 - 1))),
|
||||
('pointer-events', 'none !important'),
|
||||
('position', 'fixed !important'),
|
||||
('position', 'absolute !important'),
|
||||
('color', config.get('colors', 'hints.fg') + ' !important'),
|
||||
('background', config.get('colors', 'hints.bg') + ' !important'),
|
||||
('font', config.get('fonts', 'hints') + ' !important'),
|
||||
@@ -383,12 +361,15 @@ class HintManager(QObject):
|
||||
elem: The QWebElement to set the style attributes for.
|
||||
label: The label QWebElement.
|
||||
"""
|
||||
no_js = config.get('hints', 'find-implementation') != 'javascript'
|
||||
rect = elem.rect_on_view(adjust_zoom=False, no_js=no_js)
|
||||
rect = elem.geometry()
|
||||
left = rect.x()
|
||||
top = rect.y()
|
||||
log.hints.vdebug("Drawing label '{!r}' at {}/{} for element '{!r}' "
|
||||
"(no_js: {})".format(label, left, top, elem, no_js))
|
||||
zoom = elem.webFrame().zoomFactor()
|
||||
if not config.get('ui', 'zoom-text-only'):
|
||||
left /= zoom
|
||||
top /= zoom
|
||||
log.hints.vdebug("Drawing label '{!r}' at {}/{} for element '{!r}', "
|
||||
"zoom level {}".format(label, left, top, elem, zoom))
|
||||
label.setStyleProperty('left', '{}px !important'.format(left))
|
||||
label.setStyleProperty('top', '{}px !important'.format(top))
|
||||
|
||||
@@ -432,37 +413,22 @@ class HintManager(QObject):
|
||||
elem: The QWebElement to click.
|
||||
context: The HintContext to use.
|
||||
"""
|
||||
target_mapping = {
|
||||
Target.normal: usertypes.ClickTarget.normal,
|
||||
Target.current: usertypes.ClickTarget.normal,
|
||||
Target.tab_fg: usertypes.ClickTarget.tab,
|
||||
Target.tab_bg: usertypes.ClickTarget.tab_bg,
|
||||
Target.window: usertypes.ClickTarget.window,
|
||||
Target.hover: usertypes.ClickTarget.normal,
|
||||
}
|
||||
if config.get('tabs', 'background-tabs'):
|
||||
target_mapping[Target.tab] = usertypes.ClickTarget.tab_bg
|
||||
if context.target == Target.rapid:
|
||||
target = Target.tab_bg
|
||||
elif context.target == Target.rapid_win:
|
||||
target = Target.window
|
||||
else:
|
||||
target_mapping[Target.tab] = usertypes.ClickTarget.tab
|
||||
|
||||
# Click the center of the largest square fitting into the top/left
|
||||
# corner of the rectangle, this will help if part of the <a> element
|
||||
# is hidden behind other elements
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/1005
|
||||
rect = elem.rect_on_view()
|
||||
if rect.width() > rect.height():
|
||||
rect.setWidth(rect.height())
|
||||
else:
|
||||
rect.setHeight(rect.width())
|
||||
pos = rect.center()
|
||||
|
||||
action = "Hovering" if context.target == Target.hover else "Clicking"
|
||||
log.hints.debug("{} on '{}' at position {}".format(
|
||||
action, elem.debug_text(), pos))
|
||||
|
||||
self.start_hinting.emit(target_mapping[context.target])
|
||||
if context.target in [Target.tab, Target.tab_fg, Target.tab_bg,
|
||||
Target.window]:
|
||||
target = context.target
|
||||
# FIXME Instead of clicking the center, we could have nicer heuristics.
|
||||
# e.g. parse (-webkit-)border-radius correctly and click text fields at
|
||||
# the bottom right, and everything else on the top left or so.
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/70
|
||||
pos = elem.rect_on_view().center()
|
||||
action = "Hovering" if target == Target.hover else "Clicking"
|
||||
log.hints.debug("{} on '{}' at {}/{}".format(
|
||||
action, elem, pos.x(), pos.y()))
|
||||
self.start_hinting.emit(target.name)
|
||||
if target in (Target.tab, Target.tab_bg, Target.window):
|
||||
modifiers = Qt.ControlModifier
|
||||
else:
|
||||
modifiers = Qt.NoModifier
|
||||
@@ -470,22 +436,13 @@ class HintManager(QObject):
|
||||
QMouseEvent(QEvent.MouseMove, pos, Qt.NoButton, Qt.NoButton,
|
||||
Qt.NoModifier),
|
||||
]
|
||||
if context.target != Target.hover:
|
||||
if target != Target.hover:
|
||||
events += [
|
||||
QMouseEvent(QEvent.MouseButtonPress, pos, Qt.LeftButton,
|
||||
Qt.LeftButton, modifiers),
|
||||
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:
|
||||
self.mouse_event.emit(evt)
|
||||
if elem.is_text_input() and elem.is_editable():
|
||||
@@ -501,16 +458,12 @@ class HintManager(QObject):
|
||||
url: The URL to open as a QUrl.
|
||||
context: The HintContext to use.
|
||||
"""
|
||||
sel = (context.target == Target.yank_primary and
|
||||
utils.supports_selection())
|
||||
|
||||
sel = context.target == Target.yank_primary
|
||||
mode = QClipboard.Selection if sel else QClipboard.Clipboard
|
||||
urlstr = url.toString(QUrl.FullyEncoded | QUrl.RemovePassword)
|
||||
utils.set_clipboard(urlstr, selection=sel)
|
||||
|
||||
msg = "Yanked URL to {}: {}".format(
|
||||
"primary selection" if sel else "clipboard",
|
||||
urlstr)
|
||||
message.info(self._win_id, msg)
|
||||
QApplication.clipboard().setText(urlstr, mode)
|
||||
message.info(self._win_id, "URL yanked to {}".format(
|
||||
"primary selection" if sel else "clipboard"))
|
||||
|
||||
def _run_cmd(self, url, context):
|
||||
"""Run the command based on a hint URL.
|
||||
@@ -552,18 +505,12 @@ class HintManager(QObject):
|
||||
if url is None:
|
||||
self._show_url_error()
|
||||
return
|
||||
if context.rapid:
|
||||
prompt = False
|
||||
else:
|
||||
prompt = None
|
||||
|
||||
download_manager = objreg.get('download-manager', scope='window',
|
||||
window=self._win_id)
|
||||
download_manager.get(url, page=elem.webFrame().page(),
|
||||
prompt_download_directory=prompt)
|
||||
download_manager.get(url, elem.webFrame().page())
|
||||
|
||||
def _call_userscript(self, elem, context):
|
||||
"""Call a userscript from a hint.
|
||||
"""Call an userscript from a hint.
|
||||
|
||||
Args:
|
||||
elem: The QWebElement to use in the userscript.
|
||||
@@ -571,20 +518,18 @@ class HintManager(QObject):
|
||||
"""
|
||||
cmd = context.args[0]
|
||||
args = context.args[1:]
|
||||
frame = context.mainframe
|
||||
env = {
|
||||
'QUTE_MODE': 'hints',
|
||||
'QUTE_SELECTED_TEXT': str(elem),
|
||||
'QUTE_SELECTED_HTML': elem.toOuterXml(),
|
||||
'QUTE_HTML': frame.toHtml(),
|
||||
'QUTE_TEXT': frame.toPlainText(),
|
||||
}
|
||||
url = self._resolve_url(elem, context.baseurl)
|
||||
if url is not None:
|
||||
env['QUTE_URL'] = url.toString(QUrl.FullyEncoded)
|
||||
|
||||
try:
|
||||
userscripts.run_async(context.tab, cmd, *args, win_id=self._win_id,
|
||||
env=env)
|
||||
except userscripts.UnsupportedError as e:
|
||||
message.error(self._win_id, str(e), immediately=True)
|
||||
userscripts.run(cmd, *args, win_id=self._win_id, env=env)
|
||||
|
||||
def _spawn(self, url, context):
|
||||
"""Spawn a simple command from a hint.
|
||||
@@ -595,8 +540,11 @@ class HintManager(QObject):
|
||||
"""
|
||||
urlstr = url.toString(QUrl.FullyEncoded | QUrl.RemovePassword)
|
||||
args = context.get_args(urlstr)
|
||||
commandrunner = runners.CommandRunner(self._win_id)
|
||||
commandrunner.run_safely('spawn ' + ' '.join(args))
|
||||
try:
|
||||
subprocess.Popen(args)
|
||||
except OSError as e:
|
||||
msg = "Error while spawning command: {}".format(e)
|
||||
message.error(self._win_id, msg, immediately=True)
|
||||
|
||||
def _resolve_url(self, elem, baseurl):
|
||||
"""Resolve a URL and check if we want to keep it.
|
||||
@@ -608,9 +556,9 @@ class HintManager(QObject):
|
||||
Return:
|
||||
A QUrl with the absolute URL, or None.
|
||||
"""
|
||||
for attr in ['href', 'src']:
|
||||
for attr in ('href', 'src'):
|
||||
if attr in elem:
|
||||
text = elem[attr].strip()
|
||||
text = elem[attr]
|
||||
break
|
||||
else:
|
||||
return None
|
||||
@@ -626,7 +574,8 @@ class HintManager(QObject):
|
||||
def _find_prevnext(self, frame, prev=False):
|
||||
"""Find a prev/next element in frame."""
|
||||
# First check for <link rel="prev(ious)|next">
|
||||
elems = frame.findAllElements(webelem.SELECTORS[webelem.Group.links])
|
||||
elems = frame.findAllElements(
|
||||
webelem.SELECTORS[webelem.Group.links])
|
||||
rel_values = ('prev', 'previous') if prev else ('next')
|
||||
for e in elems:
|
||||
e = webelem.WebElementWrapper(e)
|
||||
@@ -641,16 +590,13 @@ class HintManager(QObject):
|
||||
# Then check for regular links/buttons.
|
||||
elems = frame.findAllElements(
|
||||
webelem.SELECTORS[webelem.Group.prevnext])
|
||||
elems = [webelem.WebElementWrapper(e) for e in elems]
|
||||
filterfunc = webelem.FILTERS[webelem.Group.prevnext]
|
||||
elems = [e for e in elems if 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:
|
||||
e = webelem.WebElementWrapper(e)
|
||||
text = str(e)
|
||||
if not text:
|
||||
continue
|
||||
@@ -677,8 +623,8 @@ class HintManager(QObject):
|
||||
"""
|
||||
if not isinstance(target, Target):
|
||||
raise TypeError("Target {} is no Target member!".format(target))
|
||||
if target in [Target.userscript, Target.spawn, Target.run,
|
||||
Target.fill]:
|
||||
if target in (Target.userscript, Target.spawn, Target.run,
|
||||
Target.fill):
|
||||
if not args:
|
||||
raise cmdexc.CommandError(
|
||||
"'args' is required with target userscript/spawn/run/"
|
||||
@@ -703,27 +649,14 @@ class HintManager(QObject):
|
||||
if not elems:
|
||||
raise cmdexc.CommandError("No elements found.")
|
||||
strings = self._hint_strings(elems)
|
||||
log.hints.debug("hints: {}".format(', '.join(strings)))
|
||||
for e, string in zip(elems, strings):
|
||||
label = self._draw_label(e, string)
|
||||
elem = ElemTuple(e, label)
|
||||
self._context.all_elems.append(elem)
|
||||
self._context.elems[string] = elem
|
||||
self._context.elems[string] = ElemTuple(e, label)
|
||||
keyparsers = objreg.get('keyparsers', scope='window',
|
||||
window=self._win_id)
|
||||
keyparser = keyparsers[usertypes.KeyMode.hint]
|
||||
keyparser.update_bindings(strings)
|
||||
|
||||
def _filter_matches(self, filterstr, elemstr):
|
||||
"""Return True if `filterstr` matches `elemstr`."""
|
||||
# Empty string and None always match
|
||||
if not filterstr:
|
||||
return True
|
||||
filterstr = filterstr.casefold()
|
||||
elemstr = elemstr.casefold()
|
||||
# Do multi-word matching
|
||||
return all(word in elemstr for word in filterstr.split())
|
||||
|
||||
def follow_prevnext(self, frame, baseurl, prev=False, tab=False,
|
||||
background=False, window=False):
|
||||
"""Click a "previous"/"next" element on the page.
|
||||
@@ -757,36 +690,29 @@ class HintManager(QObject):
|
||||
window=self._win_id)
|
||||
tabbed_browser.tabopen(url, background=background)
|
||||
else:
|
||||
tab = objreg.get('tab', scope='tab', window=self._win_id,
|
||||
tab=self._tab_id)
|
||||
tab.openurl(url)
|
||||
webview = objreg.get('webview', scope='tab', window=self._win_id,
|
||||
tab=self._tab_id)
|
||||
webview.openurl(url)
|
||||
|
||||
@cmdutils.register(instance='hintmanager', scope='tab', name='hint',
|
||||
star_args_optional=True, maxsplit=2,
|
||||
backend=usertypes.Backend.QtWebKit)
|
||||
@cmdutils.argument('win_id', win_id=True)
|
||||
@cmdutils.register(instance='hintmanager', scope='tab', name='hint')
|
||||
def start(self, rapid=False, group=webelem.Group.all, target=Target.normal,
|
||||
*args, win_id):
|
||||
*args: {'nargs': '*'}, win_id: {'special': 'win_id'}):
|
||||
"""Start hinting.
|
||||
|
||||
Args:
|
||||
rapid: Whether to do rapid hinting. This is only possible with
|
||||
targets `tab` (with background-tabs=true), `tab-bg`,
|
||||
`window`, `run`, `hover`, `userscript` and `spawn`.
|
||||
targets `tab-bg`, `window`, `run`, `hover`, `userscript` and
|
||||
`spawn`.
|
||||
group: The hinting mode to use.
|
||||
|
||||
- `all`: All clickable elements.
|
||||
- `links`: Only links.
|
||||
- `images`: Only images.
|
||||
- `inputs`: Only input fields.
|
||||
|
||||
target: What to do with the selected element.
|
||||
|
||||
- `normal`: Open the link.
|
||||
- `current`: Open the link in the current tab.
|
||||
- `tab`: Open the link in a new tab (honoring the
|
||||
background-tabs setting).
|
||||
- `tab-fg`: Open the link in a new foreground tab.
|
||||
- `normal`: Open the link in the current tab.
|
||||
- `tab`: Open the link in a new tab.
|
||||
- `tab-bg`: Open the link in a new background tab.
|
||||
- `window`: Open the link in a new window.
|
||||
- `hover` : Hover over the link.
|
||||
@@ -796,7 +722,7 @@ class HintManager(QObject):
|
||||
- `fill`: Fill the commandline with the command given as
|
||||
argument.
|
||||
- `download`: Download the link.
|
||||
- `userscript`: Call a userscript with `$QUTE_URL` set to the
|
||||
- `userscript`: Call an userscript with `$QUTE_URL` set to the
|
||||
link.
|
||||
- `spawn`: Spawn a command.
|
||||
|
||||
@@ -805,11 +731,7 @@ class HintManager(QObject):
|
||||
- With `spawn`: The executable and arguments to spawn.
|
||||
`{hint-url}` will get replaced by the selected
|
||||
URL.
|
||||
- With `userscript`: The userscript to execute. Either store
|
||||
the userscript in
|
||||
`~/.local/share/qutebrowser/userscripts`
|
||||
(or `$XDG_DATA_DIR`), or use an absolute
|
||||
path.
|
||||
- With `userscript`: The userscript to execute.
|
||||
- With `fill`: The command to fill the statusbar with.
|
||||
`{hint-url}` will get replaced by the selected
|
||||
URL.
|
||||
@@ -817,35 +739,26 @@ class HintManager(QObject):
|
||||
"""
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window=self._win_id)
|
||||
tab = tabbed_browser.currentWidget()
|
||||
if tab is None:
|
||||
widget = tabbed_browser.currentWidget()
|
||||
if widget is None:
|
||||
raise cmdexc.CommandError("No WebView available yet!")
|
||||
# FIXME:qtwebengine have a proper API for this
|
||||
page = tab._widget.page() # pylint: disable=protected-access
|
||||
mainframe = page.mainFrame()
|
||||
mainframe = widget.page().mainFrame()
|
||||
if mainframe is None:
|
||||
raise cmdexc.CommandError("No frame focused!")
|
||||
mode_manager = objreg.get('mode-manager', scope='window',
|
||||
window=self._win_id)
|
||||
if mode_manager.mode == usertypes.KeyMode.hint:
|
||||
modeman.leave(win_id, usertypes.KeyMode.hint, 're-hinting')
|
||||
raise cmdexc.CommandError("Already hinting!")
|
||||
|
||||
if rapid:
|
||||
if target in [Target.tab_bg, Target.window, Target.run,
|
||||
Target.hover, Target.userscript, Target.spawn,
|
||||
Target.download, Target.normal, Target.current]:
|
||||
pass
|
||||
elif (target == Target.tab and
|
||||
config.get('tabs', 'background-tabs')):
|
||||
pass
|
||||
else:
|
||||
name = target.name.replace('_', '-')
|
||||
raise cmdexc.CommandError("Rapid hinting makes no sense with "
|
||||
"target {}!".format(name))
|
||||
if rapid and target not in (Target.tab_bg, Target.window, Target.run,
|
||||
Target.hover, Target.userscript,
|
||||
Target.spawn):
|
||||
name = target.name.replace('_', '-')
|
||||
raise cmdexc.CommandError("Rapid hinting makes no sense with "
|
||||
"target {}!".format(name))
|
||||
|
||||
self._check_args(target, *args)
|
||||
self._context = HintContext()
|
||||
self._context.tab = tab
|
||||
self._context.target = target
|
||||
self._context.rapid = rapid
|
||||
try:
|
||||
@@ -861,6 +774,7 @@ class HintManager(QObject):
|
||||
self._context.args = args
|
||||
self._context.mainframe = mainframe
|
||||
self._context.group = group
|
||||
self._handle_old_rapid_targets(win_id)
|
||||
self._init_elements()
|
||||
message_bridge = objreg.get('message-bridge', scope='window',
|
||||
window=self._win_id)
|
||||
@@ -869,118 +783,79 @@ class HintManager(QObject):
|
||||
modeman.enter(self._win_id, usertypes.KeyMode.hint,
|
||||
'HintManager.start')
|
||||
|
||||
def _handle_old_rapid_targets(self, win_id):
|
||||
"""Switch to the new way for rapid hinting with a rapid target.
|
||||
|
||||
Args:
|
||||
win_id: The window ID to display the warning in.
|
||||
|
||||
DEPRECATED.
|
||||
"""
|
||||
old_rapid_targets = {
|
||||
Target.rapid: Target.tab_bg,
|
||||
Target.rapid_win: Target.window,
|
||||
}
|
||||
target = self._context.target
|
||||
if target in old_rapid_targets:
|
||||
self._context.target = old_rapid_targets[target]
|
||||
self._context.rapid = True
|
||||
name = target.name.replace('_', '-')
|
||||
group_name = self._context.group.name.replace('_', '-')
|
||||
new_name = self._context.target.name.replace('_', '-')
|
||||
message.warning(
|
||||
win_id, ':hint with target {} is deprecated, use :hint '
|
||||
'--rapid {} {} instead!'.format(name, group_name, new_name))
|
||||
|
||||
def handle_partial_key(self, keystr):
|
||||
"""Handle a new partial keypress."""
|
||||
log.hints.debug("Handling new keystring: '{}'".format(keystr))
|
||||
for string, elem in self._context.elems.items():
|
||||
for (string, elems) in self._context.elems.items():
|
||||
try:
|
||||
if string.startswith(keystr):
|
||||
matched = string[:len(keystr)]
|
||||
rest = string[len(keystr):]
|
||||
match_color = config.get('colors', 'hints.fg.match')
|
||||
elem.label.setInnerXml(
|
||||
elems.label.setInnerXml(
|
||||
'<font color="{}">{}</font>{}'.format(
|
||||
match_color, matched, rest))
|
||||
if self._is_hidden(elem.label):
|
||||
if self._is_hidden(elems.label):
|
||||
# hidden element which matches again -> show it
|
||||
self._show_elem(elem.label)
|
||||
self._show_elem(elems.label)
|
||||
else:
|
||||
# element doesn't match anymore -> hide it
|
||||
self._hide_elem(elem.label)
|
||||
self._hide_elem(elems.label)
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
|
||||
def _filter_number_hints(self):
|
||||
"""Apply filters for numbered hints and renumber them.
|
||||
|
||||
Return:
|
||||
Elements which are still visible
|
||||
"""
|
||||
# renumber filtered hints
|
||||
elems = []
|
||||
for e in self._context.all_elems:
|
||||
try:
|
||||
if not self._is_hidden(e.label):
|
||||
elems.append(e)
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
if not elems:
|
||||
# Whoops, filtered all hints
|
||||
modeman.leave(self._win_id, usertypes.KeyMode.hint,
|
||||
'all filtered')
|
||||
return {}
|
||||
|
||||
strings = self._hint_strings(elems)
|
||||
self._context.elems = {}
|
||||
for elem, string in zip(elems, strings):
|
||||
elem.label.setInnerXml(string)
|
||||
self._context.elems[string] = elem
|
||||
keyparsers = objreg.get('keyparsers', scope='window',
|
||||
window=self._win_id)
|
||||
keyparser = keyparsers[usertypes.KeyMode.hint]
|
||||
keyparser.update_bindings(strings, preserve_filter=True)
|
||||
|
||||
return self._context.elems
|
||||
|
||||
def _filter_non_number_hints(self):
|
||||
"""Apply filters for letter/word hints.
|
||||
|
||||
Return:
|
||||
Elements which are still visible
|
||||
"""
|
||||
visible = {}
|
||||
for string, elem in self._context.elems.items():
|
||||
try:
|
||||
if not self._is_hidden(elem.label):
|
||||
visible[string] = elem
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
if not visible:
|
||||
# Whoops, filtered all hints
|
||||
modeman.leave(self._win_id, usertypes.KeyMode.hint,
|
||||
'all filtered')
|
||||
return visible
|
||||
|
||||
def filter_hints(self, filterstr):
|
||||
"""Filter displayed hints according to a text.
|
||||
|
||||
Args:
|
||||
filterstr: The string to filter with, or None to use the filter
|
||||
from previous call (saved in `self._filterstr`). If
|
||||
`filterstr` is an empty string or if both `filterstr`
|
||||
and `self._filterstr` are None, all hints are shown.
|
||||
filterstr: The string to filer with, or None to show all.
|
||||
"""
|
||||
if filterstr is None:
|
||||
filterstr = self._filterstr
|
||||
else:
|
||||
self._filterstr = filterstr
|
||||
|
||||
for elem in self._context.all_elems:
|
||||
for elems in self._context.elems.values():
|
||||
try:
|
||||
if self._filter_matches(filterstr, str(elem.elem)):
|
||||
if self._is_hidden(elem.label):
|
||||
if (filterstr is None or
|
||||
str(elems.elem).lower().startswith(filterstr)):
|
||||
if self._is_hidden(elems.label):
|
||||
# hidden element which matches again -> show it
|
||||
self._show_elem(elem.label)
|
||||
self._show_elem(elems.label)
|
||||
else:
|
||||
# element doesn't match anymore -> hide it
|
||||
self._hide_elem(elem.label)
|
||||
self._hide_elem(elems.label)
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
|
||||
if config.get('hints', 'mode') == 'number':
|
||||
visible = self._filter_number_hints()
|
||||
else:
|
||||
visible = self._filter_non_number_hints()
|
||||
|
||||
if (len(visible) == 1 and
|
||||
config.get('hints', 'auto-follow') and
|
||||
filterstr is not None):
|
||||
# apply auto-follow-timeout
|
||||
timeout = config.get('hints', 'auto-follow-timeout')
|
||||
keyparsers = objreg.get('keyparsers', scope='window',
|
||||
window=self._win_id)
|
||||
normal_parser = keyparsers[usertypes.KeyMode.normal]
|
||||
normal_parser.set_inhibited_timeout(timeout)
|
||||
visible = {}
|
||||
for k, e in self._context.elems.items():
|
||||
try:
|
||||
if not self._is_hidden(e.label):
|
||||
visible[k] = e
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
if not visible:
|
||||
# Whoops, filtered all hints
|
||||
modeman.leave(self._win_id, usertypes.KeyMode.hint, 'all filtered')
|
||||
elif len(visible) == 1 and config.get('hints', 'auto-follow'):
|
||||
# unpacking gets us the first (and only) key in the dict.
|
||||
self.fire(*visible)
|
||||
|
||||
@@ -998,9 +873,7 @@ class HintManager(QObject):
|
||||
# Handlers which take a QWebElement
|
||||
elem_handlers = {
|
||||
Target.normal: self._click,
|
||||
Target.current: self._click,
|
||||
Target.tab: self._click,
|
||||
Target.tab_fg: self._click,
|
||||
Target.tab_bg: self._click,
|
||||
Target.window: self._click,
|
||||
Target.hover: self._click,
|
||||
@@ -1018,61 +891,50 @@ class HintManager(QObject):
|
||||
}
|
||||
elem = self._context.elems[keystr].elem
|
||||
if elem.webFrame() is None:
|
||||
message.error(self._win_id,
|
||||
"This element has no webframe.",
|
||||
message.error(self._win_id, "This element has no webframe.",
|
||||
immediately=True)
|
||||
return
|
||||
if self._context.target in elem_handlers:
|
||||
handler = functools.partial(elem_handlers[self._context.target],
|
||||
elem, self._context)
|
||||
handler = functools.partial(
|
||||
elem_handlers[self._context.target], elem, self._context)
|
||||
elif self._context.target in url_handlers:
|
||||
url = self._resolve_url(elem, self._context.baseurl)
|
||||
if url is None:
|
||||
self._show_url_error()
|
||||
return
|
||||
handler = functools.partial(url_handlers[self._context.target],
|
||||
url, self._context)
|
||||
handler = functools.partial(
|
||||
url_handlers[self._context.target], url, self._context)
|
||||
else:
|
||||
raise ValueError("No suitable handler found!")
|
||||
if not self._context.rapid:
|
||||
modeman.maybe_leave(self._win_id, usertypes.KeyMode.hint,
|
||||
'followed')
|
||||
else:
|
||||
# Reset filtering
|
||||
# Show all hints again
|
||||
self.filter_hints(None)
|
||||
# Undo keystring highlighting
|
||||
for string, elem in self._context.elems.items():
|
||||
elem.label.setInnerXml(string)
|
||||
for (string, elems) in self._context.elems.items():
|
||||
elems.label.setInnerXml(string)
|
||||
handler()
|
||||
|
||||
@cmdutils.register(instance='hintmanager', scope='tab', hide=True,
|
||||
modes=[usertypes.KeyMode.hint])
|
||||
def follow_hint(self, keystring=None):
|
||||
"""Follow a hint.
|
||||
|
||||
Args:
|
||||
keystring: The hint to follow, or None.
|
||||
"""
|
||||
if keystring is None:
|
||||
if self._context.to_follow is None:
|
||||
raise cmdexc.CommandError("No hint to follow")
|
||||
else:
|
||||
keystring = self._context.to_follow
|
||||
elif keystring not in self._context.elems:
|
||||
raise cmdexc.CommandError("No hint {}!".format(keystring))
|
||||
self.fire(keystring, force=True)
|
||||
@cmdutils.register(instance='hintmanager', scope='tab', hide=True)
|
||||
def follow_hint(self):
|
||||
"""Follow the currently selected hint."""
|
||||
if not self._context.to_follow:
|
||||
raise cmdexc.CommandError("No hint to follow")
|
||||
self.fire(self._context.to_follow, force=True)
|
||||
|
||||
@pyqtSlot('QSize')
|
||||
def on_contents_size_changed(self, _size):
|
||||
"""Reposition hints if contents size changed."""
|
||||
log.hints.debug("Contents size changed...!")
|
||||
for e in self._context.all_elems:
|
||||
for elems in self._context.elems.values():
|
||||
try:
|
||||
if e.elem.webFrame() is None:
|
||||
if elems.elem.webFrame() is None:
|
||||
# This sometimes happens for some reason...
|
||||
e.label.removeFromDocument()
|
||||
elems.label.removeFromDocument()
|
||||
continue
|
||||
self._set_style_position(e.elem, e.label)
|
||||
self._set_style_position(elems.elem, elems.label)
|
||||
except webelem.IsNullError:
|
||||
pass
|
||||
|
||||
@@ -1085,117 +947,3 @@ class HintManager(QObject):
|
||||
# hinting.
|
||||
return
|
||||
self._cleanup()
|
||||
|
||||
|
||||
class WordHinter:
|
||||
|
||||
"""Generator for word hints.
|
||||
|
||||
Attributes:
|
||||
words: A set of words to be used when no "smart hint" can be
|
||||
derived from the hinted element.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# will be initialized on first use.
|
||||
self.words = set()
|
||||
self.dictionary = None
|
||||
|
||||
def ensure_initialized(self):
|
||||
"""Generate the used words if yet uninitialized."""
|
||||
dictionary = config.get("hints", "dictionary")
|
||||
if not self.words or self.dictionary != dictionary:
|
||||
self.words.clear()
|
||||
self.dictionary = dictionary
|
||||
try:
|
||||
with open(dictionary, encoding="UTF-8") as wordfile:
|
||||
alphabet = set(ascii_lowercase)
|
||||
hints = set()
|
||||
lines = (line.rstrip().lower() for line in wordfile)
|
||||
for word in lines:
|
||||
if set(word) - alphabet:
|
||||
# contains none-alphabetic chars
|
||||
continue
|
||||
if len(word) > 4:
|
||||
# we don't need words longer than 4
|
||||
continue
|
||||
for i in range(len(word)):
|
||||
# remove all prefixes of this word
|
||||
hints.discard(word[:i + 1])
|
||||
hints.add(word)
|
||||
self.words.update(hints)
|
||||
except IOError as e:
|
||||
error = "Word hints requires reading the file at {}: {}"
|
||||
raise WordHintingError(error.format(dictionary, str(e)))
|
||||
|
||||
def extract_tag_words(self, elem):
|
||||
"""Extract tag words form the given element."""
|
||||
attr_extractors = {
|
||||
"alt": lambda elem: elem["alt"],
|
||||
"name": lambda elem: elem["name"],
|
||||
"title": lambda elem: elem["title"],
|
||||
"src": lambda elem: elem["src"].split('/')[-1],
|
||||
"href": lambda elem: elem["href"].split('/')[-1],
|
||||
"text": str,
|
||||
}
|
||||
|
||||
extractable_attrs = collections.defaultdict(list, {
|
||||
"IMG": ["alt", "title", "src"],
|
||||
"A": ["title", "href", "text"],
|
||||
"INPUT": ["name"]
|
||||
})
|
||||
|
||||
return (attr_extractors[attr](elem)
|
||||
for attr in extractable_attrs[elem.tagName()]
|
||||
if attr in elem or attr == "text")
|
||||
|
||||
def tag_words_to_hints(self, words):
|
||||
"""Take words and transform them to proper hints if possible."""
|
||||
for candidate in words:
|
||||
if not candidate:
|
||||
continue
|
||||
match = re.search('[A-Za-z]{3,}', candidate)
|
||||
if not match:
|
||||
continue
|
||||
if 4 < match.end() - match.start() < 8:
|
||||
yield candidate[match.start():match.end()].lower()
|
||||
|
||||
def any_prefix(self, hint, existing):
|
||||
return any(hint.startswith(e) or e.startswith(hint) for e in existing)
|
||||
|
||||
def filter_prefixes(self, hints, existing):
|
||||
return (h for h in hints if not self.any_prefix(h, existing))
|
||||
|
||||
def new_hint_for(self, elem, existing, fallback):
|
||||
"""Return a hint for elem, not conflicting with the existing."""
|
||||
new = self.tag_words_to_hints(self.extract_tag_words(elem))
|
||||
new_no_prefixes = self.filter_prefixes(new, existing)
|
||||
fallback_no_prefixes = self.filter_prefixes(fallback, existing)
|
||||
# either the first good, or None
|
||||
return (next(new_no_prefixes, None) or
|
||||
next(fallback_no_prefixes, None))
|
||||
|
||||
def hint(self, elems):
|
||||
"""Produce hint labels based on the html tags.
|
||||
|
||||
Produce hint words based on the link text and random words
|
||||
from the words arg as fallback.
|
||||
|
||||
Args:
|
||||
words: Words to use as fallback when no link text can be used.
|
||||
elems: The elements to get hint strings for.
|
||||
|
||||
Return:
|
||||
A list of hint strings, in the same order as the elements.
|
||||
"""
|
||||
self.ensure_initialized()
|
||||
hints = []
|
||||
used_hints = set()
|
||||
words = iter(self.words)
|
||||
for elem in elems:
|
||||
hint = self.new_hint_for(elem, used_hints, words)
|
||||
if not hint:
|
||||
raise WordHintingError("Not enough words in the dictionary.")
|
||||
used_hints.add(hint)
|
||||
hints.append(hint)
|
||||
return hints
|
||||
|
||||