add talon as an option; yet unconfigured

This commit is contained in:
Kate 2024-11-15 06:38:37 -07:00
parent 857666f226
commit 065a7d1e6f
552 changed files with 36089 additions and 0 deletions

44
flake.lock generated
View file

@ -611,6 +611,27 @@
"type": "github"
}
},
"nix-github-actions_2": {
"inputs": {
"nixpkgs": [
"talon",
"nixpkgs"
]
},
"locked": {
"lastModified": 1693660503,
"narHash": "sha256-B/g2V4v6gjirFmy+I5mwB2bCYc0l3j5scVfwgl6WOl8=",
"owner": "nix-community",
"repo": "nix-github-actions",
"rev": "bd5bdbb52350e145c526108f4ef192eb8e554fa0",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nix-github-actions",
"type": "github"
}
},
"nix-on-droid": {
"inputs": {
"home-manager": [
@ -963,6 +984,7 @@
"nixpkgs": "nixpkgs_6",
"openxc7": "openxc7",
"stylix": "stylix",
"talon": "talon",
"waveforms": "waveforms"
}
},
@ -1125,6 +1147,28 @@
"type": "github"
}
},
"talon": {
"inputs": {
"nix-github-actions": "nix-github-actions_2",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1720064283,
"narHash": "sha256-/C+A08rgPQ+R2SJwaGNpt/rQ0xa6cbyWHqw3nB1caxI=",
"owner": "nix-community",
"repo": "talon-nix",
"rev": "718a82d3f45fa875f00f61867d6ed9285e40ce59",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "master",
"repo": "talon-nix",
"type": "github"
}
},
"tinted-foot": {
"flake": false,
"locked": {

View file

@ -34,6 +34,12 @@
inputs.nixpkgs.follows = "nixpkgs";
};
# Talon voice.
talon = {
url = "github:nix-community/talon-nix/master";
inputs.nixpkgs.follows = "nixpkgs";
};
# For our phone.
nix-on-droid = {
url = "github:nix-community/nix-on-droid/release-24.05";
@ -83,6 +89,7 @@
attic,
nix-flatpak,
nix-on-droid,
talon,
...
}:
@ -200,6 +207,7 @@
stylix
is-hm-standalone
is-droid
talon
;
niri = niri.outputs;
@ -275,6 +283,7 @@
./nixos/hosts/valere.nix
./nixos/configs/steam.nix
./nixos/configs/vmware.nix
./nixos/configs/talon-voice.nix
./nixos/configs/power-saving.nix
];
};

View file

@ -0,0 +1,11 @@
#
# Configuration that adds talon-voice.
#
{ talon, ... }:
{
imports = [
talon.nixosModules.talon
];
programs.talon.enable = true;
}

View file

@ -0,0 +1,17 @@
# See https://EditorConfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_size = 4
indent_style = space
max_line_length = 88
trim_trailing_whitespace = true
[*.{md,yaml,yml}]
indent_size = 2
[Makefile]
indent_style = tab

View file

@ -0,0 +1,3 @@
2877a6849d75e5fa78c9453991a9235b4f6d9dcf
3bf4882fa0a05b22171e59118bd7c9640aae753a
446ec764c9caa98973eacd7f792b6a087a1b635f

View file

@ -0,0 +1,7 @@
version: 2
updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"

View file

@ -0,0 +1,25 @@
name: ci
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3.9
uses: actions/setup-python@v5
with:
python-version: "3.9"
cache: "pip"
cache-dependency-path: |
requirements-dev.txt
- run: pip install -r requirements-dev.txt
- run: pytest

9
talon/user/community/.gitignore vendored Normal file
View file

@ -0,0 +1,9 @@
# Dev stuff
__pycache__
*.sw?
.idea/
# Locally generated
/settings
.vscode/settings.json
.DS_Store
.bak

View file

@ -0,0 +1,52 @@
minimum_pre_commit_version: "3.2.0"
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: check-shebang-scripts-are-executable
- id: check-symlinks
- id: destroyed-symlinks
- id: detect-private-key
- id: fix-byte-order-marker
# NB. To avoid sometimes needing multiple runs, we need:
# - trailing-whitespace BEFORE end-of-file-fixer,
# otherwise trailing newline followed by whitespace, "\n ",
# will need multiple runs.
# - end-of-file-fixer BEFORE mixed-line-ending,
# otherwise a file with CRLF line endings but missing a trailing
# newline will need multiple runs.
- id: trailing-whitespace
- id: end-of-file-fixer
- id: mixed-line-ending
- repo: https://github.com/pre-commit/mirrors-prettier
rev: "v4.0.0-alpha.8"
hooks:
- id: prettier
- repo: https://github.com/ikamensh/flynt/
rev: "1.0.1"
hooks:
- id: flynt
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.10.0
hooks:
- id: black
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.5
hooks:
- id: remove-tabs
types: [file]
files: \.talon$
args: ["--whitespaces-count=4"]
- repo: https://github.com/wenkokke/talonfmt
rev: 1.10.2
hooks:
- id: talonfmt
args: ["--in-place"]

View file

