Showing preview only (816K chars total). Download the full file or copy to clipboard to get everything.
Repository: parkouss/webmacs
Branch: master
Commit: c42d89fcc41e
Files: 113
Total size: 777.4 KB
Directory structure:
gitextract_3q3a7_u6/
├── .flake8
├── .gitattributes
├── .gitignore
├── .gitmodules
├── .travis.yml
├── CHANGELOG.md
├── COPYING
├── README-nix.org
├── README.org
├── c/
│ └── adblock.c
├── docs/
│ ├── Makefile
│ ├── advanced_topics.rst
│ ├── api.rst
│ ├── basic_usage.rst
│ ├── concepts.rst
│ ├── conf.py
│ ├── ext/
│ │ └── webmacs_sphinx_ext.py
│ ├── faq.rst
│ ├── glossary.rst
│ ├── index.rst
│ ├── make.bat
│ └── user_configuration.rst
├── git_archive_all.py
├── pytest.ini
├── setup.py
├── test-requirements.txt
├── tests/
│ ├── integration/
│ │ ├── conftest.py
│ │ ├── iframe_follow/
│ │ │ ├── index.html
│ │ │ └── my_iframe.html
│ │ ├── javascript_prompt/
│ │ │ └── index.html
│ │ ├── navigation/
│ │ │ ├── index.html
│ │ │ └── page1.html
│ │ ├── test_copy_link.py
│ │ ├── test_iframe_navigation.py
│ │ ├── test_javascript_prompt.py
│ │ ├── test_navigation.py
│ │ └── test_user_download_dir.py
│ ├── test_prompt_history.py
│ └── test_variables.py
└── webmacs/
├── __init__.py
├── adblock.py
├── application.py
├── bookmarks.py
├── clipboard.py
├── commands/
│ ├── __init__.py
│ ├── buffer_history.py
│ ├── caret_browsing.py
│ ├── content_edit.py
│ ├── follow.py
│ ├── global.py
│ ├── isearch.py
│ ├── minibuffer.py
│ ├── webbuffer.py
│ └── webjump.py
├── content_handler.py
├── default_webjumps.py
├── download_manager/
│ ├── __init__.py
│ └── prompts.py
├── egrid.py
├── external_editor.py
├── features.py
├── filter_webengine_output.py
├── hooks.py
├── ignore_certificates.py
├── ipc.py
├── keyboardhandler.py
├── keymaps/
│ ├── __init__.py
│ ├── caret_browsing.py
│ ├── content_edit.py
│ ├── fullscreen.py
│ ├── global.py
│ ├── hints.py
│ ├── isearch.py
│ ├── minibuffer.py
│ └── webbuffer.py
├── killed_buffers.py
├── main.py
├── minibuffer/
│ ├── __init__.py
│ ├── prompt.py
│ └── right_label.py
├── mode.py
├── password_manager/
│ ├── __init__.py
│ └── password_store.py
├── profile.py
├── scheme_handlers/
│ ├── __init__.py
│ └── webmacs/
│ ├── __init__.py
│ ├── js/
│ │ └── vue.js
│ └── templates/
│ ├── base.html
│ ├── bindings.html
│ ├── command.html
│ ├── commands.html
│ ├── downloads.html
│ ├── key.html
│ ├── keymap.html
│ ├── variable.html
│ ├── variables.html
│ └── version.html
├── scripts/
│ ├── caret_browsing.js
│ ├── hint.js
│ ├── password_manager.js
│ ├── setup.js
│ ├── textedit.js
│ └── textzoom.js
├── session.py
├── spell_checking.py
├── task.py
├── url_opener.py
├── variables.py
├── version.py
├── visited_links.py
├── webbuffer.py
├── webview.py
└── window.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .flake8
================================================
[flake8]
exclude = ./vendor/,
./build/,
./dist/,
./webmacs.egg-info/,
# exclude virtual envs
./venv*,
================================================
FILE: .gitattributes
================================================
**/.gitignore export-ignore
**/.gitmodules export-ignore
**/.travis.yml export-ignore
vendor/*/test/** export-ignore
vendor/*/vendor/depot_tools/** export-ignore
vendor/*/.npmignore export-ignore
git_archive_all.py export-ignore
================================================
FILE: .gitignore
================================================
__pycache__/
venv/
================================================
FILE: .gitmodules
================================================
[submodule "vendor/hashset-cpp"]
path = vendor/hashset-cpp
url = https://github.com/bbondy/hashset-cpp
[submodule "vendor/bloom-filter-cpp"]
path = vendor/bloom-filter-cpp
url = https://github.com/bbondy/bloom-filter-cpp
[submodule "vendor/ad-block"]
path = vendor/ad-block
url = https://github.com/brave/ad-block
================================================
FILE: .travis.yml
================================================
sudo: true
language: python
dist: xenial
python: 3.7
matrix:
include:
- os: linux
env: PYQT_VERSION=5.7.1
- os: linux
env: PYQT_VERSION=5.8.2
- os: linux
env: PYQT_VERSION=5.9.2
- os: linux
env: PYQT_VERSION=5.10.1
- os: linux
env: PYQT_VERSION=5.11.2 GENERATE_GIT_ARCHIVE=true
fast_finish: true
cache:
directories:
- $HOME/.cache/pip
install:
- pip install PyQt5==$PYQT_VERSION sphinx
- pip install -r test-requirements.txt
- sudo apt-get --yes install xvfb herbstluftwm
script:
- flake8
- pytest
- cd docs && READTHEDOCS=1 make html && cd ..
before_deploy:
- python git_archive_all.py webmacs-${TRAVIS_TAG}.tar.gz
deploy:
provider: releases
api_key:
secure: MZSDuOdwUlHn55GfMy4MwkT9ZgDhd5/09gKTH2futOWtF90oIhbIO2a1ZfH8QXPB2AZtISzPFgPgMgFtdCIBkmv7ic5yD0AC+hfb6F8iWsxVaUCBQh6B7g4vUomoAEkr7+pUzjapOuJFSlaIWajSL43WcZb7Ep6gdy8M1bBnj8HsyBug5g5uV0GEFmk55Jwlg1EsXF9Jx23EstEBW1HC/wdywa3vkYZsaCzKSco+L1XU2lVtbrRBuY8SbYyvadeFPkyu315+cFNT+HkdD5yvFjRNFoaSYF4AMskv8LR7cCQ6Z7Hug1huFBNgr+LPVFxfflb+zaEETimLV5BN52pB2udv+RDJu0Xmc2hGcCb3eRrHC/w7DkxXWuldF/g8b2aCmoCUDrca1Dh+ipeXzWKo5yEFgrP4dp8l/q5ClbpH3Qc/o6IUhFvVS7YH8udO7A7bAHcZisDGuSfasQeHSDq28maU0pSH3sB0zcTvcphIzkDUjT7+9peXhn56MWUMKlBRX2JU9eoUCz1dBuxK/XCuRZ0YPHjV3xrDvEIbMBny3kdKHCTkc7rcZZs1QfZgnMmySLbM6uUXpmwxBhH5eIEqdZ5isQn/oCt+G3L99Lp7/sNr+gXJtNuocEzWcryEIeJ2Vi/hL9+Mu/I4TlgyLv6f1RS74Ndp4cPpgCCwGS2yVwY=
file: webmacs-${TRAVIS_TAG}.tar.gz
skip_cleanup: true
on:
tags: true
repo: parkouss/webmacs
branch: master
# limit to only one element in the matrix
condition: $GENERATE_GIT_ARCHIVE = true
================================================
FILE: CHANGELOG.md
================================================
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
## [Unreleased]
### Fixed
- Cookies and persistent data is now really saved on disk, and used on restart.
- Fixed a possible security issue of the webmacs.ipc file that was readable and
writable by others.
- adblock url fetching and parsing is now more verbose on error and more
resilient.
- Fixed text selection within iframes.
- Fixed minibuffer line input redo binding.
- Fixed exiting webmacs as fast as possible by rewriting long-running tasks
asynchronously.
### Added
- Added passwordstore support for storing passwords (the linux 'pass' command
line tool)
- Added **content-edit-select-all** in content edit mode, bound to **C-x h**.
- Added **minibuffer-select-all** in minibuffer keymap, bound to **C-x h**.
- Added bindings currently attached to commands when using **M-x** command.
- Added support for an off-the-record (private) mode. It is enabled by starting
webmacs using **--off-the-record** flag, or using the command
**open-off-the-record**.
### Changed
- Moved codebase to PyQt6. PyQt5 is no longer supported.
- Removed support for an internal database to store passwords.
- Moved path to the spell checking data (to ~/.webmacs/spell_checking/)
## [0.8] - 2019-09-15
### Fixed
- autocompletion for duckduckgo now uses duckduckgo servers
- Fixed displaying long lines in the minibuffer popup (such as long urls in
history)
- Fixed regression, unable to revive a buffer.
- webmacs command line now handle correctly opening relative local file paths.
- Fixed scrolling top or bottom when switching view, and losing focus sometimes.
- Use one session file per webmacs instance.
### Added
- Added **where-is** command, bound to **C-h w**, to look up what keys a command
is bound to, if any.
- Added **describe-key-briefly** command, bound to **C-h c**, as a less verbose
alternative to **describe-key**.
- Added **print-buffer** command, bound to **C-x p** to print the current
buffer.
- Improved customization of key bindings for incremental search, hinting and
minibuffer
- Added key bindings **C-/** and **C-?** (resp. undo and redo) as minibuffer
input key bindings
- Added a **clipboard-copy** variable to be able to copy to primary clipboard
(still the default), mouse selection clipboard or both.
- Added a **--list-instances** command line flag, allowing to list current
running instances.
- Passing an empty string to the **--instance** command line flag will generate
a new unique instance name.
- Added a **raise-instance** command, to raise the current window of another
webmacs instance.
- Added a **current-instance** command that show the name of the current
instance name.
- Added **C-u C-u** prefix argument for command opening urls. Using it will open
the chosen url in a new window.
- Added the variable **visited-links-display-limit**, to limit the number of
elements displayed in the **visited-links-history** command. Defaults to 2000.
- Added **--profile** command line option to allow using more than one profile.
## Changed
- **C-h c** is now bound to **describe-key-briefly** instead of
**describe-command** which has in turn been moved to **C-h f**, in accordance
with Emacs default keybindings.
## [0.7] - 2018-09-20
### Fixed
- Fix refreshing buffer count in the minibuffer right label when a buffer is
closed.
- Fix reviving buffers that were not loaded (after restoring a session).
- Fix hinting urls in iframes when opening in a new buffer.
- Fixed support for Qt 5.7
- Fixed requiring escaping of raw % signs in custom webjumps.
- Fixed zoom-normal was bound to **0** instead of **=**.
### Added
- Added the **switch-buffer-current-color** variable that customize the color of
the current buffer row in the **switch-buffer** displayed list. The color
defaults to a light blue. Set to an empty string to get the old behavior (no
specific color).
- **switch-buffer** now list buffers using the internal buffer order.
- Buffers now have numbers and the current buffer number is displayed in the
minibuffer right label as well as in the **switch-buffer** and
**switch-recent-buffer** lists.
- **M-n** and **M-p** are bound respectively to the new commands **next-buffer**
and **previous-buffer**, allowing to cycle through buffers.
- **M-<**, **M->**, **C-v**, **M-v** bindings in the minibuffer lists for
navigation.
- New commands and bindings added to copy stuff to clipboard.
**copy-current-link** bound to **c c**, **copy-current-buffer-title** bound to
**c t**, and copy-current-buffer-url bound to **c u** (all in the webbuffer
keymap)
- Added a new method for hinting: alphabet. This allow to navigate only using
the home row keys, and without using Enter. Can be enabled by setting the
new variable **hint-method** to "alphabet".
- Added a new variable **hint-alphabet-characters** to specify which characters
to use with the alphabet hinting.
- Added a new variable **hint-node-style** to change the style of the hint div
nodes.
- Added a **close-buffer-close-window** variable to be able to close a window
when a buffer is closed.
- **C-g** in webbuffer is now bound to **buffer-escape** instead of
**buffer-unselect**, which does the same thing and send the Escape too (which
closes popup and other things).
- Added two variables, **default-download-dir** and
**keep-temporary-download-dir**.
### Changed
- The old **switch-buffer** behavior is now offered with the
**switch-recent-buffer** command. The latter is now bound to **C-x b** and
**C-x C-b** so there is no visible change using those keybindings.
- The **c** (copy-link) binding is now available using **c l** (think about Copy
Link).
- The css style of the hints has been changed. If you prefer the old style, just
set the **hint-node-style** variable to {"background": "red", "color":
"white"}.
- The **go-to** command (bounds to **g**) now set the current url in the
minibuffer input and select it.
- The **go-to-selected-url** and **go-to-selected-url-new-buffer** commands are
renamed to **go-to-alternate-url** and **go-to-alternate-url-new-buffer**.
Also they do not anymore select the current url, but only set the cursor at
the end of the url.
## [0.6] - 2018-08-20
### Fixed
- crash when opening in a new window (from right-click menu on a link), in qt
5.11.1.
- crash when reviving closed buffers in some cases.
- crash when calling switch-buffer and closing buffer (including the current
one) using C-k.
### Added
- added a basic navigation toolbar, that can be shown using the command
**toggle-toolbar**. Also added a new variable, **window-toolbar-on-startup**
that can be set to True to show the toolbar automatically.
- added a database to keep feature permissions (geolocation, camera, ...) on a
per-url basis (thanks to Patrick Lafrance)
- the allow permission for feature dialog now ask for Always/Never, and save
that in the database. (thanks to Patrick Lafrance)
- it is now possible to answer Never when webmacs ask to save a password.
(thanks to Patrick Lafrance)
- it is now possible to answer Always to bypass certificate errors. (thanks to
Patrick Lafrance)
- when opening a download, there is now a prompt to ask to download or to open
the file with an external command.
### Changed
- the functions **webmacs.keymaps.global_keymap()**,
**webmacs.keymaps.webbuffer_keymap()**,
**webmacs.keymaps.content_edit_keymap()** have been deprecated in favor of
**webmacs.keymaps.keymap()** (respectively with the argument "global",
"webbuffer" and "webcontent-edit").
- loading page information is now displayed in the minibuffer right label, using
the **loading** key in the **minibuffer-right-label** variable (default value
of this variable has changed)
## [0.5] - 2018-07-08
### Fixed
- focus is not lost anymore in the minibuffer input on page loading
- adblock is fully disabled when the variable **adblock-urls-rules** is set to
an empty list.
- adblock cache is rebuilt when the variable **adblock-urls-rules** has changed.
- Fixed the **copy-link** command (**c**) when used with the argument 0.
- Added a space after the default webjump when calling **search-default**.
- Mouse events are now propagated to the minibuffer input and popup.
- Fixed a bug that prevented to use multi-modifiers keybindings (e.g., C-M-a)
- Fixed regression in **close-other-buffers** command.
- The keyboard is not anymore lost when a new buffer is opened from javascript.
- The **follow** command is now working in cross-origin iframes.
- Text edition in web pages is now working in cross-origin iframes.
- Text zoom in web pages is now working in cross-origin iframes.
- Caret navigation is now working in cross-origin iframes.
### Changed
- **scroll-page-down**, **scroll-page-up**, **scroll-bottom** and
**scroll-top** are now implemented by sending the PageDown, PageUp and End
and Home key presses.
- **search-default** now defaults to google.
### Added
- The minibuffer input now flashes under some circumstances to grab user's
attention.
- Added **minibuffer-flash-duration**, **minibuffer-flash-color**, and
**minibuffer-flash-count** variables to customize the flash animation.
## [0.4] - 2018-05-04
### Fixed
- Fixed closing buffer in some circumstances using C-k from the
switch-buffer command.
- Improved position of the minibuffer popup, removing empty pixels
between the popup and the input.
- Fixed using i-search when caret browsing is enabled
- improve using multiple views, fixing a lot of bugs around that (keyboard
focus lost, crash using switch-buffer on an already displayed buffer, ...)
### Changed
- **breaking change**. The completion\_fn argument in define\_webjump has
changed, see the documentation about that.
- improved webjump completions in multiple ways.
- switching buffers now tries its best to keep the current scroll and cursor
position, so that coming back to a previous buffer feels more natural. This
is in part implemented by keeping one internal qt webengineview per buffer.
- improved the visibility of the current view when there are multiple
views. There is now a border on each side of the view, with one pixel red and
one black.
### Added
- added a **revive-buffer** command, bound to **C-x r** in the global keymap.
This allow to reopen a previously closed buffer.
- added the **revive-buffers-limit** variable, to specify how many buffers might
be revived. This defaults to 10.
- added basic handling of web features to enable video, audio from javascript.
- added two variables to customize how webmacs starts: **home-page** and
**home-page-in-new-window**.
- added a command to restore previous session (windows and buffers),
**restore-session**.
- Under X11, if the --instance is passed at the command line, the
WM_CLASS property is set to "webmacs-{instance}".
- added basic support for multiple windows. New commands added: **make-window**,
**other-window**, **close-window**, **close-other-window**.
- saving and restoring web views in session
- saving and restoring window position and state in session
- added a variable **webview-stylesheet** to customize the above view style.
- added a command **buffer-unselect** to clear selection in the current buffer.
- bound **buffer-unselect** to **C-g** in the webbuffer keymap.
## [0.3]
### Added
- keymaps can now have a name and associated documentation
- added command **describe-commands** to list all named webmacs commands
- added command **describe-bindings** to list named commands in named keymaps
- added command **describe-variables** to list named webmacs variables
- added command **downloads** to open a buffer to see downloads of the session
- added command **version** to open a version buffer.
- added command **describe-variable** to describe a variable (bound to C-h v)
- added command **describe-command** to describe a command (bound to C-h c)
- added command **describe-key** to describe a keychord (bound to C-h k)
- added python dependency *pygments* to render source code.
- added a command line flag **--instance** to run named instances of webmacs
### Fixed
- If webmacs has crashed, the local socket used for ipc is now cleaned, so other
commands for this webmacs instance are forwarded and does not anymore create a
new instance.
- do not set the webbuffer active keymap as the current local keymap if the
minibuffer input is currently opened.
## [0.2]
The distinction between 0.1 and 0.2 version is not clear unfortunately - patches
were just going in the main git branch. To highlight some features, let's start
the version 0.2 from the commit cb0cea39eaab6a01ee74ca16261c2b467b4af5a3.
### Added
- buffers loading from last session are delayed until they are displayed
- added caret browsing support, with a specific keymap and its set of commands.
The **C** binding in a web buffer enter the caret browsing mode
- added new variable **adblock-urls-rules** to list rules url for the ad-blocker
- added new variable **webjump-default**
- better prompt completion
- added information in the minibuffer
- added new variable **minibuffer-right-label** for the format of the displayed
information in the minibuffer
- support for bookmarks (see **bookmark-add** and **bookmark-open** commands,
bound to respectively **M** and **m** in the webbuffer keymap).
- support for zoom and text zoom. See the **zoom-\*** and **text-zoom-\***
commands.
- added undefine_key method on Keymaps to unbind keys
- added command **close-other-buffers**
- added basic notion of mode to a web buffer, normal usage being "standard-mode"
and a new mode "no-keybindings"
- added new variable **auto-buffer-modes** to set up rules for settings web
buffer mode based on urls
- added new command **content-edit-open-external-editor** to open a text editor
to edit web content, bound to **Cx e** and **C-x C-e** in the webcontent-edit
keymap. The external command to run is stored in the variable
**external-editor-command**.
- added **content-edit-undo** and **content-edit-redo** commands, bound
respectively to **C-/** and **C-?** in webcontent-edit keymap.
- added spell check support, configurable with the
**spell-checking-dictionaries** variable.
### Fixed
- fixed segfault with some graphic cards
- retrieving ad-block rules and compiling them is now done in a thread, so
webmacs is not slow at startup anymore
- added a warning when using opengl with the nouveau driver.
- default qt shortcuts in webviews are removed, so webmacs bindings are working
without side-effect anymore (e.g., C-a was sometimes selecting the text)
- changed implementation of the webcontent-edit movement text commands, so now
undo redo works better and it also mostly works in contenteditable fields
================================================
FILE: COPYING
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program 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.
This program 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 this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
<program> Copyright (C) <year> <name of author>
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.
================================================
FILE: README-nix.org
================================================
* Installation with Nix
There are two recommended ways of installing webmacs:
1. Nix
2. pip/virtualenv
** Nix
Currently, the easiest way to install webmacs is via the [[https://nixos.org/nix/][Nix package
manager]]:
#+BEGIN_SRC bash
nix-env -i webmacs
#+END_SRC
*** Need more help with nix?
Nix is available for Linux, macOS and other Unix-like systems. Rest
assured that removing Nix (along with any packages installed using
Nix) is as easy as =rm /nix -rf=.
If you do not have Nix, install it. For details see
https://nixos.org/nix/manual/#chap-installation, but this step
approximates to
#+BEGIN_SRC bash
bash <(curl https://nixos.org/nix/install)
#+END_SRC
and will require you to provide a sudo password.
Look out for, and follow the instructions which will appear once
nix is installed, and which will look something like this:
#+BEGIN_SRC text
Installation finished! To ensure that the necessary environment
variables are set, either log in again, or type
. /home/yourusername/.nix-profile/etc/profile.d/nix.sh
in your shell.
#+END_SRC
If you don't spot this, the installation will appear to have failed.
Now you can use =nix-env= to install webmacs:
#+BEGIN_SRC bash
nix-env -i webmacs
#+END_SRC
For further details, see
https://nixos.org/nix/manual/#chap-quick-start.
*** working on webmacs with nix
The command
#+BEGIN_SRC bash
nix-shell -p webmacs
#+END_SRC
will drop you into a shell which makes available all the compilers and
libraries required to build and run webmacs, thus =nix-shell= plays
the role of =virtualenv= in the pip/virtualenv approach described
below. Unfortunately, some of the libraries required to run the
tests, are not yet available in this shell.
================================================
FILE: README.org
================================================
* webmacs
*webmacs* is yet another browser for keyboard-based web navigation.
It mainly target emacs-like navigation, and started as a clone (in terms of
features) of [[http://conkeror.org/][conkeror]].
See the documentation manual: https://webmacs.readthedocs.io/en/latest/
webmacs is based on qt webengine and written mainly in Python (version 3).
#+html: <p align="center"><img src="webmacs-screenshot.png" /></p>
* Features
Short list of features:
- keyboard navigation everywhere (including basic emacs movements in editable
web content)
- Integrated, fast ad-blocker
- [[https://webmacs.readthedocs.io/en/latest/basic_usage.html#live-documentation][live documentation]]
- [[https://webmacs.readthedocs.io/en/latest/user_configuration.html][highly customizable using Python]]
* Installation (... and development)
** Using Nix
See the [[./.README-nix.org][dedicated page]].
** Using pip/virtualenv
Be prepared to have a working c and c++ compiler with python development
library. Note I only have tested on linux.
You will also need the PyQt6 library, as I believe it can't be installed through
pip. It's easy to install using any package manager though.
Then you have to check out the repository (do not forget the *recursive* flag):
#+BEGIN_SRC bash
git clone --recursive https://github.com/parkouss/webmacs
#+END_SRC
To test it, or work on it, I recommend virtualenv:
#+BEGIN_SRC bash
virtualenv --system-site-packages -p python3 venv
# activate the virtualenv
source venv/bin/activate
# install webmacs in there
pip install -e <path_to_webmacs_sources>
# and now to run webmacs
python -m webmacs.main
#+END_SRC
Then you can create a system alias to run it:
#+BEGIN_SRC bash
sudo ln -s <path_to_venv>/bin/webmacs /usr/local/bin/webmacs
# now you can use the webmacs command on your system, given that
# /usr/local/bin is in your PATH.
#+END_SRC
* Running tests
To run the tests, you will need a few more dependencies (the virtualenv needs
to be activated):
#+BEGIN_SRC bash
# install test dependencies
pip install -r <path_to_webmacs_sources>/test-requirements.txt
# also install the herbstluftwm window manager, using your package manager.
# Example on fedora:
sudo dnf install herbstluftwm
#+END_SRC
Then you can run the tests (the virtualenv needs to be activated):
#+BEGIN_SRC bash
python -m pytest <path_to_webmacs_sources>/tests
# you can run them with the windows visible:
python -m pytest <path_to_webmacs_sources>/tests --no-xvfb
#+END_SRC
* Qt versions support
Every stable Qt version from (and including) 6.0 should work with webmacs.
* Contributions
Contributions are much welcome! Writing this browser is exciting and I love
that, though I don't have many time to spend on it, having a family life and a
job; And anyway the more we are to work on it and use the tool, the better!
================================================
FILE: c/adblock.c
================================================
// This file is part of webmacs.
//
// webmacs 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.
//
// webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
#include <Python.h>
#include "structmember.h"
#include <iostream>
#include <fstream>
#include "ad_block_client.h"
using namespace std;
typedef struct {
PyObject_HEAD
AdBlockClient * client;
char * data;
} AdBlock;
static void
AdBlock_dealloc(AdBlock* self)
{
delete self->client;
if (self->data) delete[] self->data;
Py_TYPE(self)->tp_free((PyObject*)self);
}
static PyObject *
AdBlock_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
AdBlock *self;
self = (AdBlock *)type->tp_alloc(type, 0);
return (PyObject *)self;
}
static int
AdBlock_init(AdBlock *self, PyObject *args, PyObject *kwds)
{
self->client = new AdBlockClient;
self->data = NULL;
return 0;
}
static PyObject *
AdBlock_parse(AdBlock* self, PyObject *args)
{
const char *data;
if (!PyArg_ParseTuple(args, "s", &data))
return NULL;
Py_BEGIN_ALLOW_THREADS
self->client->parse(data);
Py_END_ALLOW_THREADS
Py_RETURN_NONE;
}
static PyObject *
AdBlock_matches(AdBlock* self, PyObject *args)
{
const char *url, *domain;
bool result;
if (!PyArg_ParseTuple(args, "ss", &url, &domain))
return NULL;
/* I suspect that allowing threads here does create deadlocks,
but anyway I am not sure it would be useful to allow them. */
/* Py_BEGIN_ALLOW_THREADS */
result = self->client->matches(url, FONoFilterOption, domain);
/* Py_END_ALLOW_THREADS */
if (result) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
static PyObject *
AdBlock_save(AdBlock* self, PyObject *args)
{
const char *path;
if (!PyArg_ParseTuple(args, "s", &path))
return NULL;
int size;
ofstream outFile(path, ios::out | ios::binary);
if (!outFile) {
Py_RETURN_FALSE;
}
Py_BEGIN_ALLOW_THREADS
char * buffer = self->client->serialize(&size);
outFile.write(buffer, size);
outFile.close();
Py_END_ALLOW_THREADS
Py_RETURN_TRUE;
}
static PyObject *
AdBlock_load(AdBlock* self, PyObject *args)
{
const char *path;
bool result = false;
if (!PyArg_ParseTuple(args, "s", &path))
return NULL;
ifstream file(path, ios::binary | ios::ate);
if (!file) {
Py_RETURN_FALSE;
}
Py_BEGIN_ALLOW_THREADS
streamsize size = file.tellg();
file.seekg(0, ios::beg);
if (self->data) {delete[] self->data;}
self->data = new char[size];
if (file.read(self->data, size)) {
self->client->deserialize(self->data);
result = true;
}
Py_END_ALLOW_THREADS
if (result) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
static PyMethodDef AdBlock_methods[] = {
{"parse", (PyCFunction)AdBlock_parse, METH_VARARGS,
"Parse adblock data string, like the content of an easylist."
},
{"matches", (PyCFunction)AdBlock_matches, METH_VARARGS,
"matches an url, returns True if it should be filtered."
},
{"save", (PyCFunction)AdBlock_save, METH_VARARGS,
"Save serialized data into a file."
},
{"load", (PyCFunction)AdBlock_load, METH_VARARGS,
"Load serialized data from a file."
},
{NULL} /* Sentinel */
};
static PyTypeObject AdBlockType = {
PyVarObject_HEAD_INIT(NULL, 0)
"adblock.AdBlock", /* tp_name */
sizeof(AdBlock), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)AdBlock_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
"Adblock objects", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
AdBlock_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)AdBlock_init, /* tp_init */
0, /* tp_alloc */
AdBlock_new, /* tp_new */
};
static PyModuleDef adblockmodule = {
PyModuleDef_HEAD_INIT,
"adblock",
"Module to speed up ad filtering.",
-1,
NULL, NULL, NULL, NULL, NULL
};
PyMODINIT_FUNC
PyInit__adblock(void)
{
PyObject* m;
AdBlockType.tp_new = PyType_GenericNew;
if (PyType_Ready(&AdBlockType) < 0)
return NULL;
m = PyModule_Create(&adblockmodule);
if (m == NULL)
return NULL;
Py_INCREF(&AdBlockType);
PyModule_AddObject(m, "AdBlock", (PyObject *)&AdBlockType);
return m;
}
================================================
FILE: docs/Makefile
================================================
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS = -W
SPHINXBUILD = sphinx-build
SPHINXPROJ = webmacs
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
================================================
FILE: docs/advanced_topics.rst
================================================
Advanced topics
===============
.. current-keymap:: global
.. _managing_views:
Managing views
**************
It is possible to manage multiple :term:`views` in a webmacs window:
- :key:`C-x 2` split the current view in two horizontally.
- :key:`C-x 3` split the current view in two vertically.
- :key:`C-x o` navigate in current window views.
- :key:`C-x 0` close the current view.
- :key:`C-x 1` maximize the current view, closing every other view.
Managing windows
****************
You can also manage multiple windows:
- :cmd:`make-window` to create a new window.
- :cmd:`other-window` to navigate through windows.
- :cmd:`close-window` to close the current window.
- :cmd:`close-other-windows` to close all but the current window.
Caret browsing
**************
.. current-keymap:: caret-browsing
Caret browsing allows to navigate in a web page using a caret. It is mainly
useful for copying text inside a web page without using the mouse.
It is enabled by pressing :key:`C (webbuffer)`.
Then you can navigate using arrow keys or standard Emacs bindings:
- :key:`C-n` to go to the next line.
- :key:`C-p` to go to the previous line.
- :key:`C-f` to go to the next character.
- :key:`C-b` to go to the previous character.
- :key:`M-f` to go to the next word.
- :key:`M-b` to go to the previous word.
- :key:`C-e` to go to the end of the line.
- :key:`C-a` to go to the beginning of the line.
You can select some text and copy it using:
- :key:`C-Space` to toggle the mark
- :key:`M-w` to copy the current selection to the clipboard.
.. current-keymap:: webbuffer
.. note::
Incremental search can be used when in caret browsing, to allow easier
navigation.
It is also great to start caret browsing after an incremental search, as the
caret will be at the beginning of the current web selection.
Bookmarks
*********
Bookmarks are like a dictionary of URLs. Each bookmark must have a unique name.
Bookmarks are stored in the profile, and hence are persistent across sessions.
It is possible to manage bookmarks using:
- :key:`M` to create a bookmark.
- :key:`m` to open the bookmark list.
When in the bookmark list, you can:
- :key:`Return (bookmarks-list)` to open the bookmark URL in the current buffer
- :key:`C-k (bookmarks-list)` to remove the highlighted bookmark.
================================================
FILE: docs/api.rst
================================================
Public api
==========
Initialisation
**************
.. autofunction:: webmacs.main.init
Keymaps
*******
.. autofunction:: webmacs.keymaps.keymap
.. autoclass:: webmacs.keymaps.Keymap
:members: define_key, undefine_key
Webjumps
********
.. autofunction:: webmacs.commands.webjump.define_webjump
.. autoclass:: webmacs.commands.webjump.WebJumpCompleter
:members:
.. autoclass:: webmacs.commands.webjump.WebJumpRequestCompleter
.. autoclass:: webmacs.commands.webjump.SyncWebJumpCompleter
.. autofunction:: webmacs.commands.webjump.define_webjump_alias
Variables
*********
.. autofunction:: webmacs.variables.set
.. autofunction:: webmacs.variables.get
.. autoexception:: webmacs.variables.VariableConditionError
================================================
FILE: docs/basic_usage.rst
================================================
Basic usage
===========
Don't panic
***********
When you are stuck in some interactive :term:`command` or text field, and you
are unsure what to do, press **C-g**. This :term:`key binding` usually lets you
out of the current action - you may have to press it more than once. **C-g** is
the universal *get me out of there* command.
.. note::
Usually, pressing **C-g** enough times lets you focus on the current
:term:`web buffer`, and so activates the :keymap:`webbuffer` :term:`keymap`.
.. current-keymap:: global
Running a command using its name
********************************
It is always possible to run a :term:`command` using its name. Some commands
does not have default :term:`key binding`, and requires to be called this
way. To call a command using its name, use the :key:`M-x` keybinding, then
select in the list (or type) the command you want to run, followed by **Return**
(the Enter key).
For example, :key:`M-x` toggle-toolbar <Return> will toggle the webmac's
toolbar.
Live documentation
******************
webmacs is self-documenting. You can easily access the documentation by running
the following commands:
- :cmd:`describe-commands` to see all available commands.
- :cmd:`describe-command` (bound to :key:`C-h f`) to choose one command, and get
a detailed description.
- :cmd:`describe-variables` to see all the available :term:`variables`.
- :cmd:`describe-variable` (bound to :key:`C-h v`) to choose one variable and
get a detailed description.
- :cmd:`describe-key` (bound to :key:`C-h k`) or :cmd:`describe-key-briefly` (bound to :key:`C-h c`)
to discover what command a key binding would trigger.
- :cmd:`where-is` (bound to: :key:`C-h w`) to quickly find what key(s) a command is bound to.
- :cmd:`describe-bindings` to see the list of all keymaps, with the bindings
and commands they contain.
.. note::
Self-documentation is super useful for many things. If you want, for example,
to define a custom binding for a command, but don't know its name, you can
always use :key:`C-h k` to help you.
Also, do not hesitate to use :key:`C-h v` to see the description of a
:term:`variable`.
.. current-keymap:: webbuffer
Visiting urls
*************
An easy way to go to a new URL is to type :key:`g`. This calls the :cmd:`go-to`
command, that lets you type a URL or a :term:`webjump`. Pressing **Return**
will then open it in the current web buffer.
For example, try typing: **g g<tab> webmacs <Return>**. This should open a new
Google page with the query 'webmacs'.
.. important::
Typing **C-u** before :key:`g` will open the url or webjump in a new buffer.
.. _link_hinting:
Link hinting
************
Link hinting is used to navigate through visible links of the current web
buffer's page, using the keyboard only.
Press :key:`f`. You should see the :term:`minibuffer` right label displaying
that you are in the :keymap:`hint` keymap, and the links on the page
highlighted.
.. current-keymap:: hint
Hinting in webmacs can be done using two methods: filter (the default) and
alphabet. You can use the :term:`variable` :var:`hint-method` to change it.
filter
------
There is one active hint. Typing text will narrow down the hint selection by
fuzzy matching against the link's texts. It is also possible to directly type
the number of the link to activate it, and to cycle the visible hints (next,
previous) to change the active hint.
Keybindings are as follows:
- :key:`C-n` activate next visible hint
- :key:`C-p` activate previous visible hint
Note that to validate hinting, :key:`Return` has to be pressed.
alphabet
--------
This is the method used by default in vimium, for example. There is no active
hint, and each link is associated with some characters: they must all be entered
to validate hinting.
Note that the hinting characters are usually randomly picked up from the home
row of the keyboard. This behavior is configured with the :term:`variable`
:var:`hint-alphabet-characters`, defaulting to the home row characters of a
QWERTY keyboard.
.. current-keymap:: webbuffer
.. _managing_buffers:
Managing buffers
****************
You can switch to a buffer using :key:`C-x b (global)`, which opens a list on
top of the :term:`minibuffer`. Select the buffer you want to switch to by
fuzzy-matching text of the url or title page, or just use the arrow keys (or
better, standard Emacs bindings such as **C-n**, **C-p**, **C-v**, **M-v**,
etc). Finally, validate with **Return**.
.. important::
Most of the lists displayed in the :term:`minibuffer` work in this same way,
and have the same basic bindings.
The command is called :cmd:`switch-recent-buffer`.
.. note::
The above command orders the buffers so that the most recently used is on top.
If you want the buffers to be ordeded by their number, you can call the
command :cmd:`switch-buffer`.
You can also navigate to the next or previous buffer by using respectively
:key:`M-n (global)` and :key:`M-p (global)`.
A buffer can be closed by just pressing :key:`q`. When you are running
:cmd:`switch-buffer` or :cmd:`switch-recent-buffer`, pressing :key:`C-k
(buffer-list)` will also kill the buffer currently highlighted in the list.
.. important::
If you killed a buffer by accident, no worries! Just use :key:`C-x r (global)`
to resurrect it.
Navigating through buffer history
*********************************
- :key:`B` goes backward in the buffer history
- :key:`F` goes forward in the buffer history
- :key:`b` shows the current buffer's history as a list in the
:term:`minibuffer`, and allows to easily navigate it.
Navigating through global history
*********************************
Type :key:`h` to display a list of every visited URL (these are saved in a
database file and are persistent in your profile). Select one to open it in the
current buffer.
.. note::
Use **C-u** before :key:`h` to open the URL in a new buffer.
Scrolling in current web buffer
*******************************
- :key:`C-n` or :key:`n` scrolls the current buffer down a bit.
- :key:`C-p` or :key:`p` scrolls the current buffer up a bit.
- :key:`C-b` scrolls the current buffer left a bit.
- :key:`C-f` scrolls the current buffer right a bit.
- :key:`C-v` scrolls the current buffer down for one visible page.
- :key:`M-v` scrolls the current buffer up for one visible page.
- :key:`M-<` lets you go to the top of the page.
- :key:`M->` lets you go to the bottom of the page.
Searching in current web buffer
*******************************
Type :key:`C-s` to start incremental search. Then you can type the text you are
looking for. Press :key:`C-s` again to go to the next match, or :key:`C-r` to go
to the previous match.
.. note::
:key:`C-r` can also be used to start incremental search.
Copying links
*************
- :key:`c u` to copy the URL of the current buffer.
- :key:`c l` to copy a visible link in the buffer (by :term:`hinting`).
- :key:`c c` to copy the currently selected link.
- :key:`c t` to copy the current buffer page title.
Downloading
***********
A download can be started by clicking a link or button or :term:`hinting`.
When a download is about to be started, the :term:`minibuffer` will propose to
either **download** or **open** it.
- **download** will start downloading, and save the file to your hard drive.
- **open** will download to a temporary directory, then open the file with the given
command. A list of available commands is shown in the minibuffer completion
list. Note that when the command exits, the file will be automatically deleted
from your hard drive.
.. note::
open is useful for viewing PDF files for example, as you can use your
favorite PDF file viewer to read it.
The list of downloads can be accessed using the :cmd:`downloads` command.
.. seealso::
See the :var:`default-download-dir` and :var:`keep-temporary-download-dir`
variables.
Zooming
*******
- :key:`+` zoom in.
- :key:`-` zoom out.
- :key:`=` reset the zoom to its default value.
.. note::
There are variants for the zoom, using the Control modifier (:key:`C-+`,
:key:`C--`, and :key:`C-=`) that are used for text zoom only.
Printing
********
- :key:`C-x p` to print the current buffer.
================================================
FILE: docs/concepts.rst
================================================
Concepts
========
Please make sure to understand the following basic webmacs concepts before
further reading the documentation.
.. _concept_commands:
Commands, key bindings and keymaps
**********************************
These are quite similar to the definitions found in the Emacs manual.
- A :term:`command` is a named action which can be done in the browser. For
example, :cmd:`follow` is the command that allows to start hinting links to
navigate.
- A :term:`key binding` is a combination of key presses used to trigger commands.
Key bindings are represented as in Emacs, for example **C-x C-b** means
"holding the Control key while pressing x, then b on the keyboard."
.. note::
The control key is called a modifier. There are three keyboard modifiers:
- **C** represents the Control key.
- **M** represents the Alt key.
- **S** represents the Super key (often called the Windows key)
.. note::
.. current-keymap:: webbuffer
A key binding can also be a single key press. For example, pressing :key:`f`
while in the :keymap:`webbuffer` keymap will trigger the :cmd:`follow`
command.
- A :term:`keymap` is an object holding a mapping between key bindings and
commands, so that a command can be triggered by pressing keyboard keys.
Usually, there is one global keymap, and one active local keymap activated
at the same time - the local keymap changes interactively depending on the
context.
Some important keymaps:
.. webmacs-keymaps::
:only: global, webbuffer, webcontent-edit, caret-browsing
Web buffers
***********
A :term:`web buffer` is like an Emacs buffer, but applying to a Web page.
Buffers are like tabs in other browsers, except that they are not bound to
any view or window.
Windows, views
**************
Differing from Emacs terminology, a window actually is what we nowadays call a
window, and :term:`views` (sometimes called frames) correspond to the content
of a window.
================================================
FILE: docs/conf.py
================================================
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# webmacs documentation build configuration file, created by
# sphinx-quickstart on Sat Dec 23 08:47:03 2017.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
import os
import sys
sys.path.insert(0, os.path.join(os.path.abspath("."), "ext"))
sys.path.insert(0, os.path.abspath('..'))
# Generating doc don't work without that flag. That could be fixed but anyway
# on readthedocs we can't install binary package so we are stuck for now trying
# to mock everything.
if True or "READTHEDOCS" in os.environ:
# We can not install webmacs on readthedocs, as it requires to
# buid some C extensions (from dateparser, PyQt6, ...). The
# alternative is to mock any dependency used by webmacs.
class Mock(object):
def __init__(self, *a, **kw):
pass
def __getattr__(self, name): # noqa: E301
return Mock()
def __call__(self, *a, **kw): # noqa: E301
return Mock()
def __iter__(self): # noqa: E301
return iter(())
def __instancecheck__(self, instance): # noqa: E301
return True
def __subclasscheck__(self, cls): # noqa: E301
return True
def __mro_entries__(self, a): # noqa: E301
return ()
MOCK_MODULES = ["PyQt6", "PyQt6.QtCore", "PyQt6.QtGui",
"PyQt6.QtWidgets", "PyQt6.QtWebEngineWidgets",
"PyQt6.QtWebEngineCore", "PyQt6.QtWebChannel",
"PyQt6.QtNetwork", "PyQt6.QtPrintSupport",
"_adblock", "dateparser"]
sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES)
# the version number is not important, though it must be an int.
sys.modules["PyQt6.QtCore"].QT_VERSION \
= sys.modules["PyQt6.QtCore"].PYQT_VERSION = 330497
import webmacs # noqa: E402
# -- General configuration ------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = ['sphinx.ext.autodoc',
'sphinx.ext.viewcode', "webmacs_sphinx_ext"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = 'webmacs'
copyright = '2017, Julien Pagès'
author = 'Julien Pagès'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = webmacs.__version__
# The full version, including alpha/beta/rc tags.
release = version
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "en"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = False
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
'github_user': 'parkouss',
'github_repo': project,
"github_button": True,
'description': "An emacs-like keyboard-driven web browser",
# defaults is 940, gives a bit more so viewcode looks good.
'page_width': "1050px",
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = {
'**': [
'about.html',
'navigation.html',
'relations.html', # needs 'show_related': True theme option to display
'searchbox.html',
]
}
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'webmacsdoc'
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'webmacs.tex', 'webmacs Documentation',
'Julien Pagès', 'manual'),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'webmacs', 'webmacs Documentation',
[author], 1)
]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'webmacs', 'webmacs Documentation',
author, 'webmacs', 'One line description of project.',
'Miscellaneous'),
]
================================================
FILE: docs/ext/webmacs_sphinx_ext.py
================================================
import re
from docutils.parsers.rst import Directive
from docutils.statemachine import ViewList
from docutils import nodes
from webmacs import COMMANDS
from webmacs.commands import InteractiveCommand
from webmacs.commands.webjump import WEBJUMPS
from webmacs.application import _app_requires
from webmacs.variables import VARIABLES
from webmacs.mode import MODES
from webmacs.keymaps import KEYMAPS
# to include all commands, etc.
_app_requires()
def as_rest_table(data):
numcolumns = len(data[0])
colsizes = [max(len(r[i]) for r in data) for i in range(numcolumns)]
formatter = ' '.join('{:<%d}' % c for c in colsizes)
rowsformatted = [formatter.format(*row) for row in data]
header = formatter.format(*['=' * c for c in colsizes])
yield header
yield rowsformatted[0]
yield header
for row in rowsformatted[1:]:
yield row
yield header
class SimpleAutoDirective(Directive):
has_content = False
required_arguments = 0
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self):
self._result = ViewList()
self._run()
node = nodes.paragraph()
node.document = self.state.document
self.state.nested_parse(self._result, 0, node)
return node.children
class WebmacsCommands(SimpleAutoDirective):
def _run(self):
result = self._result
def get_doc(cmd):
if isinstance(cmd, InteractiveCommand):
cmd = cmd.binding
return cmd.__doc__ or "No description"
table = [("Command", "description")]
for name in sorted(COMMANDS):
table.append((name, get_doc(COMMANDS[name])))
for line in as_rest_table(table):
result.append(line, "")
class WebmacsWebjumps(SimpleAutoDirective):
def _run(self):
result = self._result
table = [("Name", "url", "description")]
for name in sorted(WEBJUMPS):
webjump = WEBJUMPS[name]
table.append((name, webjump.url, webjump.doc))
for line in as_rest_table(table):
result.append(line, "")
class WebmacsVariables(SimpleAutoDirective):
def _run(self):
result = self._result
table = [("Name", "description", "default")]
for name in sorted(VARIABLES):
variable = VARIABLES[name]
table.append((name, variable.doc, repr(variable.value)))
for line in as_rest_table(table):
result.append(line, "")
class WebmacsModes(SimpleAutoDirective):
def _run(self):
result = self._result
table = [("Name", "Description")]
for name in sorted(MODES):
mode = MODES[name]
table.append((name, mode.description))
for line in as_rest_table(table):
result.append(line, "")
class WebmacsKeymaps(SimpleAutoDirective):
option_spec = {
"only": lambda a: (a or "").replace(" ", "").split(",")
}
def _run(self):
result = self._result
keys = self.options.get("only") or sorted(KEYMAPS)
table = [("Name", "Description")]
for name in keys:
km = KEYMAPS[name]
table.append((name, km.doc or ""))
for line in as_rest_table(table):
result.append(line, "")
def webmacs_role(data):
"""
Create a simple role function handler that check for the text to be in the
given data, and just create a strong node for the it.
"""
def role(name, rawtext, text, lineno, inliner, options={}, content=[]):
if text not in data:
inliner.reporter.error("No such %s: %s" % (name, text))
node = nodes.strong(text=text)
return [node], []
return role
class CurrentKeymapDirective(Directive):
has_content = False
required_arguments = 1
optional_arguments = 0
final_argument_whitespace = False
option_spec = {}
def run(self):
env = self.state.document.settings.env
keymap = self.arguments[0].strip()
if keymap not in KEYMAPS:
self.state_machine.reporter.error("No such keymap: %s" % keymap)
env.ref_context['webmacs:keymap'] = keymap
return []
KEYMAPS_BINDINGS_CACHE = {}
RE_KEY_MAP = re.compile(r"^(.*)\s+\(([\w-]+)\)$")
def get_keymap_bindings(keymap_name):
if keymap_name not in KEYMAPS_BINDINGS_CACHE:
KEYMAPS_BINDINGS_CACHE[keymap_name] \
= {k: v for k, v in KEYMAPS[keymap_name].all_bindings(raw_fn=True)}
return KEYMAPS_BINDINGS_CACHE[keymap_name]
def key_in_keymap_role(name, rawtext, text, lineno, inliner, options={},
content=[]):
m = RE_KEY_MAP.match(text)
if m:
text = m.group(1)
km = m.group(2)
else:
env = inliner.document.settings.env
try:
km = env.ref_context["webmacs:keymap"]
except KeyError:
inliner.reporter.error(
"no current keymap. Use the current-keymap directive."
)
keys = get_keymap_bindings(km)
if text not in keys:
inliner.reporter.error("No such key: %s in keymap %s" % (text, km))
node = nodes.strong(text=text)
return [node], []
def setup(app):
app.add_directive("webmacs-commands", WebmacsCommands)
app.add_directive("webmacs-webjumps", WebmacsWebjumps)
app.add_directive("webmacs-variables", WebmacsVariables)
app.add_directive("webmacs-modes", WebmacsModes)
app.add_directive("webmacs-keymaps", WebmacsKeymaps)
app.add_directive("current-keymap", CurrentKeymapDirective)
# use them to ensure doc is not outdated, making references to things that
# do not exists.
app.add_role("cmd", webmacs_role(COMMANDS))
app.add_role("var", webmacs_role(VARIABLES))
app.add_role("keymap", webmacs_role(KEYMAPS))
app.add_role("key", key_in_keymap_role)
================================================
FILE: docs/faq.rst
================================================
FAQ
===
How do I run a new webmacs instance instead of a new buffer from the command-line?
**********************************************************************************
When a webmacs instance is already running, calling `webmacs <url>` from a
shell will open the URL in a new buffer of the running instance. To run a fresh
new instance, use `webmacs --instance <instance-unique-name> <url>`.
How do I run webmacs with a specific profile from the command-line?
**********************************************************************************
To have webmacs use a specific profile, use
`webmacs --profile <profile-name> <url>`. Each profile directory will contain
distinct navigation data (history, cookies, ...).
Website is blocked, turn off the extensions
*******************************************
This message will appear in the browser when the URL has got filtered by the
ad-blocker.
To overcome this, you can temporarily disable the ad-blocker with *M-x toggle-ad-block*.
For a permanent change, edit the :var:`adblock-urls-rules` variable, to remove
some URLs in there. Note if you set this variable to an empty list, the
adblocker will be completely disabled. See the :ref:`user_conf_variables`
section in the documentation.
================================================
FILE: docs/glossary.rst
================================================
Glossary
========
.. glossary::
buffer
web buffer
The content of a web page, not including its window or view.
You can learn the basics of buffer handling in :ref:`managing_buffers`.
command
commands
A command is a named action doable in the browser.
See :ref:`concept_commands` for a detailed description.
See :ref:`Commands <user_conf_commands>` for a list of commands; or better,
use the :cmd:`describe-commands` command to get live documentation.
hinting
Hinting is used to navigate through the visible links and objects of the
current web buffer's page, using the keyboard only.
See :ref:`link_hinting` for more information.
key binding
key bindings
A **key binding** is a combination of key presses used to trigger commands.
See :ref:`concept_commands` for a detailed description, and
:ref:`user_conf_binding_keys` for custom configuration of key bindings.
keymap
keymaps
A **keymap** is an object holding a mapping between key bindings and
commands.
See :ref:`concept_commands` for a detailed description.
See :ref:`Keymaps <user_conf_keymaps>` for a list of keymaps; or better, use
the :cmd:`describe-bindings` command to get live documentation.
minibuffer
The minibuffer is what can be seen at the bottom of a webmacs window. It
displays some information on the right, such as the currently active keymap
and the number of open buffers.
minibuffer input
When webmacs is waiting for some information from you, the
**minibuffer input** is shown: it's a text edit field in which you can type
some text.
Often, there also is a completion list above the minibuffer input.
variable
variables
Some behaviors of *webmacs* can be customized using variables.
See :ref:`user_conf_variables` for variables configuration.
See :ref:`All variables <user_conf_all_variables>` to see all the variables;
or better, use :cmd:`describe-variables` to get live documentation.
view
views
A view is a part of a window displaying a buffer. There can be multiple
views in one window.
See :ref:`managing_views`.
webjump
webjumps
A Webjump represents a quick way to access a URL, possibly with a variable
part. A webjump name becomes a part of the webmacs :cmd:`go-to` command, so
for example you can type ``google foo bar`` to execute a Google query with
"foo bar" terms.
See :ref:`user_conf_webjumps` to see the builtins webjumps and how to
configure your owns.
================================================
FILE: docs/index.rst
================================================
.. webmacs documentation master file, created by
sphinx-quickstart on Sat Dec 23 08:47:03 2017.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Welcome to webmacs's documentation!
===================================
**webmacs** is yet another browser for keyboard-based web navigation.
Code is hosted on a `github repository <https://github.com/parkouss/webmacs>`_.
.. toctree::
:maxdepth: 2
:caption: Contents:
concepts
basic_usage
advanced_topics
user_configuration
api
faq
glossary
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
================================================
FILE: docs/make.bat
================================================
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
set SPHINXPROJ=webmacs
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd
================================================
FILE: docs/user_configuration.rst
================================================
User configuration
==================
**webmacs** can be configured by writing Python code. The files should live in
a ``~/.webmacs/init`` directory, starting with an ``__init__.py`` file.
If this file exists, it will be loaded early in the application.
Note that you have the full power of Python in there, and as it is loaded early,
you can change and adapt nearly every aspect of the webmacs behavior.
.. note::
Only the documented functions and objects here are considered stable (meaning
they will not change without change notes and explanations). Any other API
you can use is considered internal and might change without notification.
The init function
*****************
You can write a function **init** that would be called when webmacs is about to
start. This function must take one parameter (usually named *opts*), that
contains the result of the parsed command line. For now, there is only one
useful parameter:
- opts.url: the url given in the command line, or None
Overriding the init function does override the default init function of webmacs,
though it is still accessible with :func:`webmacs.main.init`. Hello world
example:
.. code-block:: python
import webmacs.main
def init(opts):
print("hello wordl")
if opts.url:
print("{} was given as a command line argument".format(opts.url))
webmacs.main.init(opts)
The default webmacs.main.init function is responsible for restoring the session,
or opening the given URL, for example.
.. note::
It is not required to create an init function in the user configuration
_\_init_\_.py file. Only do so if you want to change the default webmacs
initialization. Other changes can be applied early, directly at the module
level, such as defining :term:`webjumps` or **binding keys**.
Using more than one configuration file
**************************************
It is possible to write more than one configuration file. The
directory where the ``__init__.py`` file lives is a Python package, so
it is possible to just use relative imports.
For example:
``__init__.py``
.. code-block:: python
from . import webjumps
``webjumps.py``
.. code-block:: python
print("definition of my custom webjumps should go there.")
.. _user_conf_variables:
Variables
*********
It is possible to change a variable in the configuration using
:func:`webmacs.variables.set`:
.. code-block:: python
from webmacs import variables
variables.set("webjump-default", "google")
.. _user_conf_all_variables:
Here is the list of the variables:
.. webmacs-variables::
Modes
*****
Modes are used to bind keymaps to a web buffer, by assigning the buffer a given
mode. By default, all buffers use the "standard-mode".
Here is the list of the pre-defined modes:
.. webmacs-modes::
Automatically assign modes depending on the url
-----------------------------------------------
You can use the `auto-buffer-modes` variable.
Example:
.. code-block:: python
from webmacs import variables
variables.set("auto-buffer-modes", [
(".*www.gnu.org.*", "no-keybindings"),
("https://mail.google.com/.*", "no-keybindings")
])
Binding keys
************
In webmacs, like in Emacs, it is possible to bind a key to a command on a given
keymap.
.. _user_conf_keymaps:
Keymaps
-------
Here is the list of available keymaps. Note that you can see them live (with
their associated key bindings) in webmacs by running the command
`describe-bindings`.
.. webmacs-keymaps::
A keymap object in user configuration is retrieved with
:func:`webmacs.keymaps.keymap`.
.. _user_conf_commands:
Commands
--------
Here is the list of the currently available commands:
.. webmacs-commands::
.. _user_conf_binding_keys:
Binding a command to a keymap
-----------------------------
You should use :meth:`webmacs.keymaps.Keymap.define_key`. Here is an example:
.. code-block:: python
from webmacs import keymaps
global_map = keymaps.keymap("global")
global_map.define_key("C-c |", "split-view-right")
global_map.define_key("C-c _", "split-view-bottom")
buffer_keymap = keymaps.keymap("webbuffer")
buffer_keymap.define_key("x", "close-buffer")
.. note::
The global buffer should not define single letter keychords, as you
won't be able to type that letter in editable fields; though, this is
possible in the webbuffer :term:`keymap`.
.. _user_conf_webjumps:
Webjumps
********
Here is the implementation of the google :term:`webjump`:
.. literalinclude:: ../webmacs/default_webjumps.py
:start-after: # ----------- doc example
:end-before: # ----------- end of doc example
The list of defined webjumps in webmacs:
.. webmacs-webjumps::
You can implement your own webjumps, or override the existing
ones. See :func:`webmacs.commands.webjump.define_webjump` and the
example above.
Pressing the ``s`` key will call the command
``search-default``, wich will, by default, use the Google webjump. To change
this default, change the value of the variable *webjump-default*.
It is also possible to define an alias to an existing webjump,
without duplicating its implementation.
.. code-block:: python
from webmacs.commands.webjump import define_webjump_alias
define_webjump_alias("g", "google")
================================================
FILE: git_archive_all.py
================================================
# Script to generate a git archive with submodules
# From https://github.com/Kentzo/git-archive-all
from os import extsep, path, readlink
from shlex import quote
from subprocess import CalledProcessError, Popen, PIPE
from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED
import re
import sys
import tarfile
__version__ = "1.18.1"
class GitArchiver(object):
"""
GitArchiver
Scan a git repository and export all tracked files, and submodules.
Checks for .gitattributes files in each directory and uses 'export-ignore'
pattern entries for ignore files in the archive.
>>> archiver = GitArchiver(main_repo_abspath='my/repo/path')
>>> archiver.create('output.zip')
"""
def __init__(self, prefix='', exclude=True, force_sub=False, extra=None,
main_repo_abspath=None):
"""
@param prefix: Prefix used to prepend all paths in the resulting
archive. Extra file paths are only prefixed if they are not
relative. E.g. if prefix is 'foo' and extra is ['bar', '/baz'] the
resulting archive will look like this:
/
baz
foo/
bar
@type prefix: str
@param exclude: Determines whether archiver should follow rules
specified in .gitattributes files.
@type exclude: bool
@param force_sub: Determines whether submodules are initialized and
updated before archiving. @type force_sub: bool
@param extra: List of extra paths to include in the resulting archive.
@type extra: list
@param main_repo_abspath: Absolute path to the main repository (or one
of subdirectories). If given path is path to a subdirectory (but
not a submodule directory!) it will be replaced with abspath to
top-level directory of the repository. If None, current cwd is
used.
@type main_repo_abspath: str
"""
if extra is None:
extra = []
if main_repo_abspath is None:
main_repo_abspath = path.abspath('')
elif not path.isabs(main_repo_abspath):
raise ValueError("main_repo_abspath must be an absolute path")
try:
main_repo_abspath = path.abspath(
self.run_git_shell('git rev-parse --show-toplevel',
main_repo_abspath).rstrip())
except CalledProcessError:
raise ValueError("{0} is not part of a git repository"
.format(main_repo_abspath))
self.prefix = prefix
self.exclude = exclude
self.extra = extra
self.force_sub = force_sub
self.main_repo_abspath = main_repo_abspath
def create(self, output_path, dry_run=False, output_format=None):
"""
Create the archive at output_file_path.
Type of the archive is determined either by extension of
output_file_path or by output_format. Supported formats are: gz, zip,
bz2, xz, tar, tgz, txz
@param output_path: Output file path.
@type output_path: str
@param dry_run: Determines whether create should do nothing but print
what it would archive.
@type dry_run: bool
@param output_format: Determines format of the output archive. If None,
format is determined from extension of output_file_path.
@type output_format: str
"""
if output_format is None:
file_name, file_ext = path.splitext(output_path)
output_format = file_ext[len(extsep):].lower()
if not dry_run:
if output_format == 'zip':
archive = ZipFile(path.abspath(output_path), 'w')
def add_file(file_path, arcname):
if not path.islink(file_path):
archive.write(file_path, arcname, ZIP_DEFLATED)
else:
i = ZipInfo(arcname)
i.create_system = 3
i.external_attr = 0xA1ED0000
archive.writestr(i, readlink(file_path))
elif output_format in ['tar', 'bz2', 'gz', 'xz', 'tgz', 'txz']:
if output_format == 'tar':
t_mode = 'w'
elif output_format == 'tgz':
t_mode = 'w:gz'
elif output_format == 'txz':
t_mode = 'w:xz'
else:
t_mode = 'w:{0}'.format(output_format)
archive = tarfile.open(path.abspath(output_path), t_mode)
def add_file(file_path, arcname):
archive.add(file_path, arcname)
else:
raise RuntimeError("unknown format: {0}".format(output_format))
def archiver(file_path, arcname):
add_file(file_path, arcname)
else:
archive = None
def archiver(file_path, arcname):
print("{0} => {1}".format(file_path, arcname))
self.archive_all_files(archiver)
if archive is not None:
archive.close()
def is_file_excluded(self, file_path):
"""
Checks whether file at a given path is excluded.
"""
out = self.run_git_shell(
'git check-attr -z export-ignore -- %s' % quote(file_path),
cwd=self.main_repo_abspath
).split('\0')
try:
return out[2] == 'set'
except IndexError:
return False
def archive_all_files(self, archiver):
"""
Archive all files using archiver.
@param archiver: Callable that accepts 2 arguments:
abspath to file on the system and relative path within archive.
@type archiver: Callable
"""
for file_path in self.extra:
archiver(path.abspath(file_path),
path.join(self.prefix, file_path))
for file_path in self.walk_git_files():
archiver(path.join(self.main_repo_abspath, file_path),
path.join(self.prefix, file_path))
def walk_git_files(self, repo_path=''):
"""
An iterator method that yields a file path relative to
main_repo_abspath for each file that should be included in the archive.
Skips those that match the exclusion patterns found in any discovered
.gitattributes files along the way.
Recurs into submodules as well.
@param repo_path: Path to the git submodule repository relative to
main_repo_abspath.
@type repo_path: str
@return: Iterator to traverse files under git control relative to
main_repo_abspath.
@rtype: Iterable
"""
repo_abspath = path.join(self.main_repo_abspath, repo_path)
repo_file_paths = self.run_git_shell(
'git ls-files -z --cached --full-name --no-empty-directory',
repo_abspath
).split('\0')[:-1]
for repo_file_path in repo_file_paths:
# absolute file path
repo_file_abspath = path.join(repo_abspath, repo_file_path)
# file path relative to the main repo
main_repo_file_path = path.join(repo_path, repo_file_path)
# Only list symlinks and files.
if not path.islink(repo_file_abspath) \
and path.isdir(repo_file_abspath):
continue
if self.is_file_excluded(main_repo_file_path):
continue
yield main_repo_file_path
if self.force_sub:
self.run_git_shell('git submodule init', repo_abspath)
self.run_git_shell('git submodule update', repo_abspath)
try:
repo_gitmodules_abspath = path.join(repo_abspath, ".gitmodules")
with open(repo_gitmodules_abspath) as f:
lines = f.readlines()
for l in lines:
m = re.match("^\\s*path\\s*=\\s*(.*)\\s*$", l)
if m:
repo_submodule_path = m.group(1) # relative to repo_path
# relative to main_repo_abspath
gen = self.walk_git_files(
path.join(repo_path, repo_submodule_path))
for main_repo_submodule_fpath in gen:
if self.is_file_excluded(main_repo_submodule_fpath):
continue
yield main_repo_submodule_fpath
except IOError:
pass
@staticmethod
def run_git_shell(cmd, cwd=None):
"""
Runs git shell command, reads output and decodes it into unicode
string.
@param cmd: Command to be executed.
@type cmd: str
@type cwd: str
@param cwd: Working directory.
@rtype: str
@return: Output of the command.
@raise CalledProcessError: Raises exception if return code of the
command is non-zero.
"""
p = Popen(cmd, shell=True, stdout=PIPE, cwd=cwd)
output, _ = p.communicate()
output = output.decode('unicode_escape')\
.encode('raw_unicode_escape').decode('utf-8')
if p.returncode:
if sys.version_info > (2, 6):
raise CalledProcessError(returncode=p.returncode, cmd=cmd,
output=output)
else:
raise CalledProcessError(returncode=p.returncode, cmd=cmd)
return output
def main():
from optparse import OptionParser
parser = OptionParser(
version="%prog {0}".format(__version__)
)
parser.add_option('--prefix',
type='string',
dest='prefix',
default=None,
help="""prepend PREFIX to each filename in the archive.
OUTPUT_FILE name is used by default to avoid tarbomb.
You can set it to '' in order to explicitly request
tarbomb""")
parser.add_option('-v', '--verbose',
action='store_true',
dest='verbose',
help='enable verbose mode')
parser.add_option('--no-exclude',
action='store_false',
dest='exclude',
default=True,
help="don't read .gitattributes files for patterns"
" containing export-ignore attrib")
parser.add_option('--force-submodules',
action='store_true',
dest='force_sub',
help='force a git submodule init && git submodule update'
" at each level before iterating submodules")
parser.add_option('--extra',
action='append',
dest='extra',
default=[],
help="any additional files to include in the archive")
parser.add_option('--dry-run',
action='store_true',
dest='dry_run',
help="don't actually archive anything, just show what"
" would be done")
options, args = parser.parse_args()
if len(args) != 1:
parser.error("You must specify exactly one output file")
output_file_path = args[0]
if path.isdir(output_file_path):
parser.error("You cannot use directory as output")
# avoid tarbomb
if options.prefix is not None:
options.prefix = path.join(options.prefix, '')
else:
import re
output_name = path.basename(output_file_path)
output_name = re.sub(
'(\\.zip|\\.tar|\\.tgz|\\.txz|\\.gz|\\.bz2|\\.xz|\\.tar\\.gz'
'|\\.tar\\.bz2|\\.tar\\.xz)$',
'',
output_name
) or "Archive"
options.prefix = path.join(output_name, '')
try:
archiver = GitArchiver(options.prefix,
options.exclude,
options.force_sub,
options.extra)
archiver.create(output_file_path, options.dry_run)
except Exception as e:
parser.exit(2, "{0}\n".format(e))
sys.exit(0)
if __name__ == '__main__':
main()
================================================
FILE: pytest.ini
================================================
[pytest]
addopts = tests
log_cli = true
log_cli_level = debug
log_format = %(asctime)s %(name)-10s %(levelname)s %(message)s
log_date_format = %Y-%m-%d %H:%M:%S
================================================
FILE: setup.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
import os
import re
import subprocess
from setuptools import setup, Extension, find_packages
from distutils.command.build_py import build_py as _build_py
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
bloom_dir = os.path.join(THIS_DIR, "vendor", "bloom-filter-cpp")
hashset_dir = os.path.join(THIS_DIR, "vendor", "hashset-cpp")
adblock_dir = os.path.join(THIS_DIR, "vendor", "ad-block")
if "CC" not in os.environ:
# force g++, not sure why but else gcc is used and the code does not
# compile...
os.environ["CC"] = "g++"
adblocker = Extension(
'_adblock',
define_macros=[],
language="c++",
include_dirs=[bloom_dir, hashset_dir, adblock_dir],
# not sure if that help for speed. Careful it strip the debug symbols
extra_compile_args=["-g0", "-std=c++11"],
sources=[
os.path.join(bloom_dir, "BloomFilter.cpp"),
os.path.join(bloom_dir, "hashFn.cpp"),
os.path.join(hashset_dir, "hash_set.cc"),
os.path.join(adblock_dir, "ad_block_client.cc"),
os.path.join(adblock_dir, "filter.cc"),
os.path.join(adblock_dir, "cosmetic_filter.cc"),
os.path.join(adblock_dir, "no_fingerprint_domain.cc"),
os.path.join(adblock_dir, "protocol.cc"),
os.path.join(THIS_DIR, "c", "adblock.c"),
])
def get_version():
with open(os.path.join(THIS_DIR, "webmacs", "__init__.py")) as f:
version = re.findall("__version__ = '(.+)'", f.read())
return version[0]
def get_revision():
# ensure we are in a git dir
if not os.path.exists(os.path.join(THIS_DIR, ".git")):
return None
p = subprocess.Popen(
["git", "rev-parse", "HEAD"], cwd=THIS_DIR,
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
)
out, err = p.communicate()
if p.returncode == 0:
return out.strip().decode("utf-8")
class build_py(_build_py):
"""
Override build to generate a revision file to install.
"""
def run(self):
rev = get_revision()
# honor the --dry-run flag
if not self.dry_run and rev:
target_dir = os.path.join(self.build_lib, 'webmacs')
# mkpath is a distutils helper to create directories
self.mkpath(target_dir)
with open(os.path.join(target_dir, 'revision'), 'w') as f:
f.write(rev)
# distutils uses old-style classes, so no super()
_build_py.run(self)
setup(
name='webmacs',
version=get_version(),
description='Keyboard driven web browser, emacs-like',
author='Julien Pagès',
author_email='j.parkouss@gmail.com',
url='https://github.com/parkouss/webmacs',
long_description='''
A browser for keyboard-based web navigation.
Keybindings are emacs-friendly, most of them took from the conkeror
(http://conkeror.org/) project which is not maintained anymore.
It is based on qtwebengine, which in turns uses chromium for the web engine.
Some of the features are:
- integrated ad-blocker
- emacs like navigation nearly everywhere (C-n, C-p, ...)
- hinting to navigate with keyboard only
- and a lot more, see the project url
''',
packages=find_packages(),
install_requires=["dateparser", "jinja2", "pygments"],
entry_points={"console_scripts": ["webmacs = webmacs.main:main"]},
package_data={"webmacs": [
"scripts/*.js",
"scheme_handlers/webmacs/js/*.js",
"scheme_handlers/webmacs/templates/*.html",
]},
cmdclass={'build_py': build_py},
python_requires=">=3.3",
ext_modules=[adblocker],
)
================================================
FILE: test-requirements.txt
================================================
-e .
pytest
pytest-qt
pytest-mock
pytest-xvfb
flake8
================================================
FILE: tests/integration/conftest.py
================================================
import pytest
import os
import time
import subprocess
from PyQt6.QtTest import QTest
from PyQt6.QtCore import QEvent, QTimer
from webmacs.application import Application, _app_requires
from webmacs import (windows, buffers, WINDOWS_HANDLER, current_buffer,
current_window, current_minibuffer)
from webmacs import variables as wvariables
from webmacs.webbuffer import create_buffer
from webmacs.window import Window
from webmacs.webbuffer import close_buffer
from webmacs.keymaps import KeyPress
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
_app = None
def get_test_page(name):
if name.endswith(".html"):
return os.path.join(THIS_DIR, name)
return os.path.join(THIS_DIR, name, "index.html")
@pytest.fixture(scope="session")
def wm(xvfb):
if xvfb is None:
yield None
else:
if not any(
os.access(os.path.join(path, 'herbstluftwm'), os.X_OK)
for path in os.environ["PATH"].split(os.pathsep)
):
raise RuntimeError("herbstluftwm is not installed, can not run"
" graphical tests.")
env = dict(os.environ)
env["DISPLAY"] = str(":%s" % xvfb.display)
proc = subprocess.Popen(
["herbstluftwm"], env=env
)
time.sleep(2) # wait for the wm to be active
yield proc
proc.kill()
proc.wait()
class VariablesWrapper(object):
def __init__(self):
self._original = {}
def set(self, name, value):
if name not in self._original:
self._original[name] = wvariables.get(name)
wvariables.set(name, value)
def get(self, name):
return wvariables.get(name)
def restore(self):
for name, value in self._original.items():
wvariables.set(name, value)
self._original.clear()
@pytest.fixture()
def variables():
vars = VariablesWrapper()
yield vars
vars.restore()
@pytest.fixture(scope='session')
def qapp(wm, qapp_args):
_app_requires()
global _app
# TODO FIXME use another path for tests
conf_path = os.path.join(os.path.expanduser("~"), ".webmacs")
_app = Application(conf_path, ["webmacs"])
return _app
class TestSession(object):
NAV_HIGHLIGHT_COLOR = 'rgb(136, 255, 0)'
def __init__(self, qtbot, qapp, prompt_exec):
self.qtbot = qtbot
self.qapp = qapp
self.prompt_exec = prompt_exec
def set_prompt_exec(self, fn):
self.prompt_exec.side_effect = fn
def waiter(self):
return Waiter(self)
def call_next(self, fn):
QTimer.singleShot(0, fn)
@property
def buffer(self):
return current_buffer()
@property
def window(self):
return current_window()
@property
def minibuffer(self):
return current_minibuffer()
@property
def minibuffer_input(self):
return self.minibuffer.input()
def wait_signal(self, *args, **kwargs):
return self.qtbot.wait_signal(*args, **kwargs)
def wait_until(self, func, wait=2.0, delay=0.01):
delay = int(delay * 1000)
end = time.time() + wait
while not func():
QTest.qWait(delay)
if time.time() > end:
return False
return True
def test_page_url(self, name):
return "file://" + get_test_page(name)
def load_page(self, name, buffer=None, wait_iframes=False):
buffer = buffer or self.buffer
with self.wait_signal(buffer.loadFinished):
buffer.load(self.test_page_url(name))
script = (
"__webmacs_loaded = window.__webmacsHandler__ !== null;"
"if (! __webmacs_loaded) {"
" document.addEventListener('_webmacs_external_created',"
" function() {"
" __webmacs_loaded = true;"
" });"
" }"
)
buffer.runJavaScript(script)
self.check_javascript("__webmacs_loaded", True)
if wait_iframes:
self.wait_iframes(buffer=buffer)
def check_javascript(self, script, return_value, buffer=None):
buffer = buffer or self.buffer
result = [None]
if return_value is None:
raise ValueError("return value can't be None")
def ready():
if result[0] == return_value:
return True
buffer.runJavaScript(script, lambda r: result.__setitem__(0, r))
assert self.wait_until(ready), "javascript result was %r" % result[0]
return True
def wait_hints_ready(self):
return self.qtbot.wait_signal(
self.buffer.content_handler.browserObjectsInited,
timeout=3000,
raising=True
)
def check_nav_highlighted(self, js_elem):
self.check_javascript("%s.style.backgroundColor" % js_elem,
self.NAV_HIGHLIGHT_COLOR)
def wait_iframes(self, buffer=None):
buffer = buffer or self.buffer
script = (
"var result = true;"
"for (var i = 0; i < window.frames.length; i++) { "
" if (window.frames[i].document.readyState != 'complete') {"
" result = false;"
" }"
"}"
"result;"
)
self.check_javascript(script, True, buffer=buffer)
def keyclick(self, key, **kwargs):
QTest.keyClick(self.qapp.focusWindow(), key, **kwargs)
def keyclicks(self, keys, **kwargs):
for key in keys:
self.keyclick(key, **kwargs)
def wkeyclicks(self, shortcut, widget=None):
widget = widget or self.qapp.focusWindow()
keys = [KeyPress.from_str(k) for k in shortcut.split()]
for key in keys:
evt = key.to_qevent(QEvent.Type.KeyPress)
self.keyclick(key.key, modifier=evt.modifiers())
class Waiter(object):
def __init__(self, session):
self.session = session
self.end = False
def set(self):
self.end = True
def wait(self, wait=5, **kwargs):
kwargs["wait"] = wait
return self.session.wait_until(lambda: self.end, **kwargs)
@pytest.yield_fixture()
def session(qtbot, qapp, mocker):
# do not close the application on last window closed
mocker.patch("webmacs.WindowsHandler._on_last_window_closing") \
.return_value = False
prompt_exec = mocker.patch("webmacs.minibuffer.prompt._prompt_exec")
sess = TestSession(qtbot, qapp, prompt_exec)
window = Window()
WINDOWS_HANDLER.current_window = window
window.current_webview().setBuffer(create_buffer())
window.show()
qtbot.waitForWindowShown(window)
yield sess
for w in windows():
w.current_webview().setBuffer(None)
w.close()
w.deleteLater()
for buffer in buffers():
close_buffer(buffer)
qapp.processEvents()
================================================
FILE: tests/integration/iframe_follow/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>iframe testing</title>
</head>
<body>
<input id="input0" type="text" name="fname"><br>
<iframe name="frame0" src="./my_iframe.html" frameborder="0">
</iframe>
<a id="a_top" href="https://foo/top.html">top</a>
</body>
</html>
================================================
FILE: tests/integration/iframe_follow/my_iframe.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>inside iframe</title>
</head>
<body>
<input id="input0" type="text" name="in-iframe"><br>
<a id="a_inside" href="https://foo/inside.html">inside link</a>
</body>
</html>
================================================
FILE: tests/integration/javascript_prompt/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>inside iframe</title>
<script type="text/javascript">
function fillResult(result) {
document.getElementById('result').textContent = result;
}
function getContent(clear) {
var res = document.getElementById('result');
return res.textContent;
}
</script>
</head>
<body>
<div id="result"></div>
<br/>
<button onclick="fillResult(window.confirm('hello there'))">prompt</button>
<button onclick="window.alert('hello there')">alert</button>
</body>
</html>
================================================
FILE: tests/integration/navigation/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>page index</title>
</head>
<body>
</body>
</html>
================================================
FILE: tests/integration/navigation/page1.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>first page</title>
</head>
<body>
</body>
</html>
================================================
FILE: tests/integration/test_copy_link.py
================================================
from webmacs.keyboardhandler import CommandContext
from webmacs.application import app
from webmacs.commands import COMMANDS
def clipboard_contains(text):
return app().clipboard().text() == text
def test_copy_current_link(session):
session.load_page("iframe_follow", wait_iframes=True)
link = "document.getElementById('a_top')"
session.buffer.runJavaScript("%s.focus()" % link)
session.check_javascript("document.activeElement == %s" % link, True)
COMMANDS["copy-current-link"](CommandContext())
assert session.wait_until(
lambda: clipboard_contains("https://foo/top.html")
)
def test_copy_current_link_in_subframe(session):
session.load_page("iframe_follow", wait_iframes=True)
link = "window.frames[0].document.getElementById('a_inside')"
session.buffer.runJavaScript("%s.focus()" % link)
session.check_javascript(
"document.activeElement.tagName",
'IFRAME'
)
session.check_javascript(
"window.frames[0].document.activeElement == %s" % link,
True
)
COMMANDS["copy-current-link"](CommandContext())
assert session.wait_until(
lambda: clipboard_contains("https://foo/inside.html")
)
def test_copy_current_url(session):
session.load_page("iframe_follow")
url = session.buffer.url().toString()
COMMANDS["copy-current-buffer-url"](CommandContext())
assert session.wait_until(
lambda: clipboard_contains(url)
)
def test_copy_current_title(session):
session.load_page("iframe_follow")
COMMANDS["copy-current-buffer-title"](CommandContext())
assert session.wait_until(
lambda: clipboard_contains("iframe testing")
)
================================================
FILE: tests/integration/test_iframe_navigation.py
================================================
import pytest
INPUT0 = "document.getElementById('input0')"
INPUT0_IFRAME = "window.frames[0].document.getElementById('input0')"
def test_iframe_navigation(session):
"""
Webcontent-edit keymaps are used even in sub frames.
"""
session.load_page("iframe_follow", wait_iframes=True)
session.wkeyclicks("Tab")
session.check_javascript("%s === document.activeElement" % INPUT0, True)
# type some text in INPUT0 and move word backward
session.keyclicks("hello world")
session.check_javascript("%s.value" % INPUT0, "hello world")
session.check_javascript("%s.selectionEnd" % INPUT0, 11)
session.wkeyclicks("M-b")
session.check_javascript("%s.selectionEnd" % INPUT0, 6)
session.wkeyclicks("Tab")
session.check_javascript("%s === window.frames[0].document.activeElement"
% INPUT0_IFRAME, True)
# type some text in INPUT0 inside the iframe and move word backward
session.keyclicks("hello world2")
session.check_javascript("%s.value" % INPUT0_IFRAME, "hello world2")
session.check_javascript("%s.selectionEnd" % INPUT0_IFRAME, 12)
session.wkeyclicks("M-b")
session.check_javascript("%s.selectionEnd" % INPUT0_IFRAME, 6)
@pytest.mark.parametrize("hint_method", ["filter", "alphabet"])
def test_iframe_follow(session, pytestconfig, hint_method, variables):
"""
It is possible to hint things inside sub frames.
"""
variables.set("hint-method", hint_method)
session.load_page("iframe_follow", wait_iframes=True)
end_waiter = session.waiter()
def do_check():
session.check_javascript(
"%s === window.frames[0].document.activeElement"
% INPUT0_IFRAME, True)
session.keyclicks("youhou")
session.check_javascript("%s.value" % INPUT0_IFRAME, "youhou")
end_waiter.set()
def prompt_follow(prompt, _):
wait_hint.wait()
if hint_method == "filter":
session.wkeyclicks("C-n")
# wait until the background color is green, the above keypress has
# been taken in account.
session.check_nav_highlighted(INPUT0_IFRAME)
with session.wait_signal(prompt.closed):
session.wkeyclicks("Enter")
else:
with session.wait_signal(prompt.closed):
session.keyclick("d")
session.call_next(do_check)
session.set_prompt_exec(prompt_follow)
wait_hint = session.wait_hints_ready()
session.wkeyclicks("f")
end_waiter.wait()
================================================
FILE: tests/integration/test_javascript_prompt.py
================================================
import pytest
def check_js_result(res):
def check(session):
session.check_javascript("getContent();", res)
return check
def check_minibuffer(res):
def check(session):
assert session.wait_until(
lambda: session.minibuffer.label.text() == res
)
return check
@pytest.mark.parametrize("selection,input,check", [
# first variable is what is typed in follow command,
# to select the right button to click.
("pro", "y", check_js_result("true")),
("pro", "n", check_js_result("false")),
("aler", None, check_minibuffer("[js-alert] hello there")),
])
def test_confirm(session, selection, input, check):
"""
test javascript confirm.
"""
session.load_page("javascript_prompt")
end_waiter = session.waiter()
def prompt_follow(prompt, _):
session.keyclicks(selection)
session.set_prompt_exec(confirm)
with session.wait_signal(prompt.closed):
session.wkeyclicks("Enter")
if not input:
session.call_next(do_check)
def confirm(prompt, _1):
with session.wait_signal(prompt.closed):
session.keyclicks(input)
session.call_next(do_check)
def do_check():
check(session)
end_waiter.set()
session.set_prompt_exec(prompt_follow)
session.wkeyclicks("f")
end_waiter.wait()
================================================
FILE: tests/integration/test_navigation.py
================================================
from webmacs import BUFFERS
from webmacs.webbuffer import create_buffer
def test_cycle_buffers(session):
"""
Webcontent-edit keymaps are used even in sub frames.
"""
session.load_page("navigation")
session.load_page("navigation/page1.html", buffer=create_buffer())
def on_index_page():
return session.buffer.title() == "page index"
def on_first_page():
return session.buffer.title() == "first page"
assert len(BUFFERS) == 2
assert on_index_page()
session.wkeyclicks("M-n")
assert session.wait_until(on_first_page)
session.wkeyclicks("M-n")
assert session.wait_until(on_index_page)
session.wkeyclicks("M-p")
assert session.wait_until(on_first_page)
session.wkeyclicks("M-p")
assert session.wait_until(on_index_page)
================================================
FILE: tests/integration/test_user_download_dir.py
================================================
import pytest
import os
from webmacs import download_manager
# should be more or less in unit test section, but uses the variables fixture.
def test_get_user_download_dir(variables, tmpdir):
# by default, no custom user download dir
assert download_manager.get_user_download_dir() is None
d1 = str(tmpdir.mkdir("d1"))
d2 = str(tmpdir.mkdir("d2"))
download_manager.TEMPORARY_DOWNLOAD_DIR = d2
# if default-download-_dir is set, use it
variables.set("default-download-dir", d1)
assert download_manager.get_user_download_dir() == d1
# if keep-temporary-download-dir is set, it takes precendence
variables.set("keep-temporary-download-dir", True)
assert download_manager.get_user_download_dir() == d2
variables.set("default-download-dir", "")
assert download_manager.get_user_download_dir() == d2
@pytest.mark.parametrize("fname,expected", [
("/path/to", ("/path", "to")),
("/path/to(1)", ("/path", "to")),
("/path/to(3)", ("/path", "to")),
("/path/to.txt", ("/path", "to.txt")),
("/path/to(1).txt", ("/path", "to.txt")),
("/path/to(3).tar.gz", ("/path", "to.tar.gz")),
])
def test_extract_suggested_filename(fname, expected):
assert download_manager.extract_suggested_filename(fname) == expected
@pytest.mark.parametrize("files, filename, expected", [
([], "toto", "toto"),
(["toto"], "toto", "toto(1)"),
([], "toto.txt", "toto.txt"),
(["toto.txt"], "toto.txt", "toto(1).txt"),
(["toto.tar.gz", "toto(1).tar.gz"], "toto.tar.gz", "toto(2).tar.gz"),
])
def test_find_unique_suggested_path(tmpdir, files, filename, expected):
for name in files:
tmpdir.join(name).ensure(file=True)
dir = str(tmpdir)
assert download_manager.find_unique_suggested_path(dir, filename) \
== os.path.join(dir, expected)
================================================
FILE: tests/test_prompt_history.py
================================================
from webmacs.minibuffer.prompt import PromptHistory
def test_history():
p = PromptHistory(maxsize=10)
# calling next or previous on empty history is alright
assert p.get_next() == ""
assert p.get_previous() == ""
assert p.in_user_value() # we are in user value
# insert some values, a bit more than what the buffer can store
for i in range(12):
p.push(str("test_%d" % i))
# playing around with next/previous
assert p.get_previous() == "test_11"
assert p.get_previous() == "test_10"
assert p.get_next() == "test_11"
assert not p.in_user_value()
# we get back in user value when we do an equal amount of next/previous
# calls
assert p.get_next() == ""
assert p.in_user_value()
# test the custom user value
p.set_user_value("foobar")
assert p.get_next() == "test_2"
assert not p.in_user_value()
assert p.get_previous() == "foobar"
assert p.in_user_value()
assert p.get_next() == "test_2"
assert not p.in_user_value()
# resetting put the state back to initial, including the custom user value
p.reset()
assert p.in_user_value()
assert p.get_next() == "test_2"
assert p.get_previous() == ""
================================================
FILE: tests/test_variables.py
================================================
import pytest
from webmacs.variables import (
VariableConditionError, String, Int, Bool, Float, List, Tuple, Dict
)
def check_type_error(type, value, regex):
with pytest.raises(VariableConditionError) as ei:
type.validate(value)
assert ei.match(regex)
def test_type_string():
s = String()
s.validate("")
s.validate("hello")
check_type_error(s, 1, r"Must be a string")
s = String(choices=["aha", "hoho"])
s.validate("aha")
s.validate("hoho")
check_type_error(s, 1, r"Must be a string")
check_type_error(s, "invalid", r"Must be one of \('aha', 'hoho'\)")
def test_type_int():
i = Int()
i.validate(-5)
i.validate(0)
i.validate(5)
check_type_error(i, "1", r"Must be an integer")
i = Int(min=0, max=5)
i.validate(0)
i.validate(1)
i.validate(5)
check_type_error(i, "1", r"Must be an integer")
check_type_error(i, 6, r"Must be lesser or equal to 5")
check_type_error(i, -1, r"Must be greater or equal to 0")
def test_type_bool():
b = Bool()
b.validate(True)
b.validate(False)
def test_type_list():
l = List(Float(min=0.0)) # noqa: E741
l.validate([])
l.validate([1.1, 6.1])
check_type_error(l, 123, "Must be a list")
check_type_error(l, [2.3, "1"], r"List at position 1: Must be a float")
check_type_error(l, [-2.3, 3.2],
r"List at position 0: Must be greater or equal to 0.0")
def test_type_dict():
d = Dict(String(), Tuple(Int(), Float(min=0.0, max=1.1)))
d.validate({})
d.validate({"1": (1, 0.5)})
check_type_error(d, 123, "Must be a dict")
check_type_error(d, {"1": (1,)},
"Value for key '1': Must be a tuple of size 2")
================================================
FILE: webmacs/__init__.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
import importlib
from PyQt6.QtCore import QObject, QEvent, QTimer
from . import hooks
__version__ = '0.9'
# access to every opened buffers
BUFFERS = []
# dictionary of all known commands
COMMANDS = {}
def require(module, package=__package__):
return importlib.import_module(module, package)
# handler for windows, to be able to list them and determine the one currently
# active.
class WindowsHandler(QObject):
def __init__(self, parent=None):
QObject.__init__(self, parent)
self.windows = []
self.current_window = None
def _on_last_window_closing(self):
# last window is closed, do not remove it from the list but exit the
# application. This is required from proper session saving.
from .application import app
app().quit()
return True
def register_window(self, window):
window.installEventFilter(self)
self.windows.append(window)
def eventFilter(self, window, event):
t = event.type()
if t == QEvent.Type.WindowActivate:
self.current_window = window
hooks.window_activated(window)
elif t == QEvent.Type.Close:
if window.quit_if_last_closed and len(self.windows) == 1:
if self._on_last_window_closing():
return True
self.windows.remove(window)
window.deleteLater()
if window == self.current_window:
self.current_window = None
hooks.window_closed(window)
return QObject.eventFilter(self, window, event)
WINDOWS_HANDLER = WindowsHandler()
def windows():
"""
Returns the window list.
Do not modify this list.
"""
return WINDOWS_HANDLER.windows
def current_window():
"""
Returns the currently activated window.
"""
return WINDOWS_HANDLER.current_window
def current_buffer():
"""
Returns the current buffer.
"""
w = current_window()
if w:
return w.current_webview().buffer()
def buffers():
"Returns the list of buffers."
return BUFFERS
def recent_buffers():
"""
Returns an iterable of buffers, most recently used first.
"""
return sorted(BUFFERS, key=lambda b: b.last_use, reverse=True)
def current_minibuffer():
"""
Returns the current minibuffer.
"""
w = current_window()
if w:
return w.minibuffer()
def minibuffer_show_info(text):
"""
Display text information in the current minibuffer.
"""
minibuffer = current_minibuffer()
if minibuffer:
minibuffer.show_info(text)
def call_later(fn, msec=0):
"""
Call the given function after the given time interval.
If msec is 0, the function call is still delayed to the next handling of
events in the qt event loop.
"""
QTimer.singleShot(msec, fn)
class ObjRef(object):
"""
Maintain object references.
"""
__slots__ = ("__refs",)
def __init__(self):
self.__refs = {}
def ref(self, obj, data=True):
self.__refs[obj] = data
def unref(self, obj):
return self.__refs.pop(obj)
# Sometimes we need to keep objects around with pyqt to avoid segfault;
# This global object holder is designed to allow that.
GLOBAL_OBJECTS = ObjRef()
================================================
FILE: webmacs/adblock.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
import os
import logging
import time
import json
from datetime import datetime, timezone
import dateparser
from _adblock import AdBlock
from concurrent.futures import ThreadPoolExecutor, as_completed
import urllib.request
from . import variables
from .task import Task
from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply
from PyQt6.QtCore import QUrl, QThreadPool, pyqtSignal as Signal, Qt
DEFAULT_EASYLIST = [
"https://easylist.to/easylist/easylist.txt",
# easyprivacy blocks too much right now
# "https://easylist.to/easylist/easyprivacy.txt",
"https://easylist.to/easylist/fanboy-annoyance.txt"
]
adblock_urls_rules = variables.define_variable(
"adblock-urls-rules",
"A list of urls to get rules for ad-blocking (using the Adblock format)."
" The default urls are taken from the easylist site https://easylist.to.",
DEFAULT_EASYLIST,
type=variables.List(variables.String()),
)
def cache_file(cache_path):
return os.path.join(cache_path, "cache.dat")
class AdBlockUpdateTask(Task):
adblock_ready = Signal(AdBlock)
def __init__(self, app, cache_path, ):
Task.__init__(self)
self.app = app
if not os.path.isdir(cache_path):
os.makedirs(cache_path)
self._cache_path = cache_path
self._cached_urls_path = os.path.join(self._cache_path, "urls.json")
self._cache_file = cache_file(cache_path)
self._user_urls = {
url: os.path.join(self._cache_path, url.rsplit("/", 1)[-1])
for url in adblock_urls_rules.value
}
self.adblock_ready.connect(self._on_adblock_ready,
Qt.ConnectionType.BlockingQueuedConnection)
self._adblock = None
self._replies = {}
self._modified = False
self.__thread_running = False
def start(self):
to_download = [(url, path) for url, path in self._user_urls.items()
if not os.path.isfile(path)
or (os.path.getmtime(path) + 3600) < time.time()]
for url, path in to_download:
reply = self.app.network_manager.get(QNetworkRequest(QUrl(url)))
reply.readyRead.connect(self._dl_ready_read)
reply.finished.connect(self._dl_finished)
self._replies[reply] = {"path": path}
self._maybe_finish()
def _maybe_finish(self):
if self._replies:
return
if not self._modified:
try:
with open(self._cached_urls_path) as f:
cached_urls = json.load(f)
except FileNotFoundError:
self._modified = True
except Exception:
logging.exception("Could not load cached urls. Removing %s."
% self._cached_urls_path)
os.unlink(self._cached_urls_path)
self._modified = True
else:
if cached_urls != self._user_urls or not os.path.exists(self._cache_file):
self._modified = True
self.__thread_running = True
QThreadPool.globalInstance().start(
self._parse_adblock_files if self._modified else self._adblock_from_cache)
def _adblock_from_cache(self):
adblock = AdBlock()
adblock.load(self._cache_file)
self.adblock_ready.emit(adblock)
def _parse_adblock_files(self):
adblock = AdBlock()
for path in self._user_urls.values():
logging.info("parsing adblock file: %s", path)
try:
with open(path) as f:
adblock.parse(f.read())
except Exception:
logging.exception(f"Unable to parse {f.name} adblock file")
adblock.save(self._cache_file)
self.adblock_ready.emit(adblock)
def _on_adblock_ready(self, adblock):
self.__thread_running = False
with open(self._cached_urls_path, "w") as f:
json.dump(self._user_urls, f)
self._adblock = adblock
self.finished.emit()
def adblock(self):
return self._adblock
def _dl_ready_read(self):
reply = self.sender()
data = self._replies[reply]
if "file" not in data:
headers = {bytes(k).lower(): bytes(v)
for k, v in reply.rawHeaderPairs()}
if os.path.isfile(data["path"]):
try:
last_modified = dateparser.parse(
headers[b"last-modified"].decode("utf-8"),
languages=["en"])
except Exception:
logging.exception(
"Unable to parse the last-modified header for %s",
reply.url().toString())
else:
file_time = datetime.fromtimestamp(
os.path.getmtime(data["path"]), timezone.utc)
if last_modified < file_time:
logging.info("no need to download adblock rule: %s", url)
# touch on the file
os.utime(path, None)
self._close_reply(reply)
self._maybe_finish()
return
logging.info("downloading adblock rule: %s", reply.url().toString())
data["file"] = open(data["path"], "w")
data["file"].write(bytes(reply.readAll()).decode("utf-8"))
def _dl_finished(self):
reply = self.sender()
self._modified = True
data = self._replies.pop(reply)
data["file"].close()
self._maybe_finish()
def _close_reply(self, reply):
del self._replies[reply]
reply.readyRead.disconnect(self._dl_ready_read)
reply.finished.disconnect(self._dl_finished)
reply.close()
def abort(self):
for reply, data in list(self._replies.items()):
self._close_reply(reply)
if "file" in data:
data["file"].close()
os.unlink(data["path"])
# wait for any thread to join
if self.__thread_running:
QThreadPool.globalInstance().waitForDone(1000)
================================================
FILE: webmacs/application.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
import os
import logging
from PyQt6.QtCore import pyqtSlot as Slot, Qt
from PyQt6.QtWebEngineCore import QWebEngineUrlRequestInterceptor
from PyQt6.QtWidgets import QApplication
from PyQt6.QtNetwork import QNetworkAccessManager
from . import require, version
from .task import TaskRunner
from .adblock import AdBlockUpdateTask, adblock_urls_rules, AdBlock
from .download_manager import DownloadManager
from .profile import named_profile
from .minibuffer.right_label import init_minibuffer_right_labels
from .keyboardhandler import LOCAL_KEYMAP_SETTER
from .spell_checking import SpellCheckingTask, \
spell_checking_dictionaries
from .scheme_handlers import register_schemes
if version.is_linux:
# workaround for a nvidia issue
# see https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826
import ctypes
import ctypes.util
ctypes.CDLL(ctypes.util.find_library("GL"), mode=ctypes.RTLD_GLOBAL)
THIS_DIR = os.path.dirname(os.path.realpath(__file__))
class UrlInterceptor(QWebEngineUrlRequestInterceptor):
def __init__(self, app):
QWebEngineUrlRequestInterceptor.__init__(self)
self._adblock = AdBlock()
self._use_adblock = True
@Slot(object)
def update_adblock(self, adblock):
self._adblock = adblock
def toggle_use_adblock(self):
self._use_adblock = not self._use_adblock
def interceptRequest(self, request):
url = request.requestUrl()
url_s = url.toString()
if (self._use_adblock
and self._adblock.matches(
url_s,
request.firstPartyUrl().toString())):
logging.info("filtered: %s", url_s)
request.block(True)
class WithoutAppEventFilter(object):
def __enter__(self):
app().removeEventFilter(LOCAL_KEYMAP_SETTER)
def __exit__(self, type, value, traceback):
app().installEventFilter(LOCAL_KEYMAP_SETTER)
def app():
return Application.INSTANCE
def _app_requires():
require(".commands.follow")
require(".commands.buffer_history")
require(".commands.global")
require(".commands.isearch")
require(".commands.webbuffer")
require(".commands.caret_browsing")
require(".commands.content_edit")
require(".commands.minibuffer")
require(".default_webjumps")
require(".keymaps.global")
require(".keymaps.caret_browsing")
require(".keymaps.content_edit")
require(".keymaps.fullscreen")
require(".keymaps.minibuffer")
require(".keymaps.hints")
require(".keymaps.isearch")
require(".keymaps.webbuffer")
class Application(QApplication):
INSTANCE = None
def __init__(self, conf_path, args, instance_name="default",
profile_name="default", off_the_record=False):
QApplication.__init__(self, args)
self.__class__.INSTANCE = self
self.instance_name = instance_name
self.task_runner = TaskRunner()
if version.is_mac:
self.setAttribute(
Qt.ApplicationAttribute.AA_MacDontSwapCtrlAndMeta)
register_schemes()
self._conf_path = conf_path
if not os.path.isdir(self.profiles_path()):
os.makedirs(self.profiles_path())
self._interceptor = UrlInterceptor(self)
self._download_manager = DownloadManager(self)
self.profile = named_profile(profile_name,
off_the_record=off_the_record)
self.installEventFilter(LOCAL_KEYMAP_SETTER)
self.setQuitOnLastWindowClosed(False)
self.network_manager = QNetworkAccessManager(self)
self.aboutToQuit.connect(self.task_runner.stop)
def conf_path(self):
return self._conf_path
def profiles_path(self):
return os.path.join(self.conf_path(), "profiles")
def adblock_path(self):
return os.path.join(self.conf_path(), "adblock")
def visitedlinks(self):
return self.profile.visitedlinks
def bookmarks(self):
return self.profile.bookmarks
def features(self):
return self.profile.features
def url_interceptor(self):
return self._interceptor
def download_manager(self):
return self._download_manager
def ignored_certs(self):
return self.profile.ignored_certs
def adblock_update(self):
if not adblock_urls_rules.value:
return
task = AdBlockUpdateTask(self, self.adblock_path())
def adblock_finished():
adblock = task.adblock()
if adblock:
self._interceptor.update_adblock(adblock)
task.finished.connect(adblock_finished)
self.task_runner.run(task)
def update_spell_checking(self):
if not bool(spell_checking_dictionaries.value):
return
spell_check_path = os.path.join(self._conf_path, "spell_checking")
def spc_finished(*a):
self.profile.update_spell_checking()
task = SpellCheckingTask(self, spell_check_path)
task.finished.connect(spc_finished)
self.task_runner.run(task)
def post_init(self):
self.adblock_update()
self.update_spell_checking()
init_minibuffer_right_labels()
================================================
FILE: webmacs/bookmarks.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
import sqlite3
class Bookmarks(object):
def __init__(self, dbbath):
self._conn = sqlite3.connect(dbbath)
self._conn.execute("""
CREATE TABLE IF NOT EXISTS bookmarks
(url TEXT PRIMARY KEY, name TEXT);
""")
def set(self, url, name):
self._conn.execute("""
INSERT OR REPLACE INTO bookmarks (url, name)
VALUES (?, ?)
""", (url, name))
self._conn.commit()
def list(self):
return [r for r in self._conn.execute(
"select url, name from bookmarks order by name"
)]
def remove(self, url):
self._conn.execute("""
DELETE from bookmarks WHERE url = ?
""", (url,))
self._conn.commit()
================================================
FILE: webmacs/clipboard.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
from PyQt6.QtGui import QClipboard
from . import variables, minibuffer_show_info
_CLIPBOARD = None
_COPY_MODE = None
def _clipboard():
global _CLIPBOARD
if _CLIPBOARD is None:
from .application import app
_CLIPBOARD = app().clipboard()
return _CLIPBOARD
class Mode:
PRIMARY = 1 << 0
SELECTION = 1 << 1
BOTH = PRIMARY | SELECTION
_from_str = {
"primary": PRIMARY,
"selection": SELECTION,
"both": BOTH,
}
def _copy_mode_from_var(v):
global _COPY_MODE
_COPY_MODE = Mode._from_str[v.value]
clipboard_copy = variables.define_variable(
"clipboard-copy",
"Where to copy text. Allowed values are 'primary', 'selection' or 'both'"
" Defaults to primary, which is the global clipboard. selection is for"
" clipboard mouse selection, and both will copy to both clipboards.",
"primary",
type=variables.String(choices=tuple(Mode._from_str.keys())),
callbacks=(_copy_mode_from_var,)
)
_copy_mode_from_var(clipboard_copy)
def set_text(text, mode=None):
cb = _clipboard()
if mode is None:
mode = _COPY_MODE
if mode & Mode.PRIMARY:
cb.setText(text)
if mode & Mode.SELECTION:
cb.setText(text, QClipboard.Selection)
minibuffer_show_info("Copied: {}".format(text))
================================================
FILE: webmacs/commands/__init__.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
import inspect
from .. import COMMANDS
from .. import url_opener
class InteractiveCommand(object):
"""
A command to interact with the system.
A prompt class can be given to get an argument using the minibuffer prompt.
:param binding: a callable to run when invoking the command.
:param visible: whether or not to list the command using M-x
"""
__slots__ = ("binding", "visible")
def __init__(self, binding, visible=True):
self.binding = binding
self.visible = visible
def getdoc(self):
return inspect.getdoc(self.binding)
def __call__(self, ctx):
return self.binding(ctx)
def define_command(name, binding=None, **args):
"""
Register an interactive command.
"""
command = InteractiveCommand(binding, **args)
COMMANDS[name] = command
if binding is None:
def wrapper(func):
command.binding = func
return func
return wrapper
class Opener(object):
CURRENT_BUFFER = 1
NEW_BUFFER = 2
NEW_WINDOW = 3
def __init__(self, prompt_ctor):
self.prompt_ctor = prompt_ctor
def prompt_open(self, method, ctx):
prompt = self.prompt_ctor(ctx)
if method == self.NEW_BUFFER:
prompt.label += " (new buffer)"
elif method == self.NEW_WINDOW:
prompt.label += " (new window)"
return prompt
def open(self, method, ctx, prompt, url):
opts = {}
if method == self.NEW_BUFFER:
opts["new_buffer"] = True
elif method == self.NEW_WINDOW:
opts["new_window"] = True
url_opener.url_open(ctx, url, **opts)
def closed(self, method, ctx, prompt):
pass
def run(self, method, ctx):
prompt = self.prompt_open(method, ctx)
url = ctx.minibuffer.do_prompt(prompt)
if url:
self.open(method, ctx, prompt, url)
self.closed(method, ctx, prompt)
def register_prompt_opener_commands(name, opener, doc):
if not isinstance(opener, Opener):
opener = Opener(opener)
@define_command(name + "-new-window")
def open_new_window(ctx):
opener.run(Opener.NEW_WINDOW, ctx)
@define_command(name + "-new-buffer")
def open_new_buffer(ctx):
opener.run(Opener.NEW_BUFFER, ctx)
@define_command(name)
def open(ctx):
if ctx.current_prefix_arg == (4,):
return open_new_buffer(ctx)
elif ctx.current_prefix_arg == (16,):
return open_new_window(ctx)
opener.run(Opener.CURRENT_BUFFER, ctx)
open.__name__ = name.replace("-", "_")
open_new_buffer.__name__ = open.__name__ + "_new_buffer"
open_new_window.__name__ = open.__name__ + "_new_window"
open.__doc__ = doc + "." + "\n\n You can use <C-u> as a prefix to open" \
" in a new buffer, or <C-u C-u> to open in a new window."
open_new_buffer.__doc__ = doc + " in a new buffer."
open_new_window.__doc__ = doc + " in a new window."
================================================
FILE: webmacs/commands/buffer_history.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
from PyQt6.QtCore import QAbstractTableModel, QModelIndex, Qt
from PyQt6.QtGui import QImage
from PyQt6.QtNetwork import QNetworkRequest
from .. import current_buffer
from ..minibuffer import Prompt
from ..commands import define_command
from ..application import app
class BufferHistoryTableModel(QAbstractTableModel):
def __init__(self, history):
QAbstractTableModel.__init__(self)
self._history = history
nm = app().network_manager
self._icons = {}
for h in history:
reply = nm.get(QNetworkRequest(h.iconUrl()))
reply.finished.connect(self.icon_dl_finished)
def rowCount(self, index=QModelIndex()):
return len(self._history)
def columnCount(self, index=QModelIndex()):
return 2
def data(self, index, role=Qt.ItemDataRole.DisplayRole):
hitem = index.internalPointer()
if not hitem:
return
col = index.column()
if role == Qt.ItemDataRole.DisplayRole:
if col == 0:
return hitem.url().toString()
else:
return hitem.title()
elif role == Qt.ItemDataRole.DecorationRole and col == 0:
return self._icons.get(hitem.iconUrl()) # hitem.iconUrl()
def index(self, row, col, parent=QModelIndex()):
try:
return self.createIndex(row, col, self._history[row])
except IndexError:
return QModelIndex()
def icon_dl_finished(self):
reply = self.sender()
url = reply.request().url()
img = QImage()
img.loadFromData(reply.readAll())
if not img.isNull() and img.height() != 16 and img.width != 16:
img = img.scaled(16, 16, Qt.AspectRatioMode.KeepAspectRatio)
reply.deleteLater()
self._icons[url] = img
for i, item in enumerate(self._history):
if item.iconUrl() == url:
index = self.index(i, 0)
self.dataChanged.emit(index, index)
class BufferHistoryListPrompt(Prompt):
label = "buffer history:"
complete_options = {
"match": Prompt.FuzzyMatch,
"complete-empty": True,
}
value_return_index_data = True
def enable(self, minibuffer):
self.page_history = current_buffer().history()
# keep a python reference to the items
self._items = self.page_history.items()
Prompt.enable(self, minibuffer)
minibuffer.input().popup().selectRow(
self.page_history.currentItemIndex())
def completer_model(self):
return BufferHistoryTableModel(self._items)
@define_command("buffer-history")
def buffer_history(ctx):
"""
Prompt to navigate in the local buffer history.
"""
prompt = BufferHistoryListPrompt(ctx)
item = ctx.minibuffer.do_prompt(prompt)
if item:
prompt.page_history.goToItem(item)
================================================
FILE: webmacs/commands/caret_browsing.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
from PyQt6.QtWebEngineCore import QWebEngineScript
from . import define_command
def call_js(ctx, script):
ctx.buffer.runJavaScript(script,
QWebEngineScript.ScriptWorldId.ApplicationWorld)
@define_command("caret-browsing-init")
def init(ctx):
"""
Init caret browsing in the current buffer.
"""
call_js(ctx, "CaretBrowsing.setInitialCursor();")
@define_command("caret-browsing-shutdown")
def shutdown(ctx):
"""
Shutdown caret browsing in current buffer.
"""
call_js(ctx, "CaretBrowsing.shutdown();")
@define_command("caret-browsing-down")
def down(ctx):
"""
Move the caret down a line.
"""
call_js(ctx, "CaretBrowsing.move('forward', 'line');")
@define_command("caret-browsing-up")
def up(ctx):
"""
Move the caret up a line.
"""
call_js(ctx, "CaretBrowsing.move('backward', 'line');")
@define_command("caret-browsing-backward-char")
def left_char(ctx):
"""
Move the caret one character backward.
"""
call_js(ctx, "CaretBrowsing.move('backward', 'character');")
@define_command("caret-browsing-backward-word")
def left_word(ctx):
"""
Move the caret one word backward.
"""
call_js(ctx, "CaretBrowsing.move('backward', 'word');")
@define_command("caret-browsing-forward-char")
def right_char(ctx):
"""
Move the caret one character forward.
"""
call_js(ctx, "CaretBrowsing.move('forward', 'character');")
@define_command("caret-browsing-forward-word")
def right_word(ctx):
"""
Move the caret one word forward.
"""
call_js(ctx, "CaretBrowsing.move('forward', 'word');")
@define_command("caret-browsing-toggle-mark")
def toggle_mark(ctx):
"""
Set or unset (toggle) the mark where the point is.
"""
call_js(ctx, "CaretBrowsing.toggleMark();")
@define_command("caret-browsing-cut")
def copy(ctx):
"""
Cut the current caret selection.
"""
call_js(ctx, "CaretBrowsing.cutSelection();")
@define_command("caret-browsing-end-of-line")
def end_of_line(ctx):
"""
Move the caret to the end of the current line.
"""
call_js(ctx, "CaretBrowsing.move('forward', 'lineboundary');")
@define_command("caret-browsing-beginning-of-line")
def beginning_of_line(ctx):
"""
Move the caret to the beginning of the current line.
"""
call_js(ctx, "CaretBrowsing.move('backward', 'lineboundary');")
@define_command("caret-browsing-end-of-document")
def end_of_document(ctx):
"""
Move the caret to the end of the document.
"""
call_js(ctx, "CaretBrowsing.move('forward', 'documentboundary');")
@define_command("caret-browsing-beginning-of-document")
def beginning_of_document(ctx):
"""
Move the caret to the beginning of the document.
"""
call_js(ctx, "CaretBrowsing.move('backward', 'documentboundary');")
@define_command("caret-browsing-forward-paragraph")
def forward_paragraph(ctx):
"""
Move the caret to the next paragraph (TODO FIXME not working yet)
"""
call_js(ctx, "CaretBrowsing.move('forward', 'paragraphboundary');")
@define_command("caret-browsing-backward-paragraph")
def backward_paragraph(ctx):
"""
Move the caret to the previous paragraph (TODO FIXME not working yet)
"""
call_js(ctx, "CaretBrowsing.move('backward', 'paragraphboundary');")
================================================
FILE: webmacs/commands/content_edit.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
from PyQt6.QtWebEngineCore import QWebEngineScript
from PyQt6.QtCore import Qt, QEvent
from PyQt6.QtGui import QKeyEvent
from ..application import app
from ..webbuffer import WebBuffer
from . import define_command
def send_raw_key(ctx, key, with_ctrl=False, auto_shift=True):
a = app()
modifiers = Qt.KeyboardModifier.NoModifier
if auto_shift:
if ctx.buffer.hasSelection() and not ctx.buffer.text_edit_mark:
ctx.buffer.set_text_edit_mark(True)
if ctx.buffer.text_edit_mark:
modifiers |= Qt.KeyboardModifier.ShiftModifier
if with_ctrl:
modifiers |= Qt.KeyboardModifier.ControlModifier
w = app().focusWindow()
a.postEvent(w, QKeyEvent(QEvent.Type.KeyPress, key, modifiers))
a.postEvent(w, QKeyEvent(QEvent.Type.KeyRelease, key, modifiers))
def run_js(ctx, cmd, cb=None):
if cb:
ctx.buffer.runJavaScript(
cmd, QWebEngineScript.ScriptWorldId.ApplicationWorld, cb)
else:
ctx.buffer.runJavaScript(
cmd, QWebEngineScript.ScriptWorldId.ApplicationWorld)
@define_command("content-edit-cancel")
def cancel(ctx):
"""
If a mark is active, clear that but keep the focus. If there is no
active mark, then just unfocus the editable js object.
"""
if ctx.buffer.hasSelection():
run_js(ctx, "textedit.clear_mark();")
else:
run_js(ctx, "textedit.blur();")
ctx.buffer.set_text_edit_mark(False)
@define_command("content-edit-set-mark")
def set_mark(ctx):
"""
Set or clear the mark in browser text field.
"""
if ctx.buffer.hasSelection():
run_js(ctx, "textedit.clear_mark();")
ctx.buffer.set_text_edit_mark(
not ctx.buffer.text_edit_mark
)
@define_command("content-edit-forward-char")
def forward_char(ctx):
"""
Move one character forward in browser text field.
"""
send_raw_key(ctx, Qt.Key.Key_Right)
@define_command("content-edit-backward-char")
def backward_char(ctx):
"""
Move one character backward in browser text field.
"""
send_raw_key(ctx, Qt.Key.Key_Left)
@define_command("content-edit-forward-word")
def forward_word(ctx):
"""
Move one word forward in browser text field.
"""
send_raw_key(ctx, Qt.Key.Key_Right, with_ctrl=True)
@define_command("content-edit-backward-word")
def backward_word(ctx):
"""
Move one word backward in browser text field.
"""
send_raw_key(ctx, Qt.Key.Key_Left, with_ctrl=True)
@define_command("content-edit-beginning-of-line")
def move_beginning_of_line(ctx):
"""
Move to the beginning of the line in browser text field.
"""
send_raw_key(ctx, Qt.Key.Key_Home)
@define_command("content-edit-end-of-line")
def move_end_of_line(ctx):
"""
Move to the end of the line in browser text field.
"""
send_raw_key(ctx, Qt.Key.Key_End)
def delete_selection(ctx):
def wrapper(_):
send_raw_key(ctx, Qt.Key.Key_Backspace, auto_shift=False)
ctx.buffer.set_text_edit_mark(False)
return wrapper
@define_command("content-edit-delete-forward-char")
def delete_char(ctx):
"""
Delete one character forward in browser text field.
"""
run_js(
ctx,
"textedit.select_text('forward', 'character');",
delete_selection(ctx),
)
@define_command("content-edit-delete-forward-word")
def delete_word(ctx):
"""
Delete one word forward in browser text field.
"""
run_js(
ctx,
"textedit.select_text('forward', 'word');",
delete_selection(ctx),
)
@define_command("content-edit-delete-backward-word")
def delete_word_backward(ctx):
"""
Delete one word backward in browser text field.
"""
run_js(
ctx,
"textedit.select_text('backward', 'word');",
delete_selection(ctx),
)
@define_command("content-edit-copy")
def copy(ctx):
"""
Copy browser text field selection to the clipboard.
"""
ctx.buffer.set_text_edit_mark(False)
run_js(ctx, "textedit.copy_text(true);")
@define_command("content-edit-cut")
def cut(ctx):
"""
Cut browser text field selection to the clipboard.
"""
run_js(ctx, "textedit.copy_text();",
delete_selection(ctx))
@define_command("content-edit-kill")
def kill(ctx):
"""
Kill from the cursor to end of line to the clipboard.
"""
run_js(ctx,
"textedit.select_text('forward', 'lineboundary'); \
textedit.copy_text();",
delete_selection(ctx))
@define_command("content-edit-upcase-forward-word")
def upcase_word(ctx):
"""
Upcase the word forward in browser text field.
"""
run_js(ctx, "textedit.upcase_word();")
@define_command("content-edit-downcase-forward-word")
def downcase_word(ctx):
"""
Downcase the word forward in browser text field.
"""
run_js(ctx, "textedit.downcase_word();")
@define_command("content-edit-capitalize-forward-word")
def capitalize_word(ctx):
"""
Capitalize the word forward in browser text field.
"""
run_js(ctx, "textedit.capitalize_word();")
@define_command("content-edit-open-external-editor")
def open_external_editor(ctx):
"""
Open an external editor to change the text field content.
"""
run_js(ctx, "textedit.external_editor_open()")
@define_command("content-edit-undo")
def undo(ctx):
"""
Undo the last editing action.
"""
ctx.buffer.triggerAction(WebBuffer.WebAction.Undo)
@define_command("content-edit-redo")
def redo(ctx):
"""
Redo the last editing action.
"""
ctx.buffer.triggerAction(WebBuffer.WebAction.Redo)
@define_command("content-edit-select-all")
def select_all(ctx):
"""
Select all text of the current input.
"""
run_js(ctx, "textedit.select_text()")
================================================
FILE: webmacs/commands/follow.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
from PyQt6.QtCore import QEvent, Qt
from ..minibuffer import Prompt
from ..keymaps import KeyPress, HINT_KEYMAP
from ..commands import define_command, register_prompt_opener_commands, \
Opener
from .. import variables, clipboard
HINT_METHODS = ("filter", "alphabet")
hint_method = variables.define_variable(
"hint-method",
"Method to hint things in web buffers.",
HINT_METHODS[0],
type=variables.String(choices=HINT_METHODS),
)
hint_alphabet_characters = variables.define_variable(
"hint-alphabet-characters",
"Which characters to use for alphabet hinting.",
"asdfghjkl",
type=variables.String(),
)
hint_node_style = variables.define_variable(
"hint-node-style",
"The style to apply to the hint div. Note that it is a dict of JavaScript"
" style property names to values. See"
" https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style.",
{
"whiteSpace": "nowrap",
"overflow": "hidden",
"padding": "1px 3px 0px 3px",
"background": "linear-gradient(to bottom, #fc3232 0%,#990000 100%)",
"border": "solid 1px #c32222",
"borderRadius": "3px",
"boxShadow": "0px 3px 7px 0px rgba(0, 0, 0, 0.3)",
"color": "white",
"fontWeight": "bold",
"fontSize": "13px",
"textShadow": "1px 1px 0 rgba(0, 0, 0, 0.6)",
},
type=variables.Dict(variables.String(), variables.String()),
)
def hint_method_options(method):
options = {
"hint": hint_node_style.value,
"background": "yellow",
"background_active": "#88FF00",
"text_color": "black",
}
if method == "alphabet":
options["characters"] = hint_alphabet_characters.value
return options
# took from conkeror
SELECTOR_CLICKABLE = (
"//*[@onclick or @onmouseover or @onmousedown or @onmouseup or "
"@oncommand or @role='link' or @role='button' or @role='menuitem']"
" | //input[not(@type='hidden')] | //a[@href] | //area"
" | //iframe | //textarea | //button | //select"
" | //*[@contenteditable = 'true']"
" | //xhtml:*[@onclick or @onmouseover or @onmousedown or"
" @onmouseup or @oncommand or @role='link' or @role='button' or"
" @role='menuitem'] | //xhtml:input[not(@type='hidden')]"
" | //xhtml:a[@href] | //xhtml:area | //xhtml:iframe"
" | //xhtml:textarea | //xhtml:button | //xhtml:select"
" | //xhtml:*[@contenteditable = 'true'] | //svg:a"
)
SELECTOR_LINK = "//a[@href] | //iframe"
class HintPrompt(Prompt):
keymap = HINT_KEYMAP
hint_selector = ""
def enable(self, minibuffer):
super(HintPrompt, self).enable(minibuffer)
self.page = self.ctx.buffer
self.method = hint_method.value
self.method_options = hint_method_options(self.method)
self.page.start_select_browser_objects(
self.hint_selector,
method=self.method,
method_options=self.method_options
)
self.numbers = ""
minibuffer.input().textChanged.connect(self.on_text_edited)
self.browser_object_activated = {}
self.page.content_handler.browserObjectActivated.connect(
self.on_browser_object_activated
)
minibuffer.input().installEventFilter(self)
def on_browser_object_activated(self, bo):
self.browser_object_activated = bo
self.minibuffer.input().set_right_italic_text(bo.get("url", ""))
if self.method == "alphabet":
self._on_edition_finished()
def on_text_edited(self, text):
self.page.filter_browser_objects(text)
def _update_label(self):
label = self.label
if self.numbers:
label = label + (" #%s" % self.numbers)
self.minibuffer.label.setText(label)
def eventFilter(self, obj, event):
numbers = ("1", "2", "3", "4", "5", "6", "7", "8", "9", "0")
if event.type() == QEvent.Type.KeyPress:
if self.method == "filter":
text = event.text()
if text in numbers:
self.numbers += text
self.page.select_visible_hint(self.numbers)
self._update_label()
return True
elif not event.key() in (
Qt.Key.Key_Control,
Qt.Key.Key_Shift,
Qt.Key.Key_Alt,
Qt.Key.Key_Meta,
Qt.Key.Key_unknown,
Qt.Key.Key_Return,
):
self.numbers = ""
self._update_label()
elif self.method == "alphabet":
kp = KeyPress.from_qevent(event)
if kp is not None:
char = kp.char()
if not kp.has_any_modifier() \
and len(char) == 1 \
and char not in self.method_options["characters"]:
return True
return super(HintPrompt, self).eventFilter(obj, event)
def value(self):
return super().value() is not None
class CopyLinkPrompt(HintPrompt):
label = "copy link:"
hint_selector = SELECTOR_LINK
def eventFilter(self, obj, event):
res = super(CopyLinkPrompt, self).eventFilter(obj, event)
if self.numbers == "0":
self.minibuffer.input().set_right_italic_text(
self.page.url().toString()
)
return res
class FollowPrompt(HintPrompt):
label = "follow:"
hint_selector = SELECTOR_CLICKABLE
class FollowOpener(Opener):
def prompt_open(self, method, ctx):
prompt = super().prompt_open(method, ctx)
if method != self.CURRENT_BUFFER:
prompt.hint_selector = SELECTOR_LINK
return prompt
def open(self, method, ctx, prompt, url):
if method == self.CURRENT_BUFFER:
ctx.buffer.focus_active_browser_object()
elif "url" in prompt.browser_object_activated:
super().open(method, ctx, prompt,
prompt.browser_object_activated["url"])
ctx.buffer.stop_select_browser_objects()
register_prompt_opener_commands(
"follow",
FollowOpener(FollowPrompt),
"Hint links in the buffer and follow them on selection"
)
@define_command("copy-link")
def copy_link(ctx):
"""
Hint links in the buffer to copy them.
"""
prompt = CopyLinkPrompt(ctx)
if not ctx.minibuffer.do_prompt(prompt):
return
url = None
ctx.buffer.stop_select_browser_objects()
if prompt.numbers == "0":
# special case, copy current url
url = str(ctx.buffer.url().toEncoded(), "utf-8")
else:
url = prompt.browser_object_activated.get("url")
if url:
clipboard.set_text(url)
@define_command("hint-abort")
def cancel(ctx):
"""
Abort current hint session.
"""
ctx.buffer.stop_select_browser_objects()
ctx.minibuffer.close_prompt()
@define_command("hint-next")
def next_completion(ctx):
"""
Select the next hint.
"""
ctx.buffer.select_nex_browser_object()
@define_command("hint-prev")
def previous_completion(ctx):
"""
Select the previous hint.
"""
ctx.buffer.select_nex_browser_object(False)
================================================
FILE: webmacs/commands/global.py
================================================
# This file is part of webmacs.
#
# webmacs 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.
#
# webmacs 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 webmacs. If not, see <http://www.gnu.org/licenses/>.
import itertools
import os
import sys
from PyQt6.QtCore import QStringListModel, QModelIndex, QProcess
from . import define_command, COMMANDS, register_prompt_opener_commands
from ..minibuffer import Prompt
from ..minibuffer.prompt import PromptTableModel, PromptHistory
from ..application import app
from ..webbuffer import create_buffer
from ..keymaps import KeyPress, VISITEDLINKS_KEYMAP, BOOKMARKS_KEYMAP, \
KEYMAPS, GLOBAL_KEYMAP
from ..keyboardhandler import send_key_event, local_keymap, KEY_EATER, \
CallHandler
from .. import BUFFERS, windows, variables
from ..mode import MODES
from ..window import Window
from ..session import session_clean, session_load
from ..ipc import IpcServer
from ..url_opener import url_open
class CommandsListPrompt(Prompt):
label = "M-x: "
complete_options = {
"match": Prompt.FuzzyMatch,
"complete-empty": True,
}
history = PromptHistory()
def __init__(self, ctx, local_keymap=None):
Prompt.__init__(self, ctx)
self.__local_keymap = local_keymap
def completer_model(self):
if self.__local_keymap:
bindings = {}
def add(prefix, cmd, parent):
bindings[cmd] = " ".join(str(k) for k in prefix)
# add bindings from currently active keymaps: global and the local
# one, registered before opening the minibuffer.
GLOBAL_KEYMAP.traverse_commands(add)
self.__local_keymap.traverse_commands(add)
data = [(k, bindings.get(k, ""))
for k, v in COMMANDS.items() if v.visible]
else:
data = [(k,) for k, v in COMMANDS.items() if v.visible]
model = PromptTableModel(data, self)
return model
@define_command("quit")
def quit(ctx):
"""
Quit the application.
"""
app().quit()
@define_command("M-x", visible=False)
def commands(ctx):
"""
Prompt for a command name to execute.
"""
prompt = CommandsListPrompt(ctx, local_keymap())
value = ctx.minibuffer.do_prompt(prompt)
try:
COMMANDS[value](ctx)
except KeyError:
pass
@define_command("toggle-fullscreen")
def toggle_fullscreen(ctx):
"""
Toggle fullscreen state of the current window.
"""
win = ctx.window
if not win:
return
if win.isFullScreen():
win.showNormal()
else:
win.showFullScreen()
@define_command("toggle-maximized")
def toggle_maximised(ctx):
"""
Toggle maximised state of the current window.
"""
win = ctx.window
if not win:
return
if win.isMaximized():
win.showNormal()
else:
win.showMaximized()
def _get_or_create_buffer(win):
visible_buffers = []
for awin in windows():
for view in awin.webviews():
visible_buffers.append(view.buffer())
current_buffer = win.current_webview().buffer()
buffers = [b for b in BUFFERS
if b not in visible_buffers
or b == current_buffer]
# if there is at least one buffer not visible, use the one just
# after the current one in the list
if len(buffers) > 1:
ibuffers = itertools.cycle(buffers)
while True:
buff = next(ibuffers)
if buff == current_buffer:
return next(ibuffers)
# else create a new buffer, reusing the current buffer's url
return create_buffer(url=current_buffer.url())
@define_command("split-view-right")
def split_window_right(ctx):
"""
Create a new view on the right of the current one.
"""
win = ctx.window
view = win.create_webview_on_right()
view.setBuffer(_get_or_create_buffer(win))
win.set_current_webview(view)
@define_command("split-view-bottom")
def split_window_bottom(ctx):
"""
Create a new view below the current one.
"""
win = ctx.window
view = win.create_webview_on_bottom()
view.setBuffer(_get_or_create_buffer(win))
win.set_current_webview(view)
@define_command("make-window")
def create_window(ctx):
"""
Create a new window and focus it.
"""
win = Window()
home_page = variables.get("home-page")
win.current_webview().setBuffer(
create_buffer(home_page)
if home_page and variables.get("home-page-in-new-window")
else _get_or_create_buffer(ctx.window)
)
win.show()
win.activateWindow()
@define_command("other-window")
def other_window(ctx):
"""
Switch to the next window.
"""
if len(windows()) <= 1:
return False
iterwindows = itertools.cycle(windows())
while True:
win = next(iterwindows)
if win == ctx.window:
next(iterwindows).activateWindow()
return True
@define_command("close-window")
def close_window(ctx):
"""
Close the current window, unless there is only one left.
"""
# first activate the next view
if other_window(ctx):
ctx.window.close()
@define_command("close-other-windows")
def close_other_windows(ctx):
"""
Close all windows except the current one.
"""
for win in windows():
if win != ctx.window:
win.close()
@define_command("other-view")
def other_view(ctx):
"""
Focus on the next view.
"""
win = ctx.window
win.other_view()
@define_command("close-view")
def close_view(ctx):
"""
Close the current view.
"""
window = ctx.window
window.close_view(window.current_webview())
@define_command("maximise-view")
def maximise_view(ctx):
"""
Close all the views in the current window except the current one.
"""
ctx.window.close_other_views()
@define_command("toggle-ad-block")
def toggle_ad_block(ctx):
"""
Toggle ad-blocking on or off.
"""
from .webbuffer import reload_buffer_no_cache
app().url_interceptor().toggle_use_adblock()
reload_buffer_no_cache(ctx)
@define_command("toggle-toolbar")
def toggle_toolbar(ctx):
"""
Toggle the main window toolbar on or off.
"""
ctx.window.toggle_toolbar()
class VisitedLinksModel(PromptTableModel):
def __init__(self, parent):
visitedlinks = app().visitedlinks()
PromptTableModel.__init__(self, visitedlinks.visited_urls())
self.visitedlinks = visitedlinks
def remove_history_entry(self, index):
self.beginRemoveRows(QModelIndex(), index.row(), index.row())
self.visitedlinks.remove(self._data.pop(index.row())[0])
self.endRemoveRows()
class VisitedLinksPrompt(Prompt):
label = "Find url from visited links:"
complete_options = {
"match": Prompt.FuzzyMatch,
"complete-empty": True,
}
keymap = VISITEDLINKS_KEYMAP
value_return_index_data = True
def completer_model(self):
return VisitedLinksModel(self)
@define_command("visited-links-delete-highlighted")
def visited_links_remove_entry(ctx):
"""
Deletes from the database the currently highlighted visited link.
"""
pinput = ctx.minibuffer.input()
selection = pinput.popup().selectionModel().currentIndex()
if not selection.isValid():
return
selection = selection.model().mapToSource(selection)
pinput.completer_model().remove_history_entry(selection)
register_prompt_opener_commands(
"visited-links-history",
VisitedLinksPrompt,
"Prompt to open a link previously visited",
)
class BookmarksModel(VisitedLinksModel):
def __init__(self, parent):
bookmarks = app().bookmarks()
PromptTableModel.__init__(self, bookmarks.list())
# this makes the remove_history_entry method works
self.visitedlinks = bookmarks
@define_command("bookmarks-delete-highlighted")
def bookmarks_remove_entry(ctx):
"""
Deletes from the database the currently highlighted bookmark.
"""
# removing a bookmark is like removing a visited link
visited_links_remove_entry(ctx)
class BookmarksPrompt(VisitedLinksPrompt):
label = "Open bookmark:"
keymap = BOOKMARKS_KEYMAP
history = PromptHistory()
def completer_model(self):
return BookmarksModel(self)
register_prompt_opener_commands(
"bookmark-open",
BookmarksPrompt,
"Prompt to open a bookmark",
)
class BookmarkAddPrompt(Prompt):
label = "Create a bookmark for: "
def enable(self, minibuffer):
Prompt.enable(self, minibuffer)
url = self.ctx.buffer.url().toString()
input = minibuffer.input()
input.setText(url)
input.setSelection(0, len(url))
class BookmarkNamePrompt(Prompt):
label = "bookmark's name: "
def enable(self, minibuffer):
Prompt.enable(self, minibuffer)
name = self.ctx.buffer.title()
input = minibuffer.input()
input.setText(name)
input.setSelection(0, len(name))
@define_command("bookmark-add")
def bookmark_add(ctx):
"""
Create or rename a bookmark for the current url.
"""
prompt = BookmarkAddPrompt(ctx)
url = ctx.minibuffer.do_prompt(prompt)
if not url:
return
otherprompt = BookmarkNamePrompt(ctx)
name = ctx.minibuffer.do_prompt(otherprompt)
if name:
app().bookmarks().set(url, name)
ctx.minibuffer.show_info("Bookmark {} created.".format(name))
class ModesPrompt(Prompt):
label = "switch to mode:"
complete_options = {
"match": Prompt.FuzzyMatch,
"complete-empty": True,
}
value_return_index_data = True
def completer_model(self):
return PromptTableModel([
(name, MODES[name].description) for name in sorted(MODES)
])
@define_command("buffer-set-mode")
def buffer_set_mode(ctx):
"""
Change the mode of the current buffer.
"""
prompt = ModesPrompt(ctx)
mode = ctx.minibuffer.do_prompt(prompt)
if mode:
ctx.buffer.set_mode(mode)
@define_command("send-key-down")
def send_down(ctx):
"""Send a key down event."""
send_key_event(KeyPress.from_str("Down"))
@define_command("send-key-up")
def send_up(ctx):
"""Send a key up event."""
send_key_event(KeyPress.from_str("Up"))
@define_command("send-key-right")
def send_right(ctx):
"""Send a key right event."""
send_key_event(KeyPress.from_str("Right"))
@define_command("send-key-left")
def send_left(ctx):
"""Send a key left event."""
send_key_event(KeyPress.from_str("Left"))
@define_command("describe-bindings")
def describe_bindings(ctx):
"""
Dis
gitextract_3q3a7_u6/
├── .flake8
├── .gitattributes
├── .gitignore
├── .gitmodules
├── .travis.yml
├── CHANGELOG.md
├── COPYING
├── README-nix.org
├── README.org
├── c/
│ └── adblock.c
├── docs/
│ ├── Makefile
│ ├── advanced_topics.rst
│ ├── api.rst
│ ├── basic_usage.rst
│ ├── concepts.rst
│ ├── conf.py
│ ├── ext/
│ │ └── webmacs_sphinx_ext.py
│ ├── faq.rst
│ ├── glossary.rst
│ ├── index.rst
│ ├── make.bat
│ └── user_configuration.rst
├── git_archive_all.py
├── pytest.ini
├── setup.py
├── test-requirements.txt
├── tests/
│ ├── integration/
│ │ ├── conftest.py
│ │ ├── iframe_follow/
│ │ │ ├── index.html
│ │ │ └── my_iframe.html
│ │ ├── javascript_prompt/
│ │ │ └── index.html
│ │ ├── navigation/
│ │ │ ├── index.html
│ │ │ └── page1.html
│ │ ├── test_copy_link.py
│ │ ├── test_iframe_navigation.py
│ │ ├── test_javascript_prompt.py
│ │ ├── test_navigation.py
│ │ └── test_user_download_dir.py
│ ├── test_prompt_history.py
│ └── test_variables.py
└── webmacs/
├── __init__.py
├── adblock.py
├── application.py
├── bookmarks.py
├── clipboard.py
├── commands/
│ ├── __init__.py
│ ├── buffer_history.py
│ ├── caret_browsing.py
│ ├── content_edit.py
│ ├── follow.py
│ ├── global.py
│ ├── isearch.py
│ ├── minibuffer.py
│ ├── webbuffer.py
│ └── webjump.py
├── content_handler.py
├── default_webjumps.py
├── download_manager/
│ ├── __init__.py
│ └── prompts.py
├── egrid.py
├── external_editor.py
├── features.py
├── filter_webengine_output.py
├── hooks.py
├── ignore_certificates.py
├── ipc.py
├── keyboardhandler.py
├── keymaps/
│ ├── __init__.py
│ ├── caret_browsing.py
│ ├── content_edit.py
│ ├── fullscreen.py
│ ├── global.py
│ ├── hints.py
│ ├── isearch.py
│ ├── minibuffer.py
│ └── webbuffer.py
├── killed_buffers.py
├── main.py
├── minibuffer/
│ ├── __init__.py
│ ├── prompt.py
│ └── right_label.py
├── mode.py
├── password_manager/
│ ├── __init__.py
│ └── password_store.py
├── profile.py
├── scheme_handlers/
│ ├── __init__.py
│ └── webmacs/
│ ├── __init__.py
│ ├── js/
│ │ └── vue.js
│ └── templates/
│ ├── base.html
│ ├── bindings.html
│ ├── command.html
│ ├── commands.html
│ ├── downloads.html
│ ├── key.html
│ ├── keymap.html
│ ├── variable.html
│ ├── variables.html
│ └── version.html
├── scripts/
│ ├── caret_browsing.js
│ ├── hint.js
│ ├── password_manager.js
│ ├── setup.js
│ ├── textedit.js
│ └── textzoom.js
├── session.py
├── spell_checking.py
├── task.py
├── url_opener.py
├── variables.py
├── version.py
├── visited_links.py
├── webbuffer.py
├── webview.py
└── window.py
SYMBOL INDEX (1363 symbols across 67 files)
FILE: c/adblock.c
type AdBlock (line 26) | typedef struct {
function AdBlock_dealloc (line 34) | static void
function PyObject (line 42) | static PyObject *
function AdBlock_init (line 52) | static int
function PyObject (line 60) | static PyObject *
function PyObject (line 75) | static PyObject *
function PyObject (line 97) | static PyObject *
function PyObject (line 120) | static PyObject *
function PyMODINIT_FUNC (line 221) | PyMODINIT_FUNC
FILE: docs/conf.py
class Mock (line 33) | class Mock(object):
method __init__ (line 34) | def __init__(self, *a, **kw):
method __getattr__ (line 36) | def __getattr__(self, name): # noqa: E301
method __call__ (line 38) | def __call__(self, *a, **kw): # noqa: E301
method __iter__ (line 40) | def __iter__(self): # noqa: E301
method __instancecheck__ (line 42) | def __instancecheck__(self, instance): # noqa: E301
method __subclasscheck__ (line 44) | def __subclasscheck__(self, cls): # noqa: E301
method __mro_entries__ (line 46) | def __mro_entries__(self, a): # noqa: E301
FILE: docs/ext/webmacs_sphinx_ext.py
function as_rest_table (line 20) | def as_rest_table(data):
class SimpleAutoDirective (line 35) | class SimpleAutoDirective(Directive):
method run (line 42) | def run(self):
class WebmacsCommands (line 53) | class WebmacsCommands(SimpleAutoDirective):
method _run (line 54) | def _run(self):
class WebmacsWebjumps (line 70) | class WebmacsWebjumps(SimpleAutoDirective):
method _run (line 71) | def _run(self):
class WebmacsVariables (line 83) | class WebmacsVariables(SimpleAutoDirective):
method _run (line 84) | def _run(self):
class WebmacsModes (line 96) | class WebmacsModes(SimpleAutoDirective):
method _run (line 97) | def _run(self):
class WebmacsKeymaps (line 109) | class WebmacsKeymaps(SimpleAutoDirective):
method _run (line 114) | def _run(self):
function webmacs_role (line 127) | def webmacs_role(data):
class CurrentKeymapDirective (line 140) | class CurrentKeymapDirective(Directive):
method run (line 147) | def run(self):
function get_keymap_bindings (line 160) | def get_keymap_bindings(keymap_name):
function key_in_keymap_role (line 167) | def key_in_keymap_role(name, rawtext, text, lineno, inliner, options={},
function setup (line 188) | def setup(app):
FILE: git_archive_all.py
class GitArchiver (line 16) | class GitArchiver(object):
method __init__ (line 27) | def __init__(self, prefix='', exclude=True, force_sub=False, extra=None,
method create (line 79) | def create(self, output_path, dry_run=False, output_format=None):
method is_file_excluded (line 144) | def is_file_excluded(self, file_path):
method archive_all_files (line 158) | def archive_all_files(self, archiver):
method walk_git_files (line 174) | def walk_git_files(self, repo_path=''):
method run_git_shell (line 241) | def run_git_shell(cmd, cwd=None):
function main (line 273) | def main():
FILE: setup.py
function get_version (line 56) | def get_version():
function get_revision (line 62) | def get_revision():
class build_py (line 75) | class build_py(_build_py):
method run (line 79) | def run(self):
FILE: tests/integration/conftest.py
function get_test_page (line 23) | def get_test_page(name):
function wm (line 30) | def wm(xvfb):
class VariablesWrapper (line 51) | class VariablesWrapper(object):
method __init__ (line 52) | def __init__(self):
method set (line 55) | def set(self, name, value):
method get (line 60) | def get(self, name):
method restore (line 63) | def restore(self):
function variables (line 70) | def variables():
function qapp (line 77) | def qapp(wm, qapp_args):
class TestSession (line 86) | class TestSession(object):
method __init__ (line 89) | def __init__(self, qtbot, qapp, prompt_exec):
method set_prompt_exec (line 94) | def set_prompt_exec(self, fn):
method waiter (line 97) | def waiter(self):
method call_next (line 100) | def call_next(self, fn):
method buffer (line 104) | def buffer(self):
method window (line 108) | def window(self):
method minibuffer (line 112) | def minibuffer(self):
method minibuffer_input (line 116) | def minibuffer_input(self):
method wait_signal (line 119) | def wait_signal(self, *args, **kwargs):
method wait_until (line 122) | def wait_until(self, func, wait=2.0, delay=0.01):
method test_page_url (line 131) | def test_page_url(self, name):
method load_page (line 134) | def load_page(self, name, buffer=None, wait_iframes=False):
method check_javascript (line 154) | def check_javascript(self, script, return_value, buffer=None):
method wait_hints_ready (line 169) | def wait_hints_ready(self):
method check_nav_highlighted (line 176) | def check_nav_highlighted(self, js_elem):
method wait_iframes (line 180) | def wait_iframes(self, buffer=None):
method keyclick (line 193) | def keyclick(self, key, **kwargs):
method keyclicks (line 196) | def keyclicks(self, keys, **kwargs):
method wkeyclicks (line 200) | def wkeyclicks(self, shortcut, widget=None):
class Waiter (line 208) | class Waiter(object):
method __init__ (line 209) | def __init__(self, session):
method set (line 213) | def set(self):
method wait (line 216) | def wait(self, wait=5, **kwargs):
function session (line 222) | def session(qtbot, qapp, mocker):
FILE: tests/integration/test_copy_link.py
function clipboard_contains (line 6) | def clipboard_contains(text):
function test_copy_current_link (line 10) | def test_copy_current_link(session):
function test_copy_current_link_in_subframe (line 25) | def test_copy_current_link_in_subframe(session):
function test_copy_current_url (line 48) | def test_copy_current_url(session):
function test_copy_current_title (line 59) | def test_copy_current_title(session):
FILE: tests/integration/test_iframe_navigation.py
function test_iframe_navigation (line 8) | def test_iframe_navigation(session):
function test_iframe_follow (line 38) | def test_iframe_follow(session, pytestconfig, hint_method, variables):
FILE: tests/integration/test_javascript_prompt.py
function check_js_result (line 4) | def check_js_result(res):
function check_minibuffer (line 10) | def check_minibuffer(res):
function test_confirm (line 25) | def test_confirm(session, selection, input, check):
FILE: tests/integration/test_navigation.py
function test_cycle_buffers (line 5) | def test_cycle_buffers(session):
FILE: tests/integration/test_user_download_dir.py
function test_get_user_download_dir (line 9) | def test_get_user_download_dir(variables, tmpdir):
function test_extract_suggested_filename (line 38) | def test_extract_suggested_filename(fname, expected):
function test_find_unique_suggested_path (line 49) | def test_find_unique_suggested_path(tmpdir, files, filename, expected):
FILE: tests/test_prompt_history.py
function test_history (line 4) | def test_history():
FILE: tests/test_variables.py
function check_type_error (line 8) | def check_type_error(type, value, regex):
function test_type_string (line 15) | def test_type_string():
function test_type_int (line 28) | def test_type_int():
function test_type_bool (line 44) | def test_type_bool():
function test_type_list (line 50) | def test_type_list():
function test_type_dict (line 60) | def test_type_dict():
FILE: webmacs/__init__.py
function require (line 33) | def require(module, package=__package__):
class WindowsHandler (line 39) | class WindowsHandler(QObject):
method __init__ (line 40) | def __init__(self, parent=None):
method _on_last_window_closing (line 45) | def _on_last_window_closing(self):
method register_window (line 52) | def register_window(self, window):
method eventFilter (line 56) | def eventFilter(self, window, event):
function windows (line 77) | def windows():
function current_window (line 86) | def current_window():
function current_buffer (line 93) | def current_buffer():
function buffers (line 102) | def buffers():
function recent_buffers (line 107) | def recent_buffers():
function current_minibuffer (line 114) | def current_minibuffer():
function minibuffer_show_info (line 123) | def minibuffer_show_info(text):
function call_later (line 132) | def call_later(fn, msec=0):
class ObjRef (line 142) | class ObjRef(object):
method __init__ (line 148) | def __init__(self):
method ref (line 151) | def ref(self, obj, data=True):
method unref (line 154) | def unref(self, obj):
FILE: webmacs/adblock.py
function cache_file (line 50) | def cache_file(cache_path):
class AdBlockUpdateTask (line 54) | class AdBlockUpdateTask(Task):
method __init__ (line 57) | def __init__(self, app, cache_path, ):
method start (line 78) | def start(self):
method _maybe_finish (line 89) | def _maybe_finish(self):
method _adblock_from_cache (line 112) | def _adblock_from_cache(self):
method _parse_adblock_files (line 117) | def _parse_adblock_files(self):
method _on_adblock_ready (line 129) | def _on_adblock_ready(self, adblock):
method adblock (line 136) | def adblock(self):
method _dl_ready_read (line 139) | def _dl_ready_read(self):
method _dl_finished (line 169) | def _dl_finished(self):
method _close_reply (line 176) | def _close_reply(self, reply):
method abort (line 182) | def abort(self):
FILE: webmacs/application.py
class UrlInterceptor (line 48) | class UrlInterceptor(QWebEngineUrlRequestInterceptor):
method __init__ (line 49) | def __init__(self, app):
method update_adblock (line 55) | def update_adblock(self, adblock):
method toggle_use_adblock (line 58) | def toggle_use_adblock(self):
method interceptRequest (line 61) | def interceptRequest(self, request):
class WithoutAppEventFilter (line 72) | class WithoutAppEventFilter(object):
method __enter__ (line 73) | def __enter__(self):
method __exit__ (line 76) | def __exit__(self, type, value, traceback):
function app (line 80) | def app():
function _app_requires (line 84) | def _app_requires():
class Application (line 106) | class Application(QApplication):
method __init__ (line 109) | def __init__(self, conf_path, args, instance_name="default",
method conf_path (line 141) | def conf_path(self):
method profiles_path (line 144) | def profiles_path(self):
method adblock_path (line 147) | def adblock_path(self):
method visitedlinks (line 150) | def visitedlinks(self):
method bookmarks (line 153) | def bookmarks(self):
method features (line 156) | def features(self):
method url_interceptor (line 159) | def url_interceptor(self):
method download_manager (line 162) | def download_manager(self):
method ignored_certs (line 165) | def ignored_certs(self):
method adblock_update (line 168) | def adblock_update(self):
method update_spell_checking (line 182) | def update_spell_checking(self):
method post_init (line 195) | def post_init(self):
FILE: webmacs/bookmarks.py
class Bookmarks (line 19) | class Bookmarks(object):
method __init__ (line 20) | def __init__(self, dbbath):
method set (line 27) | def set(self, url, name):
method list (line 34) | def list(self):
method remove (line 39) | def remove(self, url):
FILE: webmacs/clipboard.py
function _clipboard (line 25) | def _clipboard():
class Mode (line 33) | class Mode:
function _copy_mode_from_var (line 45) | def _copy_mode_from_var(v):
function set_text (line 63) | def set_text(text, mode=None):
FILE: webmacs/commands/__init__.py
class InteractiveCommand (line 22) | class InteractiveCommand(object):
method __init__ (line 33) | def __init__(self, binding, visible=True):
method getdoc (line 37) | def getdoc(self):
method __call__ (line 40) | def __call__(self, ctx):
function define_command (line 44) | def define_command(name, binding=None, **args):
class Opener (line 57) | class Opener(object):
method __init__ (line 62) | def __init__(self, prompt_ctor):
method prompt_open (line 65) | def prompt_open(self, method, ctx):
method open (line 73) | def open(self, method, ctx, prompt, url):
method closed (line 81) | def closed(self, method, ctx, prompt):
method run (line 84) | def run(self, method, ctx):
function register_prompt_opener_commands (line 92) | def register_prompt_opener_commands(name, opener, doc):
FILE: webmacs/commands/buffer_history.py
class BufferHistoryTableModel (line 26) | class BufferHistoryTableModel(QAbstractTableModel):
method __init__ (line 27) | def __init__(self, history):
method rowCount (line 36) | def rowCount(self, index=QModelIndex()):
method columnCount (line 39) | def columnCount(self, index=QModelIndex()):
method data (line 42) | def data(self, index, role=Qt.ItemDataRole.DisplayRole):
method index (line 56) | def index(self, row, col, parent=QModelIndex()):
method icon_dl_finished (line 62) | def icon_dl_finished(self):
class BufferHistoryListPrompt (line 80) | class BufferHistoryListPrompt(Prompt):
method enable (line 88) | def enable(self, minibuffer):
method completer_model (line 96) | def completer_model(self):
function buffer_history (line 101) | def buffer_history(ctx):
FILE: webmacs/commands/caret_browsing.py
function call_js (line 21) | def call_js(ctx, script):
function init (line 27) | def init(ctx):
function shutdown (line 35) | def shutdown(ctx):
function down (line 43) | def down(ctx):
function up (line 51) | def up(ctx):
function left_char (line 59) | def left_char(ctx):
function left_word (line 67) | def left_word(ctx):
function right_char (line 75) | def right_char(ctx):
function right_word (line 83) | def right_word(ctx):
function toggle_mark (line 91) | def toggle_mark(ctx):
function copy (line 99) | def copy(ctx):
function end_of_line (line 107) | def end_of_line(ctx):
function beginning_of_line (line 115) | def beginning_of_line(ctx):
function end_of_document (line 123) | def end_of_document(ctx):
function beginning_of_document (line 131) | def beginning_of_document(ctx):
function forward_paragraph (line 139) | def forward_paragraph(ctx):
function backward_paragraph (line 147) | def backward_paragraph(ctx):
FILE: webmacs/commands/content_edit.py
function send_raw_key (line 25) | def send_raw_key(ctx, key, with_ctrl=False, auto_shift=True):
function run_js (line 41) | def run_js(ctx, cmd, cb=None):
function cancel (line 51) | def cancel(ctx):
function set_mark (line 64) | def set_mark(ctx):
function forward_char (line 76) | def forward_char(ctx):
function backward_char (line 84) | def backward_char(ctx):
function forward_word (line 92) | def forward_word(ctx):
function backward_word (line 100) | def backward_word(ctx):
function move_beginning_of_line (line 108) | def move_beginning_of_line(ctx):
function move_end_of_line (line 116) | def move_end_of_line(ctx):
function delete_selection (line 123) | def delete_selection(ctx):
function delete_char (line 131) | def delete_char(ctx):
function delete_word (line 143) | def delete_word(ctx):
function delete_word_backward (line 155) | def delete_word_backward(ctx):
function copy (line 167) | def copy(ctx):
function cut (line 176) | def cut(ctx):
function kill (line 185) | def kill(ctx):
function upcase_word (line 196) | def upcase_word(ctx):
function downcase_word (line 204) | def downcase_word(ctx):
function capitalize_word (line 212) | def capitalize_word(ctx):
function open_external_editor (line 220) | def open_external_editor(ctx):
function undo (line 228) | def undo(ctx):
function redo (line 236) | def redo(ctx):
function select_all (line 244) | def select_all(ctx):
FILE: webmacs/commands/follow.py
function hint_method_options (line 64) | def hint_method_options(method):
class HintPrompt (line 94) | class HintPrompt(Prompt):
method enable (line 98) | def enable(self, minibuffer):
method on_browser_object_activated (line 116) | def on_browser_object_activated(self, bo):
method on_text_edited (line 122) | def on_text_edited(self, text):
method _update_label (line 125) | def _update_label(self):
method eventFilter (line 131) | def eventFilter(self, obj, event):
method value (line 161) | def value(self):
class CopyLinkPrompt (line 165) | class CopyLinkPrompt(HintPrompt):
method eventFilter (line 169) | def eventFilter(self, obj, event):
class FollowPrompt (line 178) | class FollowPrompt(HintPrompt):
class FollowOpener (line 183) | class FollowOpener(Opener):
method prompt_open (line 184) | def prompt_open(self, method, ctx):
method open (line 190) | def open(self, method, ctx, prompt, url):
function copy_link (line 207) | def copy_link(ctx):
function cancel (line 228) | def cancel(ctx):
function next_completion (line 237) | def next_completion(ctx):
function previous_completion (line 245) | def previous_completion(ctx):
FILE: webmacs/commands/global.py
class CommandsListPrompt (line 38) | class CommandsListPrompt(Prompt):
method __init__ (line 46) | def __init__(self, ctx, local_keymap=None):
method completer_model (line 50) | def completer_model(self):
function quit (line 70) | def quit(ctx):
function commands (line 78) | def commands(ctx):
function toggle_fullscreen (line 91) | def toggle_fullscreen(ctx):
function toggle_maximised (line 105) | def toggle_maximised(ctx):
function _get_or_create_buffer (line 118) | def _get_or_create_buffer(win):
function split_window_right (line 142) | def split_window_right(ctx):
function split_window_bottom (line 153) | def split_window_bottom(ctx):
function create_window (line 164) | def create_window(ctx):
function other_window (line 180) | def other_window(ctx):
function close_window (line 195) | def close_window(ctx):
function close_other_windows (line 205) | def close_other_windows(ctx):
function other_view (line 215) | def other_view(ctx):
function close_view (line 224) | def close_view(ctx):
function maximise_view (line 233) | def maximise_view(ctx):
function toggle_ad_block (line 241) | def toggle_ad_block(ctx):
function toggle_toolbar (line 252) | def toggle_toolbar(ctx):
class VisitedLinksModel (line 259) | class VisitedLinksModel(PromptTableModel):
method __init__ (line 261) | def __init__(self, parent):
method remove_history_entry (line 266) | def remove_history_entry(self, index):
class VisitedLinksPrompt (line 272) | class VisitedLinksPrompt(Prompt):
method completer_model (line 281) | def completer_model(self):
function visited_links_remove_entry (line 286) | def visited_links_remove_entry(ctx):
class BookmarksModel (line 307) | class BookmarksModel(VisitedLinksModel):
method __init__ (line 309) | def __init__(self, parent):
function bookmarks_remove_entry (line 317) | def bookmarks_remove_entry(ctx):
class BookmarksPrompt (line 325) | class BookmarksPrompt(VisitedLinksPrompt):
method completer_model (line 330) | def completer_model(self):
class BookmarkAddPrompt (line 341) | class BookmarkAddPrompt(Prompt):
method enable (line 344) | def enable(self, minibuffer):
class BookmarkNamePrompt (line 352) | class BookmarkNamePrompt(Prompt):
method enable (line 355) | def enable(self, minibuffer):
function bookmark_add (line 364) | def bookmark_add(ctx):
class ModesPrompt (line 381) | class ModesPrompt(Prompt):
method completer_model (line 389) | def completer_model(self):
function buffer_set_mode (line 396) | def buffer_set_mode(ctx):
function send_down (line 407) | def send_down(ctx):
function send_up (line 413) | def send_up(ctx):
function send_right (line 419) | def send_right(ctx):
function send_left (line 425) | def send_left(ctx):
function describe_bindings (line 431) | def describe_bindings(ctx):
function describe_commands (line 439) | def describe_commands(ctx):
function describe_variables (line 447) | def describe_variables(ctx):
function downloads (line 455) | def downloads(ctx):
function version (line 463) | def version(ctx):
class VariableListPrompt (line 470) | class VariableListPrompt(Prompt):
method completer_model (line 478) | def completer_model(self):
function describe_variable (line 485) | def describe_variable(ctx):
class DescribeCommandsListPrompt (line 495) | class DescribeCommandsListPrompt(CommandsListPrompt):
function describe_command (line 501) | def describe_command(ctx):
class ReportCallHandler (line 510) | class ReportCallHandler(CallHandler):
method __init__ (line 512) | def __init__(self, prompt):
method keys_as_text (line 517) | def keys_as_text(self):
method no_call (line 520) | def no_call(self, sender, keymap, keypress):
method partial_call (line 526) | def partial_call(self, sender, keymap, keypress):
method call (line 531) | def call(self, ctx, keymap, keypress, command):
class BindingPrompt (line 545) | class BindingPrompt(Prompt):
method enable (line 549) | def enable(self, minibuffer):
method close (line 555) | def close(self):
method value (line 559) | def value(self):
function describe_binding (line 564) | def describe_binding(ctx):
function describe_binding_briefly (line 577) | def describe_binding_briefly(ctx):
class WhereIsCommandsListPrompt (line 590) | class WhereIsCommandsListPrompt(CommandsListPrompt):
function where_is (line 596) | def where_is(ctx):
function restore_session (line 613) | def restore_session(ctx):
class InstancesListPrompt (line 630) | class InstancesListPrompt(Prompt):
method __init__ (line 639) | def __init__(self, ctx):
method completer_model (line 647) | def completer_model(self):
function raise_instance (line 654) | def raise_instance(ctx):
function current_instance (line 669) | def current_instance(ctx):
function is_off_the_record (line 677) | def is_off_the_record(ctx):
function open_off_the_record (line 685) | def open_off_the_record(ctx):
FILE: webmacs/commands/isearch.py
function search_next (line 25) | def search_next(ctx):
function search_previous (line 38) | def search_previous(ctx):
function validate (line 51) | def validate(ctx):
function cancel (line 61) | def cancel(ctx):
class ISearchPrompt (line 72) | class ISearchPrompt(Prompt):
method enable (line 80) | def enable(self, minibuffer):
method set_isearch_direction (line 92) | def set_isearch_direction(self, direction):
method set_page_scroll_pos (line 96) | def set_page_scroll_pos(self, page_scroll_pos):
method find_text (line 99) | def find_text(self):
method on_text_edited (line 106) | def on_text_edited(self, text):
method _update_label (line 111) | def _update_label(self):
method close (line 115) | def close(self):
function i_search_forward (line 123) | def i_search_forward(ctx):
class ISearchPromptBackward (line 130) | class ISearchPromptBackward(ISearchPrompt):
function i_search_backward (line 135) | def i_search_backward(ctx):
FILE: webmacs/commands/minibuffer.py
function move_next_word (line 21) | def move_next_word(edit, forward, mark):
function complete (line 42) | def complete(ctx):
function next_completion (line 55) | def next_completion(ctx):
function previous_completion (line 63) | def previous_completion(ctx):
function first_complqetion (line 71) | def first_complqetion(ctx):
function last_completion (line 79) | def last_completion(ctx):
function next_page_completion (line 87) | def next_page_completion(ctx):
function previous_page_completion (line 95) | def previous_page_completion(ctx):
function _prompt_history (line 102) | def _prompt_history(ctx, func):
function prompt_history_next (line 114) | def prompt_history_next(ctx):
function prompt_history_previous (line 122) | def prompt_history_previous(ctx):
function edition_finished (line 130) | def edition_finished(ctx):
function cancel (line 141) | def cancel(ctx):
function clean_aindent_bsunindent (line 153) | def clean_aindent_bsunindent(ctx):
function set_mark (line 167) | def set_mark(ctx):
function select_all (line 177) | def select_all(ctx):
function forward_char (line 186) | def forward_char(ctx):
function backward_char (line 195) | def backward_char(ctx):
function forward_word (line 204) | def forward_word(ctx):
function backward_word (line 213) | def backward_word(ctx):
function copy (line 222) | def copy(ctx):
function cut (line 232) | def cut(ctx):
function paste (line 240) | def paste(ctx):
function delete_char (line 248) | def delete_char(ctx):
function delete_word (line 256) | def delete_word(ctx):
function beginning_of_line (line 270) | def beginning_of_line(ctx):
function end_of_line (line 279) | def end_of_line(ctx):
function undo (line 288) | def undo(ctx):
function redo (line 296) | def redo(ctx):
FILE: webmacs/commands/webbuffer.py
class BufferTableModel (line 44) | class BufferTableModel(QAbstractTableModel):
method __init__ (line 46) | def __init__(self, buffers):
method rowCount (line 50) | def rowCount(self, index=QModelIndex()):
method columnCount (line 53) | def columnCount(self, index=QModelIndex()):
method data (line 56) | def data(self, index, role=Qt.ItemDataRole.DisplayRole):
method index (line 74) | def index(self, row, col, parent=QModelIndex()):
method close_buffer_at (line 80) | def close_buffer_at(self, index):
function close_buffer_in_prompt_selection (line 93) | def close_buffer_in_prompt_selection(ctx):
class BufferListPrompt (line 107) | class BufferListPrompt(Prompt):
method completer_model (line 116) | def completer_model(self):
method ordered_buffers (line 119) | def ordered_buffers(self):
method enable (line 125) | def enable(self, minibuffer):
class RecentBufferListPrompt (line 135) | class RecentBufferListPrompt(BufferListPrompt):
method ordered_buffers (line 137) | def ordered_buffers(self):
class BufferSwitchListPrompt (line 141) | class BufferSwitchListPrompt(BufferListPrompt):
class RecentBufferSwitchListPrompt (line 145) | class RecentBufferSwitchListPrompt(RecentBufferListPrompt):
class BufferKillListPrompt (line 149) | class BufferKillListPrompt(BufferListPrompt):
method enable (line 152) | def enable(self, minibuffer):
function show_buffer (line 159) | def show_buffer(buffer, view):
function switch_buffer (line 176) | def switch_buffer(ctx):
function switch_recent_buffer (line 186) | def switch_recent_buffer(ctx):
function _next_buffer (line 195) | def _next_buffer(ctx, reverse=False):
function next_buffer (line 208) | def next_buffer(ctx):
function previous_buffer (line 216) | def previous_buffer(ctx):
class OpenDevToolsPrompt (line 223) | class OpenDevToolsPrompt(BufferListPrompt):
method enable (line 227) | def enable(self, minibuffer):
function open_dev_tools (line 234) | def open_dev_tools(ctx):
function go_forward (line 245) | def go_forward(ctx):
function go_backward (line 257) | def go_backward(ctx):
function scroll_down (line 269) | def scroll_down(ctx):
function scroll_up (line 277) | def scroll_up(ctx):
function scroll_page_down (line 285) | def scroll_page_down(ctx):
function scroll_page_up (line 293) | def scroll_page_up(ctx):
function scroll_top (line 301) | def scroll_top(ctx):
function scroll_bottom (line 309) | def scroll_bottom(ctx):
function webcontent_copy (line 317) | def webcontent_copy(ctx):
function webcontent_cut (line 325) | def webcontent_cut(ctx):
function webcontent_paste (line 333) | def webcontent_paste(ctx):
function reload_buffer (line 341) | def reload_buffer(ctx):
function reload_buffer_no_cache (line 349) | def reload_buffer_no_cache(ctx):
function buffer_close (line 357) | def buffer_close(ctx):
function close_other_buffers (line 365) | def close_other_buffers(ctx):
function buffer_select_content (line 378) | def buffer_select_content(ctx):
function zoom_in (line 386) | def zoom_in(ctx):
function zoom_out (line 394) | def zoom_out(ctx):
function zoom_normal (line 402) | def zoom_normal(ctx):
function _show_info_text_zoom (line 409) | def _show_info_text_zoom(ctx):
function text_zoom_in (line 417) | def text_zoom_in(ctx):
function text_zoom_out (line 427) | def text_zoom_out(ctx):
function text_zoom_reset (line 437) | def text_zoom_reset(ctx):
function buffer_unselect (line 447) | def buffer_unselect(ctx):
function buffer_escape (line 455) | def buffer_escape(ctx):
class KilledBufferTableModel (line 468) | class KilledBufferTableModel(QAbstractTableModel):
method __init__ (line 470) | def __init__(self):
method rowCount (line 474) | def rowCount(self, index=QModelIndex()):
method columnCount (line 477) | def columnCount(self, index=QModelIndex()):
method data (line 480) | def data(self, index, role=Qt.ItemDataRole.DisplayRole):
method index (line 494) | def index(self, row, col, parent=QModelIndex()):
class KilledBufferListPrompt (line 501) | class KilledBufferListPrompt(Prompt):
method completer_model (line 509) | def completer_model(self):
method enable (line 512) | def enable(self, minibuffer):
function revive_buffer (line 519) | def revive_buffer(ctx):
function copy_current_link (line 530) | def copy_current_link(ctx):
function copy_buffer_url (line 555) | def copy_buffer_url(ctx):
function copy_buffer_title (line 564) | def copy_buffer_title(ctx):
function print_buffer (line 572) | def print_buffer(ctx):
function password_manager_fill_buffer (line 596) | def password_manager_fill_buffer(ctx):
FILE: webmacs/commands/webjump.py
function define_webjump (line 47) | def define_webjump(name, url, doc="", complete_fn=None, protocol=False):
function define_webjump_alias (line 76) | def define_webjump_alias(alias_name, webjump_name):
function define_protocol (line 91) | def define_protocol(name, doc="", complete_fn=None):
function set_default (line 95) | def set_default(name):
class WebJumpCompleter (line 106) | class WebJumpCompleter(object if version.building_doc else QObject):
method complete (line 123) | def complete(self, text):
method abort (line 127) | def abort(self):
class SyncWebJumpCompleter (line 136) | class SyncWebJumpCompleter(WebJumpCompleter):
method __init__ (line 147) | def __init__(self, complete_fn):
method complete (line 151) | def complete(self, text):
function empty_completer (line 155) | def empty_completer():
class WebJumpRequestCompleter (line 159) | class WebJumpRequestCompleter(WebJumpCompleter):
method __init__ (line 174) | def __init__(self, url_fn, extract_completions_fn):
method complete (line 180) | def complete(self, text):
method abort (line 191) | def abort(self):
method _on_reply_finished (line 195) | def _on_reply_finished(self):
function wb_complete (line 212) | def wb_complete(ctx):
class WebJumpPrompt (line 224) | class WebJumpPrompt(Prompt):
method completer_model (line 233) | def completer_model(self):
method enable (line 243) | def enable(self, minibuffer):
method eventFilter (line 267) | def eventFilter(self, obj, event):
method _set_active_webjump (line 275) | def _set_active_webjump(self, wj):
method _popup_selection_changed (line 312) | def _popup_selection_changed(self, _sel, _desel):
method _text_edited (line 318) | def _text_edited(self, text):
method start_completion (line 329) | def start_completion(self, webjump):
method _got_completions (line 336) | def _got_completions(self, data):
method close (line 344) | def close(self):
method _on_completion_activated (line 351) | def _on_completion_activated(self, index):
method value (line 373) | def value(self):
function wj_prompt (line 437) | def wj_prompt(default_input):
FILE: webmacs/content_handler.py
class WebContentHandler (line 26) | class WebContentHandler(QObject):
method __init__ (line 35) | def __init__(self, buff):
method onTextFocus (line 40) | def onTextFocus(self, enabled):
method currentLinkUrl (line 45) | def currentLinkUrl(self, url):
method onCaretBrowsing (line 49) | def onCaretBrowsing(self, enabled):
method _browserObjectActivated (line 53) | def _browserObjectActivated(self, obj):
method copyToClipboard (line 63) | def copyToClipboard(self, text):
method openExternalEditor (line 67) | def openExternalEditor(self, request_id, content):
FILE: webmacs/default_webjumps.py
function complete_google (line 28) | def complete_google():
function complete_fs (line 50) | def complete_fs():
function complete_pages (line 67) | def complete_pages():
function complete_protocol (line 78) | def complete_protocol(protocol):
function complete_duckduckgo (line 105) | def complete_duckduckgo():
FILE: webmacs/download_manager/__init__.py
function dl_path (line 50) | def dl_path(dl):
function get_user_download_dir (line 54) | def get_user_download_dir():
function find_unique_suggested_path (line 66) | def find_unique_suggested_path(dirname, filename):
function state_str (line 97) | def state_str(state):
function download_to_json (line 101) | def download_to_json(dlitem):
class DownloadManager (line 116) | class DownloadManager(QObject):
method __init__ (line 119) | def __init__(self, parent=None):
method attach_buffer (line 135) | def attach_buffer(self, buffer):
method detach_buffer (line 140) | def detach_buffer(self, buffer):
method _start_download (line 146) | def _start_download(self, dlitem):
method _download_state_changed (line 161) | def _download_state_changed(self):
method download_requested (line 168) | def download_requested(self, dl):
method _run_program (line 236) | def _run_program(self, executable, path):
method _program_finished (line 249) | def _program_finished(self, code, status):
function get_shell (line 259) | def get_shell():
FILE: webmacs/download_manager/prompts.py
function OverwriteFilePrompt (line 21) | def OverwriteFilePrompt(path):
class DlChooseActionPrompt (line 25) | class DlChooseActionPrompt(Prompt):
method __init__ (line 32) | def __init__(self, path, mimetype):
method completer_model (line 43) | def completer_model(self):
method enable (line 46) | def enable(self, minibuffer):
class DlOpenActionPrompt (line 51) | class DlOpenActionPrompt(Prompt):
method __init__ (line 59) | def __init__(self):
method completer_model (line 62) | def completer_model(self):
class DlPrompt (line 66) | class DlPrompt(Prompt):
method __init__ (line 71) | def __init__(self, path, mimetype):
method completer_model (line 76) | def completer_model(self):
method enable (line 81) | def enable(self, minibuffer):
function list_executables (line 86) | def list_executables():
FILE: webmacs/egrid.py
class LayoutEntry (line 23) | class LayoutEntry(object):
method __init__ (line 25) | def __init__(self, parent=None, item=None):
method do_split (line 31) | def do_split(self, item, direction):
method pop (line 44) | def pop(self):
method set_geometry (line 56) | def set_geometry(self, rect):
method __iter__ (line 76) | def __iter__(self):
method entry_for_item (line 83) | def entry_for_item(self, item):
class ViewGridLayout (line 90) | class ViewGridLayout(QLayout):
method __init__ (line 94) | def __init__(self, window=None):
method current_view (line 107) | def current_view(self):
method set_current_view (line 110) | def set_current_view(self, widget):
method views (line 114) | def views(self):
method __sort_views_by_position (line 117) | def __sort_views_by_position(self):
method _sort_views_by_position (line 128) | def _sort_views_by_position(self):
method add_view (line 135) | def add_view(self, widget, parent_entry, direction=None):
method entries (line 146) | def entries(self):
method addItem (line 149) | def addItem(self, item):
method count (line 152) | def count(self):
method itemAt (line 155) | def itemAt(self, index):
method takeAt (line 161) | def takeAt(self, index):
method sizeHint (line 168) | def sizeHint(self):
method setGeometry (line 175) | def setGeometry(self, rect):
method split_view (line 178) | def split_view(self, direction, reference=None):
method dump_state (line 189) | def dump_state(self):
method restore_state (line 208) | def restore_state(self, grid_data):
FILE: webmacs/external_editor.py
function open_external_editor (line 18) | def open_external_editor(content):
FILE: webmacs/features.py
class Features (line 21) | class Features(object):
method __init__ (line 22) | def __init__(self, db_path):
method set_permission (line 32) | def set_permission(self, url, feature, permission):
method get_permission (line 40) | def get_permission(self, url, feature):
FILE: webmacs/filter_webengine_output.py
class NoFilter (line 27) | class NoFilter(object):
method __init__ (line 28) | def __init__(self, *a, **kw):
method enable (line 31) | def enable(self):
class OutputFilter (line 35) | class OutputFilter(object):
method __init__ (line 39) | def __init__(self, regexes):
method enable (line 64) | def enable(self):
method _redirect (line 68) | def _redirect(self):
class FilterRegexes (line 74) | class FilterRegexes(object):
method __init__ (line 75) | def __init__(self):
method get_level_for_line (line 78) | def get_level_for_line(self, line):
method filter (line 84) | def filter(self, regexstr, level=logging.DEBUG):
function make_filter (line 88) | def make_filter():
FILE: webmacs/hooks.py
class Hook (line 17) | class Hook(list):
method call (line 18) | def call(self, *arg, **kwargs):
method remove_if_exists (line 26) | def remove_if_exists(self, cb):
FILE: webmacs/ignore_certificates.py
class IgnoredCertificates (line 19) | class IgnoredCertificates(object):
method __init__ (line 20) | def __init__(self, dbbath):
method is_ignored (line 27) | def is_ignored(self, url):
method ignore (line 32) | def ignore(self, url):
method remove (line 39) | def remove(self, url):
FILE: webmacs/ipc.py
class IPcReader (line 30) | class IPcReader(QObject):
method __init__ (line 33) | def __init__(self, sock):
method on_ready_read (line 40) | def on_ready_read(self):
method send_data (line 58) | def send_data(self, data):
method get_data (line 64) | def get_data(self):
method clear (line 70) | def clear(self):
class IpcServer (line 75) | class IpcServer(QObject):
method get_sock_name (line 77) | def get_sock_name(cls, instance):
method list_all_instances (line 86) | def list_all_instances(cls, check=True):
method instance_send (line 109) | def instance_send(cls, instance, data, cb=None):
method check_server_connection (line 126) | def check_server_connection(cls, instance=None):
method __init__ (line 133) | def __init__(self, instance=None):
method cleanup (line 146) | def cleanup(self):
method _on_new_connection (line 153) | def _on_new_connection(self):
method handle_data (line 162) | def handle_data(self, data):
method reader_disconnected (line 174) | def reader_disconnected(self):
function ipc_dispatch (line 181) | def ipc_dispatch(data):
FILE: webmacs/keyboardhandler.py
class CommandContext (line 28) | class CommandContext(object):
method __init__ (line 29) | def __init__(self):
method minibuffer (line 37) | def minibuffer(self):
class LocalKeymapSetter (line 43) | class LocalKeymapSetter(QObject):
method __init__ (line 44) | def __init__(self):
method eventFilter (line 48) | def eventFilter(self, obj, evt):
method minibuffer_input_focus_changed (line 84) | def minibuffer_input_focus_changed(self, mbi, enabled):
method view_focus_changed (line 95) | def view_focus_changed(self, view, enabled):
method web_content_edit_focus_changed (line 104) | def web_content_edit_focus_changed(self, buff, enabled):
method caret_browsing_changed (line 114) | def caret_browsing_changed(self, buff, enabled):
method buffer_mode_changed (line 124) | def buffer_mode_changed(self, buffer, old_mode):
method buffer_opened_in_view (line 130) | def buffer_opened_in_view(self, buffer):
class KeyEater (line 138) | class KeyEater(object):
method __init__ (line 142) | def __init__(self):
method set_call_handler (line 155) | def set_call_handler(self, call_handler):
method set_local_key_map (line 158) | def set_local_key_map(self, keymap):
method local_key_map (line 164) | def local_key_map(self):
method set_global_keymap_enabled (line 167) | def set_global_keymap_enabled(self, enable):
method event_filter (line 170) | def event_filter(self, obj, event):
method active_keymaps (line 178) | def active_keymaps(self):
method _add_keypress (line 184) | def _add_keypress(self, keypress):
method _num_update_prefix_arg (line 188) | def _num_update_prefix_arg(self, numstr):
method _show_info_kbd (line 194) | def _show_info_kbd(self, extra=""):
method _handle_keypress (line 200) | def _handle_keypress(self, sender, keypress):
class CallHandler (line 258) | class CallHandler(object):
method __init__ (line 259) | def __init__(self):
method call (line 262) | def call(self, ctx, keymap, keypress, command):
method no_call (line 271) | def no_call(self, sender, keymap, keypress):
method partial_call (line 274) | def partial_call(self, sender, keymap, keypress):
function send_key_event (line 281) | def send_key_event(keypress):
function local_keymap (line 289) | def local_keymap():
function set_local_keymap (line 293) | def set_local_keymap(keymap):
function set_global_keymap_enabled (line 297) | def set_global_keymap_enabled(enable):
FILE: webmacs/keymaps/__init__.py
function _set_key (line 30) | def _set_key(key, char, *chars):
function is_one_letter_upcase (line 213) | def is_one_letter_upcase(char):
class KeyPress (line 221) | class KeyPress(_KeyPress):
method from_qevent (line 223) | def from_qevent(cls, event):
method from_str (line 248) | def from_str(cls, string):
method to_qevent (line 282) | def to_qevent(self, type):
method has_any_modifier (line 297) | def has_any_modifier(self):
method char (line 301) | def char(self):
method __str__ (line 307) | def __str__(self):
method __repr__ (line 321) | def __repr__(self):
class InternalKeymap (line 329) | class InternalKeymap(object):
method __init__ (line 332) | def __init__(self, parent=None):
method _traverse_commands (line 336) | def _traverse_commands(self, prefix, acc_fn, parent=None):
method traverse_commands (line 352) | def traverse_commands(self, acc_fn):
method all_bindings (line 355) | def all_bindings(self, raw_fn=False, with_parent=True):
method _define_key (line 371) | def _define_key(self, key, binding):
method define_key (line 390) | def define_key(self, key, binding=None):
method undefine_key (line 409) | def undefine_key(self, key):
method _look_up (line 424) | def _look_up(self, keypress):
method lookup (line 432) | def lookup(self, keypresses):
class Keymap (line 455) | class Keymap(InternalKeymap):
method __init__ (line 458) | def __init__(self, name, parent=None, doc=None):
method __str__ (line 467) | def __str__(self):
method brief_doc (line 471) | def brief_doc(self):
function global_keymap (line 538) | def global_keymap():
function webbuffer_keymap (line 552) | def webbuffer_keymap():
function content_edit_keymap (line 566) | def content_edit_keymap():
function keymap (line 581) | def keymap(name):
FILE: webmacs/keymaps/fullscreen.py
function exit_full_screen (line 25) | def exit_full_screen(ctx):
FILE: webmacs/killed_buffers.py
class KilledBuffer (line 34) | class KilledBuffer(object):
method update_max_size (line 38) | def update_max_size(cls, nb):
method __init__ (line 44) | def __init__(self, url, title, icon, history_data, delayed):
method from_buffer (line 53) | def from_buffer(cls, buff):
method revive (line 66) | def revive(self):
FILE: webmacs/main.py
function signal_wakeup (line 41) | def signal_wakeup(app):
function setup_logging (line 60) | def setup_logging(level, webcontent_level):
function setup_logging_on_disk (line 83) | def setup_logging_on_disk(log_dir, backup_count=5):
function parse_args (line 111) | def parse_args(argv=None):
function init (line 157) | def init(opts):
function _handle_user_init_error (line 203) | def _handle_user_init_error(conf_path, msg):
function load_user_module (line 221) | def load_user_module(conf_path):
function load_user_module (line 232) | def load_user_module(conf_path):
function main (line 240) | def main():
FILE: webmacs/minibuffer/__init__.py
class Popup (line 30) | class Popup(QTableView):
method __init__ (line 31) | def __init__(self, window, buffer_input):
method _resize (line 57) | def _resize(self, size):
method popup (line 74) | def popup(self):
method eventFilter (line 80) | def eventFilter(self, obj, event):
class MinibufferInput (line 87) | class MinibufferInput(QLineEdit):
method __init__ (line 93) | def __init__(self, parent, window):
method configure_completer (line 111) | def configure_completer(self, opts):
method keymap (line 120) | def keymap(self):
method eventFilter (line 126) | def eventFilter(self, obj, event):
method event (line 155) | def event(self, evt):
method set_completer_model (line 163) | def set_completer_model(self, completer_model):
method completer_model (line 166) | def completer_model(self):
method set_match (line 169) | def set_match(self, type):
method _on_row_changed (line 174) | def _on_row_changed(self, current, old):
method _show_completions (line 178) | def _show_completions(self, txt, force=False):
method show_completions (line 200) | def show_completions(self, filter_text=None):
method _on_completion_activated (line 204) | def _on_completion_activated(self, index, hide_popup=True):
method popup (line 214) | def popup(self):
method complete (line 217) | def complete(self, hide_popup=True):
method select_next_completion (line 228) | def select_next_completion(self, forward=True, steps=1):
method select_first_completion (line 250) | def select_first_completion(self):
method select_last_completion (line 253) | def select_last_completion(self):
method select_next_page_completion (line 257) | def select_next_page_completion(self, forward=True):
method mark (line 261) | def mark(self):
method set_mark (line 264) | def set_mark(self, value=None):
method reinit (line 270) | def reinit(self):
method set_right_italic_text (line 276) | def set_right_italic_text(self, text):
method paintEvent (line 280) | def paintEvent(self, event):
method background_color (line 294) | def background_color(self):
method background_color (line 298) | def background_color(self, color):
function _update_minibuffer_height (line 304) | def _update_minibuffer_height(var):
class Minibuffer (line 318) | class Minibuffer(QWidget):
method __init__ (line 319) | def __init__(self, window):
method set_height (line 347) | def set_height(self, height):
method eventFilter (line 350) | def eventFilter(self, obj, event):
method show_info (line 360) | def show_info(self, text):
method input (line 364) | def input(self):
method prompt (line 367) | def prompt(self):
method do_prompt (line 370) | def do_prompt(self, prompt, **kwargs):
method close_prompt (line 378) | def close_prompt(self):
method _prompt_closed (line 383) | def _prompt_closed(self):
FILE: webmacs/minibuffer/prompt.py
class FSModel (line 53) | class FSModel(QAbstractTableModel):
method __init__ (line 60) | def __init__(self, parent=None):
method rowCount (line 65) | def rowCount(self, index=QModelIndex()):
method columnCount (line 68) | def columnCount(self, index=QModelIndex()):
method data (line 71) | def data(self, index, role=Qt.ItemDataRole.DisplayRole):
method text_changed (line 81) | def text_changed(self, text):
class PromptTableModel (line 98) | class PromptTableModel(QAbstractTableModel):
method __init__ (line 99) | def __init__(self, data, parent=None):
method rowCount (line 103) | def rowCount(self, index=QModelIndex()):
method columnCount (line 106) | def columnCount(self, index=QModelIndex()):
method data (line 111) | def data(self, index, role=Qt.ItemDataRole.DisplayRole):
method index (line 117) | def index(self, row, col, parent=QModelIndex()):
function _prompt_exec (line 124) | def _prompt_exec(prompt, loop):
class Prompt (line 129) | class Prompt(QObject):
method __init__ (line 142) | def __init__(self, ctx):
method completer_model (line 147) | def completer_model(self):
method enable (line 150) | def enable(self, minibuffer):
method close (line 173) | def close(self):
method flash (line 201) | def flash(self):
method _create_flash_animation (line 209) | def _create_flash_animation(self):
method _on_completion_activated (line 231) | def _on_completion_activated(self, index):
method value (line 234) | def value(self):
method index (line 244) | def index(self):
method _on_edition_finished (line 248) | def _on_edition_finished(self):
method exec (line 256) | def exec(self, minibuffer, flash=False, sync=True):
class PromptHistory (line 267) | class PromptHistory(object):
method __init__ (line 272) | def __init__(self, maxsize=50):
method reset (line 276) | def reset(self):
method push (line 281) | def push(self, text):
method in_user_value (line 287) | def in_user_value(self):
method set_user_value (line 293) | def set_user_value(self, text):
method __get (line 296) | def __get(self, delta):
method get_next (line 314) | def get_next(self):
method get_previous (line 317) | def get_previous(self):
class YesNoPrompt (line 321) | class YesNoPrompt(Prompt):
method __init__ (line 329) | def __init__(self, label, parent=None, always=False, never=False):
method build_label (line 337) | def build_label(self):
method build_valid_keys (line 345) | def build_valid_keys(self):
method enable (line 353) | def enable(self, minibuffer):
method _on_text_edited (line 365) | def _on_text_edited(self, text):
method value (line 378) | def value(self):
method eventFilter (line 381) | def eventFilter(self, obj, evt):
method close (line 391) | def close(self):
class AskPasswordPrompt (line 397) | class AskPasswordPrompt(Prompt):
method __init__ (line 398) | def __init__(self, buffer):
method _on_edition_finished (line 405) | def _on_edition_finished(self):
FILE: webmacs/minibuffer/right_label.py
function update_minibuffer_right_label (line 31) | def update_minibuffer_right_label(window):
function _update_minibuffer_right_label (line 40) | def _update_minibuffer_right_label(window):
function update_minibuffer_right_labels (line 74) | def update_minibuffer_right_labels():
function update_label_for_buffer (line 82) | def update_label_for_buffer(buff):
function init_minibuffer_right_labels (line 86) | def init_minibuffer_right_labels():
FILE: webmacs/mode.py
class Mode (line 27) | class Mode(object):
method __init__ (line 33) | def __init__(self, name, description):
method keymap (line 43) | def keymap(self):
method content_edit_keymap (line 46) | def content_edit_keymap(self):
method caret_browsing_keymap (line 49) | def caret_browsing_keymap(self):
method keymap_for_mode (line 52) | def keymap_for_mode(self, mode):
method fullscreen_keymap (line 55) | def fullscreen_keymap(self):
method __str__ (line 58) | def __str__(self):
function get_mode (line 62) | def get_mode(name):
function define_mode (line 66) | def define_mode(mode):
class EmptyMode (line 74) | class EmptyMode(Mode):
method keymap (line 76) | def keymap(self):
function get_auto_modename_for_url (line 90) | def get_auto_modename_for_url(url, default="standard-mode"):
function _set_auto_buffer_modes (line 98) | def _set_auto_buffer_modes(modes):
FILE: webmacs/password_manager/__init__.py
function make_password_manager (line 45) | def make_password_manager():
class PasswordManagerNotReady (line 55) | class PasswordManagerNotReady(Exception):
class BasePaswordManager (line 59) | class BasePaswordManager(QObject):
method __init__ (line 60) | def __init__(self, parent=None):
method credential_for_url (line 63) | def credential_for_url(self, url):
method complete_buffer (line 67) | def complete_buffer(self, buffer, credential):
FILE: webmacs/password_manager/password_store.py
class PassCredentials (line 36) | class PassCredentials:
method __init__ (line 40) | def __init__(self):
method add_credential (line 46) | def add_credential(self, name, url, credential):
method compile (line 52) | def compile(self):
method for_url (line 66) | def for_url(self, url):
method names (line 75) | def names(self):
method for_name (line 82) | def for_name(self, name):
class ReadCredentialsTask (line 89) | class ReadCredentialsTask(Task):
method __init__ (line 90) | def __init__(self):
method start (line 98) | def start(self):
method __process_next (line 105) | def __process_next(self):
method __process_finished (line 118) | def __process_finished(self, code, status):
method __process_read (line 141) | def __process_read(self):
method credentials (line 144) | def credentials(self):
method abort (line 147) | def abort(self):
class Pass (line 154) | class Pass(BasePaswordManager):
method __init__ (line 161) | def __init__(self):
method __on_reloaded (line 167) | def __on_reloaded(self):
method reload (line 173) | def reload(self):
method credential_for_url (line 183) | def credential_for_url(self, url):
FILE: webmacs/profile.py
function make_dir (line 51) | def make_dir(*parts):
class Profile (line 57) | class Profile(object):
method __init__ (line 58) | def __init__(self, name, off_the_record=False):
method update_spell_checking (line 183) | def update_spell_checking(self):
method is_off_the_record (line 188) | def is_off_the_record(self):
FILE: webmacs/scheme_handlers/__init__.py
function all_schemes (line 20) | def all_schemes():
function register_schemes (line 24) | def register_schemes():
FILE: webmacs/scheme_handlers/webmacs/__init__.py
function register_page (line 35) | def register_page(match_url=None, visible=True):
class WebmacsSchemeHandler (line 52) | class WebmacsSchemeHandler(QWebEngineUrlSchemeHandler):
method __init__ (line 55) | def __init__(self, parent=None):
method reply_template (line 62) | def reply_template(self, job, tpl_name, data):
method requestStarted (line 68) | def requestStarted(self, job):
method to_js (line 79) | def to_js(self, job, _, path):
method version (line 86) | def version(self, job, _, name):
method downloads (line 102) | def downloads(self, job, _, name):
method commands (line 106) | def commands(self, job, _, name):
method command (line 110) | def command(self, job, _, command):
method variables (line 135) | def variables(self, job, _, name):
method variable (line 139) | def variable(self, job, _, name):
method keymap (line 143) | def keymap(self, job, _, keymap):
method bindings (line 166) | def bindings(self, job, _, name):
method pydoc (line 170) | def pydoc(self, job, url):
method key (line 200) | def key(self, job, url):
function get_src_url (line 248) | def get_src_url(obj):
FILE: webmacs/scheme_handlers/webmacs/js/vue.js
function isUndef (line 16) | function isUndef (v) {
function isDef (line 20) | function isDef (v) {
function isTrue (line 24) | function isTrue (v) {
function isFalse (line 28) | function isFalse (v) {
function isPrimitive (line 35) | function isPrimitive (value) {
function isObject (line 48) | function isObject (obj) {
function isPlainObject (line 58) | function isPlainObject (obj) {
function isRegExp (line 62) | function isRegExp (v) {
function isValidArrayIndex (line 69) | function isValidArrayIndex (val) {
function toString (line 77) | function toString (val) {
function toNumber (line 89) | function toNumber (val) {
function makeMap (line 98) | function makeMap (
function remove (line 125) | function remove (arr, item) {
function hasOwn (line 138) | function hasOwn (obj, key) {
function cached (line 145) | function cached (fn) {
function bind (line 179) | function bind (fn, ctx) {
function toArray (line 196) | function toArray (list, start) {
function extend (line 209) | function extend (to, _from) {
function toObject (line 219) | function toObject (arr) {
function noop (line 234) | function noop (a, b, c) {}
function genStaticKeys (line 249) | function genStaticKeys (modules) {
function looseEqual (line 259) | function looseEqual (a, b) {
function looseIndexOf (line 292) | function looseIndexOf (arr, val) {
function once (line 302) | function once (fn) {
function isReserved (line 428) | function isReserved (str) {
function def (line 436) | function def (obj, key, val, enumerable) {
function parsePath (line 449) | function parsePath (path) {
function handleError (line 560) | function handleError (err, vm, info) {
function isNative (line 630) | function isNative (Ctor) {
function nextTickHandler (line 646) | function nextTickHandler () {
function Set (line 732) | function Set () {
function pushTarget (line 791) | function pushTarget (_target) {
function popTarget (line 796) | function popTarget () {
function protoAugment (line 904) | function protoAugment (target, src, keys) {
function copyAugment (line 915) | function copyAugment (target, src, keys) {
function observe (line 927) | function observe (value, asRootData) {
function defineReactive$$1 (line 952) | function defineReactive$$1 (
function set (line 1013) | function set (target, key, val) {
function del (line 1043) | function del (target, key) {
function dependArray (line 1070) | function dependArray (value) {
function mergeData (line 1107) | function mergeData (to, from) {
function mergeDataOrFn (line 1127) | function mergeDataOrFn (
function mergeHook (line 1194) | function mergeHook (
function mergeAssets (line 1218) | function mergeAssets (parentVal, childVal) {
function checkComponents (line 1284) | function checkComponents (options) {
function normalizeProps (line 1300) | function normalizeProps (options) {
function normalizeInject (line 1331) | function normalizeInject (options) {
function normalizeDirectives (line 1344) | function normalizeDirectives (options) {
function mergeOptions (line 1360) | function mergeOptions (
function resolveAsset (line 1407) | function resolveAsset (
function validateProp (line 1437) | function validateProp (
function getPropDefaultValue (line 1473) | function getPropDefaultValue (vm, prop, key) {
function assertProp (line 1506) | function assertProp (
function assertType (line 1558) | function assertType (value, type) {
function getType (line 1586) | function getType (fn) {
function isType (line 1591) | function isType (type, fn) {
function createTextVNode (line 1757) | function createTextVNode (val) {
function cloneVNode (line 1765) | function cloneVNode (vnode, deep) {
function cloneVNodes (line 1787) | function cloneVNodes (vnodes, deep) {
function createFnInvoker (line 1815) | function createFnInvoker (fns) {
function prioritizePlainEvents (line 1835) | function prioritizePlainEvents (a, b) {
function updateListeners (line 1839) | function updateListeners (
function mergeVNodeHook (line 1887) | function mergeVNodeHook (def, hookKey, hook) {
function extractPropsFromVNodeData (line 1919) | function extractPropsFromVNodeData (
function checkProp (line 1960) | function checkProp (
function simpleNormalizeChildren (line 1999) | function simpleNormalizeChildren (children) {
function normalizeChildren (line 2012) | function normalizeChildren (children) {
function isTextNode (line 2020) | function isTextNode (node) {
function normalizeArrayChildren (line 2024) | function normalizeArrayChildren (children, nestedIndex) {
function ensureCtor (line 2065) | function ensureCtor (comp, base) {
function createAsyncPlaceholder (line 2074) | function createAsyncPlaceholder (
function resolveAsyncComponent (line 2087) | function resolveAsyncComponent (
function isAsyncPlaceholder (line 2189) | function isAsyncPlaceholder (node) {
function getFirstComponentChild (line 2195) | function getFirstComponentChild (children) {
function initEvents (line 2210) | function initEvents (vm) {
function add (line 2222) | function add (event, fn, once$$1) {
function remove$1 (line 2230) | function remove$1 (event, fn) {
function updateComponentListeners (line 2234) | function updateComponentListeners (
function eventsMixin (line 2243) | function eventsMixin (Vue) {
function resolveSlots (line 2350) | function resolveSlots (
function isWhitespace (line 2389) | function isWhitespace (node) {
function resolveScopedSlots (line 2393) | function resolveScopedSlots (
function initLifecycle (line 2413) | function initLifecycle (vm) {
function lifecycleMixin (line 2439) | function lifecycleMixin (Vue) {
function mountComponent (line 2529) | function mountComponent (
function updateChildComponent (line 2594) | function updateChildComponent (
function isInInactiveTree (line 2659) | function isInInactiveTree (vm) {
function activateChildComponent (line 2666) | function activateChildComponent (vm, direct) {
function deactivateChildComponent (line 2684) | function deactivateChildComponent (vm, direct) {
function callHook (line 2700) | function callHook (vm, hook) {
function resetSchedulerState (line 2732) | function resetSchedulerState () {
function flushSchedulerQueue (line 2744) | function flushSchedulerQueue () {
function callUpdatedHooks (line 2799) | function callUpdatedHooks (queue) {
function queueActivatedComponent (line 2814) | function queueActivatedComponent (vm) {
function callActivatedHooks (line 2821) | function callActivatedHooks (queue) {
function queueWatcher (line 2833) | function queueWatcher (watcher) {
function traverse (line 3069) | function traverse (val) {
function _traverse (line 3074) | function _traverse (val, seen) {
function proxy (line 3106) | function proxy (target, sourceKey, key) {
function initState (line 3116) | function initState (vm) {
function checkOptionType (line 3132) | function checkOptionType (vm, name) {
function initProps (line 3142) | function initProps (vm, propsOptions) {
function initData (line 3186) | function initData (vm) {
function getData (line 3228) | function getData (data, vm) {
function initComputed (line 3239) | function initComputed (vm, computed) {
function defineComputed (line 3280) | function defineComputed (
function createComputedGetter (line 3313) | function createComputedGetter (key) {
function initMethods (line 3328) | function initMethods (vm, methods) {
function initWatch (line 3357) | function initWatch (vm, watch) {
function createWatcher (line 3371) | function createWatcher (
function stateMixin (line 3387) | function stateMixin (Vue) {
function initProvide (line 3436) | function initProvide (vm) {
function initInjections (line 3445) | function initInjections (vm) {
function resolveInject (line 3466) | function resolveInject (inject, vm) {
function createFunctionalComponent (line 3498) | function createFunctionalComponent (
function mergeProps (line 3538) | function mergeProps (to, from) {
function createComponent (line 3616) | function createComponent (
function createComponentInstanceForVnode (line 3714) | function createComponentInstanceForVnode (
function mergeHooks (line 3741) | function mergeHooks (data) {
function mergeHook$1 (line 3753) | function mergeHook$1 (one, two) {
function transformModel (line 3762) | function transformModel (options, data) {
function createElement (line 3780) | function createElement (
function _createElement (line 3799) | function _createElement (
function applyNS (line 3879) | function applyNS (vnode, ns) {
function renderList (line 3900) | function renderList (
function renderSlot (line 3934) | function renderSlot (
function resolveFilter (line 3967) | function resolveFilter (id) {
function checkKeyCodes (line 3976) | function checkKeyCodes (
function bindObjectProps (line 3994) | function bindObjectProps (
function renderStatic (line 4048) | function renderStatic (
function markOnce (line 4071) | function markOnce (
function markStatic (line 4080) | function markStatic (
function markStaticNode (line 4096) | function markStaticNode (node, key, isOnce) {
function bindObjectListeners (line 4104) | function bindObjectListeners (data, value) {
function initRender (line 4125) | function initRender (vm) {
function renderMixin (line 4156) | function renderMixin (Vue) {
function initMixin (line 4242) | function initMixin (Vue) {
function initInternalComponent (line 4299) | function initInternalComponent (vm, options) {
function resolveConstructorOptions (line 4316) | function resolveConstructorOptions (Ctor) {
function resolveModifiedOptions (line 4340) | function resolveModifiedOptions (Ctor) {
function dedupe (line 4354) | function dedupe (latest, extended, sealed) {
function Vue$3 (line 4373) | function Vue$3 (options) {
function initUse (line 4390) | function initUse (Vue) {
function initMixin$1 (line 4412) | function initMixin$1 (Vue) {
function initExtend (line 4421) | function initExtend (Vue) {
function initProps$1 (line 4503) | function initProps$1 (Comp) {
function initComputed$1 (line 4510) | function initComputed$1 (Comp) {
function initAssetRegisters (line 4519) | function initAssetRegisters (Vue) {
function getComponentName (line 4558) | function getComponentName (opts) {
function matches (line 4562) | function matches (pattern, name) {
function pruneCache (line 4574) | function pruneCache (cache, current, filter) {
function pruneCacheEntry (line 4589) | function pruneCacheEntry (vnode) {
function initGlobalAPI (line 4659) | function initGlobalAPI (Vue) {
function genClassForVnode (line 4762) | function genClassForVnode (vnode) {
function mergeClassData (line 4780) | function mergeClassData (child, parent) {
function renderClass (line 4789) | function renderClass (
function concat (line 4800) | function concat (a, b) {
function stringifyClass (line 4804) | function stringifyClass (value) {
function stringifyArray (line 4818) | function stringifyArray (value) {
function stringifyObject (line 4830) | function stringifyObject (value) {
function getTagNamespace (line 4877) | function getTagNamespace (tag) {
function isUnknownElement (line 4889) | function isUnknownElement (tag) {
function query (line 4921) | function query (el) {
function createElement$1 (line 4938) | function createElement$1 (tagName, vnode) {
function createElementNS (line 4950) | function createElementNS (namespace, tagName) {
function createTextNode (line 4954) | function createTextNode (text) {
function createComment (line 4958) | function createComment (text) {
function insertBefore (line 4962) | function insertBefore (parentNode, newNode, referenceNode) {
function removeChild (line 4966) | function removeChild (node, child) {
function appendChild (line 4970) | function appendChild (node, child) {
function parentNode (line 4974) | function parentNode (node) {
function nextSibling (line 4978) | function nextSibling (node) {
function tagName (line 4982) | function tagName (node) {
function setTextContent (line 4986) | function setTextContent (node, text) {
function setAttribute (line 4990) | function setAttribute (node, key, val) {
function registerRef (line 5027) | function registerRef (vnode, isRemoval) {
function sameVnode (line 5070) | function sameVnode (a, b) {
function sameInputType (line 5087) | function sameInputType (a, b) {
function createKeyToOldIdx (line 5095) | function createKeyToOldIdx (children, beginIdx, endIdx) {
function createPatchFunction (line 5105) | function createPatchFunction (backend) {
function updateDirectives (line 5750) | function updateDirectives (oldVnode, vnode) {
function _update (line 5756) | function _update (oldVnode, vnode) {
function normalizeDirectives$1 (line 5818) | function normalizeDirectives$1 (
function getRawDirName (line 5838) | function getRawDirName (dir) {
function callHook$1 (line 5842) | function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) {
function updateAttrs (line 5860) | function updateAttrs (oldVnode, vnode) {
function setAttr (line 5900) | function setAttr (el, key, value) {
function updateClass (line 5938) | function updateClass (oldVnode, vnode) {
function parseFilters (line 5978) | function parseFilters (exp) {
function wrapFilter (line 6060) | function wrapFilter (exp, filter) {
function baseWarn (line 6074) | function baseWarn (msg) {
function pluckModuleFunction (line 6078) | function pluckModuleFunction (
function addProp (line 6087) | function addProp (el, name, value) {
function addAttr (line 6091) | function addAttr (el, name, value) {
function addDirective (line 6095) | function addDirective (
function addHandler (line 6106) | function addHandler (
function getBindingAttr (line 6158) | function getBindingAttr (
function getAndRemoveAttr (line 6176) | function getAndRemoveAttr (el, name) {
function genComponentModel (line 6195) | function genComponentModel (
function genAssignmentCode (line 6227) | function genAssignmentCode (
function parseModel (line 6260) | function parseModel (val) {
function next (line 6288) | function next () {
function eof (line 6292) | function eof () {
function isStringStart (line 6296) | function isStringStart (chr) {
function parseBracket (line 6300) | function parseBracket (chr) {
function parseString (line 6318) | function parseString (chr) {
function model (line 6337) | function model (
function genCheckboxModel (line 6395) | function genCheckboxModel (
function genRadioModel (line 6426) | function genRadioModel (
function genSelect (line 6438) | function genSelect (
function genDefaultModel (line 6455) | function genDefaultModel (
function normalizeEvents (line 6498) | function normalizeEvents (on) {
function add$1 (line 6517) | function add$1 (
function remove$2 (line 6545) | function remove$2 (
function updateDOMListeners (line 6554) | function updateDOMListeners (oldVnode, vnode) {
function updateDOMProps (line 6572) | function updateDOMProps (oldVnode, vnode) {
function shouldUpdateValue (line 6618) | function shouldUpdateValue (
function isDirty (line 6630) | function isDirty (elm, checkVal) {
function isInputChanged (line 6640) | function isInputChanged (elm, newVal) {
function normalizeStyleData (line 6673) | function normalizeStyleData (data) {
function normalizeStyleBinding (line 6683) | function normalizeStyleBinding (bindingStyle) {
function getStyle (line 6697) | function getStyle (vnode, checkChild) {
function updateStyle (line 6767) | function updateStyle (oldVnode, vnode) {
function addClass (line 6821) | function addClass (el, cls) {
function removeClass (line 6846) | function removeClass (el, cls) {
function resolveTransition (line 6879) | function resolveTransition (def$$1) {
function nextFrame (line 6937) | function nextFrame (fn) {
function addTransitionClass (line 6943) | function addTransitionClass (el, cls) {
function removeTransitionClass (line 6951) | function removeTransitionClass (el, cls) {
function whenTransitionEnds (line 6958) | function whenTransitionEnds (
function getTransitionInfo (line 6991) | function getTransitionInfo (el, expectedType) {
function getTimeout (line 7040) | function getTimeout (delays, durations) {
function toMs (line 7051) | function toMs (s) {
function enter (line 7057) | function enter (vnode, toggleDisplay) {
function leave (line 7206) | function leave (vnode, rm) {
function checkDuration (line 7309) | function checkDuration (val, name, vnode) {
function isValidDuration (line 7325) | function isValidDuration (val) {
function getHookArgumentsLength (line 7335) | function getHookArgumentsLength (fn) {
function _enter (line 7352) | function _enter (_, vnode) {
function setSelected (line 7451) | function setSelected (el, binding, vm) {
function actuallySetSelected (line 7461) | function actuallySetSelected (el, binding, vm) {
function hasNoMatchingOption (line 7494) | function hasNoMatchingOption (value, options) {
function getValue (line 7498) | function getValue (option) {
function onCompositionStart (line 7504) | function onCompositionStart (e) {
function onCompositionEnd (line 7508) | function onCompositionEnd (e) {
function trigger (line 7515) | function trigger (el, type) {
function locateNode (line 7524) | function locateNode (vnode) {
function getRealChild (line 7615) | function getRealChild (vnode) {
function extractTransitionData (line 7624) | function extractTransitionData (comp) {
function placeholder (line 7640) | function placeholder (h, rawChild) {
function hasParentTransition (line 7648) | function hasParentTransition (vnode) {
function isSameChild (line 7656) | function isSameChild (child, oldChild) {
function callPendingCbs (line 7917) | function callPendingCbs (c) {
function recordPosition (line 7928) | function recordPosition (c) {
function applyTranslation (line 7932) | function applyTranslation (c) {
function shouldDecode (line 8003) | function shouldDecode (content, encoded) {
function parseText (line 8024) | function parseText (
function transformNode (line 8054) | function transformNode (el, options) {
function genData (line 8077) | function genData (el) {
function transformNode$1 (line 8096) | function transformNode$1 (el, options) {
function genData$1 (line 8121) | function genData$1 (el) {
function text (line 8145) | function text (el, dir) {
function html (line 8153) | function html (el, dir) {
function decodeAttr (line 8262) | function decodeAttr (value, shouldDecodeNewlines) {
function parseHTML (line 8267) | function parseHTML (html, options) {
function parse (line 8547) | function parse (
function processPre (line 8790) | function processPre (el) {
function processRawAttrs (line 8796) | function processRawAttrs (el) {
function processKey (line 8812) | function processKey (el) {
function processRef (line 8822) | function processRef (el) {
function processFor (line 8830) | function processFor (el) {
function processIf (line 8855) | function processIf (el) {
function processIfConditions (line 8874) | function processIfConditions (el, parent) {
function findPrevElement (line 8889) | function findPrevElement (children) {
function addIfCondition (line 8906) | function addIfCondition (el, condition) {
function processOnce (line 8913) | function processOnce (el) {
function processSlot (line 8920) | function processSlot (el) {
function processComponent (line 8943) | function processComponent (el) {
function processAttrs (line 8953) | function processAttrs (el) {
function checkInFor (line 9029) | function checkInFor (el) {
function parseModifiers (line 9040) | function parseModifiers (name) {
function makeAttrsMap (line 9049) | function makeAttrsMap (attrs) {
function isTextTag (line 9064) | function isTextTag (el) {
function isForbiddenTag (line 9068) | function isForbiddenTag (el) {
function guardIESVGBug (line 9082) | function guardIESVGBug (attrs) {
function checkForAliasModel (line 9094) | function checkForAliasModel (el, value) {
function optimize (line 9128) | function optimize (root, options) {
function genStaticKeys$1 (line 9138) | function genStaticKeys$1 (keys) {
function markStatic$1 (line 9145) | function markStatic$1 (node) {
function markStaticRoots (line 9177) | function markStaticRoots (node, isInFor) {
function isStatic (line 9207) | function isStatic (node) {
function isDirectChildOfTemplateFor (line 9224) | function isDirectChildOfTemplateFor (node) {
function genHandlers (line 9273) | function genHandlers (
function genHandler (line 9296) | function genHandler (
function genKeyFilter (line 9346) | function genKeyFilter (keys) {
function genFilterCode (line 9350) | function genFilterCode (key) {
function on (line 9361) | function on (el, dir) {
function bind$1 (line 9370) | function bind$1 (el, dir) {
function generate (line 9400) | function generate (
function genElement (line 9412) | function genElement (el, state) {
function genStatic (line 9445) | function genStatic (el, state) {
function genOnce (line 9452) | function genOnce (el, state) {
function genIf (line 9478) | function genIf (
function genIfConditions (line 9488) | function genIfConditions (
function genFor (line 9515) | function genFor (
function genData$2 (line 9547) | function genData$2 (el, state) {
function genDirectives (line 9624) | function genDirectives (el, state) {
function genInlineTemplate (line 9649) | function genInlineTemplate (el, state) {
function genScopedSlots (line 9662) | function genScopedSlots (
function genScopedSlot (line 9671) | function genScopedSlot (
function genForScopedSlot (line 9685) | function genForScopedSlot (
function genChildren (line 9701) | function genChildren (
function getNormalizationType (line 9731) | function getNormalizationType (
function needsNormalization (line 9754) | function needsNormalization (el) {
function genNode (line 9758) | function genNode (node, state) {
function genText (line 9768) | function genText (text) {
function genComment (line 9774) | function genComment (comment) {
function genSlot (line 9778) | function genSlot (el, state) {
function genComponent (line 9797) | function genComponent (
function genProps (line 9806) | function genProps (props) {
function transformSpecialNewlines (line 9816) | function transformSpecialNewlines (text) {
function detectErrors (line 9844) | function detectErrors (ast) {
function checkNode (line 9852) | function checkNode (node, errors) {
function checkEvent (line 9878) | function checkEvent (exp, text, errors) {
function checkFor (line 9890) | function checkFor (node, text, errors) {
function checkIdentifier (line 9897) | function checkIdentifier (ident, type, text, errors) {
function checkExpression (line 9903) | function checkExpression (exp, text, errors) {
function createFunction (line 9921) | function createFunction (code, errors) {
function createCompileToFunctionFn (line 9930) | function createCompileToFunctionFn (compile) {
function createCompilerCreator (line 10016) | function createCompilerCreator (baseCompile) {
function getOuterHTML (line 10169) | function getOuterHTML (el) {
FILE: webmacs/scripts/caret_browsing.js
function listener (line 765) | function listener() {
FILE: webmacs/scripts/hint.js
function clickLike (line 1) | function clickLike(elem) {
function rectElementInViewport (line 22) | function rectElementInViewport(elem, w) { // eslint-disable-line comple...
function escapeRegExp (line 49) | function escapeRegExp(str) {
class BaseHint (line 53) | class BaseHint {
method constructor (line 54) | constructor(obj, manager, index, rect) {
method text (line 70) | text() {
method url (line 77) | url() {
method id (line 84) | id() {
method serialize (line 88) | serialize() {
method remove (line 97) | remove() {
method setVisible (line 101) | setVisible(on) {
method isVisible (line 105) | isVisible() {
method refresh (line 109) | refresh() {}
class Hint (line 112) | class Hint extends BaseHint {
method constructor (line 113) | constructor(obj, manager, rect, index) {
method remove (line 121) | remove() {
method setVisible (line 127) | setVisible(on) {
method refresh (line 132) | refresh() {
method id (line 146) | id() {
class AlphabetHint (line 151) | class AlphabetHint extends BaseHint {
method constructor (line 152) | constructor(obj, manager, rect, index) {
class HintFrame (line 157) | class HintFrame {
method constructor (line 158) | constructor(frame) {
method remove (line 162) | remove() {
function xpath_lookup_namespace (line 176) | function xpath_lookup_namespace (prefix) {
class Hinter (line 180) | class Hinter {
method init (line 181) | init(selector, method, options) {
method lookup (line 194) | lookup(hint_index) {
method hintsCreated (line 240) | hintsCreated(count) {
method configure_hints (line 258) | configure_hints(index, labels, parent_indexes) {
method frame_configure_hints (line 287) | frame_configure_hints(args) {
method selectBrowserObjects (line 291) | selectBrowserObjects(selector, method, options) {
method clearBrowserObjects (line 299) | clearBrowserObjects() {
method frameUpActivateHint (line 310) | frameUpActivateHint(indexes) {
method clearFrameSelection (line 331) | clearFrameSelection() {
method setCurrentActiveHint (line 345) | setCurrentActiveHint(indexes) {
method frameActivateNextHint (line 354) | frameActivateNextHint(args) {
method activateNextHint (line 428) | activateNextHint(backward) {
method followCurrentLink (line 435) | followCurrentLink() {
method frameSelectVisibleHint (line 446) | frameSelectVisibleHint(args) {
method selectVisibleHint (line 478) | selectVisibleHint(index) {
method frameFilterSelection (line 485) | frameFilterSelection(args) {
method filterSelection (line 564) | filterSelection(text) {
function currentLinkUrl (line 576) | function currentLinkUrl() {
FILE: webmacs/scripts/setup.js
constant MESSAGE_HANDLERS (line 17) | let MESSAGE_HANDLERS = {};
function post_message (line 30) | function post_message(w, name, args) {
function register_message_handler (line 53) | function register_message_handler(name, func) {
function post_webmacs_message (line 63) | function post_webmacs_message(name, args) {
function isTextInput (line 71) | function isTextInput(node) {
function registerWebmacs (line 111) | function registerWebmacs(w) {
function registerWebChannel (line 127) | function registerWebChannel() {
FILE: webmacs/session.py
function _session_load (line 27) | def _session_load(stream):
function _session_save (line 74) | def _session_save(stream):
function session_clean (line 89) | def session_clean():
function session_load (line 101) | def session_load(session_file):
function session_save (line 120) | def session_save(session_file):
FILE: webmacs/spell_checking.py
class Versions (line 43) | class Versions(dict):
method from_file (line 45) | def from_file(cls, path):
method get_version (line 52) | def get_version(self, name):
method write (line 55) | def write(self, path):
class SpellCheckingTask (line 60) | class SpellCheckingTask(Task):
method __init__ (line 61) | def __init__(self, app, path):
method start (line 70) | def start(self):
method _on_bdic_files_list (line 85) | def _on_bdic_files_list(self):
method _install (line 126) | def _install(self, r):
method _bdic_ready_read (line 140) | def _bdic_ready_read(self):
method _bdic_finished (line 144) | def _bdic_finished(self):
FILE: webmacs/task.py
class Task (line 23) | class Task(QObject):
method __init__ (line 34) | def __init__(self):
method start (line 39) | def start(self):
method abort (line 45) | def abort(self):
method error (line 50) | def error(self):
method error_message (line 56) | def error_message(self):
method set_error (line 62) | def set_error(self, message):
method __str__ (line 65) | def __str__(self):
class TaskRunner (line 69) | class TaskRunner(QObject):
method __init__ (line 70) | def __init__(self):
method run (line 74) | def run(self, task):
method stop (line 84) | def stop(self):
method _on_task_finished (line 88) | def _on_task_finished(self):
FILE: webmacs/url_opener.py
function url_open (line 20) | def url_open(ctx, url, instance=None, new_window=False, new_buffer=False):
FILE: webmacs/variables.py
class VariableConditionError (line 4) | class VariableConditionError(Exception):
function condition (line 8) | def condition(func, doc):
class Variable (line 17) | class Variable(object):
method __init__ (line 20) | def __init__(self, name, doc, value, conditions=(), callbacks=(),
method validate (line 31) | def validate(self, value):
method set_value (line 42) | def set_value(self, value):
method add_callback (line 49) | def add_callback(self, callback):
class Type (line 55) | class Type(object):
method validate (line 56) | def validate(self, value):
method _describe (line 59) | def _describe(self, result):
method type_name (line 62) | def type_name(self):
method describe (line 65) | def describe(self):
class ChoiceMixin (line 71) | class ChoiceMixin(object):
method __init__ (line 72) | def __init__(self, choices=None, **kwargs):
method validate (line 76) | def validate(self, value):
method _describe (line 82) | def _describe(self, result):
class RangeMixin (line 88) | class RangeMixin(object):
method __init__ (line 89) | def __init__(self, min=None, max=None, **kwargs):
method validate (line 94) | def validate(self, value):
method _describe (line 105) | def _describe(self, result):
class String (line 113) | class String(ChoiceMixin, Type):
method validate (line 114) | def validate(self, value):
method type_name (line 119) | def type_name(self):
class Int (line 123) | class Int(ChoiceMixin, RangeMixin, Type):
method validate (line 124) | def validate(self, value):
method type_name (line 129) | def type_name(self):
class Float (line 133) | class Float(ChoiceMixin, RangeMixin, Type):
method validate (line 134) | def validate(self, value):
method type_name (line 139) | def type_name(self):
class Bool (line 143) | class Bool(Type):
method validate (line 144) | def validate(self, value):
method type_name (line 148) | def type_name(self):
class List (line 152) | class List(Type):
method __init__ (line 153) | def __init__(self, type):
method validate (line 156) | def validate(self, value):
method type_name (line 167) | def type_name(self):
method describe (line 170) | def describe(self):
class Tuple (line 176) | class Tuple(Type):
method __init__ (line 177) | def __init__(self, *types):
method validate (line 180) | def validate(self, value):
method type_name (line 192) | def type_name(self):
method describe (line 195) | def describe(self):
class Dict (line 202) | class Dict(Type):
method __init__ (line 203) | def __init__(self, key_type, value_type):
method validate (line 207) | def validate(self, value):
method type_name (line 220) | def type_name(self):
method describe (line 223) | def describe(self):
function define_variable (line 230) | def define_variable(name, doc, value, **kwargs):
function get_variable (line 236) | def get_variable(name):
function get (line 243) | def get(name):
function set (line 252) | def set(name, value):
FILE: webmacs/version.py
function QT_VERSION_CHECK (line 37) | def QT_VERSION_CHECK(major, minor=0, patch=0):
class _QtVersionChecker (line 41) | class _QtVersionChecker(object):
method __init__ (line 44) | def __init__(self, version):
method __eq__ (line 47) | def __eq__(self, other):
method __lt__ (line 50) | def __lt__(self, other):
method __gt__ (line 53) | def __gt__(self, other):
method __le__ (line 56) | def __le__(self, other):
method __ge__ (line 59) | def __ge__(self, other):
function chromium_version (line 68) | def chromium_version():
function webmacs_revision (line 87) | def webmacs_revision():
FILE: webmacs/visited_links.py
class VisitedLinks (line 30) | class VisitedLinks(object):
method __init__ (line 31) | def __init__(self, dbbath):
method visit (line 38) | def visit(self, url, title):
method visited_urls (line 45) | def visited_urls(self):
method remove (line 51) | def remove(self, url):
FILE: webmacs/webbuffer.py
function close_buffer (line 52) | def close_buffer(wb):
class WebBuffer (line 97) | class WebBuffer(QWebEnginePage):
method __init__ (line 110) | def __init__(self, url=None):
method internal_view (line 151) | def internal_view(self):
method view (line 154) | def view(self):
method mode (line 160) | def mode(self):
method text_edit_mark (line 164) | def text_edit_mark(self):
method set_text_edit_mark (line 167) | def set_text_edit_mark(self, on):
method set_mode (line 170) | def set_mode(self, modename):
method load (line 177) | def load(self, url):
method delayed_loading_url (line 183) | def delayed_loading_url(self):
method url (line 186) | def url(self):
method title (line 191) | def title(self):
method content_handler (line 197) | def content_handler(self):
method javaScriptConsoleMessage (line 200) | def javaScriptConsoleMessage(self, level, message, lineno, source):
method active_keymap (line 207) | def active_keymap(self):
method keymap_mode (line 211) | def keymap_mode(self):
method set_keymap_mode (line 214) | def set_keymap_mode(self, enabled):
method async_scroll_pos (line 217) | def async_scroll_pos(self, func):
method set_scroll_pos (line 220) | def set_scroll_pos(self, x=0, y=0):
method scroll_by (line 223) | def scroll_by(self, x=0, y=0):
method start_select_browser_objects (line 226) | def start_select_browser_objects(self, selector, method="filter",
method stop_select_browser_objects (line 233) | def stop_select_browser_objects(self):
method select_nex_browser_object (line 238) | def select_nex_browser_object(self, forward=True):
method filter_browser_objects (line 243) | def filter_browser_objects(self, text):
method focus_active_browser_object (line 248) | def focus_active_browser_object(self):
method select_visible_hint (line 253) | def select_visible_hint(self, hint_id):
method _on_full_screen_requested (line 259) | def _on_full_screen_requested(self, request):
method _on_feature_requested (line 267) | def _on_feature_requested(self, url, feature):
method createWindow (line 291) | def createWindow(self, type):
method finished (line 303) | def finished(self):
method handle_authentication (line 321) | def handle_authentication(self, url, authenticator):
method certificateError (line 340) | def certificateError(self, error):
method javaScriptConfirm (line 354) | def javaScriptConfirm(self, url, msg):
method javaScriptAlert (line 360) | def javaScriptAlert(self, url, msg):
method on_url_hovered (line 364) | def on_url_hovered(self, url):
method main_window (line 367) | def main_window(self):
method _incr_zoom (line 372) | def _incr_zoom(self, forward):
method set_zoom (line 384) | def set_zoom(self, zoom_factor):
method zoom_in (line 390) | def zoom_in(self):
method zoom_out (line 393) | def zoom_out(self):
method zoom_normal (line 396) | def zoom_normal(self):
method __on_title_changed (line 400) | def __on_title_changed(self, title):
FILE: webmacs/webview.py
function _update_stylesheets (line 27) | def _update_stylesheets(var):
class WebView (line 52) | class WebView(QFrame):
method __init__ (line 53) | def __init__(self, window):
method _attach_view (line 62) | def _attach_view(self, view):
method _detach_view (line 66) | def _detach_view(self):
method setBuffer (line 71) | def setBuffer(self, buffer, update_last_use=True):
method buffer (line 110) | def buffer(self):
method show_focused (line 114) | def show_focused(self, active):
method internal_view (line 121) | def internal_view(self):
class InternalWebView (line 126) | class InternalWebView(QWebEngineView):
method __init__ (line 128) | def __init__(self, view):
method view (line 133) | def view(self):
method event (line 136) | def event(self, evt):
method eventFilter (line 143) | def eventFilter(self, obj, evt):
method request_fullscreen (line 158) | def request_fullscreen(self, toggle_on):
class FullScreenState (line 172) | class FullScreenState(object):
method __init__ (line 173) | def __init__(self, internal_view):
method restore (line 186) | def restore(self):
FILE: webmacs/window.py
function remove_layout_spaces (line 34) | def remove_layout_spaces(layout):
class Window (line 39) | class Window(QWidget):
method __init__ (line 40) | def __init__(self):
method _update_toolbar (line 69) | def _update_toolbar(self, buffer):
method toggle_toolbar (line 80) | def toggle_toolbar(self):
method set_current_webview (line 94) | def set_current_webview(self, webview):
method current_webview (line 100) | def current_webview(self):
method webviews (line 103) | def webviews(self):
method create_webview_on_right (line 106) | def create_webview_on_right(self):
method create_webview_on_bottom (line 109) | def create_webview_on_bottom(self):
method _delete_webview (line 112) | def _delete_webview(self, webview):
method minibuffer (line 117) | def minibuffer(self):
method other_view (line 120) | def other_view(self):
method close_view (line 129) | def close_view(self, view):
method close_other_views (line 144) | def close_other_views(self):
method update_title (line 157) | def update_title(self, title=None):
method dump_state (line 167) | def dump_state(self):
method restore_state (line 174) | def restore_state(self, data, version):
Condensed preview — 113 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (838K chars).
[
{
"path": ".flake8",
"chars": 152,
"preview": "[flake8]\nexclude = ./vendor/,\n ./build/,\n ./dist/,\n ./webmacs.egg-info/,\n # exclude "
},
{
"path": ".gitattributes",
"chars": 230,
"preview": "**/.gitignore export-ignore\n**/.gitmodules export-ignore\n**/.travis.yml export-ignore\n\nvendor/*/test/** export-ignore\nve"
},
{
"path": ".gitignore",
"chars": 18,
"preview": "__pycache__/\nvenv/"
},
{
"path": ".gitmodules",
"chars": 320,
"preview": "[submodule \"vendor/hashset-cpp\"]\n\tpath = vendor/hashset-cpp\n\turl = https://github.com/bbondy/hashset-cpp\n[submodule \"ven"
},
{
"path": ".travis.yml",
"chars": 1648,
"preview": "sudo: true\nlanguage: python\ndist: xenial\npython: 3.7\n\nmatrix:\n include:\n - os: linux\n env: PYQT_VERSION=5.7.1\n "
},
{
"path": "CHANGELOG.md",
"chars": 14932,
"preview": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Change"
},
{
"path": "COPYING",
"chars": 35148,
"preview": " GNU GENERAL PUBLIC LICENSE\n Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
},
{
"path": "README-nix.org",
"chars": 1707,
"preview": "* Installation with Nix\n\nThere are two recommended ways of installing webmacs:\n\n1. Nix\n2. pip/virtualenv\n\n** Nix\n\nCurren"
},
{
"path": "README.org",
"chars": 2836,
"preview": "* webmacs\n\n*webmacs* is yet another browser for keyboard-based web navigation.\n\nIt mainly target emacs-like navigation, "
},
{
"path": "c/adblock.c",
"chars": 6037,
"preview": "// This file is part of webmacs.\n//\n// webmacs is free software: you can redistribute it and/or modify\n// it under the t"
},
{
"path": "docs/Makefile",
"chars": 608,
"preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS = -W\nS"
},
{
"path": "docs/advanced_topics.rst",
"chars": 2309,
"preview": "Advanced topics\n===============\n\n.. current-keymap:: global\n\n\n.. _managing_views:\n\nManaging views\n**************\n\nIt is "
},
{
"path": "docs/api.rst",
"chars": 734,
"preview": "Public api\n==========\n\n\nInitialisation\n**************\n\n.. autofunction:: webmacs.main.init\n\n\nKeymaps\n*******\n\n.. autofun"
},
{
"path": "docs/basic_usage.rst",
"chars": 8230,
"preview": "Basic usage\n===========\n\nDon't panic\n***********\n\nWhen you are stuck in some interactive :term:`command` or text field, "
},
{
"path": "docs/concepts.rst",
"chars": 1970,
"preview": "Concepts\n========\n\nPlease make sure to understand the following basic webmacs concepts before\nfurther reading the docume"
},
{
"path": "docs/conf.py",
"chars": 7056,
"preview": "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n#\n# webmacs documentation build configuration file, created by\n# sphinx-q"
},
{
"path": "docs/ext/webmacs_sphinx_ext.py",
"chars": 5892,
"preview": "import re\n\nfrom docutils.parsers.rst import Directive\nfrom docutils.statemachine import ViewList\nfrom docutils import no"
},
{
"path": "docs/faq.rst",
"chars": 1251,
"preview": "FAQ\n===\n\nHow do I run a new webmacs instance instead of a new buffer from the command-line?\n****************************"
},
{
"path": "docs/glossary.rst",
"chars": 2547,
"preview": "Glossary\n========\n\n.. glossary::\n\n buffer\n web buffer\n The content of a web page, not including its window or view."
},
{
"path": "docs/index.rst",
"chars": 683,
"preview": ".. webmacs documentation master file, created by\n sphinx-quickstart on Sat Dec 23 08:47:03 2017.\n You can adapt this"
},
{
"path": "docs/make.bat",
"chars": 811,
"preview": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sp"
},
{
"path": "docs/user_configuration.rst",
"chars": 5283,
"preview": "User configuration\n==================\n\n**webmacs** can be configured by writing Python code. The files should live in\na "
},
{
"path": "git_archive_all.py",
"chars": 12418,
"preview": "# Script to generate a git archive with submodules\n# From https://github.com/Kentzo/git-archive-all\n\nfrom os import exts"
},
{
"path": "pytest.ini",
"chars": 161,
"preview": "[pytest]\naddopts = tests\n\nlog_cli = true\nlog_cli_level = debug\nlog_format = %(asctime)s %(name)-10s %(levelname)s %(mess"
},
{
"path": "setup.py",
"chars": 4204,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "test-requirements.txt",
"chars": 53,
"preview": "-e .\npytest\npytest-qt\npytest-mock\npytest-xvfb\nflake8\n"
},
{
"path": "tests/integration/conftest.py",
"chars": 6937,
"preview": "import pytest\nimport os\nimport time\nimport subprocess\n\nfrom PyQt6.QtTest import QTest\nfrom PyQt6.QtCore import QEvent, Q"
},
{
"path": "tests/integration/iframe_follow/index.html",
"chars": 292,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>iframe testing</title>\n </head>\n <body>\n\n <input id=\"input0\" type=\"text\""
},
{
"path": "tests/integration/iframe_follow/my_iframe.html",
"chars": 227,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>inside iframe</title>\n </head>\n <body>\n <input id=\"input0\" type=\"text\" n"
},
{
"path": "tests/integration/javascript_prompt/index.html",
"chars": 578,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>inside iframe</title>\n <script type=\"text/javascript\">\n function fill"
},
{
"path": "tests/integration/navigation/index.html",
"chars": 99,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>page index</title>\n </head>\n <body>\n </body>\n</html>\n"
},
{
"path": "tests/integration/navigation/page1.html",
"chars": 99,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>first page</title>\n </head>\n <body>\n </body>\n</html>\n"
},
{
"path": "tests/integration/test_copy_link.py",
"chars": 1704,
"preview": "from webmacs.keyboardhandler import CommandContext\nfrom webmacs.application import app\nfrom webmacs.commands import COMM"
},
{
"path": "tests/integration/test_iframe_navigation.py",
"chars": 2541,
"preview": "import pytest\n\n\nINPUT0 = \"document.getElementById('input0')\"\nINPUT0_IFRAME = \"window.frames[0].document.getElementById('"
},
{
"path": "tests/integration/test_javascript_prompt.py",
"chars": 1374,
"preview": "import pytest\n\n\ndef check_js_result(res):\n def check(session):\n session.check_javascript(\"getContent();\", res)"
},
{
"path": "tests/integration/test_navigation.py",
"chars": 808,
"preview": "from webmacs import BUFFERS\nfrom webmacs.webbuffer import create_buffer\n\n\ndef test_cycle_buffers(session):\n \"\"\"\n W"
},
{
"path": "tests/integration/test_user_download_dir.py",
"chars": 1835,
"preview": "import pytest\nimport os\n\nfrom webmacs import download_manager\n\n# should be more or less in unit test section, but uses t"
},
{
"path": "tests/test_prompt_history.py",
"chars": 1221,
"preview": "from webmacs.minibuffer.prompt import PromptHistory\n\n\ndef test_history():\n p = PromptHistory(maxsize=10)\n # callin"
},
{
"path": "tests/test_variables.py",
"chars": 1737,
"preview": "import pytest\n\nfrom webmacs.variables import (\n VariableConditionError, String, Int, Bool, Float, List, Tuple, Dict\n)"
},
{
"path": "webmacs/__init__.py",
"chars": 3949,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/adblock.py",
"chars": 6887,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/application.py",
"chars": 5882,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/bookmarks.py",
"chars": 1387,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/clipboard.py",
"chars": 1969,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/commands/__init__.py",
"chars": 3652,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/commands/buffer_history.py",
"chars": 3540,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/commands/caret_browsing.py",
"chars": 4001,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/commands/content_edit.py",
"chars": 6459,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/commands/follow.py",
"chars": 7945,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/commands/global.py",
"chars": 18712,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/commands/isearch.py",
"chars": 4456,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/commands/minibuffer.py",
"chars": 6731,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/commands/webbuffer.py",
"chars": 16628,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/commands/webjump.py",
"chars": 15051,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/content_handler.py",
"chars": 2638,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/default_webjumps.py",
"chars": 3587,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/download_manager/__init__.py",
"chars": 8760,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/download_manager/prompts.py",
"chars": 2857,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/egrid.py",
"chars": 7871,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/external_editor.py",
"chars": 699,
"preview": "import os\nimport tempfile\nimport subprocess\nfrom . import variables\nimport shlex\n\n\neditor_cmd = variables.define_variabl"
},
{
"path": "webmacs/features.py",
"chars": 2042,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/filter_webengine_output.py",
"chars": 3501,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/hooks.py",
"chars": 1148,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/ignore_certificates.py",
"chars": 1372,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/ipc.py",
"chars": 6202,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/keyboardhandler.py",
"chars": 10290,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/keymaps/__init__.py",
"chars": 18442,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/keymaps/caret_browsing.py",
"chars": 2215,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/keymaps/content_edit.py",
"chars": 2053,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/keymaps/fullscreen.py",
"chars": 1056,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/keymaps/global.py",
"chars": 1558,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/keymaps/hints.py",
"chars": 923,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/keymaps/isearch.py",
"chars": 1001,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/keymaps/minibuffer.py",
"chars": 2810,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/keymaps/webbuffer.py",
"chars": 2428,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/killed_buffers.py",
"chars": 2373,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/main.py",
"chars": 10546,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/minibuffer/__init__.py",
"chars": 13444,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/minibuffer/prompt.py",
"chars": 12450,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/minibuffer/right_label.py",
"chars": 3991,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/mode.py",
"chars": 2958,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/password_manager/__init__.py",
"chars": 2054,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/password_manager/password_store.py",
"chars": 6262,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/profile.py",
"chars": 7059,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/scheme_handlers/__init__.py",
"chars": 970,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/scheme_handlers/webmacs/__init__.py",
"chars": 8018,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/scheme_handlers/webmacs/js/vue.js",
"chars": 268934,
"preview": "/*!\n * Vue.js v2.4.4\n * (c) 2014-2017 Evan You\n * Released under the MIT License.\n */\n(function (global, factory) {\n\ttyp"
},
{
"path": "webmacs/scheme_handlers/webmacs/templates/base.html",
"chars": 242,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n {% block head %}\n <title>{% block title %}{% endblock %} - webmacs</tit"
},
{
"path": "webmacs/scheme_handlers/webmacs/templates/bindings.html",
"chars": 670,
"preview": "{% extends \"base.html\" %}\n\n{% block title %}Bindings{% endblock %}\n{% block content %}\n<h1>Webmacs bindings</h1>\n{% for "
},
{
"path": "webmacs/scheme_handlers/webmacs/templates/command.html",
"chars": 658,
"preview": "{% extends \"base.html\" %}\n\n{% block title %}{{command_name}} command{% endblock %}\n{% block content %}\n<h1>Command: {{co"
},
{
"path": "webmacs/scheme_handlers/webmacs/templates/commands.html",
"chars": 382,
"preview": "{% extends \"base.html\" %}\n\n{% block title %}Available commands{% endblock %}\n{% block content %}\n<h1>Webmacs commands</h"
},
{
"path": "webmacs/scheme_handlers/webmacs/templates/downloads.html",
"chars": 1318,
"preview": "{% extends \"base.html\" %}\n\n{% block head %}\n<style>\n .progress { width: 100%; background-color: #ddd }\n .bar { height: 3"
},
{
"path": "webmacs/scheme_handlers/webmacs/templates/key.html",
"chars": 614,
"preview": "{% extends \"base.html\" %}\n\n{% block title %}key {{key}} on {{keymap}}{% endblock %}\n{% block content %}\n<p>{{key}} run t"
},
{
"path": "webmacs/scheme_handlers/webmacs/templates/keymap.html",
"chars": 460,
"preview": "{% extends \"base.html\" %}\n\n{% block title %}Keymap {{name}}{% endblock %}\n{% block content %}\n<h1>Keymap: {{name}}</h1>\n"
},
{
"path": "webmacs/scheme_handlers/webmacs/templates/variable.html",
"chars": 733,
"preview": "{% macro var_type(v) -%}\n{% set desc = {\"Type\": v.type.describe()}.items() %}\n{%- for k, v in desc recursive %}\n{% set o"
},
{
"path": "webmacs/scheme_handlers/webmacs/templates/variables.html",
"chars": 378,
"preview": "{% extends \"base.html\" %}\n\n{% block title %}Available variables{% endblock %}\n{% block content %}\n<h1>Webmacs variables<"
},
{
"path": "webmacs/scheme_handlers/webmacs/templates/version.html",
"chars": 229,
"preview": "{% extends \"base.html\" %}\n\n{% block title %}Version{% endblock %}\n{% block content %}\n<table>\n {% for name, version in "
},
{
"path": "webmacs/scripts/caret_browsing.js",
"chars": 35593,
"preview": "// eslint-disable-line max-lines\n/**\n * Copyright 2016-2017 Florian Bruhin (The Compiler) <mail@qutebrowser.org>\n *\n * T"
},
{
"path": "webmacs/scripts/hint.js",
"chars": 19584,
"preview": "function clickLike(elem) {\n elem.focus();\n var doc = elem.ownerDocument;\n var view = doc.defaultView;\n\n var "
},
{
"path": "webmacs/scripts/password_manager.js",
"chars": 3414,
"preview": "// This file is part of webmacs.\n//\n// webmacs is free software: you can redistribute it and/or modify\n// it under the t"
},
{
"path": "webmacs/scripts/setup.js",
"chars": 4650,
"preview": "// This file is part of webmacs.\n//\n// webmacs is free software: you can redistribute it and/or modify\n// it under the t"
},
{
"path": "webmacs/scripts/textedit.js",
"chars": 5699,
"preview": "// This file is part of webmacs.\n//\n// webmacs is free software: you can redistribute it and/or modify\n// it under the t"
},
{
"path": "webmacs/scripts/textzoom.js",
"chars": 2488,
"preview": "var textzoom = {};\n\ntextzoom.totalRatio = 1;\n\ntextzoom.IGNORED_TAGS = /SCRIPT|NOSCRIPT|LINK|BR|EMBED|IFRAME|IMG|VIDEO|CA"
},
{
"path": "webmacs/session.py",
"chars": 3532,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/spell_checking.py",
"chars": 5600,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/task.py",
"chars": 2446,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/url_opener.py",
"chars": 1306,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/variables.py",
"chars": 7717,
"preview": "VARIABLES = {}\n\n\nclass VariableConditionError(Exception):\n \"Raised when a variable condition is not fulfilled\"\n\n\ndef "
},
{
"path": "webmacs/version.py",
"chars": 3541,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/visited_links.py",
"chars": 1808,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/webbuffer.py",
"chars": 14241,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/webview.py",
"chars": 6145,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
},
{
"path": "webmacs/window.py",
"chars": 6205,
"preview": "# This file is part of webmacs.\n#\n# webmacs is free software: you can redistribute it and/or modify\n# it under the terms"
}
]
About this extraction
This page contains the full source code of the parkouss/webmacs GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 113 files (777.4 KB), approximately 194.9k tokens, and a symbol index with 1363 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.