@ -0,0 +1,44 @@
This file lists known changes to `community` that are likely to have
broken existing functionality. The file is sorted by date with the
newest entries up the top.
Be aware there may be some difference between the date in this file
and when the change was applied given the delay between changes being
submitted and the time they were reviewed and merged.
---
* 2024-09-07 Removed `get_list_from_csv` from `user_settings.py`. Please
use the new `track_csv_list` decorator, which leverages Talon's
`talon.watch` API for robustness on Talon launch.
* 2024-09-07 If you've updated `community` since 2024-08-31, you may
need to replace `host:` with `hostname:` in the header of
`core/system_paths-<hostname>.talon-list` due to an issue with
automatic conversion from CSV (#1268).
* 2024-07-31 Remove commands `"command mode"`, `"dictation mode"` from
custom user modes. Note that if you have any custom modes where you
want these commands you could add that mode to the context of
`command_and_dictation_mode.talon` or copying the command to one of
your custom files.
* 2024-07-30 Deprecate `lend` and `bend` commands in favor of `go line
end | tail` and `go line start | head`.
* 2024-07-28 Removed the following user namespace actions in favor of
the new action/modifier grammar.
https://github.com/talonhub/community/blob/37a8ebde90c8120a0b52555030988d4f54e65159/core/edit/edit.talon#L3
cut_word, copy_word, paste_word
cut_all, copy_all, paste_all, delete_all
copy_line, paste_line
cut_line_start, copy_line_start, paste_line_start, delete_line_start
cut_line_end, copy_line_end, paste_line_end, delete_line_end
* 2024-05-30 Deprecate 'drop down <user.number_small>' in favor of
overridable 'choose' helper
* 2024-01-27 Deprecate '<user.number_string>' command without a spoken
prefix like `numb`. See `numbers.talon` and
`numbers_unprefixed.talon.` If in the future you want to still use
unprefixed numbers, you will need to comment out the
`tag(): user.prefixed_numbers` line in your `settings.talon` file.
* 2023-06-06 Deprecate `go` command for VSCode. Use 'bar marks' instead.
* 2023-02-04 Deprecate `murder` command for i3wm. Use 'win kill' instead.
* 2022-12-11 Deprecate user.insert_with_history. Just use
`user.add_phrase_to_history(text); insert(text)` instead. See #939.
* 2022-10-01 Large refactoring of code base that moves many files into
new locations. No other backwards-incompatible changes included.

View file

@ -0,0 +1,13 @@
This document attempts to list a set of principles for contributors to the `community` repository to consider. The idea is to document some agreed upon approaches toward reviewing and including code so we can all more easily make consistent decisions.
Each of the principles is numbered for easy referencing. The body is formatted as a short single-line summary of the principle followed by elaboration and discussion links.
# Voice command principles
- P01 - Prefer [object][verb] rather than [verb][object] for new commands. For example 'file save' is better than 'save file'. It may not sound as natural, but it helps for grouping related commands in lists and avoiding conflicting names.
- P02 - Use `browser.host` matcher for web apps. Though this matcher requires a [browser extension](https://github.com/talonhub/community/blob/main/apps/README.md) on some operating systems it is the only unambiguous way of referring to a web app.
# Coding principles
- P03 - Use the `app.bundle` matcher for apps on OSX. This is the least ambiguous way of referring to a particular program.
- P04 - Use both `app.name` and `app.exe` matchers for apps on Windows. That is the context should OR together one matcher of each type. Apparently the [MUICache](https://www.magnetforensics.com/blog/forensic-analysis-of-muicache-files-in-windows/) can break, perhaps making one of these matchers stop working.

View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Jeff Knaus, Ryan Hileman, Zach Dwiel, Michael Arntzenius, and others
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,382 @@
# community
Voice command set for [Talon](https://talonvoice.com/), community-supported.
_(Originally called `knausj_talon`, after [its original creator :superhero:](https://github.com/knausj85))_
Can be used on its own, but shines when combined with:
- [Cursorless](https://www.cursorless.org/) for programming and text editing
- [Rango](https://github.com/david-tejada/rango) for browser navigation
- [gaze-ocr](https://github.com/wolfmanstout/talon-gaze-ocr) for advanced cursor control using eye tracking and text recognition (OCR)
- [AXKit](https://github.com/phillco/talon-axkit) (macOS only) to enhance Talon with native OS accessibility integrations
- [Other user file sets](https://talon.wiki/talon_user_file_sets/)
## Installation
### Prerequisites
- [Talon](https://talonvoice.com/)
- Mac, Windows, or Linux
- Talon's built-in Conformer (wav2letter) speech recognition engine (recommended), or Dragon NaturallySpeaking (Windows) / Dragon for Mac (although beware that Dragon for Mac is discontinued and its use deprecated).
Includes commands for working with an eye tracker; an [eye tracker](https://talon.wiki/Quickstart/Hardware/#eye-trackers) is not required.
### Linux & Mac
It is recommended to install `community` using [`git`](https://git-scm.com/).
1. Install [`git`](https://git-scm.com/)
2. Open a terminal ([Mac](https://support.apple.com/en-gb/guide/terminal/apd5265185d-f365-44cb-8b09-71a064a42125/mac) / [Ubuntu](https://ubuntu.com/tutorials/command-line-for-beginners#3-opening-a-terminal))
3. Paste the following into the terminal window then press Enter/Return:
```bash
cd ~/.talon/user
git clone https://github.com/talonhub/community community
```
Note that it is also possible to install `community` by [downloading and extracting a zip file](#alternate-installation-method-zip-file), but this approach is discouraged because it makes it more difficult to keep track of any changes you may make to your copy of the files.
### Windows
It is recommended to install `community` using [`git`](https://git-scm.com/).
1. Install [`git`](https://git-scm.com/)
2. Open a [command prompt](https://www.wikihow.com/Open-the-Command-Prompt-in-Windows)
3. Paste the following into the command prompt window then press Enter:
```
cd %AppData%\Talon\user
git clone https://github.com/talonhub/community community
```
Note that it is also possible to install `community` by [downloading and extracting a zip file](#alternate-installation-method-zip-file), but this approach is discouraged because it makes it more difficult to keep track of any changes you may make to your copy of the files.
## Getting started with Talon
1. `help active` displays commands available in the active (frontmost) application.
- Available commands can change by application, or even the window title.
- Navigate help by voice using the displayed numbers (e.g., `help one one` or `help eleven` to open the item numbered 11), or by speaking button titles that don't start with numbers (e.g., `help next` to see the next page of contexts).
- Help-related commands are defined in [help.talon](core/help/help.talon) and [help_open.talon](core/help/help_open.talon).
2. Search for commands by saying `help search <phrase>`. For example, `help search tab` displays all tab-related commands, and `help search help` displays all help-related commands.
3. Jump immediately to help for a particular help context with the name displayed the in help window (based on the name of the .talon file), e.g. `help context symbols` or `help context visual studio`
4. `help alphabet` displays words for letters of the alphabet; `help symbols` displays words for symbols.
5. `command history` toggles display of recent voice commands.
6. `help format` displays available [formatters](#formatters) with examples.
7. Many useful, basic commands are defined in [edit.talon](core/edit/edit.talon).
- `undo that` and `redo that` are the default undo/redo commands.
- `paste that`, `copy that`, and `cut that` for pasting/copy/cutting, respectively.
8. For community-generated documentation on Talon itself, please visit https://talon.wiki/.
It's recommended to learn the alphabet first, then get familiar with the keys, symbols, formatters, mouse, and generic_editor commands.
Once you have the basics of text input down, try copying some code from one window to another.
After that, explore using ordinal repetition for easily repeating a command without pausing (e.g., saying `go up fifth` will go up five lines), window switching (`focus chrome`), and moving around in your text editor of choice.
If you use vim, just start with the numbers and alphabet, otherwise look at generic_editor.talon as well at jetbrains, vscode, and any other integrations.
### Alphabet
The alphabet is defined in
[this Talon list file](core/keys/letter.talon-list).
Say `help alphabet` to open a window displaying the alphabet. `help close` closes the window.
Try saying e.g. `air bat cap` to insert abc.
### Keys
All key commands are defined in [keys.talon](core/keys/keys.talon). Say letters of the [Talon alphabet](#alphabet) for AZ.
For modifier keys, say `help modifiers`. For example, say `shift air` to press `shift-a`, which types a capital `A`.
For symbols, say `help symbols`. These are defined in keys.py;
search for `modifier_keys` and then keep scrolling — roughly starting [here](core/keys/keys.py#L124).
On Windows, try commands such as:
- `control air` to press Control+A and select all.
- `super-shift-sun` to press Win+Shift+S, triggering the screenshot application (Windows 10). Then try `escape` to exit.
On Mac, try commands such as:
- `command air` to press ⌘A and select all.
- `control shift command 4` to press ⌃⇧⌘4, copying a screenshot of the selected area to the clipboard. Then try `escape` to exit. Please note the order of the modifiers doesn't matter.
Say any combination of modifiers, symbols, alphabet, numbers and function keys to execute keyboard shortcuts. Modifier keys can be tapped using `press`, for example `press control` taps the Control (⌃) key by itself.
### Symbols
Some symbols are defined in [keys.py](core/keys/keys.py#L144), so you can say, e.g. `control colon` to press those keys.
Multi-character punctuation (e.g., ellipses) is defined in [symbols.talon](plugin/symbols/symbols.talon).
### Formatters
Formatters allow you to insert words with consistent capitalization and punctuation. `help format` displays available formatters with examples of their output when followed by `one two three`.
Try using a formatter by saying `snake hello world`. This inserts "hello_world".
Multiple formatters can be chained together — for example, `dubstring snake hello world` inserts "hello_world".
Prose formatters (marked with \* in the help window) preserve hyphens and apostrophes. Non-prose (code) formatters strip punctuation instead, for example to generate a valid variable name. `title how's it going` inserts "How's It Going"; `hammer how's it going` inserts "HowsItGoing".
Reformat existing text with one or more formatters by selecting it, then saying the formatter name(s) followed by `that`. Say `help reformat` to display how each formatter reformats `one_two_three`.
Formatter names (snake, dubstring) are defined [here](core/text/formatters.py#L245). Formatter-related commands are defined in [text.talon](core/text/text.talon#L8).
### Mouse commands
See [mouse.talon](plugin/mouse/mouse.talon) for commands to click, drag, scroll, and use an eye tracker. To use a grid to click at a certain location on the screen, see [mouse_grid](core/mouse_grid).
### Generic editing commands
Editing commands in [edit.talon](core/edit/edit.talon) are global. Commands such as `go word left` will work in any text box that uses standard platform text navigation conventions.
### Repeating commands
Voice commands for repeating commands are defined in [repeater.talon](plugin/repeater/repeater.talon).
Say `go up fifth` or `go up five times` to go up five lines. `select up third` will press Shift+Up three times to select several lines of text.
### Window management
Global window managment commands are defined in [window_management.talon](core/windows_and_tabs/window_management.talon).
- `running list` toggles a window displaying words you can say to switch to running applications. To customize the spoken forms for an app (or hide an app entirely from the list), edit the `app_name_overrides_<platform>.csv` files in the [core/app_switcher](core/app_switcher) directory.
- `focus chrome` will focus the Chrome application.
- `launch music` will launch the music application. Note this is currently only implemented on macOS.
### Screenshot commands
See [screenshot.talon](plugin/screenshot/screenshot.talon).
### Programming languages
Specific programming languages may be activated by voice commands, or via title tracking.
Activating languages via commands will enable the commands globally, e.g. they'll work in any application. This will also disable the title tracking method (code.language in .talon files) until the "clear language modes" voice command is used.
Commands for enabling languages are defined in [language_modes.talon](core/modes/language_modes.talon).
By default, title tracking activates languages in supported applications such as VSCode, Visual Studio (requires plugin), and Notepad++.
To enable title tracking for your application:
1. Ensure the active filename (including extension) is included in the window title.
2. Implement the required Talon-defined `filename` action to correctly extract the filename from the window title. See the [Visual Studio Code implementation](apps/vscode/vscode.py#L137-L153) for an example.
Python, C#, Talon and JavaScript language support is broken up into multiple tags in an attempt to standardize common voice commands for features available across languages. Each tag is defined in a .talon file named after a `user.code_` tag (e.g., `user.code_functions``functions.talon`) containing voice commands and a Python file declaring the actions that should be implemented by each concrete language implementation to support the voice commands. These files include:
- `lang/tags/comment_block.{talon,py}` - block comments (e.g., C++'s `/* */`)
- `lang/tags/comment_documentation.{talon,py}` - documentation comments (e.g., Java's `/** */`)
- `lang/tags/comment_line.{talon,py}` - line comments (e.g., Python's `#`)
- `lang/tags/data_null.{talon,py}` - null & null checks (e.g., Python's `None`)
- `lang/tags/data_bool.{talon,py}` - booleans (e.g., Haskell's `True`)
- `lang/tags/functions.{talon,py}` - functions and definitions
- `lang/tags/functions_common.{talon,py}` - common functions (also includes a GUI for picking functions)
- `lang/tags/imperative.{talon,py}` - statements (e.g., `if`, `while`, `switch`)
- `lang/tags/libraries.{talon,py}` - libraries and imports
- `lang/tags/libraries_gui.{talon,py}` - graphical helper for common libraries
- `lang/tags/object_oriented.{talon,py}` - objects and classes (e.g., `this`)
- `lang/tags/operators_array.{talon,py}` - array operators (e.g., Ruby's `x[0]`)
- `lang/tags/operators_assignment.{talon,py}` - assignment operators (e.g., C++'s `x += 5`)
- `lang/tags/operators_bitwise.{talon,py}` - bitwise operators (e.g., C's `x >> 1`)
- `lang/tags/operators_lambda.{talon,py}` - anonymous functions (e.g., JavaScript's `x => x + 1`)
- `lang/tags/operators_math.{talon,py}` - numeric, comparison, and logical operators
- `lang/tags/operators_pointer.{talon,py}` - pointer operators (e.g., C's `&x`)
Language-specific implementations of the above features are in files named `lang/{your-language}/{your-language}.py`.
To add support for a new language, ensure appropriate extension is added/uncommented in the [`language_extensions` dictionary in language_modes.py](core/modes/language_modes.py#L9). Then create the following files:
- `lang/{your-language}/{your-language}.py`
- `lang/{your-language}/{your-language}.talon`
Activate the appropriate tags in `{your-language}.talon` and implement the corresponding actions in `{your-language}.py`, following existing language implementations. Put additional voice commands for your language (not shared with other languages) in `{your-language}.talon`.
## File manager commands
For the following file manager commands to work, your file manager must display the full folder path in the title bar. tags/file_manager/file_manager.talon
For the Mac Finder, run this command in Terminal to display the full path in the window title:
```
defaults write com.apple.finder _FXShowPosixPathInTitle -bool YES
```
For Windows Explorer, [follow these directions](https://www.howtogeek.com/121218/beginner-how-to-make-explorer-always-show-the-full-path-in-windows-8/).
For the Windows command line, the `refresh title` command will force the title to the current directory, and all directory commands (`follow 1`) will automatically update the title.
Notes:
- Both Windows Explorer and Finder hide certain files and folders by default, so it's often best to use the imgui to list the options before issuing commands.
- If there no hidden files or folders, and the items are displayed in alphabetical order, you can typically issue the `follow <number>`, `file <number>` and `open <number>` commands based on the displayed order.
To implement support for a new program, implement the relevant file manager actions for your application and assert the `user.file_manager` tag. There are a number of example implementations in the repository. [Finder](apps/finder/finder.py) is a good example to copy and mdoify.
## Terminal commands
Many terminal applications are supported out of the box, but you may not want all the commands enabled.
To use command sets in your terminal applications, enable/disable the corresponding tags in the terminal application-specific .talon file.
```
tag(): user.file_manager
tag(): user.git
tag(): user.kubectl
tag(): user.tabs
```
For instance, kubectl commands (kubernetes) aren't relevant to everyone.
Note also that while some of the command sets associated with these tags are defined in talon files within [tags](tags), others, like git, are defined within [apps](apps). Commands for tabs are defined in [tabs.talon](core/windows_and_tabs/tabs.talon).
### Unix utilities
If you have a Unix (e.g. macOS) or Linux computer, you can enable support for a number of
common terminal utilities like `cat`, `tail`, or `grep` by uncommenting the following
line in [unix_shell.py](tags/terminal/unix_shell.py):
```
# ctx.tags = ["user.unix_utilities"]
```
Once you have uncommented the line, you can customize your utility commands by editing
`tags/terminal/unix_utility.talon-list`.
## Jetbrains commands
For Jetbrains commands to work you must install https://plugins.jetbrains.com/plugin/10504-voice-code-idea
into each editor.
## Additional commands
There are other commands not described fully within this file. As an overview:
- The apps folder has command sets for use within different applications
- The core folder has various commands described [here](core/README.md)
- The lang folder has commands for writing [programming languages](#programming-languages)
- The plugin folder has various commands described [here](plugin/README.md)
- The tags folder has various other commands, such as using a browser, navigating a filesystem in terminal, and managing multiple cursors
## Settings
Several options are configurable via a [single settings file](settings.talon) out of the box. Any setting can be made context specific as needed (e.g., per-OS, per-app, etc).
The most commonly adjusted settings are probably
- `imgui.scale` to improve the visibility of all imgui-based windows (help, history, etc). This is simply a scale factor, 1.3 = 130%.
- `user.help_max_command_lines_per_page` and `user.help_max_contexts_per_page` to ensure all help information is visible.
- `user.mouse_wheel_down_amount` and `user.mouse_continuous_scroll_amount` for adjusting the scroll amounts for the various scroll commands.
## Customizing words and lists
Most lists of words are provided as Talon list files, with an extension of `.talon-list`. Read about the syntax of these files [on the Talon wiki](https://talon.wiki/Customization/talon_lists).
Some lists with multiple spoken forms/alternatives are instead provided as CSV files. Some are in the `settings` folder and are not created until you launch Talon with `community` installed.
You can customize common Talon list and CSV files with voice commands: say the word `customize` followed by `abbreviations`, `additional words`, `alphabet`, `homophones`, `search engines`, `Unix utilities`, `websites` or `words to replace`. These open the file in a text editor and move the insertion point to the bottom of the file so you can add to it.
You can also add words to the vocabulary or replacements (words_to_replace) by using the commands in [edit_vocabulary.talon](core/vocabulary/edit_vocabulary.talon).
## 💡 Tip: Overriding cleanly
You can override Talon lists by creating a new `.talon-list` file of your own, rather than changing the existing list in the repository.
This reduces how much manual `git merge`-ing you'll have to do in the future, when you go to merge new versions of this repository (colloquially called "upstream") with your local changes. This is because _new_ files you create will almost never conflict with upstream changes, whereas changing an existing file (especially hot spots, like commonly-customized lists) frequently do.
Your override files can even live outside of the `community` repository (anywhere in the Talon user directory), if you prefer, further simplifying merging.
To do so, simply create a `.talon-list` file with a more specific [context header](https://talon.wiki/Customization/talon-files#context-header) than the default. (For example, `lang: en` or `os: mac` main). Talon ensures that the most specific header (your override file) wins.
For example, to override `user.modifier_key`, you could create `modifier_keys_MYNAME.talon`:
```talon
list: user.modifier_key
language: en
-
# My preferred modifier keys
rose: cmd
troll: control
shift: shift
alt: alt
```
## Other Talon user file sets
In addition to this repo, there are [other Talon user file sets](https://talon.wiki/talon_user_file_sets/) containing additional commands that you may want to experiment with if you're feeling adventurous 😊. Many of them are meant to be used alongside `community`, but a few of them are designed as replacements. If it's not clear which, please file an issue against the given GitHub repository for that user file set!
# Collaborators
This repository is now officially a team effort. The following contributors have direct access:
- @dwiel
- @fidgetingbits
- @knausj85
- @rntz
- @splondike
- @pokey
Collaborators will reply to issues and pull requests as time and health permits. Please be patient.
## Guidelines for collaborators
1. Collaborators prioritize their health and their personal/professional needs first. Their time commitment to this effort is limited.
2. For "minor" fixes and improvements/bugs/new apps, collaborators are free to contribute without any review
3. For "significant" new development and refactors, collaborators should seek appropriate input and reviews from each-other. Collaborators are encouraged to open a discussion before committing their time to any major effort.
# Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for our guidelines for contributors
## Automatic formatting/linters
This repository uses [`pre-commit`](https://pre-commit.com/) to run and manage its formatters/linters. Running these yourself is optional. If you wish to do so, first [install](https://pre-commit.com/#install) `pre-commit`:
```bash
$ pip install pre-commit
```
You then have a few options as to when to run it:
- Run yourself at any time on your locally changed files: `pre-commit run`
- Run yourself on all files in the repository: `pre-commit run --all-files`
- Run automatically on your PRs (fixes will be pushed automatically to your branch):
- Visit https://pre-commit.ci/ and authorize the app to connect to your `community` fork.
- Set up an editor hook to run on save:
- You could follow the instructions for [Black](https://black.readthedocs.io/en/stable/integrations/editors.html), which are well written; simply replace `black <path>` with `pre-commit run --files <file>`.
- It's more performant to only reformat the specific file you're editing, rather than all changed files.
- Install a git pre-commit hook with `pre-commit install` (optional)
- This essentially runs `pre-commit run` automatically before creating local commits, applying formatters/linters on all changed files. If it "fails", the commit will be blocked.
- Note that because many of the rules automatically apply fixes, typically you just need to stage the changes that they made, then reattempt your commit.
- Whether to use the hook comes down to personal taste. If you like to make many small incremental "work" commits developing a feature, it may be too much overhead.
If you run into setup difficulty with `pre-commit`, you might want to ensure that you have a modern Python 3 local environment first. [pyenv](https://github.com/pyenv/pyenv) is good way to install such Python versions without affecting your system Python (recommend installing 3.9 to match Talon's current version). On macOS you can also `brew install pre-commit`.
## Automated tests
There are a number of automated unit tests in the repository. These are all run _outside_ of the Talon environment (e.g. we don't have access to Talon's window management APIs). These make use of a set of stubbed out Talon APIs in `test/stubs/` and a bit of class loader trickery in `conftest.py`.
To run the test suite you just need to install the `pytest` python package in to a non-Talon Python runtime you want to use for tests (i.e. don't install in the `~/.talon/.venv directory`). You can then just run the `pytest` command from the repository root to execute all the tests.
## Talon documentation
For official documentation on Talon's API and features, please visit https://talonvoice.com/docs/.
For community-generated documentation on Talon, please visit https://talon.wiki/.
## Alternate installation method: Zip file
It is possible to install `community` by downloading and extracting a zip file instead of using `git`. Note that this approach is discouraged, because it makes it more difficult to keep track of any changes you may make to your copy of the files.
If you wish to install `community` by downloading and extracting a zip file, proceed as follows:
1. Download the [zip archive of community](https://github.com/talonhub/community/archive/refs/heads/main.zip).
1. Extract the files. If you dont know how to extract zip files, a quick google search for "extract zip files" may be helpful.
1. Place these extracted files inside the `user` folder of the Talon Home directory. You can find this folder by right-clicking the Talon icon in the taskbar (Windows) or clicking the Talon icon in the menu bar (Mac), clicking Scripting > Open ~/talon, and navigating to `user`.

View file

@ -0,0 +1,6 @@
app: one_password
-
password new: user.password_new()
password dup: user.password_duplicate()
password edit: user.password_edit()
password delete: user.password_delete()

View file

@ -0,0 +1,4 @@
#todo: tags
-
password fill: user.password_fill()
password show: user.password_show()

View file

@ -0,0 +1,31 @@
from talon import Context, actions
ctx = Context()
# i don't see a need to restrict the app here, this just defines the actions
# each app can support appropriate voice commands as needed
# the below are for 1password, redefine as needed
ctx.matches = r"""
os: mac
"""
@ctx.action_class("user")
class UserActions:
def password_fill():
actions.key("cmd-\\")
def password_show():
actions.key("cmd-alt-\\")
def password_new():
actions.key("cmd-i")
def password_duplicate():
actions.key("cmd-d")
def password_edit():
actions.key("cmd-e")
def password_delete():
actions.key("cmd-backspace")

View file

@ -0,0 +1,31 @@
from talon import Context, actions
ctx = Context()
# i don't see a need to restrict the app here, this just defines the actions
# each app can support appropriate voice commands as needed
# the below are for 1password, redefine as needed
ctx.matches = r"""
os: windows
"""
@ctx.action_class("user")
class UserActions:
def password_fill():
actions.key("ctrl-\\\\")
def password_show():
actions.key("alt-ctrl-\\\\")
def password_new():
actions.key("ctrl-n")
def password_duplicate():
actions.key("ctrl-d")
def password_edit():
actions.key("ctrl-e")
def password_delete():
actions.key("ctrl-delete")

View file

@ -0,0 +1,29 @@
from talon import Module
mod = Module()
# 1password
mod.apps.one_password = "app.bundle: com.agilebits.onepassword7"
mod.apps.one_password = "app.name: 1Password for Windows desktop"
mod.apps.one_password = "app.name: 1Password.exe"
@mod.action_class
class Actions:
def password_fill():
"""fill the password"""
def password_show():
"""show the password"""
def password_new():
"""New password"""
def password_duplicate():
"""Duplicate password"""
def password_edit():
"""Edit password"""
def password_delete():
"""Delete password"""

View file

@ -0,0 +1,15 @@
# Web apps and browser extensions
Some of the Talon files for web apps (e.g. `apps/github/github_web.talon`) use a `browser.host` matcher. These talon files should work out of the box for Safari, Chrome, Brave, on Mac, but require additional configuration on other browsers/operating systems.
`community` is set up so that if a URL is found in the titlebar of an application matching the 'browser' tag it will be used to populate the browser.host matcher (see `code/browser.py`). This probably means that you will need an extension to make the browser.host based scripts work.
Browser extensions that can add the protocol and hostname or even the entire URL to the window title:
Firefox:
- https://addons.mozilla.org/en-US/firefox/addon/keepass-helper-url-in-title/
Chrome:
- https://chrome.google.com/webstore/detail/url-in-title/ignpacbgnbnkaiooknalneoeladjnfgb

View file

@ -0,0 +1,15 @@
from talon import Module
# --- App definition ---
mod = Module()
mod.apps.adobe_acrobat_reader_dc = r"""
os: windows
and app.name: Adobe Acrobat DC
os: windows
and app.exe: /^acrobat\.exe$/i
os: windows
and app.name: Adobe Acrobat Reader DC
os: windows
and app.exe: /^acrord32\.exe$/i
"""
# TODO: mac context and implementation

View file

@ -0,0 +1,5 @@
app: adobe_acrobat_reader_dc
-
# Set tags
tag(): user.tabs
tag(): user.pages

View file

@ -0,0 +1,61 @@
from talon import Context, actions
# Context matching
ctx = Context()
ctx.matches = """
os: windows
app: adobe_acrobat_reader_dc
"""
# --- Implement actions ---
@ctx.action_class("app")
class AppActions:
# app.tabs
def tab_next():
actions.key("ctrl-tab")
def tab_previous():
actions.key("ctrl-shift-tab")
@ctx.action_class("edit")
class EditActions:
def zoom_in():
actions.key("ctrl-0") # in german version
def zoom_out():
actions.key("ctrl-1") # in german version TODO: differentiate languages
def zoom_reset():
actions.key("ctrl-2")
@ctx.action_class("user")
class UserActions:
# user.pages
def page_current():
actions.key("ctrl-shift-n")
page = actions.edit.selected_text()
actions.key("tab:2 enter")
return int(page)
def page_next():
actions.key("ctrl-pagedown")
def page_previous():
actions.key("ctrl-pageup")
def page_jump(number: int):
actions.key("ctrl-shift-n")
actions.insert(str(number))
actions.key("enter")
def page_final():
actions.key("end")
def page_rotate_right():
actions.key("shift-ctrl-0")
def page_rotate_left():
actions.key("shift-ctrl-1")

View file

@ -0,0 +1,13 @@
user.running: amethyst
-
window next: key("alt-shift-j")
window previous: key("alt-shift-k")
# window move desk: key("ctrl-alt-shift-h")
window full: key("alt-shift-d")
window float: key(alt-shift-t)
window tall: key("alt-shift-a")
window middle: key("alt-shift-`")
window move main: key("alt-shift-enter")
window grow: key("alt-shift-l")
window shrink: key("alt-shift-h")
window reevaluate: key("alt-shift-z")

View file

@ -0,0 +1,9 @@
from talon import Context, Module
mod = Module()
mod.tag("anaconda", desc="tag for enabling anaconda commands in your terminal")
ctx = Context()
ctx.matches = r"""
tag: user.anaconda
"""

View file

@ -0,0 +1,40 @@
tag: terminal
and tag: user.anaconda
-
anaconda: "conda "
anaconda help: "conda --help\n"
anaconda version: "conda --version\n"
anaconda environment list: "conda env list\n"
anaconda environment create: "conda env create -f "
anaconda environment remove: "conda env remove -n "
anaconda activate: "conda activate "
anaconda clean: "conda clean "
anaconda compare: "conda compare "
anaconda config: "conda config "
anaconda create: "conda create "
anaconda info: "conda info "
anaconda init: "conda init "
anaconda install: "conda install "
anaconda list: "conda list "
anaconda package: "conda package "
anaconda remove: "conda remove "
anaconda uninstall: "conda uninstall "
anaconda run: "conda run "
anaconda search: "conda search "
anaconda update: "conda update "
anaconda upgrade: "conda upgrade "
anaconda build: "conda build "
anaconda convert: "conda convert "
anaconda debug: "conda debug "
anaconda develop: "conda develop "
anaconda environment: "conda env "
anaconda index: "conda index "
anaconda inspect: "conda inspect "
anaconda metapackage: "conda metapackage "
anaconda render: "conda render "
anaconda server: "conda server "
anaconda skeleton: "conda skeleton "
anaconda verify: "conda verify "

View file

@ -0,0 +1,21 @@
from talon import Context, actions
ctx = Context()
ctx.matches = r"""
app: notes
"""
@ctx.action_class("edit")
class EditActions:
def zoom_in():
actions.key("shift-cmd->")
def zoom_out():
actions.key("shift-cmd-<")
def zoom_reset():
actions.key("shift-cmd-0")
def indent_less():
actions.key("cmd-[")

View file

@ -0,0 +1,32 @@
os: mac
and app: notes
-
new note: key(cmd-n)
duplicate note: key(cmd-d)
new folder: key(shift-cmd-n)
toggle folders: key(alt-cmd-s)
show main: key(cmd-0)
list view: key(cmd-1)
gallery view: key(cmd-2)
toggle attachments: key(cmd-3)
find all: key(alt-cmd-f)
print note: key(cmd-p)
attach file: key(shift-cmd-a)
create link: key(cmd-k)
insert table: key(alt-cmd-t)
apply title: key(shift-cmd-t)
apply heading: key(shift-cmd-h)
apply subheading: key(shift-cmd-j)
apply body: key(shift-cmd-b)
apply mono: key(shift-cmd-m)
apply bullet: key(shift-cmd-7)
apply dash: key(shift-cmd-8)
apply number: key(shift-cmd-9)
apply checklist: key(shift-cmd-l)
increase font: key(cmd-+)
decrease font: key(cmd--)
line break: key(ctrl-enter)
mark: key(shift-cmd-u)
drag [line] down: key('ctrl-cmd-down')
drag [line] up: key('ctrl-cmd-up')

View file

@ -0,0 +1,90 @@
import os
from talon import Context, actions, ui
# TODO: fit this to terminal.py
ctx = Context()
ctx.matches = r"""
app: apple_terminal
"""
directories_to_remap = {}
directories_to_exclude = {}
@ctx.action_class("edit")
class EditActions:
def delete_line():
actions.key("ctrl-u")
@ctx.action_class("user")
class UserActions:
def file_manager_current_path():
title = ui.active_window().title
# take the first split for the zsh-based terminal
if "" in title:
title = title.split("")[0]
if "~" in title:
title = os.path.expanduser(title)
if title in directories_to_remap:
title = directories_to_remap[title]
if title in directories_to_exclude:
title = None
return title
def file_manager_show_properties():
"""Shows the properties for the file"""
def file_manager_open_directory(path: str):
"""opens the directory that's already visible in the view"""
actions.insert("cd ")
path = f'"{path}"'
actions.insert(path)
actions.key("enter")
# jtk - refresh title isn't necessary since the apple terminal does it for us
# actions.user.file_manager_refresh_title()
def file_manager_open_parent():
actions.insert("cd ..")
actions.key("enter")
def file_manager_select_directory(path: str):
"""selects the directory"""
actions.insert(path)
def file_manager_new_folder(name: str):
"""Creates a new folder in a gui filemanager or inserts the command to do so for terminals"""
name = f'"{name}"'
actions.insert("mkdir " + name)
def file_manager_open_file(path: str):
"""opens the file"""
actions.insert(path)
actions.key("enter")
def file_manager_select_file(path: str):
"""selects the file"""
actions.insert(path)
def file_manager_refresh_title():
return
@ctx.action_class("app")
class app_actions:
# other tab functions should already be implemented in
# code/platforms/mac/app.py
def tab_previous():
actions.key("ctrl-shift-tab")
def tab_next():
actions.key("ctrl-tab")

View file

@ -0,0 +1,25 @@
app: apple_terminal
-
# makes the commands in terminal.talon available
tag(): terminal
# use readline keybindings for various editing commands
tag(): user.readline
# activates the implementation of the commands/functions in terminal.talon
tag(): user.generic_unix_shell
# makes commands for certain applications available
# you can deactivate them if you do not use the application
tag(): user.git
tag(): user.anaconda
tag(): user.kubectl
# TODO: explain
tag(): user.tabs
tag(): user.file_manager
suspend: key(ctrl-z)
resume:
insert("fg")
key(enter)

View file

@ -0,0 +1,34 @@
from talon import Context, Module, actions, app
ctx = Context()
mod = Module()
mod.apps.arc = "app.name: Arc"
mod.apps.arc = """
os: mac
app.bundle: company.thebrowser.Browser
"""
ctx.matches = r"""
app: arc
"""
@ctx.action_class("user")
class UserActions:
def tab_close_wrapper():
actions.sleep("180ms")
actions.app.tab_close()
def command_search(command: str = ""):
actions.key("cmd-l")
if command != "":
actions.sleep("200ms")
actions.insert(command)
@ctx.action_class("browser")
class BrowserActions:
def show_extensions():
actions.app.tab_open()
actions.browser.go("arc://extensions")

View file

@ -0,0 +1,6 @@
app: arc
os: mac
-
tag(): browser
tag(): user.tabs
tag(): user.command_search

View file

@ -0,0 +1,8 @@
user.running: arc
os: mac
-
# This assumes that you have not disabled Little Arc
little arc [<user.text>]:
key("cmd-alt-n")
sleep(200ms)
insert(user.text or "")

View file

@ -0,0 +1,39 @@
from talon import Context, Module, actions
# --- App definition ---
mod = Module()
mod.apps.atril = """
os: linux
and app.name: Atril
"""
# Context matching
ctx = Context()
ctx.matches = r"""
app: atril
"""
# --- Implement actions ---
@ctx.action_class("user")
class UserActions:
# user.pages
def page_current():
actions.key("ctrl-l")
page = actions.edit.selected_text()
actions.key("right escape")
return int(page)
def page_next():
actions.key("ctrl-pagedown")
def page_previous():
actions.key("ctrl-pageup")
def page_jump(number: int):
actions.key("ctrl-l")
actions.insert(str(number))
actions.key("enter")
def page_final():
actions.key("ctrl-end")

View file

@ -0,0 +1,4 @@
app: atril
-
# Set tags
tag(): user.pages

View file

@ -0,0 +1,32 @@
from talon import Context, Module, actions, app
ctx = Context()
mod = Module()
mod.apps.brave = "app.name: Brave Browser"
mod.apps.brave = "app.name: Brave-browser"
mod.apps.brave = r"""
os: windows
and app.exe: /^brave\.exe$/i
os: linux
and app.exe: brave
os: mac
and app.bundle: com.brave.Browser
"""
ctx.matches = r"""
app: brave
"""
@ctx.action_class("user")
class UserActions:
def tab_close_wrapper():
actions.sleep("180ms")
actions.app.tab_close()
@ctx.action_class("browser")
class BrowserActions:
def show_extensions():
actions.app.tab_open()
actions.browser.go("brave://extensions")

View file

@ -0,0 +1,4 @@
app: brave
-
tag(): browser
tag(): user.tabs

View file

@ -0,0 +1,19 @@
from talon import Module
# --- App definition ---
mod = Module()
mod.apps.calibre = r"""
os: windows
and app.name: calibre.exe
os: windows
and app.exe: /^calibre\.exe$/i
os: windows
and app.name: calibre-parallel.exe
os: windows
and app.exe: /^calibre-parallel\.exe$/i
"""
mod.apps.calibre = """
os: linux
app.name: calibre
"""
# TODO: mac context

View file

@ -0,0 +1,39 @@
from talon import Context, Module, actions
# --- App definition ---
mod = Module()
mod.apps.calibre_viewer = """
app: calibre
title: /E-book viewer$/
title: /eBook-Betrachter$/
"""
# Context matching
ctx = Context()
ctx.matches = """
os: windows
os: linux
app: calibre_viewer
"""
# TODO: mac implementation
# --- Implement actions ---
@ctx.action_class("user")
class UserActions:
# user.pages
def page_next():
actions.key("pagedown")
def page_previous():
actions.key("pageup")
def page_final():
actions.key("ctrl-end")
# user.chapters
def chapter_next():
actions.key("ctrl-pagedown")
def chapter_previous():
actions.key("ctrl-pageup")

View file

@ -0,0 +1,5 @@
app: calibre_viewer
-
# Set tags
tag(): user.pages
tag(): user.chapters

View file

@ -0,0 +1,54 @@
from talon import Context, Module, actions, app
ctx = Context()
mod = Module()
mod.apps.chrome = "app.name: Google Chrome"
mod.apps.chrome = r"""
os: windows
and app.exe: /^chrome\.exe$/i
"""
mod.apps.chrome = """
os: mac
app.bundle: com.google.Chrome
app.bundle: com.google.Chrome.canary
app.bundle: org.chromium.Chromium
"""
mod.apps.chrome = """
os: linux
app.exe: chrome
app.exe: chromium-browser
app.exe: chromium
"""
mod.apps.chrome = """
os: linux
and app.name: Google-chrome
"""
ctx.matches = r"""
app: chrome
"""
@mod.action_class
class Actions:
def chrome_mod(key: str):
"""Press the specified key with the correct modifier key for the OS"""
if app.platform == "mac":
actions.key(f"cmd-{key}")
else:
actions.key(f"ctrl-{key}")
@ctx.action_class("user")
class UserActions:
def tab_close_wrapper():
actions.sleep("180ms")
actions.app.tab_close()
@ctx.action_class("browser")
class BrowserActions:
def show_extensions():
actions.app.tab_open()
actions.browser.go("chrome://extensions")

View file

@ -0,0 +1,14 @@
app: chrome
-
tag(): browser
tag(): user.tabs
profile switch: user.chrome_mod("shift-m")
tab search: user.chrome_mod("shift-a")
tab search <user.text>$:
user.chrome_mod("shift-a")
sleep(200ms)
insert("{text}")
key(down)

View file

@ -0,0 +1,6 @@
os: windows
app.exe: /^conemu64\.exe$/i
-
tag(): terminal
tag(): user.git

View file

@ -0,0 +1,85 @@
from talon import Context, Module, actions
mod = Module()
apps = mod.apps
apps.discord = "app.bundle: com.hnc.Discord"
apps.discord = "app.name: Discord"
apps.discord = "app.name: Discord.exe"
apps.discord = """
tag: browser
browser.host: discord.com
"""
mod.list("discord_destination", desc="discord destination")
ctx = Context()
ctx.matches = r"""
app: discord
"""
ctx.lists["user.discord_destination"] = {
"user": "@",
"voice": "!",
"server": "*",
}
@mod.action_class
class discord_actions:
def discord_mentions_last():
"""Go up to channel with unread mentions"""
def discord_mentions_next():
"""Go down to channel with unread mentions"""
def discord_oldest_unread():
"""Go to oldest unread message"""
def discord_toggle_pins():
"""Toggle pins popout"""
def discord_toggle_inbox():
"""Toggle inbox popout"""
def discord_toggle_members():
"""Toggle channel member list"""
def discord_emoji_picker():
"""Toggle emoji picker"""
def discord_gif_picker():
"""Toggle gif picker"""
def discord_sticker_picker():
"""Toggle sticker picker"""
def discord_mark_inbox_read():
"""Mark top inbox channel read"""
def discord_mute():
"""Toggle mute"""
def discord_deafen():
"""Toggle deafen"""
def discord_answer_call():
"""Answer incoming call"""
def discord_decline_call():
"""Decline incoming call"""
def discord_quick_switcher(dest_type: str, dest_search: str):
"""Open up the quick switcher, optionally specifying a type of destination"""
def discord_go_current_call():
"""Go to current call"""
def discord_toggle_dms():
"""Toggle between dms and your most recent server"""
@ctx.action_class("user")
class UserActions:
# Navigation: Channels
def messaging_open_channel_picker():
actions.user.discord_quick_switcher("#", "")

View file

@ -0,0 +1,34 @@
app: discord
-
tag(): user.messaging
tag(): user.emoji
# Navigation: QuickSwitcher
{user.discord_destination} [<user.text>]:
user.discord_quick_switcher(user.discord_destination, user.text or "")
switcher: user.discord_quick_switcher("", "")
# Navigation: Channels
[channel] mentions last: user.discord_mentions_last()
[channel] mentions next: user.discord_mentions_next()
oldest unread: user.discord_oldest_unread()
current call: user.discord_go_current_call()
# UI
toggle pins: user.discord_toggle_pins()
toggle inbox: user.discord_toggle_inbox()
toggle (members | member list): user.discord_toggle_members()
toggle (dee ems | dims): user.discord_toggle_dms()
pick emoji: user.discord_emoji_picker()
pick (jif | gif | gift): user.discord_gif_picker()
pick sticker: user.discord_sticker_picker()
# Misc
mark inbox channel read: user.discord_mark_inbox_read()
[toggle] (mute | unmute): user.discord_mute()
(mute | unmute) and sleep:
user.discord_mute()
speech.disable()
[toggle] (deafen | undeafen): user.discord_deafen()
answer call: user.discord_answer_call()
decline call: user.discord_decline_call()

View file

@ -0,0 +1,96 @@
from talon import Context, actions
ctx = Context()
ctx.matches = r"""
os: mac
app: discord
"""
@ctx.action_class("user")
class UserActions:
# Navigation: QuickSwitcher
def discord_quick_switcher(dest_type: str, dest_search: str):
actions.key("cmd-k")
actions.insert(dest_type)
if dest_search:
actions.insert(dest_search)
# Navigation: Servers
def messaging_workspace_previous():
actions.key("cmd-alt-up")
def messaging_workspace_next():
actions.key("cmd-alt-down")
# Navigation: Channels
def messaging_channel_previous():
actions.key("alt-up")
def messaging_channel_next():
actions.key("alt-down")
def messaging_unread_previous():
actions.key("alt-shift-up")
def messaging_unread_next():
actions.key("alt-shift-down")
def discord_mentions_last():
actions.key("cmd-alt-shift-up")
def discord_mentions_next():
actions.key("cmd-alt-shift-down")
def discord_oldest_unread():
actions.key("shift-pageup")
# UI
def discord_toggle_pins():
actions.key("cmd-p")
def discord_toggle_inbox():
actions.key("cmd-i")
def discord_toggle_members():
actions.key("cmd-u")
def discord_emoji_picker():
actions.key("cmd-e")
def discord_gif_picker():
actions.key("cmd-g")
def discord_sticker_picker():
actions.key("cmd-s")
# Misc
def messaging_mark_workspace_read():
actions.key("shift-esc")
def messaging_mark_channel_read():
actions.key("esc")
def messaging_upload_file():
actions.key("cmd-shift-u")
def discord_mark_inbox_read():
actions.key("cmd-shift-e")
def discord_mute():
actions.key("cmd-shift-m")
def discord_deafen():
actions.key("cmd-shift-d")
def discord_answer_call():
actions.key("cmd-enter")
def discord_decline_call():
actions.key("esc")
def discord_go_current_call():
actions.key("cmd-alt-a")
def discord_toggle_dms():
actions.key("cmd-alt-right")

View file

@ -0,0 +1,97 @@
from talon import Context, actions
ctx = Context()
ctx.matches = r"""
os: windows
os: linux
app: discord
"""
@ctx.action_class("user")
class UserActions:
# Navigation: QuickSwitcher
def discord_quick_switcher(dest_type: str, dest_search: str):
actions.key("ctrl-k")
actions.insert(dest_type)
if dest_search:
actions.insert(dest_search)
# Navigation: Servers
def messaging_workspace_previous():
actions.key("ctrl-alt-up")
def messaging_workspace_next():
actions.key("ctrl-alt-down")
# Navigation: Channels
def messaging_channel_previous():
actions.key("alt-up")
def messaging_channel_next():
actions.key("alt-down")
def messaging_unread_previous():
actions.key("alt-shift-up")
def messaging_unread_next():
actions.key("alt-shift-down")
def discord_mentions_last():
actions.key("ctrl-alt-shift-up")
def discord_mentions_next():
actions.key("ctrl-alt-shift-down")
def discord_oldest_unread():
actions.key("shift-pageup")
# UI
def discord_toggle_pins():
actions.key("ctrl-p")
def discord_toggle_inbox():
actions.key("ctrl-i")
def discord_toggle_members():
actions.key("ctrl-u")
def discord_emoji_picker():
actions.key("ctrl-e")
def discord_gif_picker():
actions.key("ctrl-g")
def discord_sticker_picker():
actions.key("ctrl-s")
# Misc
def messaging_mark_workspace_read():
actions.key("shift-esc")
def messaging_mark_channel_read():
actions.key("esc")
def messaging_upload_file():
actions.key("ctrl-shift-u")
def discord_mark_inbox_read():
actions.key("ctrl-shift-e")
def discord_mute():
actions.key("ctrl-shift-m")
def discord_deafen():
actions.key("ctrl-shift-d")
def discord_answer_call():
actions.key("ctrl-enter")
def discord_decline_call():
actions.key("esc")
def discord_go_current_call():
actions.key("ctrl-shift-alt-v")
def discord_toggle_dms():
actions.key("ctrl-alt-right")

View file

@ -0,0 +1,22 @@
from talon import Context, Module, actions, clip, ui
ctx = Context()
mod = Module()
ctx.matches = """
os: mac
"""
@mod.action_class
class Actions:
def dock_send_notification(notification: str):
"""Send a CoreDock notification to the macOS Dock using SPI"""
@ctx.action_class("user")
class UserActions:
def dock_send_notification(notification: str):
from talon.mac.dock import dock_notify
dock_notify(notification)

View file

@ -0,0 +1,5 @@
os: mac
-
^desktop$: user.dock_send_notification("com.apple.showdesktop.awake")
^window$: user.dock_send_notification("com.apple.expose.front.awake")
^launch pad$: user.dock_send_notification("com.apple.launchpad.toggle")

View file

@ -0,0 +1,9 @@
os: linux
-
show notifications: key(ctrl-`)
dismiss [notifications]: user.system_command("dunstctl close")
dismiss all [notifications]: user.system_command("dunstctl close-all")
#dunce pause: user.system_command('notify-send "DUNST_COMMAND_PAUSE"')
#dunce resume: user.system_command('notify-send "DUNST_COMMAND_RESUME"')
#test notification: user.system_command('notify-send "Hello from Talon"')

View file

@ -0,0 +1,144 @@
#custom eclipse commands go here
app: eclipse
-
tag(): user.find_and_replace
tag(): user.line_commands
# tag(): user.multiple_cursors
tag(): user.splits
tag(): user.tabs
tag(): user.command_search
# splits.py support end
# Sidebar
bar explore: key(alt-shift-w p)
# bar extensions:
bar outline: key(alt-shift-q o)
# bar run:
# bar source:
# bar switch:
# Panels
# panel control:
panel output:
key(alt-shift-q)
sleep(200ms)
key(c)
panel problems:
key(alt-shift-q)
sleep(200ms)
key(x)
panel errors:
key(alt-shift-q)
sleep(200ms)
key(l)
panel breakpoints:
key(alt-shift-q)
sleep(200ms)
key(b)
panel search:
key(alt-shift-q)
sleep(200ms)
key(s)
panel variables:
key(alt-shift-q)
sleep(200ms)
key(v)
# panel switch:
# panel terminal:
# Settings
show settings: key(alt-w p)
show shortcuts: key(ctrl-shift-l)
#show snippets:
# Display
# centered switch:
# fullscreen switch:
# theme switch:
# wrap switch:
# zen switch:
# File Commands
file hunt [<user.text>]:
key(ctrl-shift-r)
sleep(50ms)
insert(text or "")
# file copy path:
# file create sibling:
file create: key(ctrl-n)
file open folder: key(alt-shift-w x)
file rename: key(alt-shift-w p enter f2)
file reveal: key(alt-shift-w p enter)
# Language Features
# suggest show:
# hint show:
# definition show:
# definition peek:
# definition side:
# references show:
# references find:
# format that:
# format selection:
imports fix: key(ctrl-shift-o)
# problem last:
# problem fix:
# rename that:
# refactor that:
# whitespace trim:
# language switch:
refactor rename: key(alt-shift-r)
refactor this: key(alt-shift-i)
#code navigation
(go declaration | follow): key(f3)
go back: key(alt-left)
go forward: key(alt-right)
# go implementation:
# go recent:
# go type:
# go usage:
# Bookmarks.
#requires https://marketplace.eclipse.org/content/quick-bookmarks
go marks: key(alt-end)
toggle mark: key(ctrl-alt-b down enter)
go next mark: key(alt-pagedown)
go last mark: key(alt-pageup)
# Folding
# fold that:
# unfold that:
# fold those:
# unfold those:
# fold all:
# unfold all:
# fold comments:
#Debugging
break point: key(ctrl-shift-b)
step over: key(f6)
debug step into: key(f5)
debug step out [of]: key(f7)
#debug start: user.vscode("workbench.action.debug.start")
#debug pause:
#debug stopper:
debug continue: key(f8)
#debug restart:
# Terminal
# terminal external: user.vscode("workbench.action.terminal.openNativeConsole")
# terminal new: user.vscode("workbench.action.terminal.new")
# terminal next: user.vscode("workbench.action.terminal.focusNextPane")
# terminal last:user.vscode("workbench.action.terminal.focusPreviousPane")
# terminal split: user.vscode("workbench.action.terminal.split")
# terminal trash: user.vscode("Terminal:Kill")
# terminal scroll up: user.vscode("Terminal:ScrollUp")
# terminal scroll down: user.vscode("Terminal:ScrollDown")
#TODO: should this be added to linecommands?
copy line down: key(ctrl-alt-down)
copy line up: key(ctrl-alt-up)

View file

@ -0,0 +1,175 @@
from talon import Context, Module, actions
ctx = Context()
mod = Module()
mod.apps.eclipse = """
os: windows
and app.name: eclipse.exe
"""
ctx.matches = r"""
app: eclipse
"""
@ctx.action_class("app")
class AppActions:
# talon app actions
def tab_close():
actions.key("ctrl-w")
def tab_next():
actions.key("ctrl-pagedown")
def tab_previous():
actions.key("ctrl-pageup")
# action(app.tab_reopen):
def window_close():
actions.key("alt-f4")
def window_open():
actions.key("alt-w n")
@ctx.action_class("code")
class CodeActions:
# talon code actions
def toggle_comment():
actions.key("ctrl-7")
@ctx.action_class("edit")
class EditActions:
def find_next():
actions.key("enter")
def find_previous():
actions.key("shift-enter")
def line_swap_up():
actions.key("alt-up")
def line_swap_down():
actions.key("alt-down")
def line_clone():
actions.key("ctrl-alt-down")
def jump_line(n: int):
actions.key("ctrl-l")
actions.insert(str(n))
actions.key("enter")
def delete_line():
actions.key("ctrl-d")
def indent_more():
actions.key("tab")
def indent_less():
actions.key("shift-tab")
def save_all():
actions.key("ctrl-shift-s")
@ctx.action_class("user")
class UserActions:
# splits.py support begin
# requires https://marketplace.eclipse.org/content/handysplit
def split_clear_all():
actions.key("alt-shift-s f")
def split_clear():
actions.key("alt-shift-s f")
# action(user.split_flip):
def split_last():
actions.key("alt-shift-s t")
def split_next():
actions.key("alt-shift-s t")
def split_window_down():
actions.key("alt-shift-s m")
def split_window_horizontally():
actions.key("alt-ctrl-s s")
def split_window_right():
actions.key("alt-shift-s m")
def split_window_up():
actions.key("alt-shift-s m")
def split_window_vertically():
actions.key("alt-shift-s s")
def split_window():
actions.key("alt-ctrl-s s")
def command_search(command: str = ""):
actions.key("ctrl-3")
if command != "":
actions.insert(command)
# splits.py support end
# find_and_replace.py support begin
def find_everywhere(text: str):
"""Triggers find across project"""
actions.key("ctrl-h")
if text:
actions.insert(text)
# todo: these commands should only be available
# when it's focused
def find_toggle_match_by_case():
"""Toggles find match by case sensitivity"""
actions.key("alt-c")
def find_toggle_match_by_word():
"""Toggles find match by whole words"""
actions.key("alt-w")
def find_toggle_match_by_regex():
"""Toggles find match by regex"""
actions.key("alt-e")
def replace(text: str):
"""Search and replaces in the active editor"""
actions.key("ctrl-f")
if text:
actions.insert(text)
def replace_everywhere(text: str):
"""Search and replaces in the entire project"""
actions.key("alt-a f")
if text:
actions.insert(text)
def replace_confirm():
"""Confirm replace at current position"""
actions.key("alt-r")
def replace_confirm_all():
"""Confirm replace all"""
actions.key("alt-a")
def select_previous_occurrence(text: str):
actions.edit.find(text)
actions.sleep("100ms")
actions.key("alt-b alt-f enter esc")
def select_next_occurrence(text: str):
actions.edit.find(text)
actions.sleep("100ms")
actions.key("alt-f alt-o esc")
# find_and_replace.py support end

View file

@ -0,0 +1,28 @@
from talon import Context, Module, actions
mod = Module()
ctx = Context()
mod.apps.microsoft_edge = r"""
os: windows
and app.name: msedge.exe
os: windows
and app.name: Microsoft Edge
os: windows
and app.exe: /^msedge\.exe$/i
os: mac
and app.bundle: com.microsoft.edgemac
os: linux
and app.exe: msedge
"""
ctx.matches = r"""
app: microsoft_edge
"""
@ctx.action_class("browser")
class BrowserActions:
def show_extensions():
actions.app.tab_open()
actions.browser.go("edge://extensions")

View file

@ -0,0 +1,4 @@
app: microsoft_edge
-
tag(): browser
tag(): user.tabs

View file

@ -0,0 +1,363 @@
import logging
from typing import Optional
from talon import Context, Module, actions, settings
mod = Module()
mod.setting(
"emacs_meta",
type=str,
default="esc",
desc="""What to use for the meta key in emacs. Defaults to 'esc', since that should work everywhere. Other options are 'alt' and 'cmd'.""",
)
mod.apps.emacs = "app.name: Emacs"
mod.apps.emacs = "app.name: emacs"
mod.apps.emacs = "app.name: /^GNU Emacs/"
mod.apps.emacs = """
os: mac
app.bundle: org.gnu.Emacs
"""
mod.apps.emacs = r"""
os: windows
app.exe: /^emacs\.exe$/i
"""
ctx = Context()
ctx.matches = "app: emacs"
def meta(keys):
m = settings.get("user.emacs_meta")
if m == "alt":
return " ".join("alt-" + k for k in keys.split())
elif m == "cmd":
return " ".join("cmd-" + k for k in keys.split())
elif m != "esc":
logging.error(
f"Unrecognized 'emacs_meta' setting: {m!r}. Falling back to 'esc'."
)
return "esc " + keys
def meta_fixup(k):
if k.startswith("meta-"):
k = meta(k[len("meta-") :])
elif "meta-" in k:
raise NotImplementedError("user.emacs_key(): please put meta- first")
return k
@mod.action_class
class Actions:
def emacs_meta(key: str):
"Presses some keys modified by Emacs' meta key."
actions.key(meta(key))
def emacs_key(keys: str):
"""
Presses some keys, translating 'meta-' prefix to the appropriate keys. For
example, if the setting user.emacs_meta = 'esc', user.emacs_key("meta-ctrl-a")
becomes key("esc ctrl-a").
"""
# TODO: handle corner-cases like key(" ") and key("ctrl- "), etc.
actions.key(" ".join(meta_fixup(k) for k in keys.split()))
def emacs_prefix(n: Optional[int] = None):
"Inputs a prefix argument."
if n is None:
# `M-x universal-argument` doesn't have the same effect as pressing the key.
prefix_key = actions.user.emacs_command_keybinding("universal-argument")
actions.key(prefix_key or "ctrl-u") # default to ctrl-u
else:
# Applying meta to each key can use fewer keypresses and 'works' in ansi-term
# mode.
actions.user.emacs_meta(" ".join(str(n)))
def emacs(command_name: str, prefix: Optional[int] = None):
"""
Runs the emacs command `command_name`. Defaults to using M-x, but may use
a key binding if known or rpc if available. Provides numeric prefix argument
`prefix` if specified.
"""
meta_x = actions.user.emacs_command_keybinding("execute-extended-command")
keys = actions.user.emacs_command_keybinding(command_name)
short_form = actions.user.emacs_command_short_form(command_name)
if prefix is not None:
actions.user.emacs_prefix(prefix)
if keys is not None:
actions.user.emacs_key(keys)
else:
actions.user.emacs_key(meta_x or "meta-x")
actions.insert(short_form or command_name)
actions.key("enter")
def emacs_help(key: str = None):
"Runs the emacs help command prefix, optionally followed by some keys."
# NB. f1 works in ansi-term mode; C-h doesn't.
actions.key("f1")
if key is not None:
actions.key(key)
@ctx.action_class("user")
class UserActions:
def cut_line():
actions.edit.line_start()
actions.user.emacs("kill-line", 1)
def split_window():
actions.user.emacs("split-window-below")
def split_window_vertically():
actions.user.emacs("split-window-below")
def split_window_up():
actions.user.emacs("split-window-below")
def split_window_down():
actions.user.emacs("split-window-below")
actions.user.emacs("other-window")
def split_window_horizontally():
actions.user.emacs("split-window-right")
def split_window_left():
actions.user.emacs("split-window-right")
def split_window_right():
actions.user.emacs("split-window-right")
actions.user.emacs("other-window")
def split_clear():
actions.user.emacs("delete-window")
def split_clear_all():
actions.user.emacs("delete-other-windows")
def split_reset():
actions.user.emacs("balance-windows")
def split_next():
actions.user.emacs("other-window")
def split_last():
actions.user.emacs("other-window", -1)
def split_flip():
# only works reliably if there are only two panes/windows.
actions.key("ctrl-x b enter ctrl-x o ctrl-x b enter")
actions.user.split_last()
actions.key("ctrl-x b enter ctrl-x o")
def select_range(line_start, line_end):
# Assumes transient mark mode.
actions.edit.jump_line(line_start)
actions.edit.jump_line(line_end + 1)
actions.user.emacs("exchange-point-and-mark")
# # Version that highlights without transient-mark-mode:
# def select_range(line_start, line_end):
# actions.edit.jump_line(line_end + 1)
# actions.key("ctrl-@ ctrl-@")
# actions.edit.jump_line(line_start)
# dictation_peek() probably won't work in a terminal. PRs welcome.
def dictation_peek(left, right):
# clobber transient selection if it exists
actions.key("space backspace")
before, after = None, None
if left:
actions.edit.extend_word_left()
before = actions.edit.selected_text()
actions.user.emacs("pop-to-mark-command")
if right:
actions.edit.extend_line_end()
after = actions.edit.selected_text()
actions.user.emacs("pop-to-mark-command")
return (before, after)
@ctx.action_class("edit")
class EditActions:
def save():
actions.user.emacs("save-buffer")
def save_all():
actions.user.emacs("save-some-buffers")
def copy():
actions.user.emacs("kill-ring-save")
def cut():
actions.user.emacs("kill-region")
def undo():
actions.user.emacs("undo")
def paste():
actions.user.emacs("yank")
def delete():
actions.user.emacs("kill-region")
def file_start():
actions.user.emacs("beginning-of-buffer")
def file_end():
actions.user.emacs("end-of-buffer")
# works for eg 'select to top', but not if preceded by other selections :(
def extend_file_start():
actions.user.emacs("beginning-of-buffer")
def extend_file_end():
actions.user.emacs("end-of-buffer")
def select_none():
actions.user.emacs("keyboard-quit")
def select_all():
actions.user.emacs("mark-whole-buffer")
# If you don't use transient-mark-mode, maybe do this:
# actions.key('ctrl-u ctrl-x ctrl-x')
def word_left():
actions.user.emacs("backward-word")
def word_right():
actions.user.emacs("forward-word")
def extend_word_left():
actions.user.emacs_meta("shift-b")
def extend_word_right():
actions.user.emacs_meta("shift-f")
def sentence_start():
actions.user.emacs("backward-sentence")
def sentence_end():
actions.user.emacs("forward-sentence")
def extend_sentence_start():
actions.user.emacs_meta("shift-a")
def extend_sentence_end():
actions.user.emacs_meta("shift-e")
def paragraph_start():
actions.user.emacs("backward-paragraph")
def paragraph_end():
actions.user.emacs("forward-paragraph")
def line_start():
actions.user.emacs("move-beginning-of-line")
def line_end():
actions.user.emacs("move-end-of-line")
def extend_line_start():
actions.key("shift-ctrl-a")
def extend_line_end():
actions.key("shift-ctrl-e")
def line_swap_down():
actions.key("down ctrl-x ctrl-t up")
def line_swap_up():
actions.key("ctrl-x ctrl-t up:2")
def delete_line():
actions.key("ctrl-a ctrl-k")
def line_clone():
actions.user.emacs_key("ctrl-a meta-1 ctrl-k ctrl-y ctrl-y up meta-m")
def jump_line(n):
actions.user.emacs("goto-line", n)
def select_line(n: int = None):
if n is not None:
actions.edit.jump_line(n)
else:
actions.edit.line_start()
actions.edit.extend_line_end()
actions.edit.extend_right()
# This makes it so the cursor is on the same line, which can make
# subsequent commands more convenient.
actions.user.emacs("exchange-point-and-mark")
def indent_more():
actions.user.emacs("indent-rigidly", 4)
def indent_less():
actions.user.emacs("indent-rigidly", -4)
# These all perform text-scale-adjust, which examines the actual key pressed, so can't
# be done with actions.user.emacs.
def zoom_in():
actions.key("ctrl-x ctrl-+")
def zoom_out():
actions.key("ctrl-x ctrl--")
def zoom_reset():
actions.key("ctrl-x ctrl-0")
# Some modes override ctrl-s/r to do something other than isearch-forward, so we
# deliberately don't use actions.user.emacs.
def find(text: str = None):
actions.key("ctrl-s")
if text:
actions.insert(text)
def find_next():
actions.key("ctrl-s")
def find_previous():
actions.key("ctrl-r")
@ctx.action_class("app")
class AppActions:
def window_open():
actions.user.emacs("make-frame-command")
def tab_next():
actions.user.emacs("tab-next")
def tab_previous():
actions.user.emacs("tab-previous")
def tab_close():
actions.user.emacs("tab-close")
def tab_reopen():
actions.user.emacs("tab-undo")
def tab_open():
actions.user.emacs("tab-new")
@ctx.action_class("code")
class CodeActions:
def toggle_comment():
actions.user.emacs("comment-dwim")
def language():
# Assumes win.filename() gives buffer name.
if "*scratch*" == actions.win.filename():
return "elisp"
return actions.next()
@ctx.action_class("win")
class WinActions:
# This assumes the title is/contains the filename.
# To do this, put this in init.el:
# (setq-default frame-title-format '((:eval (buffer-name (window-buffer (minibuffer-selected-window))))))
def filename():
return actions.win.title()

View file

@ -0,0 +1,367 @@
app: emacs
-
tag(): user.tabs
tag(): user.splits
tag(): user.line_commands
# ----- GENERAL ----- #
#suplex: key(ctrl-x)
cancel: user.emacs("keyboard-quit")
exchange: user.emacs("exchange-point-and-mark")
execute: user.emacs("execute-extended-command")
execute {user.emacs_command}$: user.emacs(emacs_command)
execute <user.text>$:
user.emacs("execute-extended-command")
user.insert_formatted(text, "DASH_SEPARATED")
evaluate | (evaluate | eval) (exper | expression): user.emacs("eval-expression")
prefix: user.emacs_prefix()
prefix <user.number_signed_small>: user.emacs_prefix(number_signed_small)
abort recursive [edit]: user.emacs("abort-recursive-edit")
browse kill ring: user.emacs("browse-kill-ring")
fill paragraph: user.emacs("fill-paragraph")
insert char: user.emacs("insert-char")
occurs: user.emacs("occur")
other scroll [down]: user.emacs("scroll-other-window")
other scroll up: user.emacs("scroll-other-window-down")
package autoremove: user.emacs("package-autoremove")
package list | [package] list packages: user.emacs("list-packages")
reverse (lines | region): user.emacs("reverse-region")
save buffers kill emacs: user.emacs("save-buffers-kill-emacs")
save some buffers: user.emacs("save-some-buffers")
sort lines: user.emacs("sort-lines")
sort words: user.emacs("sort-words")
file [loop] continue: user.emacs("fileloop-continue")
go directory: user.emacs("dired-jump")
other go directory: user.emacs("dired-jump-other-window")
[toggle] debug on error: user.emacs("toggle-debug-on-error")
[toggle] debug on quit: user.emacs("toggle-debug-on-quit")
[toggle] input method: user.emacs("toggle-input-method")
[toggle] truncate lines: user.emacs("toggle-truncate-lines")
[toggle] word wrap: user.emacs("toggle-word-wrap")
manual: user.emacs("man")
manual <user.text>:
user.emacs("man")
user.insert_formatted(text, "DASH_SEPARATED")
# BUFFER SWITCHING #
switch: user.emacs("switch-to-buffer")
other switch: user.emacs("switch-to-buffer-other-window")
display: user.emacs("display-buffer")
# SHELL COMMANDS #
shell command: user.emacs("shell-command")
shell command inserting:
user.emacs_prefix()
user.emacs("shell-command")
shell command on region: user.emacs("shell-command-on-region")
shell command on region replacing:
user.emacs_prefix()
user.emacs("shell-command-on-region")
# CUSTOMIZE #
customize face: user.emacs("customize-face")
customize face <user.text>$:
user.emacs("customize-face")
user.insert_formatted(text, "DASH_SEPARATED")
customize group: user.emacs("customize-group")
customize variable: user.emacs("customize-variable")
(customize | custom) [theme] visit theme: user.emacs("custom-theme-visit-theme")
# MODE COMMANDS #
auto fill mode: user.emacs("auto-fill-mode")
dired omit mode: user.emacs("dired-omit-mode")
display line numbers mode: user.emacs("display-line-numbers-mode")
electric quote local mode: user.emacs("electric-quote-local-mode")
emacs lisp mode: user.emacs("emacs-lisp-mode")
fundamental mode: user.emacs("fundamental-mode")
global display line numbers mode: user.emacs("global-display-line-numbers-mode")
global highlight line mode: user.emacs("global-hl-line-mode")
global visual line mode: user.emacs("global-visual-line-mode")
highlight line mode: user.emacs("hl-line-mode")
lisp interaction mode: user.emacs("lisp-interaction-mode")
markdown mode: user.emacs("markdown-mode")
menu bar mode: user.emacs("menu-bar-mode")
overwrite mode: user.emacs("overwrite-mode")
paredit mode: user.emacs("paredit-mode")
rainbow mode: user.emacs("rainbow-mode")
read only mode: user.emacs("read-only-mode")
shell script mode: user.emacs("sh-mode")
sub word mode: user.emacs("subword-mode")
tab bar mode: user.emacs("tab-bar-mode")
talon script mode: user.emacs("talonscript-mode")
text mode: user.emacs("text-mode")
transient mark mode: user.emacs("transient-mark-mode")
visual line mode: user.emacs("visual-line-mode")
whitespace mode: user.emacs("whitespace-mode")
# MACROS #
emacs record: user.emacs("kmacro-start-macro")
emacs stop: user.emacs("kmacro-end-macro")
emacs play: user.emacs("kmacro-end-and-call-macro")
# PROFILER #
profiler start: user.emacs("profiler-start")
profiler stop: user.emacs("profiler-stop")
profiler report: user.emacs("profiler-report")
# WINDOW/SPLIT MANAGEMENT #
# What emacs calls windows, we call splits.
split solo: user.emacs("delete-other-windows")
[split] rebalance: user.emacs("balance-windows")
split shrink: user.emacs("shrink-window-if-larger-than-buffer")
other [split] shrink:
user.split_next()
user.emacs("shrink-window-if-larger-than-buffer")
user.split_last()
split grow: user.emacs("enlarge-window")
split grow <number_small>: user.emacs("enlarge-window", number_small)
split shrink <number_small>:
amount = number_small or 1
user.emacs("enlarge-window", 0 - amount)
split widen [<number_small>]:
user.emacs("enlarge-window-horizontally", number_small or 1)
split narrow [<number_small>]:
user.emacs("shrink-window-horizontally", number_small or 1)
# ----- HELP ----- #
apropos: user.emacs_help("a")
describe (fun | function): user.emacs_help("f")
describe key: user.emacs_help("k")
describe key briefly: user.emacs_help("c")
describe symbol: user.emacs_help("o")
describe variable: user.emacs_help("v")
describe mode: user.emacs_help("m")
describe bindings: user.emacs_help("b")
describe (char | character): user.emacs("describe-character")
describe text properties: user.emacs("describe-text-properties")
describe face: user.emacs("describe-face")
view lossage: user.emacs_help("l")
apropos <user.text>$:
user.emacs_help("a")
user.insert_formatted(text, "DASH_SEPARATED")
key(enter)
describe (fun | function) <user.text>$:
user.emacs_help("f")
user.insert_formatted(text, "DASH_SEPARATED")
key(enter)
describe symbol <user.text>$:
user.emacs_help("o")
user.insert_formatted(text, "DASH_SEPARATED")
key(enter)
describe variable <user.text>$:
user.emacs_help("v")
user.insert_formatted(text, "DASH_SEPARATED")
key(enter)
# ----- FILES & BUFFERS -----
file open: user.emacs("find-file")
file rename: user.emacs("rename-file")
(file open | find file) at point: user.emacs("ffap")
other file open: user.emacs("find-file-other-window")
(file | buffer) close:
user.emacs("kill-buffer")
key(enter)
buffer kill: user.emacs("kill-buffer")
buffer bury: user.emacs("bury-buffer")
buffer revert | revert buffer: user.emacs("revert-buffer")
buffer finish:
edit.save()
user.emacs("server-edit")
buffer list: user.emacs("buffer-menu")
buffer next: user.emacs("next-buffer")
buffer last: user.emacs("previous-buffer")
buffer rename: user.emacs("rename-buffer")
buffer widen: user.emacs("widen")
buffer narrow | [buffer] narrow to region: user.emacs("narrow-to-region")
diff (buffer | [buffer] with file):
user.emacs("diff-buffer-with-file")
key(enter)
# ----- MOTION AND EDITING ----- #
mark: user.emacs("set-mark-command")
go back: user.emacs("pop-to-mark-command")
global [go] back: user.emacs("pop-global-mark")
auto indent: user.emacs("indent-region")
indent <user.number_signed_small>: user.emacs("indent-rigidly", number_signed_small)
search back: user.emacs("isearch-backward")
(search regex | regex search): user.emacs("isearch-forward-regexp")
(search regex | regex search) back: user.emacs("isearch-backward-regexp")
replace: user.emacs("query-replace")
replace regex | regex replace: user.emacs("query-replace-regexp")
# These start a word/symbol-search or toggle an existing search's mode.
search [toggle] words: user.emacs("isearch-forward-word")
search [toggle] symbol: user.emacs("isearch-forward-symbol")
# These keybindings are only active in isearch-mode.
search edit: user.emacs_meta("e")
search toggle case [fold | sensitive]: user.emacs_meta("c")
search toggle regex: user.emacs_meta("r")
highlight lines matching [regex]: user.emacs("highlight-lines-matching-regexp")
highlight phrase: user.emacs("highlight-phrase")
highlight regex: user.emacs("highlight-regexp")
unhighlight (regex | phrase): user.emacs("unhighlight-regexp")
unhighlight all:
user.emacs_prefix()
user.emacs("unhighlight-regexp")
recenter:
user.emacs_prefix()
user.emacs("recenter-top-bottom")
(center | [center] <number_small> from) top:
user.emacs("recenter-top-bottom", number_small or 0)
(center | [center] <number_small> from) bottom:
number = number_small or 0
user.emacs("recenter-top-bottom", -1 - number)
go <number> top:
edit.jump_line(number)
user.emacs("recenter-top-bottom", 0)
go <number> bottom:
edit.jump_line(number)
user.emacs("recenter-top-bottom", -2)
next error | error next: user.emacs("next-error")
last error | error last: user.emacs("previous-error")
term right: user.emacs("forward-sexp")
term left: user.emacs("backward-sexp")
term up: user.emacs("backward-up-list")
term end: user.emacs("up-list")
term down: user.emacs("down-list")
term kill: user.emacs("kill-sexp")
term wipe: user.emacs("kill-sexp", -1)
term (mark | select): user.emacs("mark-sexp")
term copy:
user.emacs("mark-sexp")
edit.copy()
term freeze:
user.emacs("mark-sexp")
user.emacs("comment-region")
term [auto] indent:
user.emacs("mark-sexp")
user.emacs("indent-region")
(sentence | sent) (right | end): edit.sentence_end()
(sentence | sent) (left | start): edit.sentence_start()
(sentence | sent) kill: user.emacs("kill-sentence")
graph kill: user.emacs("kill-paragraph")
graph up: edit.paragraph_start()
graph down: edit.paragraph_end()
graph mark: user.emacs("mark-paragraph")
graph copy:
user.emacs("mark-paragraph")
edit.copy()
graph cut:
user.emacs("mark-paragraph")
edit.cut()
# NB. can use these to implement "drag <X> left/right/up/down" commands,
# but note that 'transpose line' and 'drag line down' are different.
transpose [word | words]: user.emacs("transpose-words")
transpose (term | terms): user.emacs("transpose-sexps")
transpose (char | chars): user.emacs("transpose-chars")
transpose (line | lines): user.emacs("transpose-lines")
transpose (sentence | sentences): user.emacs("transpose-sentences")
transpose (graph | graphs | paragraphs): user.emacs("transpose-paragraphs")
register (copy | save): user.emacs("copy-to-register")
register (paste | insert): user.emacs("insert-register")
register jump: user.emacs("jump-to-register")
register (copy | save) rectangle: user.emacs("copy-rectangle-to-register")
rectangle clear: user.emacs("clear-rectangle")
rectangle delete: user.emacs("delete-rectangle")
rectangle kill: user.emacs("kill-rectangle")
rectangle open: user.emacs("open-rectangle")
rectangle (copy | save) [to] register: user.emacs("copy-rectangle-to-register")
rectangle (yank | paste): user.emacs("yank-rectangle")
rectangle copy: user.emacs("copy-rectangle-as-kill")
rectangle number lines: user.emacs("rectangle-number-lines")
# ----- XREF SUPPORT ----- #
[xref] find definition: user.emacs("xref-find-definitions")
[xref] find definition other window: user.emacs("xref-find-definitions-other-window")
[xref] find definition other frame: user.emacs("xref-find-definitions-other-frame")
[xref] find references: user.emacs("xref-find-references")
[xref] find references [and] replace: user.emacs("xref-find-references-and-replace")
xref find apropos: user.emacs("xref-find-apropos")
xref go back: user.emacs("xref-go-back")
visit tags table: user.emacs("visit-tags-table")
# ----- PROJECT SUPPORT ----- #
project [find] file: user.emacs("project-find-file")
project [find] (regex | grep): user.emacs("project-find-regexp")
project [query] replace regex: user.emacs("project-query-replace-regexp")
project (dired | directory): user.emacs("projectile-dired")
project [run] shell: user.emacs("projectile-run-shell")
project [run] eshell: user.emacs("projectile-run-eshell")
project search: user.emacs("project-search")
project vc dir: user.emacs("project-vc-dir")
project compile [project]: user.emacs("projectile-compile-project")
project [run] shell command: user.emacs("projectile-run-shell-command-in-root")
project [run] async shell command:
user.emacs("projectile-run-async-shell-command-in-root")
project (switch [to buffer] | buffer | buff): user.emacs("projectile-switch-to-buffer")
project kill [buffers]: user.emacs("projectile-kill-buffers")
project switch [project]: user.emacs("project-switch-project")
# ----- VC/GIT SUPPORT ----- #
vc (annotate | blame): user.emacs("vc-annotate")
# ----- MAJOR & MINOR MODES ----- #
# python-mode #
python mode: user.emacs("python-mode")
run python: user.emacs("run-python")
python [shell] send buffer: user.emacs("python-shell-send-buffer")
python [shell] send file: user.emacs("python-shell-send-file")
python [shell] send region: user.emacs("python-shell-send-region")
python [shell] send (function | defun): user.emacs("python-shell-send-defun")
python [shell] send statement: user.emacs("python-shell-send-statement")
python (shell switch | switch [to] shell): user.emacs("python-shell-switch-to-shell")
# smerge-mode #
smerge mode: user.emacs("smerge-mode")
merge next: user.emacs("smerge-next")
merge last: user.emacs("smerge-prev")
merge keep upper: user.emacs("smerge-keep-upper")
merge keep lower: user.emacs("smerge-keep-lower")
merge keep base: user.emacs("smerge-keep-base")
merge keep (this | current): user.emacs("smerge-keep-current")
merge refine: user.emacs("smerge-refine")
merge split: user.emacs("smerge-resolve")
# outline-minor-mode #
# frequent: overview, show, hide, next, last, forward, backward, up
outline minor mode: user.emacs("outline-minor-mode")
outline show all: user.emacs("outline-show-all")
outline show entry: user.emacs("outline-show-entry")
outline hide entry: user.emacs("outline-hide-entry")
outline show [subtree]: user.emacs("outline-show-subtree")
outline hide [subtree]: user.emacs("outline-hide-subtree")
outline show children: user.emacs("outline-show-children")
outline show branches: user.emacs("outline-show-branches")
outline hide leaves: user.emacs("outline-hide-leaves")
outline hide sublevels: user.emacs("outline-hide-sublevels")
outline (hide body | [show] (overview | outline)): user.emacs("outline-hide-body")
outline hide other: user.emacs("outline-hide-other")
outline forward [same level]: user.emacs("outline-forward-same-level")
outline (backward | back) [same level]: user.emacs("outline-backward-same-level")
outline next [visible heading]: user.emacs("outline-next-visible-heading")
outline (previous | last) [visible heading]:
user.emacs("outline-previous-visible-heading")
outline insert [heading]: user.emacs("outline-insert-heading")
outline up [heading]: user.emacs("outline-up-heading")
outline promote: user.emacs("outline-promote")
outline demote: user.emacs("outline-demote")
outline move [subtree] down: user.emacs("outline-move-subtree-down")
outline move [subtree] up: user.emacs("outline-move-subtree-up")
outline mark [subtree]: user.emacs("outline-mark-subtree")

View file

@ -0,0 +1,231 @@
Command, Key binding, Short form, Spoken form
abort-recursive-edit, ctrl-]
auto-fill-mode,, auto-f
backward-paragraph, meta-{
backward-sentence, meta-a
backward-sexp, meta-ctrl-b
backward-up-list, meta-ctrl-up
backward-word, meta-b
balance-windows, ctrl-x +
beginning-of-buffer, meta-<
browse-kill-ring,, b-k-r
buffer-menu, ctrl-x ctrl-b
bury-buffer,, bur
byte-compile-file,, by-c
byte-recompile-directory
byte-recompile-file
clear-rectangle, ctrl-x r c
clone-indirect-buffer,, clo-i
comment-dwim, meta-;
comment-line,, comment-l
comment-region, ctrl-c ;
compile
compile-defun,, co-def
copy-rectangle-as-kill, ctrl-x r meta-w
copy-rectangle-to-register, ctrl-x r r
copy-to-register, ctrl-x r s
custom-theme-visit-theme
customize-face
customize-group
customize-variable
delete-other-windows, ctrl-x 1
delete-rectangle, ctrl-x r d
delete-window, ctrl-x 0
describe-character,, desc-char
diff-buffer-with-file,, d-b-w-f
display-buffer, ctrl-x 4 ctrl-o
dired-jump, ctrl-x ctrl-j
dired-jump-other-window, ctrl-x 4 ctrl-j
display-buffer, ctrl-x 4 ctrl-o
display-line-numbers-mode,, dis-num
down-list, meta-ctrl-down
electric-quote-local-mode
electric-quote-mode
end-of-buffer, meta->
enlarge-window, ctrl-x ^
enlarge-window-horizontally, ctrl-x }
eval-expression, meta-:
eval-print-last-sexp,, ev-p
eval-region,, ev-r
exchange-point-and-mark, ctrl-x ctrl-x
execute-extended-command, meta-x
fileloop-continue,, filel
fill-paragraph, meta-q
find-file, ctrl-x ctrl-f
font-lock-update, ctrl-x x f
forward-paragraph, meta-}
forward-sentence, meta-e
forward-sexp, meta-ctrl-f
forward-word, meta-f
fundamental-mode,, fun-m
global-display-line-numbers-mode,, g-d-l-n-m
global-hl-line-mode,, g-hl-l-m
global-visual-line-mode,, gl-v-l-m
goto-line, meta-g meta-g
highlight-lines-matching-regexp, meta-s h l
highlight-phrase, meta-s h p
highlight-regexp, meta-s h r
hl-line-mode,, hl-l-m
indent-region, meta-ctrl-\
indent-rigidly, ctrl-x tab
insert-char, ctrl-x 8 enter
insert-register, ctrl-x r i
isearch-backward, ctrl-r
isearch-backward-regexp, meta-ctrl-r
isearch-forward, ctrl-s
isearch-forward-regexp, meta-ctrl-s
isearch-forward-symbol, meta-s _
isearch-forward-word, meta-s w
jump-to-register, ctrl-x r j
keyboard-quit, ctrl-g
kill-buffer, ctrl-x k
kill-line, ctrl-k
kill-paragraph,, kill-par
kill-rectangle, ctrl-x r k
kill-region, ctrl-w
kill-ring-save, meta-w
kill-sentence, meta-k
kill-sexp, meta-ctrl-k
kill-word, meta-d
kmacro-end-and-call-macro, ctrl-x e
kmacro-end-macro, ctrl-x )
kmacro-start-macro, ctrl-x (
list-packages
make-frame-command, ctrl-x 5 2
mark-paragraph, meta-h
mark-sexp, meta-ctrl-@
mark-whole-buffer, ctrl-x h
move-beginning-of-line, ctrl-a
move-end-of-line, ctrl-e
narrow-to-region, ctrl-x n n
next-buffer, ctrl-x right
next-error, meta-g n
occur, meta-s o
open-rectangle, ctrl-x r o
other-frame, ctrl-x 5 o
other-window, ctrl-x o
outline-backward-same-level, ctrl-c @ ctrl-b
outline-demote, ctrl-c @ ctrl->
outline-forward-same-level, ctrl-c @ ctrl-f
outline-hide-body, ctrl-c @ ctrl-t
outline-hide-entry, ctrl-c @ ctrl-c
outline-hide-leaves, ctrl-c @ ctrl-l
outline-hide-other, ctrl-c @ ctrl-o
outline-hide-sublevels, ctrl-c @ ctrl-q
outline-hide-subtree, ctrl-c @ ctrl-d
outline-insert-heading, ctrl-c @ RET
outline-mark-subtree, ctrl-c @ @
outline-move-subtree-down, ctrl-c @ ctrl-v
outline-move-subtree-up, ctrl-c @ ctrl-^
outline-next-visible-heading, ctrl-c @ ctrl-n
outline-previous-visible-heading, ctrl-c @ ctrl-p
outline-promote, ctrl-c @ ctrl-<
outline-show-all, ctrl-c @ ctrl-a
outline-show-branches, ctrl-c @ ctrl-k
outline-show-children, ctrl-c @ tab
outline-show-entry, ctrl-c @ ctrl-e
outline-show-subtree, ctrl-c @ ctrl-s
outline-up-heading, ctrl-c @ ctrl-u
overwrite-mode,, overwr
package-autoremove
pop-global-mark, ctrl-x ctrl-@
pop-to-mark-command, ctrl-u ctrl-space
previous-buffer, ctrl-x left
previous-error, meta-g p
project-find-file, ctrl-x p f
project-find-regexp, ctrl-x p g
project-query-replace-regexp, ctrl-x p r
project-search, ctrl-x p ctrl-s
project-switch-project
project-vc-dir, ctrl-x p v
projectile-compile-project, ctrl-x p c
projectile-dired, ctrl-x p d
projectile-kill-buffers, ctrl-x p k
projectile-run-async-shell-command-in-root, ctrl-x p &
projectile-run-eshell, ctrl-x p e
projectile-run-shell, ctrl-x p s
projectile-run-shell-command-in-root, ctrl-x p !
projectile-switch-to-buffer, ctrl-x p b
query-replace, meta-%
query-replace-regexp, meta-ctrl-%
read-only-mode, ctrl-x ctrl-q
recenter-top-bottom, ctrl-l
recenter-top-bottom, ctrl-l
rectangle-number-lines, ctrl-x r N
rename-buffer, ctrl-x x r, ren-b
rename-uniquely, ctrl-x x u
reverse-region
revert-buffer,, rev-buf
revert-buffer-quick, ctrl-x x g
save-buffer, ctrl-x ctrl-s
save-buffers-kill-emacs,, s-b-k-e
save-some-buffers, ctrl-x s
scroll-other-window, meta-pagedown
scroll-other-window-down, meta-pageup
server-edit, ctrl-x #
set-mark-command, ctrl-@
sh-mode
shell-command, meta-!
shell-command-on-region, meta-|
shell-script-mode
shrink-window-horizontally, ctrl-x {
shrink-window-if-larger-than-buffer, ctrl-x -
smerge-combine-with-next
smerge-diff-base-lower
smerge-diff-base-upper
smerge-diff-upper-lower
smerge-ediff
smerge-keep-all
smerge-keep-base
smerge-keep-current, ctrl-c ^ enter
smerge-keep-lower, ctrl-c ^ l
smerge-keep-upper, ctrl-c ^ u
smerge-next, ctrl-c ^ n
smerge-prev, ctrl-c ^ p
smerge-refine, ctrl-c ^ R
smerge-resolve, ctrl-c ^ r
sort-lines
sort-words
split-window-below, ctrl-x 2
split-window-right, ctrl-x 3
switch-to-buffer, ctrl-x b
switch-to-buffer-other-window, ctrl-x 4 b
tab-bar-mode
tab-close, ctrl-x t 0
tab-close-other, ctrl-x t 1
tab-new, ctrl-x t 2
tab-next, ctrl-x t o
tab-previous, ctrl-x t O
tab-undo, ctrl-x t u
text-mode,, text-m
toggle-debug-on-error,, t-d-on-e
toggle-debug-on-quit,, t-d-on-q
toggle-input-method, ctrl-\
toggle-truncate-lines, ctrl-x x t
toggle-word-wrap
transient-mark-mode,, tr-m-m
transpose-chars, ctrl-t
transpose-lines, ctrl-x ctrl-t
transpose-paragraphs,, tr-par
transpose-sentences,, tr-sen
transpose-sexps, meta-ctrl-t
transpose-words, meta-t
undo, ctrl-_
unhighlight-regexp, meta-s h u
universal-argument, ctrl-u
vc-annotate, ctrl-x v g
view-lossage, ctrl-h l
visit-tags-table,, v-t-t
visual-line-mode,, visu-l-m
whitespace-cleanup,, wh-cl
whitespace-mode,, white-m
widen, ctrl-x n w
xref-find-apropos, meta-ctrl-.
xref-find-definitions, meta-.
xref-find-definitions-other-frame, ctrl-x 5 .
xref-find-definitions-other-window, ctrl-x 4 .
xref-find-references, meta-?
xref-find-references-and-replace
xref-go-back,"meta-,"
yank, ctrl-y
yank-rectangle, ctrl-x r y
Can't render this file because it has a wrong number of fields in line 2.

View file

@ -0,0 +1,71 @@
import csv
from dataclasses import dataclass
from pathlib import Path
from typing import NamedTuple, Optional
from talon import Context, Module, actions, app, resource
mod = Module()
mod.list("emacs_command", desc="Emacs commands")
ctx = Context()
class Command(NamedTuple):
name: str
keys: Optional[str] = None
short: Optional[str] = None
spoken: Optional[str] = None
# Maps command name to Command.
emacs_commands = {}
@mod.action_class
class Actions:
def emacs_command_keybinding(command_name: str) -> Optional[str]:
"Looks up the keybinding for command_name in emacs_commands.csv."
return emacs_commands.get(command_name, Command(command_name)).keys
def emacs_command_short_form(command_name: str) -> Optional[str]:
"Looks up the short form for command_name in emacs_commands.csv."
return emacs_commands.get(command_name, Command(command_name)).short
@resource.watch("emacs_commands.csv")
def load_commands(f):
rows = list(csv.reader(f))
# Check headers
assert rows[0] == ["Command", " Key binding", " Short form", " Spoken form"]
commands = []
for row in rows[1:]:
if 0 == len(row):
continue
if len(row) > 4:
print(
f"emacs_commands.csv: More than four values in row: {row}. "
+ " Ignoring the extras"
)
name, keys, short, spoken = (
[x.strip() or None for x in row] + [None, None, None]
)[:4]
commands.append(Command(name=name, keys=keys, short=short, spoken=spoken))
# Update global command info.
global emacs_commands
emacs_commands = {c.name: c for c in commands}
# Generate spoken forms and apply overrides.
try:
command_list = actions.user.create_spoken_forms_from_list(
[c.name for c in commands], generate_subsequences=False
)
except:
pass
else:
for c in commands:
if c.spoken:
command_list[c.spoken] = c.name
ctx.lists["self.emacs_command"] = command_list

View file

@ -0,0 +1,6 @@
app: evernote
os: mac
-
settings():
# Necessary to stop commands like 'slap' getting jumbled
key_wait = 9.0

View file

@ -0,0 +1,4 @@
app: evince
-
# Set tags
tag(): user.pages

View file

@ -0,0 +1,39 @@
from talon import Context, Module, actions
# --- App definition ---
mod = Module()
mod.apps.evince = """
os: linux
and app.name: Evince
"""
# Context matching
ctx = Context()
ctx.matches = r"""
app: evince
"""
# --- Implement actions ---
@ctx.action_class("user")
class UserActions:
# user.pages
def page_current():
actions.key("ctrl-l")
page = actions.edit.selected_text()
actions.key("escape")
return int(page)
def page_next():
actions.key("n")
def page_previous():
actions.key("p")
def page_jump(number: int):
actions.key("ctrl-l")
actions.insert(str(number))
actions.key("enter")
def page_final():
actions.key("ctrl-end")

View file

@ -0,0 +1,77 @@
import os
from talon import Context, actions, ui
from talon.mac import applescript
ctx = Context()
ctx.matches = r"""
app: finder
"""
directories_to_remap = {"": "/Volumes"}
directories_to_exclude = {}
@ctx.action_class("user")
class UserActions:
def file_manager_open_parent():
actions.key("cmd-up")
def file_manager_current_path():
title = ui.active_window().title
if "~" in title:
title = os.path.expanduser(title)
if title in directories_to_remap:
title = directories_to_remap[title]
if title in directories_to_exclude:
title = ""
return title
def file_manager_terminal_here():
applescript.run(
r"""
tell application "Finder"
set myWin to window 1
set thePath to (quoted form of POSIX path of (target of myWin as alias))
tell application "Terminal"
activate
tell window 1
do script "cd " & thePath
end tell
end tell
end tell"""
)
def file_manager_show_properties():
"""Shows the properties for the file"""
actions.key("cmd-i")
def file_manager_open_directory(path: str):
"""opens the directory that's already visible in the view"""
actions.key("cmd-shift-g")
actions.sleep("50ms")
actions.insert(path)
actions.key("enter")
def file_manager_select_directory(path: str):
"""selects the directory"""
actions.insert(path)
def file_manager_new_folder(name: str):
"""Creates a new folder in a gui filemanager or inserts the command to do so for terminals"""
actions.key("cmd-shift-n")
actions.insert(name)
def file_manager_open_file(path: str):
"""opens the file"""
actions.key("home")
actions.insert(path)
actions.key("cmd-o")
def file_manager_select_file(path: str):
"""selects the file"""
actions.key("home")
actions.insert(path)

View file

@ -0,0 +1,28 @@
os: mac
app: finder
-
tag(): user.file_manager
tag(): user.tabs
preferences: key(cmd-,)
options: key(cmd-j)
search: key(cmd-alt-f)
# bit of a mouthful, but it's probably not the kind of thing you'd be saying frequently
sort by none: key(ctrl-alt-cmd-0)
sort by name: key(ctrl-alt-cmd-1)
sort by kind: key(ctrl-alt-cmd-2)
sort by date opened: key(ctrl-alt-cmd-3)
sort by date added: key(ctrl-alt-cmd-4)
sort by date modified: key(ctrl-alt-cmd-5)
sort by size: key(ctrl-alt-cmd-6)
icon view: key(cmd-1)
column view: key(cmd-3)
list view: key(cmd-2)
gallery view: key(cmd-4)
copy path: key(alt-cmd-c)
trash it: key(cmd-backspace)
hide [finder]: key(cmd-h)
hide others: app.window_hide_others()

View file

@ -0,0 +1,60 @@
from talon import Context, Module, actions, app
ctx = Context()
mod = Module()
apps = mod.apps
apps.firefox = "app.name: Firefox"
apps.firefox = "app.name: Firefox Developer Edition"
apps.firefox = "app.name: firefox"
apps.firefox = "app.name: Firefox-esr"
apps.firefox = "app.name: firefox-esr"
apps.firefox = "app.name: LibreWolf"
apps.firefox = "app.name: waterfox"
apps.firefox = r"""
os: windows
and app.name: Firefox
os: windows
and app.exe: /^firefox\.exe$/i
"""
apps.firefox = """
os: mac
and app.bundle: org.mozilla.firefox
"""
# Make the context match more specifically than anything else. This is important, eg. to
# override the browser.go_home() implementation in tags/browser/browser_mac.py.
ctx.matches = r"""
os: windows
os: linux
os: mac
tag: browser
app: firefox
"""
@mod.action_class
class Actions:
def firefox_bookmarks_sidebar():
"""Toggles the Firefox bookmark sidebar"""
def firefox_history_sidebar():
"""Toggles the Firefox history sidebar"""
@ctx.action_class("user")
class UserActions:
def tab_close_wrapper():
actions.sleep("180ms")
actions.app.tab_close()
@ctx.action_class("browser")
class BrowserActions:
def focus_page():
actions.browser.focus_address()
actions.edit.find()
actions.sleep("180ms")
actions.key("escape")
def go_home():
actions.key("alt-home")

View file

@ -0,0 +1,15 @@
app: firefox
-
tag(): browser
tag(): user.tabs
tab search:
browser.focus_address()
insert("% ")
tab search <user.text>$:
browser.focus_address()
insert("% {text}")
key(down)
(sidebar | panel) bookmarks: user.firefox_bookmarks_sidebar()
(sidebar | panel) history: user.firefox_history_sidebar()

View file

@ -0,0 +1,33 @@
from talon import Context, actions
ctx = Context()
ctx.matches = r"""
os: mac
tag: browser
app: firefox
"""
@ctx.action_class("user")
class UserActions:
def firefox_bookmarks_sidebar():
actions.key("cmd-b")
def firefox_history_sidebar():
actions.key("cmd-shift-h")
@ctx.action_class("browser")
class BrowserActions:
def bookmarks():
actions.key("cmd-shift-o")
def open_private_window():
actions.key("cmd-shift-p")
def show_downloads():
actions.key("cmd-j")
def show_extensions():
actions.key("cmd-shift-a")

View file

@ -0,0 +1,44 @@
from talon import Context, actions, app
ctx = Context()
ctx.matches = r"""
os: windows
os: linux
tag: browser
app: firefox
"""
@ctx.action_class("user")
class UserActions:
def firefox_bookmarks_sidebar():
actions.key("ctrl-b")
def firefox_history_sidebar():
actions.key("ctrl-h")
@ctx.action_class("browser")
class BrowserActions:
def focus_address():
# Only using "ctrl-l" might fail and clear the console if the user
# is focused in the devtools
actions.key("f6")
actions.sleep("100ms")
actions.key("ctrl-l")
def open_private_window():
actions.key("ctrl-shift-p")
def show_downloads():
if app.platform == "linux":
actions.key("ctrl-shift-y")
else:
actions.key("ctrl-j")
def show_extensions():
actions.key("ctrl-shift-a")
def show_history():
actions.key("ctrl-shift-h")

View file

@ -0,0 +1,70 @@
from talon import Context, Module, actions
mod = Module()
ctx = Context()
# --- App definition ---
mod.apps.foxit_reader = r"""
os: windows
and app.name: /^Foxit Reader/
os: windows
and app.exe: /^foxitreader\.exe$/i
os: windows
and app.name: Foxit PDF Reader
os: windows
and app.exe: /^foxitpdfreader\.exe$/i
"""
# Context matching
ctx.matches = """
app: foxit_reader
"""
@ctx.action_class("app")
class AppActions:
# app.tabs
def tab_open():
actions.key("ctrl-o")
def tab_reopen():
actions.app.notify("Foxit does not support this action.")
@ctx.action_class("user")
class UserActions:
# user.tabs
def tab_jump(number):
actions.app.notify("Foxit does not support this action.")
def tab_final():
actions.app.notify("Foxit does not support this action.")
def tab_duplicate():
actions.app.notify("Foxit does not support this action.")
# user.pages
def page_current() -> int:
actions.key("ctrl-g")
page = actions.edit.selected_text()
return int(page)
def page_next():
actions.key("right")
def page_previous():
actions.key("left")
def page_jump(number: int):
actions.key("ctrl-g")
actions.insert(str(number))
actions.key("enter")
def page_final():
# actions.key("fn-right")
actions.key("end")
def page_rotate_right():
actions.key("shift-ctrl-keypad_plus")
def page_rotate_left():
actions.key("shift-ctrl-keypad_minus")

View file

@ -0,0 +1,6 @@
app: foxit_reader
-
tag(): user.tabs
tag(): user.pages
tab close all: key(ctrl-shift-w)

View file

@ -0,0 +1,117 @@
from talon import Context, Module, actions
mod = Module()
mod.tag("gdb", "Tag to enabled gdb-related functionality")
# user.gdb-specific context
ctx_gdb_enabled = Context()
ctx_gdb_enabled.matches = r"""
tag: user.gdb
"""
# global context for enabling and disabling user.gdb tag
ctx_global = Context()
@mod.action_class
class Actions:
def gdb_enable():
"""Enables the gdb tag"""
ctx_global.tags = ["user.gdb"]
def gdb_disable():
"""Disables the gdb tag"""
ctx_global.tags = []
@ctx_gdb_enabled.action_class("user")
class UserActions:
##
# Generic debugger actions
##
# Code execution
def debugger_step_into():
actions.auto_insert("stepi\n")
def debugger_step_over():
actions.auto_insert("nexti\n")
def debugger_step_line():
actions.auto_insert("step\n")
def debugger_step_over_line():
actions.auto_insert("next\n")
def debugger_step_out():
actions.auto_insert("finish\n")
def debugger_continue():
actions.auto_insert("c\n")
def debugger_stop():
actions.key("ctrl-c")
def debugger_start():
actions.auto_insert("run\n")
def debugger_restart():
actions.auto_insert("run\n")
# XXX -
def debugger_detach():
actions.auto_insert("")
# Registers
def debugger_show_registers():
actions.auto_insert("info registers\n")
def debugger_get_register():
actions.auto_insert("r ")
def debugger_set_register():
actions.user.insert_between("set $", "=")
# Breakpoints
def debugger_show_breakpoints():
actions.auto_insert("info breakpoints\n")
def debugger_add_sw_breakpoint():
actions.auto_insert("break ")
# XXX -
def debugger_add_hw_breakpoint():
actions.auto_insert("")
def debugger_break_now():
actions.key("ctrl-c")
def debugger_break_here():
actions.auto_insert("break\n")
def debugger_clear_all_breakpoints():
actions.auto_insert("d br\n")
def debugger_clear_breakpoint():
actions.insert("d br ")
def debugger_enable_all_breakpoints():
actions.insert("enable br\n")
def debugger_enable_breakpoint():
actions.insert("enable br ")
def debugger_disable_all_breakpoints():
actions.insert("disable br\n")
def debugger_disable_breakpoint():
actions.insert("disable br ")
def debugger_clear_breakpoint_id(number_small: int):
actions.insert(f"d br {number_small}\n")
def debugger_disable_breakpoint_id(number_small: int):
actions.insert(f"disable br {number_small}\n")
def debugger_enable_breakpoint_id(number_small: int):
actions.insert(f"enable br {number_small}\n")

View file

@ -0,0 +1,102 @@
os: linux
# XXX - this matches .gdb files atm
#win.title: /gdb/
tag: terminal
and tag: user.gdb
-
tag(): user.debugger
until <number>: "until {number}"
force clear all break points:
insert("d br\n")
insert("y\n")
break [on] clipboard:
insert("break ")
key(ctrl-shift-v)
key(enter)
# information
list [source]: "list\n"
info source: "info source\n"
print: "p "
print [variable] <user.text>: "p {text}"
print hex: "p/x "
print hex [variable] <user.text>: "p/x {text}"
print string: "p/s "
# hexdumping
# XXX - switch the sizes to a list in python?
# XXX - should cache the last used size
hex dump <number> bytes: "x/{number}bx "
hex dump <number> (half | short) words: "x/{number}hx "
hex dump <number> (d | long) words: "x/{number}dx "
hex dump <number> quad words: "x/{number}gx "
# this is some arbitrary default for convenience
hex dump: "x/100gx "
hex dump highlighted:
insert("x/100gx ")
edit.copy()
edit.paste()
key(enter)
hex dump clipboard:
insert("x/100gx ")
edit.paste()
key(enter)
# execution
source: "source \t\t"
# displays
# XXX - move thee invoke command into a python script
(list | show | info) display: "info display\n"
display assembly line$: "display /i $pc\n"
display source: "display "
enable display <number_small>: "enable display {number_small}\n"
disable display <number_small>: "disable display {number_small}\n"
undisplay: "undisplay\n"
# variables
(list | show | info) local: "info local "
(list | show | info) local typed: "info local -t "
(list | show | info) variable: "info variable "
(list | show | info) variable typed: "info variable -t "
(list | show | info) locals: "info local\n"
(list | show | info) variables: "info variables\n"
# threads
info threads: "info threads\n"
restart [program]: "r\n"
continue: "c\n"
back trace: "bt\n"
debug quit: "quit\n"
# more quickly quit when there are inferiors
debug force quit: "quit\ny\n"
(show | info) (inf | inferiors): "info inferiors\n"
inferior <number_small>$: "inferior {number_small}\n"
inferior: "inferior "
resume main (inf | inferior):
insert("inferior 1\n")
insert("c\n")
resume [from] (inf | inferior) <number_small>$:
insert("inferior {number_small}\n")
insert("c\n")
# arguments
set args: "set args "
# settings
show follow (fork | forks) [mode]: "show follow-fork-mode\n"
[set] follow (fork | forks) [mode] child: "set follow-fork-mode child\n"
[set] follow (fork | forks) [mode] parent: "set follow-fork-mode parent\n"
show detach on fork: "show detach-on-fork\n"
set detach on fork: "set detach-on-fork on\n"
unset detach on fork: "set detach-on-fork off\n"
# list
show list size: "show listsize\n"
set list size <number_small>: "set listsize {number_small}\n"
# misc
clear screen: "shell clear\n"

View file

@ -0,0 +1,2 @@
[enable] debug mode: user.gdb_enable()
disable debug mode: user.gdb_disable()

View file

@ -0,0 +1,17 @@
import csv
import os
from pathlib import Path
from talon import Context, Module, actions, resource
mod = Module()
ctx = Context()
mod.list("git_command", desc="Git commands.")
mod.list("git_argument", desc="Command-line git options and arguments.")
@mod.capture(rule="{user.git_argument}+")
def git_arguments(m) -> str:
"""A non-empty sequence of git command arguments, preceded by a space."""
return " " + " ".join(m.git_argument_list)

View file

@ -0,0 +1,51 @@
tag: terminal
and tag: user.git
-
git {user.git_command} [<user.git_arguments>]:
args = git_arguments or ""
"git {git_command}{args} "
git commit [<user.git_arguments>] message [<user.prose>]:
args = git_arguments or ""
message = prose or ""
user.insert_between('git commit{args} --message "{message}', '"')
git stash [push] [<user.git_arguments>] message [<user.prose>]:
args = git_arguments or ""
message = prose or ""
user.insert_between('git stash push{args} --message "{message}', '"')
# Optimistic execution for frequently used commands that are harmless (don't
# change repository or index state).
git status$: "git status\n"
git add patch$: "git add --patch\n"
git show head$: "git show HEAD\n"
git diff$: "git diff\n"
git diff (cached | cashed)$: "git diff --cached\n"
# Convenience
git clone clipboard:
insert("git clone ")
edit.paste()
key(enter)
git diff highlighted:
edit.copy()
insert("git diff ")
edit.paste()
key(enter)
git diff clipboard:
insert("git diff ")
edit.paste()
key(enter)
git add highlighted:
edit.copy()
insert("git add ")
edit.paste()
key(enter)
git add clipboard:
insert("git add ")
edit.paste()
key(enter)
git commit highlighted:
edit.copy()
insert("git add ")
edit.paste()
insert("\ngit commit\n")

View file

@ -0,0 +1,19 @@
tag: terminal
and tag: user.git
title: /git add .*\-p/
-
yank:
key(y)
key(enter)
near:
key(n)
key(enter)
quench:
key(q)
key(enter)
drum:
key(d)
key(enter)
air:
key(a)
key(enter)

View file

@ -0,0 +1,69 @@
list: user.git_argument
-
abort: --abort
all: --all
allow empty: --allow-empty
amend: --amend
cached: --cached
cashed: --cached
color words: --color-words
colour words: --color-words
continue: --continue
copy: --copy
create: --create
delete: --delete
detach: --detach
dir diff: --dir-diff
directory diff: --dir-diff
dry run: --dry-run
edit: --edit
fast forward only: --ff-only
force: --force
force create: --force-create
force with lease: --force-with-lease
global: --global
global: --global
hard: --hard
ignore case: --ignore-case
include untracked: --include-untracked
interactive: --interactive
keep index: --keep-index
list: --list
local: --local
mixed: --mixed
move: --move
no edit: --no-edit
no keep index: --no-keep-index
no rebase: --no-rebase
no track: --no-track
no verify: --no-verify
orphan: --orphan
patch: --patch
prune: --prune
quiet: --quiet
quit: --quit
rebase: --rebase
remote: --remote
set up stream: --set-upstream
set up stream to: --set-upstream-to
short: --short
short stat: --shortstat
skip: --skip
soft: --soft
staged: --staged
stat: --stat
system: --system
track: --track
update: --update
verbose: --verbose
branch: -b
combined: -c
deep: -d
very verbose: -vv
HEAD
main
master
origin
upstream
origin main: origin/main
origin master: origin/master

View file

@ -0,0 +1,71 @@
list: user.git_command
-
add
archive
bisect
blame
branch
checkout
cherry pick: cherry-pick
clean
clone
commit
config
diff
diff tool: difftool
fetch
gc
grep
help
in it: init
log
ls files: ls-files
merge
merge tool: mergetool
move: mv
pull
push
range diff: range-diff
rebase
ref log: reflog
remote
remote add
remote remove
remote rename
remote set url: remote set-url
remote set you are el: remote set-url
remote show
rerere
rerere diff
rerere status
reset
restore
revert
remove: rm
short log: shortlog
show
sparse checkout: sparse-checkout
stash
stash apply
stash list
stash pop
stash push
stash show
stash save
status
submodule
submodule add
submodule in it: submodule init
submodule status
submodule update
switch
tag
worktree
worktree add
worktree list
worktree lock
worktree move
worktree prune
worktree remove
worktree repair
worktree unlock

View file

@ -0,0 +1,70 @@
# https://help.github.com/en/github/getting-started-with-github/keyboard-shortcuts
tag: browser
browser.host: github.com
-
# site wide shortcuts
focus search: key(s)
go to notifications: insert("gn")
go to dashboard: insert("gd")
(keyboard shortcuts show | show keyboard shortcuts): key(?)
(selection move down | move selection down): key(j)
(selection move up | move selection up): key(k)
(selection toggle | toggle selection): key(x)
(selection open | open selection): key(o)
# repositories
go to code: insert("gc")
go to issues: insert("gi")
go to pull requests: insert("gp")
go to wiki: insert("gw")
go to actions: insert("ga")
go to projects: insert("gb")
go to discussions: insert("gg")
# source code editing
[web] editor open: key(.)
# source code browsing
(file find | find file): key(t)
jump to line: key(l)
((branch | tag) switch | switch (branch | tag)): key(w)
(url expand | expand url): key(y)
(show | hide) comments: key(i)
blame view open: key(b)
(show | hide) annotations: key(a)
# issues
(issue create | create [an] issue): key(c)
search (issues | [pull] requests): key(/)
(filter by | edit) labels: key(l)
(filter by | edit) milestones: key(m)
(filter by | edit) assignee: key(a)
reply: key(r)
(comment submit | submit comment): key(ctrl-enter)
(comment preview | preview comment): key(ctrl-shift-p)
git hub full screen: key(ctrl-shift-l)
# browsing commit
(form close | close form): key(escape)
parent commit: key(p)
other parent commit: key(o)
# notifications
mark as read: key(y)
(thread mute | mute thread): key(shift-m)
# issue or pull request list
(issue open | open issue): key(o)
(issue create | create issue): key(c)
# issues and pull requests
reviewer request: key(q)
milestone set: key(m)
assignee set: key(a)
label set: key(l)
# actions
go to workflow: insert("gf")
timestamps toggle: key(shift-t)
fullscreen toggle: key(shift-f)

View file

@ -0,0 +1,60 @@
# Shortcuts taken from: https://docs.gitlab.com/ee/user/shortcuts.html
#
tag: browser
browser.host: /gitlab\.com/
#win.title: /GitLab/
-
# global shortcuts
show shortcuts: key(?)
go to projects [page]: key(shift-p)
go to groups [page]: key(shift-g)
go to activity [page]: key(shift-a)
go to milestones [page]: key(shift-l)
go to snippets [page]: key(shift-s)
search page: key(s)
go to issues [page]: key(shift-i)
go to merge requests [page]: key(shift-m)
go to to do [list] [page]: key(shift-t)
(show | hide) performance bar: key(p)
edit last comment: key(1)
toggle mark down [preview]: key(ctrl-shift-p)
# projects
go [to] project home [page]: insert("gp")
go [to] project activity [feed]: insert("gv")
go [to] project releases [list]: insert("gr")
go [to] project files [list]: insert("gf")
go [to] project file search [page]: key(t)
go [to] project (commit | commits) [list]: insert("gc")
go [to] (repository | repo) graph [page]: insert("gn")
go [to] (repository | repo) charts: insert("gd")
go [to] project issues [list]: insert("gi")
go [to] new issues [list]: insert("i")
go [to] project issues boards [list]: insert("gb")
go [to] project merge requests [list]: insert("gm")
go [to] jobs [list]: insert("gj")
go [to] project metrics: insert("gl")
go [to] project environments: insert("ge")
go [to] project cubes: insert("gk")
go [to] project snippets [list]: insert("gs")
go [to] project wiki: insert("gw")
# issues and merge requests
edit description: key(e)
change assignee: key(a)
change milestone: key(m)
change label: key(l)
right comment: key(r)
next [unresolved] discussion: key(n)
previous [unresolved] discussion: key(p)
next file: key(])
previous file: key([)
# project files
back to files: key(escape)
open permalink: key(y)
# wiki pages
edit page: key(e)

View file

@ -0,0 +1,93 @@
from talon import Context, Module, actions
# App definition
mod = Module()
mod.apps.gnome_terminal = """
os: linux
and app.exe: gnome-terminal-server
os: linux
and app.name: Gnome-terminal
os: linux
and app.name: Mate-terminal
"""
# Context matching
ctx = Context()
ctx.matches = r"""
app: gnome_terminal
"""
# --- Implement actions ---
@ctx.action_class("user")
class user_actions:
# user.tabs
def tab_jump(number):
actions.key(f"alt-{number}")
@ctx.action_class("app")
class app_actions:
# app.tabs
def tab_open():
actions.key("ctrl-shift-t")
def tab_previous():
actions.key("ctrl-pageup")
def tab_next():
actions.key("ctrl-pagedown")
def tab_close():
actions.key("ctrl-shift-w")
# global (overwrite linux/app.py)
def window_open():
actions.key("ctrl-shift-n")
def window_close():
actions.key("ctrl-shift-q")
# global (overwrite linux/edit.py)
@ctx.action_class("edit")
class EditActions:
def page_down():
actions.key("shift-pagedown")
def page_up():
actions.key("shift-pageup")
def paste():
actions.key("ctrl-shift-v")
def copy():
actions.key("ctrl-shift-c")
def find(text: str = None):
actions.key("ctrl-shift-f")
if text:
actions.insert(text)
def delete_line():
actions.edit.line_start()
actions.key("ctrl-k")
# afaik not possible in gnome-terminal
def extend_left():
pass
def extend_right():
pass
def extend_up():
pass
def extend_down():
pass
def extend_word_left():
pass
def extend_word_right():
pass

View file

@ -0,0 +1,8 @@
app: gnome_terminal
-
# Set tags
tag(): terminal
tag(): user.tabs
tag(): user.generic_unix_shell
tag(): user.git
tag(): user.kubectl

View file

@ -0,0 +1,23 @@
from talon import Context, actions
ctx = Context()
ctx.matches = r"""
os: linux
app: Guake
"""
ctx.tags = ["user.git", "user.kubectl", "user.tabs", "terminal"]
@ctx.action_class("app")
class AppActions:
def tab_open():
actions.key("ctrl-shift-t")
def tab_close():
actions.key("ctrl-shift-w")
def tab_next():
actions.key("ctrl-pagedown")
def tab_previous():
actions.key("ctrl-pageup")

View file

@ -0,0 +1,118 @@
import subprocess
from typing import Optional, Union
from talon import Context, Module, actions, settings
mod = Module()
ctx = Context()
mod.tag("i3wm", desc="tag for loading i3wm related files")
mod.setting(
"i3_config_path",
type=str,
default="~/.i3/config",
desc="Where to find the configuration path",
)
mod.setting(
"i3_mod_key",
type=str,
default="super",
desc="The default key to use for i3wm commands",
)
ctx.matches = """
tag: user.i3wm
"""
@ctx.action_class("app")
class AppActions:
def window_close():
subprocess.check_call(("i3-msg", "kill"))
@mod.action_class
class Actions:
def i3wm_mode(name: str):
"""Switch i3 mode"""
subprocess.check_call(("i3-msg", "mode", name))
def i3wm_reload():
"""Reload the i3 config"""
subprocess.check_call(("i3-msg", "reload"))
def i3wm_restart():
"""Restart the window manager"""
subprocess.check_call(("i3-msg", "restart"))
def i3wm_layout(layout: Optional[str] = None):
"""Change to specified layout. Toggle split if unspecified."""
if layout is None:
subprocess.check_call(("i3-msg", "layout", "toggle", "split"))
else:
subprocess.check_call(("i3-msg", "layout", layout))
def i3wm_fullscreen():
"""Fullscreen the current container"""
subprocess.check_call(("i3-msg", "fullscreen"))
def i3wm_split(direction: str):
"""Split the focused container"""
subprocess.check_call(("i3-msg", "split", direction))
def i3wm_float():
"""Toggle whether the focused container should float."""
subprocess.check_call(("i3-msg", "floating", "toggle"))
def i3wm_launch():
"""Trigger the i3 launcher: ex rofi"""
key = settings.get("user.i3_mod_key")
actions.key(f"{key}-d")
def i3wm_shell():
"""Launch a shell"""
key = settings.get("user.i3_mod_key")
actions.key(f"{key}-enter")
def i3wm_focus(what: str):
"""Move focus"""
subprocess.check_call(("i3-msg", "focus", what))
def i3wm_switch_to_workspace(which: Union[str, int]):
"""Focus the specified workspace"""
if isinstance(which, int):
subprocess.check_call(("i3-msg", "workspace", "number", str(which)))
else:
subprocess.check_call(("i3-msg", "workspace", which))
def i3wm_show_scratchpad():
"""Focus/cycle/hide the scratchpad"""
subprocess.check_call(("i3-msg", "scratchpad", "show"))
def i3wm_move(to: str):
"""Move the focused container"""
subprocess.check_call(("i3-msg", "move", to))
def i3wm_move_to_workspace(which: Union[str, int]):
"""Move the focused container to the specified workspace"""
if isinstance(which, int):
subprocess.check_call(
("i3-msg", "move", "container", "to", "workspace", "number", str(which))
)
else:
subprocess.check_call(
("i3-msg", "move", "container", "to", "workspace", which)
)
def i3wm_move_to_output(which: str):
"""Move the focused container to the specified output."""
subprocess.check_call(("i3-msg", "move", "container", "to", "output", which))
def i3wm_move_position(where: str):
"""Move the focused container to the specified position."""
subprocess.check_call(("i3-msg", "move", "position", where))
def i3wm_lock():
"""Trigger the lock screen"""
key = settings.get("user.i3_mod_key")
actions.key(f"{key}-shift-x")

View file

@ -0,0 +1,99 @@
# NOTE: If you want to use i3wm you must enable the tag settings.talon. i.e.: `tag(): user.i3wm`
os: linux
tag: user.i3wm
-
port <number_small>: user.i3wm_switch_to_workspace(number_small)
(port flip | flipper): user.i3wm_switch_to_workspace("back_and_forth")
port right: user.i3wm_switch_to_workspace("next")
port left: user.i3wm_switch_to_workspace("prev")
(win | window) left: user.i3wm_focus("left")
(win | window) right: user.i3wm_focus("right")
(win | window) up: user.i3wm_focus("up")
(win | window) down: user.i3wm_focus("down")
(win | window) kill: app.window_close()
(win | window) stacking: user.i3wm_layout("stacking")
(win | window) default: user.i3wm_layout()
(win | window) tabbed: user.i3wm_layout("tabbed")
reload i three config: user.i3wm_reload()
restart i three: user.i3wm_restart()
(full screen | scuba): user.i3wm_fullscreen()
toggle floating: user.i3wm_float()
focus floating: user.i3wm_focus("mode_toggle")
center window: user.i3wm_move_position("center")
resize mode: user.i3wm_mode("resize")
focus parent: user.i3wm_focus("parent")
focus child: user.i3wm_focus("child")
# resize helpers
grow window:
user.i3wm_mode("resize")
key(right:10)
key(down:10)
# escape resize mode
key(escape)
# center window
sleep(200ms)
user.i3wm_move_position("center")
# resize helpers
shrink window:
user.i3wm_mode("resize")
key(left:10)
key(up:10)
# escape resize mode
key(escape)
# center window
sleep(200ms)
user.i3wm_move_position("center")
horizontal (shell | terminal):
user.i3wm_split("h")
user.i3wm_shell()
vertical (shell | terminal):
user.i3wm_split("v")
user.i3wm_shell()
# XXX - just replace with shuffle eventually?
# XXX - like also need to match the generic talon commands
(shuffle | move (win | window) [to] port) <number_small>:
user.i3wm_move_to_workspace(number_small)
(shuffle | move (win | window) [to] last port):
user.i3wm_move_to_workspace("back_and_forth")
(shuffle | move) flipper: user.i3wm_move_to_workspace("back_and_forth")
(shuffle | move (win | window) left): user.i3wm_move("left")
(shuffle | move (win | window) right): user.i3wm_move("right")
(shuffle | move (win | window) up): user.i3wm_move("up")
(shuffle | move (win | window) down): user.i3wm_move("down")
(win | window) horizontal: user.i3wm_split("h")
(win | window) vertical: user.i3wm_split("v")
make scratch: user.i3wm_move("scratchpad")
[(show | hide)] scratch: user.i3wm_show_scratchpad()
next scratch:
user.i3wm_show_scratchpad()
user.i3wm_show_scratchpad()
# these rely on the user settings for the mod key. see i3wm.py Actions class
launch: user.i3wm_launch()
launch <user.text>:
user.i3wm_launch()
sleep(100ms)
insert("{text}")
lock screen: user.i3wm_lock()
(launch shell | koopa): user.i3wm_shell()
new scratch (shell | window):
user.i3wm_shell()
sleep(200ms)
user.i3wm_move("scratchpad")
user.i3wm_show_scratchpad()
murder:
user.deprecate_command("2023-02-04", "murder", "win kill")
app.window_close()

View file

@ -0,0 +1,81 @@
from talon import Context, Module, actions
ctx = Context()
mod = Module()
mod.apps.iterm2 = """
os: mac
and app.bundle: com.googlecode.iterm2
"""
ctx.matches = r"""
app: iterm2
"""
directories_to_remap = {}
directories_to_exclude = {}
@ctx.action_class("edit")
class EditActions:
def line_start():
actions.key("home")
def line_end():
actions.key("end")
@ctx.action_class("user")
class UserActions:
# def file_manager_current_path():
# title = ui.active_window().title
# if "~" in title:
# title = os.path.expanduser(title)
# if title in directories_to_remap:
# title = directories_to_remap[title]
# if title in directories_to_exclude:
# title = None
# return title
# def file_manager_show_properties():
# """Shows the properties for the file"""
# def file_manager_open_directory(path: str):
# """opens the directory that's already visible in the view"""
# actions.insert("cd ")
# path = '"{}"'.format(path)
# actions.insert(path)
# actions.key("enter")
# actions.user.file_manager_refresh_title()
# def file_manager_select_directory(path: str):
# """selects the directory"""
# actions.insert(path)
# def file_manager_new_folder(name: str):
# """Creates a new folder in a gui filemanager or inserts the command to do so for terminals"""
# name = '"{}"'.format(name)
# actions.insert("mkdir " + name)
# def file_manager_open_file(path: str):
# """opens the file"""
# actions.insert(path)
# actions.key("enter")
# def file_manager_select_file(path: str):
# """selects the file"""
# actions.insert(path)
def tab_jump(number: int):
actions.key(f"cmd-{number}")
def tab_final():
actions.key("cmd-9")
def terminal_clear_screen():
"""Clear screen"""
actions.key("ctrl-l")

View file

@ -0,0 +1,11 @@
os: mac
app: iterm2
-
tag(): terminal
# todo: filemanager support
#tag(): user.file_manager
tag(): user.generic_unix_shell
tag(): user.git
tag(): user.kubectl
tag(): user.tabs
tag(): user.readline

View file

@ -0,0 +1,376 @@
import os
import os.path
import tempfile
from pathlib import Path
from typing import Optional
import requests
from talon import Context, Module, actions, app, clip, ui
# Courtesy of https://github.com/anonfunc/talon-user/blob/master/apps/jetbrains.py
# Each IDE gets its own port, as otherwise you wouldn't be able
# to run two at the same time and switch between them.
# Note that MPS and IntelliJ ultimate will conflict...
port_mapping = {
"com.google.android.studio": 8652,
"com.jetbrains.AppCode": 8655,
"com.jetbrains.CLion": 8657,
"com.jetbrains.datagrip": 8664,
"com.jetbrains.goland-EAP": 8659,
"com.jetbrains.goland": 8659,
"com.jetbrains.intellij-EAP": 8653,
"com.jetbrains.intellij.ce": 8654,
"com.jetbrains.intellij": 8653,
"com.jetbrains.PhpStorm": 8662,
"com.jetbrains.pycharm": 8658,
"com.jetbrains.rider": 8660,
"com.jetbrains.rubymine": 8661,
"com.jetbrains.rubymine-EAP": 8661,
"com.jetbrains.WebStorm": 8663,
"google-android-studio": 8652,
"idea64.exe": 8653,
"IntelliJ IDEA": 8653,
"jetbrains-appcode": 8655,
"jetbrains-clion": 8657,
"jetbrains-datagrip": 8664,
"jetbrains-goland-eap": 8659,
"jetbrains-goland": 8659,
"jetbrains-idea-ce": 8654,
"jetbrains-idea-eap": 8653,
"jetbrains-idea": 8653,
"jetbrains-phpstorm": 8662,
"jetbrains-pycharm-ce": 8658,
"jetbrains-pycharm": 8658,
"jetbrains-rider": 8660,
"JetBrains Rider": 8660,
"jetbrains-rubymine": 8661,
"jetbrains-rubymine-eap": 8661,
"jetbrains-studio": 8652,
"jetbrains-webstorm": 8663,
"RubyMine": 8661,
"RubyMine-EAP": 8661,
"PyCharm": 8658,
"pycharm64.exe": 8658,
"WebStorm": 8663,
"webstorm64.exe": 8663,
}
def _get_nonce(port: int, file_prefix: str) -> Optional[str]:
file_name = file_prefix + str(port)
try:
with open(os.path.join(tempfile.gettempdir(), file_name)) as fh:
return fh.read()
except FileNotFoundError:
try:
with open(Path.home() / file_name) as fh:
return fh.read()
except FileNotFoundError:
print(f"Could not find {file_name} in tmp or home")
return None
except OSError as e:
print(e)
return None
def send_idea_command(cmd: str) -> str:
active_app = ui.active_app()
bundle = active_app.bundle or active_app.name
port = port_mapping.get(bundle, None)
if not port:
raise Exception(f"unknown application {bundle}")
nonce = _get_nonce(port, ".vcidea_") or _get_nonce(port, "vcidea_")
if not nonce:
raise FileNotFoundError(f"Couldn't find IDEA nonce file for port {port}")
response = requests.get(
f"http://localhost:{port}/{nonce}/{cmd}",
proxies={"http": None, "https": None},
timeout=(0.05, 3.05),
)
response.raise_for_status()
return response.text
def get_idea_location() -> list[str]:
return send_idea_command("location").split()
ctx = Context()
mod = Module()
mod.apps.jetbrains = "app.name: /jetbrains/"
mod.apps.jetbrains = "app.name: CLion"
mod.apps.jetbrains = "app.name: IntelliJ IDEA"
mod.apps.jetbrains = "app.name: PhpStorm"
mod.apps.jetbrains = "app.name: PyCharm"
mod.apps.jetbrains = "app.name: WebStorm"
mod.apps.jetbrains = "app.name: RubyMine"
mod.apps.jetbrains = "app.name: RubyMine-EAP"
mod.apps.jetbrains = "app.name: DataGrip"
mod.apps.jetbrains = """
os: mac
and app.bundle: com.google.android.studio
"""
# windows
mod.apps.jetbrains = r"app.exe: /^idea64\.exe$/i"
mod.apps.jetbrains = r"app.exe: /^PyCharm64\.exe$/i"
mod.apps.jetbrains = r"app.exe: /^webstorm64\.exe$/i"
mod.apps.jetbrains = """
os: mac
and app.bundle: com.jetbrains.pycharm
os: mac
and app.bundle: com.jetbrains.rider
"""
mod.apps.jetbrains = r"""
os: windows
and app.name: JetBrains Rider
os: windows
and app.exe: /^rider64\.exe$/i
"""
@mod.action_class
class Actions:
def idea(commands: str):
"""Send a command to Jetbrains product"""
command_list = commands.split(",")
try:
for cmd in command_list:
if cmd:
send_idea_command(cmd.strip())
actions.sleep(0.1)
except Exception as e:
app.notify(e)
raise
def idea_grab(times: int):
"""Copies specified number of words to the left"""
old_clip = clip.get()
try:
original_line, original_column = get_idea_location()
for _ in range(times):
send_idea_command("action EditorSelectWord")
send_idea_command("action EditorCopy")
send_idea_command(f"goto {original_line} {original_column}")
send_idea_command("action EditorPaste")
finally:
clip.set(old_clip)
ctx.matches = r"""
app: jetbrains
"""
@ctx.action_class("app")
class AppActions:
def tab_next():
actions.user.idea("action NextTab")
def tab_previous():
actions.user.idea("action PreviousTab")
def tab_close():
actions.user.idea("action CloseContent")
def tab_reopen():
actions.user.idea("action ReopenClosedTab")
@ctx.action_class("code")
class CodeActions:
# talon code actions
def toggle_comment():
actions.user.idea("action CommentByLineComment")
@ctx.action_class("edit")
class EditActions:
# talon edit actions
def copy():
actions.user.idea("action EditorCopy")
def cut():
actions.user.idea("action EditorCut")
def delete():
actions.user.idea("action EditorBackSpace")
def paste():
actions.user.idea("action EditorPaste")
def find_next():
actions.user.idea("action FindNext")
def find_previous():
actions.user.idea("action FindPrevious")
def find(text: str = None):
actions.user.idea("action Find")
if text:
actions.insert(text)
def line_clone():
actions.user.idea("action EditorDuplicate")
def line_swap_down():
actions.user.idea("action MoveLineDown")
def line_swap_up():
actions.user.idea("action MoveLineUp")
def indent_more():
actions.user.idea("action EditorIndentLineOrSelection")
def indent_less():
actions.user.idea("action EditorUnindentSelection")
def select_line(n: int = None):
actions.user.idea("action EditorSelectLine")
def select_word():
actions.user.idea("action EditorSelectWord")
def select_all():
actions.user.idea("action $SelectAll")
def file_start():
actions.user.idea("action EditorTextStart")
def file_end():
actions.user.idea("action EditorTextEnd")
def extend_file_start():
actions.user.idea("action EditorTextStartWithSelection")
def extend_file_end():
actions.user.idea("action EditorTextEndWithSelection")
def extend_word_left():
actions.user.idea("action EditorPreviousWordWithSelection")
def extend_word_right():
actions.user.idea("action EditorNextWordWithSelection")
def jump_line(n: int):
actions.user.idea(f"goto {n} 0")
# move the cursor to the first nonwhite space character of the line
actions.user.idea("action EditorLineEnd")
actions.user.idea("action EditorLineStart")
@ctx.action_class("win")
class WinActions:
def filename() -> str:
title: str = actions.win.title()
result = title.split()
# iterate over reversed result
# to support titles such as
# Class.Library2 a.js [.workspace]
for word in reversed(result):
if not word.startswith("[") and "." in word:
return word
return ""
@ctx.action_class("user")
class UserActions:
def tab_jump(number: int):
# depends on plugin GoToTabs
if number < 10:
actions.user.idea(f"action GoToTab{number}")
def extend_until_line(line: int):
actions.user.idea(f"extend {line}")
def select_range(line_start: int, line_end: int):
# if it's a single line, select the entire thing including the ending new-line5
if line_start == line_end:
actions.user.idea(f"goto {line_start} 0")
actions.user.idea("action EditorSelectLine")
else:
actions.user.idea(f"range {line_start} {line_end}")
def extend_camel_left():
actions.user.idea("action EditorPreviousWordInDifferentHumpsModeWithSelection")
def extend_camel_right():
actions.user.idea("action EditorNextWordInDifferentHumpsModeWithSelection")
def camel_left():
actions.user.idea("action EditorPreviousWordInDifferentHumpsMode")
def camel_right():
actions.user.idea("action EditorNextWordInDifferentHumpsMode")
def command_search(command: str = ""):
actions.user.idea("action GotoAction")
if command != "":
actions.insert(command)
def line_clone(line: int):
actions.user.idea(f"clone {line}")
# multi-cursor tag functions
def multi_cursor_enable():
actions.skip()
def multi_cursor_disable():
actions.key("escape")
def multi_cursor_add_above():
actions.user.idea("action EditorCloneCaretAbove")
def multi_cursor_add_below():
actions.user.idea("action EditorCloneCaretBelow")
def multi_cursor_select_fewer_occurrences():
actions.user.idea("action UnselectPreviousOccurrence")
def multi_cursor_select_more_occurrences():
actions.user.idea("action SelectNextOccurrence")
# def multi_cursor_skip_occurrence():
def multi_cursor_select_all_occurrences():
actions.user.idea("action SelectAllOccurrences")
def multi_cursor_add_to_line_ends():
actions.user.idea("action EditorAddCaretPerSelectedLine")
# splits tag functions
# def split_window_right():
# actions.user.idea("action OpenInRightSplit")
# def split_window_left():
# def split_window_down():
# def split_window_up():
def split_window_vertically():
actions.user.idea("action SplitVertically")
def split_window_horizontally():
actions.user.idea("action SplitHorizontally")
def split_flip():
actions.user.idea("action ChangeSplitOrientation")
def split_maximize():
actions.key("ctrl-shift-f12")
def split_reset():
actions.key("shift-f12")
# def split_window():
def split_clear():
actions.user.idea("action Unsplit")
def split_clear_all():
actions.user.idea("action UnsplitAll")
def split_next():
actions.user.idea("action NextSplitter")
# def split_last():
# def split_number(index: int):

View file

@ -0,0 +1,286 @@
# Requires https://plugins.jetbrains.com/plugin/10504-voice-code-idea
app: jetbrains
-
tag(): user.line_commands
tag(): user.multiple_cursors
tag(): user.splits
tag(): user.tabs
tag(): user.command_search
# multiple_cursors.py support end
# Auto complete
complete: user.idea("action CodeCompletion")
perfect: user.idea("action CodeCompletion,action CodeCompletion")
smart: user.idea("action SmartTypeCompletion")
(done | finish): user.idea("action EditorCompleteStatement")
# Copying
grab <number>: user.idea_grab(number)
action [<user.text>]: user.deprecate_command("2024-09-02", "action", "please")
# Refactoring
refactor: user.idea("action Refactorings.QuickListPopupAction")
refactor <user.text>:
user.idea("action Refactorings.QuickListPopupAction")
insert(text)
extract variable: user.idea("action IntroduceVariable")
extract field: user.idea("action IntroduceField")
extract constant: user.idea("action IntroduceConstant")
extract parameter: user.idea("action IntroduceParameter")
extract interface: user.idea("action ExtractInterface")
extract method: user.idea("action ExtractMethod")
refactor in line: user.idea("action Inline")
refactor move: user.idea("action Move")
refactor rename: user.idea("action RenameElement")
rename file: user.idea("action RenameFile")
fix (format | formatting): user.idea("action ReformatCode")
fix imports: user.idea("action OptimizeImports")
#navigation
(go declaration | follow): user.idea("action GotoDeclaration")
go implementation: user.idea("action GotoImplementation")
go usage: user.idea("action FindUsages")
go type: user.idea("action GotoTypeDeclaration")
go test: user.idea("action GotoTest")
go back: user.idea("action Back")
go forward: user.idea("action Forward")
# Search
find (everywhere | all): user.idea("action SearchEverywhere")
find (everywhere | all) <user.text> [over]:
user.idea("action SearchEverywhere")
sleep(500ms)
insert(text)
(search | find) class: user.idea("action GotoClass")
(search | find) file: user.idea("action GotoFile")
(search | find) path: user.idea("action FindInPath")
(search | find) symbol: user.idea("action GotoSymbol")
(search | find) symbol <user.text>$:
user.idea("action GotoSymbol")
insert(text)
key("enter")
recent: user.idea("action RecentFiles")
surround [this] with <user.text> [over]:
idea("action SurroundWith")
sleep(500ms)
insert(text)
# Making these longer to reduce collisions with real code dictation.
insert generated <user.text> [over]:
user.idea("action Generate")
sleep(500ms)
insert(text)
insert template <user.text> [over]:
idea("action InsertLiveTemplate")
sleep(500ms)
insert(text)
create (template | snippet): user.idea("action SaveAsTemplate")
# Recording
toggle recording: user.idea("action StartStopMacroRecording")
change (recording | recordings): user.idea("action EditMacros")
play recording: user.idea("action PlaybackLastMacro")
play recording <user.text> [over]:
idea("action PlaySavedMacrosAction")
insert(text)
sleep(500ms)
Key("enter")
# Marks
go mark: user.idea("action ShowBookmarks")
toggle mark: user.idea("action ToggleBookmark")
go next mark: user.idea("action GotoNextBookmark")
go last mark: user.idea("action GotoPreviousBookmark")
toggle mark <number>: user.idea("action ToggleBookmark{number}")
go mark <number>: user.idea("action GotoBookmark{number}")
# Folding
expand deep: user.idea("action ExpandRegionRecursively")
expand all: user.idea("action ExpandAllRegions")
collapse deep: user.idea("action CollapseRegionRecursively")
collapse all: user.idea("action CollapseAllRegions")
# miscellaneous
# XXX These might be better than the structural ones depending on language.
go next (method | function): user.idea("action MethodDown")
go last (method | function): user.idea("action MethodUp")
# Clipboard
clippings: user.idea("action PasteMultiple")
copy path: user.idea("action CopyPaths")
copy reference: user.idea("action CopyReference")
copy pretty: user.idea("action CopyAsRichText")
# File Creation
create sibling: user.idea("action NewElementSamePlace")
create sibling <user.text> [over]:
user.idea("action NewElementSamePlace")
sleep(500ms)
insert(text)
create file: user.idea("action NewElement")
create file <user.text> [over]:
user.idea("action NewElement")
sleep(500ms)
insert(text)
# Task Management
go task: user.idea("action tasks.goto")
go browser task: user.idea("action tasks.open.in.browser")
switch task: user.idea("action tasks.switch")
clear task: user.idea("action tasks.close")
configure servers: user.idea("action tasks.configure.servers")
# Git / Github (not using verb-noun-adjective pattern, mirroring terminal commands.)
git pull: user.idea("action Vcs.UpdateProject")
git commit: user.idea("action CheckinProject")
git push: user.idea("action CheckinProject")
git log: user.idea("action Vcs.ShowTabbedFileHistory")
git browse: user.idea("action Github.Open.In.Browser")
git (gets | gist): user.idea("action Github.Create.Gist")
git (pull request | request): user.idea("action Github.Create.Pull.Request")
git (view | show | list) (requests | request):
user.idea("action Github.View.Pull.Request")
git (annotate | blame): user.idea("action Annotate")
git menu: user.idea("action Vcs.QuickListPopupAction")
# Tool windows:
# Toggling various tool windows
toggle project: user.idea("action ActivateProjectToolWindow")
toggle find: user.idea("action ActivateFindToolWindow")
toggle run: user.idea("action ActivateRunToolWindow")
toggle debug: user.idea("action ActivateDebugToolWindow")
toggle events: user.idea("action ActivateEventLogToolWindow")
toggle terminal: user.idea("action ActivateTerminalToolWindow")
toggle git: user.idea("action ActivateVersionControlToolWindow")
toggle structure: user.idea("action ActivateStructureToolWindow")
toggle database: user.idea("action ActivateDatabaseToolWindow")
toggle database changes: user.idea("action ActivateDatabaseChangesToolWindow")
toggle make: user.idea("action ActivatemakeToolWindow")
toggle to do: user.idea("action ActivateTODOToolWindow")
toggle docker: user.idea("action ActivateDockerToolWindow")
toggle favorites: user.idea("action ActivateFavoritesToolWindow")
toggle last: user.idea("action JumpToLastWindow")
# Pin/dock/float
toggle pinned: user.idea("action TogglePinnedMode")
toggle docked: user.idea("action ToggleDockMode")
toggle floating: user.idea("action ToggleFloatingMode")
toggle windowed: user.idea("action ToggleWindowedMode")
toggle split: user.idea("action ToggleSideMode")
# Settings, not windows
toggle tool buttons: user.idea("action ViewToolButtons")
toggle toolbar: user.idea("action ViewToolBar")
toggle status [bar]: user.idea("action ViewStatusBar")
toggle navigation [bar]: user.idea("action ViewNavigationBar")
# Active editor settings
toggle power save: user.idea("action TogglePowerSave")
toggle whitespace: user.idea("action EditorToggleShowWhitespaces")
toggle indents: user.idea("action EditorToggleShowIndentLines")
toggle line numbers: user.idea("action EditorToggleShowLineNumbers")
toggle (bread crumbs | breadcrumbs): user.idea("action EditorToggleShowBreadcrumbs")
toggle gutter icons: user.idea("action EditorToggleShowGutterIcons")
toggle wrap: user.idea("action EditorToggleUseSoftWraps")
toggle parameters: user.idea("action ToggleInlineHintsAction")
# Toggleable views
toggle fullscreen: user.idea("action ToggleFullScreen")
toggle distraction [free mode]: user.idea("action ToggleDistractionFreeMode")
toggle presentation [mode]: user.idea("action TogglePresentationMode")
# Toggle additionals
toggle comment: code.toggle_comment()
# Quick popups
change scheme: user.idea("action QuickChangeScheme")
# Always javadoc
(toggle | pop) (doc | documentation): user.idea("action QuickJavaDoc")
(pop deaf | toggle definition): user.idea("action QuickImplementations")
pop type: user.idea("action ExpressionTypeInfo")
pop parameters: user.idea("action ParameterInfo")
# Breakpoints / debugging
go breakpoints: user.idea("action ViewBreakpoints")
toggle [line] breakpoint: user.idea("action ToggleLineBreakpoint")
toggle method breakpoint: user.idea("action ToggleMethodBreakpoint")
run menu: user.idea("action ChooseRunConfiguration")
run test: user.idea("action RunClass")
run test again: user.idea("action Rerun")
debug test: user.idea("action DebugClass")
step over: user.idea("action StepOver")
step into: user.idea("action StepInto")
step smart: user.idea("action SmartStepInto")
step to line: user.idea("action RunToCursor")
continue: user.idea("action Resume")
# Grow / Shrink
(grow | shrink) window right: user.idea("action ResizeToolWindowRight")
(grow | shrink) window left: user.idea("action ResizeToolWindowLeft")
(grow | shrink) window up: user.idea("action ResizeToolWindowUp")
(grow | shrink) window down: user.idea("action ResizeToolWindowDown")
# Movement
go next (error | air): user.idea("action GotoNextError")
go last (error | air): user.idea("action GotoPreviousError")
fix next (error | air):
user.idea("action GotoNextError")
user.idea("action ShowIntentionActions")
fix last (error | air):
user.idea("action GotoPreviousError")
user.idea("action ShowIntentionActions")
# Special Selects
select less: user.idea("action EditorUnSelectWord")
select (more | this): user.idea("action EditorSelectWord")
#jet brains-specific line commands. see line_commands.talon for generic ones
expand <number> until <number>:
user.select_range(number_1, number_2)
user.idea("action ExpandRegion")
collapse <number> until <number>:
user.select_range(number_1, number_2)
user.idea("action CollapseRegion")
paste <number> until <number>:
user.select_range(number_1, number_2)
user.idea("action EditorPaste")
refactor <number> until <number>:
user.select_range(number_1, number_2)
user.idea("action Refactorings.QuickListPopupAction")
clone <number>: user.line_clone(number)
#find/replace
clear last <user.text> [over]: user.idea("find prev {text}, action EditorBackSpace")
clear next <user.text> [over]: user.idea("find next {text}, action EditorBackSpace")
comment last <user.text> [over]:
user.idea("find prev {text}, action CommentByLineComment")
comment next <user.text> [over]:
user.idea("find next {text}, action CommentByLineComment")
go last <user.text> [over]: user.idea("find prev {text}, action EditorRight")
go next <user.text> [over]: user.idea("find next {text}, action EditorRight")
go <number> <user.text> [over]:
user.idea("goto {number} 0,find next {text}, action EditorRight")
paste last <user.text> [over]:
user.idea("find prev {text}, action EditorRight, action EditorPaste")
paste next <user.text> [over]:
user.idea("find next {text}, action EditorRight, action EditorPaste")
refactor <number> <user.text> [over]:
user.idea("goto {number} 0,find next {text}, action Refactorings.QuickListPopupAction")
refactor last <user.text> [over]:
user.idea("find prev {text}, action Refactorings.QuickListPopupAction")
refactor next <user.text> [over]:
user.idea("find next {text}, action Refactorings.QuickListPopupAction")
rename <number> <user.text> [over]:
user.idea("goto {number} 0,find next {text}, action RenameElement")
rename next <user.text> [over]: user.idea("find next {text}, action RenameElement")
rename last <user.text> [over]: user.idea("find prev {text}, action RenameElement")
complete <number> <user.text> [over]:
user.idea("goto {number} 0,find next {text},action CodeCompletion")
complete next <user.text> [over]: user.idea("find next {text},action CodeCompletion")
complete last <user.text> [over]: user.idea("find prev {text},action CodeCompletion")
quick fix <number> <user.text> [over]:
user.idea("goto {number} 0,find next {text},action ShowIntentionActions")
quick fix next <user.text> [over]:
user.idea("find next {text},action ShowIntentionActions")
quick fix last <user.text> [over]:
user.idea("find prev {text},action ShowIntentionActions")
replace last <user.text> [over]: user.idea("find prev {text}, action EditorPaste")
replace next <user.text> [over]: user.idea("find next {text}, action EditorPaste")
follow <number> <user.text> [over]:
user.idea("goto {number} 0,find next {text},action GotoDeclaration")
follow next <user.text> [over]: user.idea("find next {text},action GotoDeclaration")
follow last <user.text> [over]: user.idea("find prev {text},action GotoDeclaration")
reference <number> <user.text> [over]:
user.idea("goto {number} 0,find next {text},action FindUsages")
reference next <user.text> [over]: user.idea("find next {text},action FindUsages")
reference last <user.text> [over]: user.idea("find prev {text},action FindUsages")
select last <user.text> [over]: user.idea("find prev {text}")
select next <user.text> [over]: user.idea("find next {text}")
select <number> <user.text> [over]: user.idea("goto {number} 0,find next {text}")
select camel left: user.extend_camel_left()
select camel right: user.extend_camel_right()
go camel left: user.camel_left()
go camel right: user.camel_right()
# requires plug-in: black-pycharm
blacken: user.idea("action BLACKReformatCode")

View file

@ -0,0 +1,65 @@
from talon import Context, actions
ctx = Context()
ctx.matches = r"""
app.name: konsole
"""
@ctx.action_class("user")
class user_actions:
# tabs-tag functions implementations
def tab_jump(number):
actions.key(f"alt-{number}")
# tab_final is not supported by konsole by default
# but short cut can be configured
@ctx.action_class("app")
class app_actions:
# tabs-tag functions implementations
def tab_open():
actions.key("ctrl-shift-t")
def tab_previous():
actions.key("shift-left")
def tab_next():
actions.key("shift-right")
def tab_close():
actions.key("ctrl-shift-w")
def tab_reopen():
# TODO: decide whether this notification is good style
# (if this function wouldn't be defined here a wrong default would be activated)
actions.app.notify("tab reopen is not possible in kde konsole")
def window_open():
actions.key("ctrl-shift-n")
# this overwrites the unfitting parts of linux/edit.py
@ctx.action_class("edit")
class EditActions:
def page_down():
actions.key("shift-pagedown")
def page_up():
actions.key("shift-pageup")
def paste():
actions.key("ctrl-shift-v")
def copy():
actions.key("ctrl-shift-c")
def find(text: str = None):
actions.key("ctrl-shift-f")
if str:
actions.insert(text)
# TODO: fix select line and other selection (like shift-right)
# see: https://unix.stackexchange.com/questions/485536/kde-konsole-swallows-shift-left-and-shift-right
# also fix extend_left and co

View file

@ -0,0 +1,17 @@
os: linux
and app.name: konsole
-
# makes the commands in terminal.talon available
tag(): terminal
# activates the implementation of the commands/functions in terminal.talon
tag(): user.generic_unix_shell
# makes commands for certain applications available
# you can deactivate them if you do not use the application
tag(): user.git
tag(): user.anaconda
# tag(): user.kubectl
tag(): user.tabs
# TODO: add file_manager support

View file

@ -0,0 +1,22 @@
app: keepass
-
# Database
open database: key(ctrl-o)
save database: key(ctrl-s)
close database: key(ctrl-w)
lock database: key(ctrl-l)
quit: key(ctrl-q)
# Entries
[add] new entry: key(ctrl-n)
clone entry: key(ctrl-k)
(view | edit) entry: key(ctrl-e)
delete entry: key(ctrl-d)
copy user [name]: key(ctrl-b)
copy password: key(ctrl-c)
open (earl | url | link): key(ctrl-u)
copy (earl | url | link): key(ctrl-alt-u)
find: key(ctrl-f)
find <user.text>:
key(ctrl-f)
insert("{text}")

View file

@ -0,0 +1,11 @@
from talon import Module
# --- App definition ---
mod = Module()
mod.apps.kindle = """
os: windows
and app.name: Kindle
os: windows
and app.exe: /^kindle\.exe$/i
"""
# TODO: mac context and implementation

View file

@ -0,0 +1,4 @@
app: kindle
-
# Set tags
tag(): user.pages

View file

@ -0,0 +1,24 @@
from talon import Context, actions
# Context matching
ctx = Context()
ctx.matches = """
os: windows
app: kindle
"""
# --- Implement actions ---
@ctx.action_class("user")
class UserActions:
# user.pages
def page_next():
actions.key("down")
def page_previous():
actions.key("up")
def page_jump(number: int):
actions.key("ctrl-g")
actions.insert(str(number))
actions.key("enter")

View file

@ -0,0 +1,26 @@
from talon import Context, Module
mod = Module()
mod.tag("kubectl", desc="tag for enabling kubectl commands in your terminal")
kubectl = "kubectl"
ctx = Context()
ctx.matches = r"""
tag: user.kubectl
"""
mod.list("kubectl_action", desc="actions performed by kubectl")
ctx.lists["self.kubectl_action"] = ("get", "delete", "describe", "label")
mod.list("kubectl_object", desc="objects performed by kubectl")
ctx.lists["self.kubectl_object"] = (
"nodes",
"jobs",
"pods",
"namespaces",
"services",
"events",
"deployments",
"replicasets",
"daemonsets",
)

View file

@ -0,0 +1,66 @@
tag: terminal
and tag: user.kubectl
-
cube [control]: "kubectl "
cube create: "kubectl create "
cube expose: "kubectl expose "
cube run: "kubectl run "
cube set: "kubectl set "
cube run container: "kubectl run-container "
cube explain: "kubectl explain "
cube get: "kubectl get "
cube edit: "kubectl edit "
cube delete: "kubectl delete "
cube rollout: "kubectl rollout "
cube rolling update: "kubectl rolling-update "
cube scale: "kubectl scale "
cube auto scale: "kubectl autoscale "
cube certificate: "kubectl certificate "
cube top: "kubectl top "
cube drain: "kubectl drain "
cube taint: "kubectl taint "
cube (cord | cordon): "kubectl cordon "
cube (uncord | uncordon): "kubectl uncordon "
cube cluster (info | information): "kubectl cluster-info "
cube describe: "kubectl describe "
cube logs: "kubectl logs "
cube attach: "kubectl attach "
cube exec: "kubectl exec "
cube port forward: "kubectl port-forward "
cube proxy: "kubectl proxy "
cube copy: "kubectl cp "
cube auth: "kubectl auth "
cube diff: "kubectl diff "
cube apply: "kubectl apply "
cube patch: "kubectl patch "
cube replace: "kubectl replace "
cube wait: "kubectl wait "
cube convert: "kubectl convert "
cube customize: "kubectl kustomize "
cube label: "kubectl label "
cube annotate: "kubectl annotate "
cube completion: "kubectl completion "
cube (interface | API): "kubectl api "
cube interface resources: "kubectl api-resources "
cube interface versions: "kubectl api-versions "
cube config: "kubectl config "
cube help: "kubectl help "
cube plugin: "kubectl plugin "
cube version: "kubectl version "
cube {user.kubectl_action} [{user.kubectl_object}]:
insert("kubectl {kubectl_action} ")
insert(kubectl_object or "")
cube detach:
key("ctrl-p")
key("ctrl-q")
cube shell: user.insert_between("kubectl exec -it ", " -- /bin/bash")

View file

@ -0,0 +1,44 @@
from talon import Context, Module, actions
mod = Module()
ctx = Context()
apps = mod.apps
apps.meld = """
os: windows
and app.name: Visual diff and merge tool
os: windows
and app.exe: meld.exe
"""
ctx.matches = r"""
app: meld
"""
@ctx.action_class("app")
class AppActions:
def tab_open():
actions.key("ctrl-n")
def tab_previous():
actions.key("ctrl-alt-pageup")
def tab_next():
actions.key("ctrl-alt-pagedown")
def tab_reopen():
print("Meld does not support this action.")
@ctx.action_class("user")
class UserActions:
def tab_jump(number):
if number < 10:
actions.key(f"alt-{number}")
def tab_final():
print("Meld does not support this action.")
def tab_duplicate():
print("Meld does not support this action.")

View file

@ -0,0 +1,6 @@
app: meld
-
tag(): user.tabs
change next: key(alt-down)
change (previous | last): key(alt-up)

View file

@ -0,0 +1,144 @@
import subprocess
from talon import Context, Module, actions, settings, ui
mod = Module()
mod.apps.mintty = """
os: windows
and app.name: Terminal
os: windows
and app.name: mintty.exe
"""
ctx = Context()
ctx.matches = r"""
app: mintty
"""
ctx.tags = [
"terminal",
"user.generic_unix_shell",
"user.file_manager",
"user.git",
"user.kubectl",
]
directories_to_remap = {}
directories_to_exclude = {}
mod.setting(
"cygpath",
type=str,
default="C:\\cygwin64\\bin\\cygpath.exe",
desc="Path to cygpath.exe",
)
def get_win_path(cyg_path):
path = ""
try:
si = subprocess.STARTUPINFO()
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
path = (
subprocess.check_output(
[settings.get("user.cygpath"), "-w", cyg_path], startupinfo=si
)
.strip(b"\n")
.decode()
)
except:
path = ""
return path
@ctx.action_class("edit")
class EditActions:
def paste():
actions.key("shift-insert")
def copy():
actions.key("ctrl-insert")
def delete_line():
actions.key("ctrl-u")
@ctx.action_class("user")
class UserActions:
def file_manager_open_parent():
actions.insert("cd ..")
actions.key("enter")
def file_manager_current_path():
path = ui.active_window().title
path = get_win_path(path)
if path in directories_to_remap:
path = directories_to_remap[title]
if path in directories_to_exclude:
path = ""
return path
def file_manager_show_properties():
"""Shows the properties for the file"""
def file_manager_open_directory(path: str):
"""opens the directory that's already visible in the view"""
actions.insert("cd ")
path = f'"{path}"'
actions.insert(path)
actions.key("enter")
def file_manager_select_directory(path: str):
"""selects the directory"""
actions.insert(path)
def file_manager_new_folder(name: str):
"""Creates a new folder in a gui filemanager or inserts the command to do so for terminals"""
name = f'"{name}"'
actions.insert("mkdir " + name)
def file_manager_open_file(path: str):
"""opens the file"""
actions.insert(path)
actions.key("enter")
def file_manager_select_file(path: str):
"""selects the file"""
actions.insert(path)
def file_manager_open_volume(volume: str):
"""file_manager_open_volume"""
actions.user.file_manager_open_directory(volume)
def terminal_list_directories():
actions.insert("ls")
actions.key("enter")
def terminal_list_all_directories():
actions.insert("ls -a")
actions.key("enter")
def terminal_change_directory(path: str):
actions.insert(f"cd {path}")
if path:
actions.key("enter")
def terminal_change_directory_root():
"""Root of current drive"""
actions.insert("cd /")
actions.key("enter")
def terminal_clear_screen():
"""Clear screen"""
actions.key("ctrl-l")
def terminal_run_last():
actions.key("up enter")
def terminal_kill_all():
actions.key("ctrl-c")
actions.insert("y")
actions.key("enter")

View file

@ -0,0 +1,66 @@
from talon import Context, Module, actions, clip, ui
# App definition
mod = Module()
mod.apps.nautilus = """
os: linux
and app.exe: nautilus
os: linux
and app.name: Org.gnome.Nautilus
os: linux
and app.name: Caja
"""
# Context matching
ctx = Context()
ctx.matches = r"""
app: nautilus
"""
# --- Implement actions ---
@ctx.action_class("app")
class AppActions:
# app.tabs
def tab_next():
actions.key("ctrl-pagedown")
def tab_previous():
actions.key("ctrl-pageup")
@ctx.action_class("user")
class UserActions:
# user.tabs
def tab_jump(number: int):
actions.key(f"alt-{number}")
# user.navigation
def go_back():
actions.key("alt-left")
def go_forward():
actions.key("alt-right")
# user.file_manager
def file_manager_open_parent():
actions.key("alt-up")
def file_manager_show_properties():
actions.key("ctrl-i")
def file_manager_open_directory(path: str):
actions.key("ctrl-l")
actions.insert(path)
actions.key("enter")
def file_manager_new_folder(name: str = None):
actions.key("ctrl-shift-n")
if name:
actions.insert(name)
def file_manager_terminal_here():
actions.key("ctrl-l")
with clip.capture() as path:
actions.edit.copy()
ui.launch(path="gnome-terminal", args=[f"--working-directory={path.get()}"])

View file

@ -0,0 +1,5 @@
app: nautilus
-
# Set tags
tag(): user.tabs
tag(): user.file_manager

Some files were not shown because too many files have changed in this diff Show more