diff --git a/README.md b/README.md index a5b483c..f8bd6bb 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,382 @@ -# Kate's Dotfiles +# community -These are our personal dotfiles -- if you find this link, please don't pass it around. <3 +Voice command set for [Talon](https://talonvoice.com/), community-supported. -## Notes +_(Originally called `knausj_talon`, after [its original creator :superhero:](https://github.com/knausj85))_ -Most things in here are managed by `NixOS`, nix-darwin`, or `home-manager`. The following things, -however, need to work on native Windows, and thus shouldn't be Nixified: +Can be used on its own, but shines when combined with: -- neovim's configuration -- wezterm's configruation -- xonsh's configruation +- [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 `. 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 A–Z. + +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_.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 `, `file ` and `open ` 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 ` with `pre-commit run --files `. + - 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 don’t 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`. diff --git a/flake.nix b/flake.nix index ca03c32..beb693a 100644 --- a/flake.nix +++ b/flake.nix @@ -207,7 +207,6 @@ stylix is-hm-standalone is-droid - talon ; niri = niri.outputs; @@ -218,6 +217,7 @@ attic = attic.outputs.packages.${system}; esp-dev = esp-dev.outputs.packages.${system}; lix = lix.outputs.packages.${system}; + talon = talon.outputs.packages.${system}; # Helper to convert hm modules into NixOS or nix-on-droid modules. callHm = module: (specialArgs: (import module) specialArgs); @@ -283,7 +283,6 @@ ./nixos/hosts/valere.nix ./nixos/configs/steam.nix ./nixos/configs/vmware.nix - ./nixos/configs/talon-voice.nix ./nixos/configs/power-saving.nix ]; }; diff --git a/nixos/configs/dotfiles/hm.nix b/nixos/configs/dotfiles/hm.nix index f2035c5..4205e95 100644 --- a/nixos/configs/dotfiles/hm.nix +++ b/nixos/configs/dotfiles/hm.nix @@ -39,4 +39,7 @@ xdg.configFile."task/taskrc".source = ../../../taskwarrior/taskrc; xdg.dataFile."task/hooks".source = ../../../taskwarrior/hooks; + # talon + home.file.".talon/user".source = ../../../talon; + } diff --git a/nixos/configs/gui/waybar.nix b/nixos/configs/gui/waybar.nix index 5f015ec..404c15e 100644 --- a/nixos/configs/gui/waybar.nix +++ b/nixos/configs/gui/waybar.nix @@ -245,7 +245,7 @@ in "custom/events" "privacy" "custom/yubikey" - "wireplumber" + #"wireplumber" "custom/puckdown" "battery" ]; diff --git a/nixos/configuration.linux.nix b/nixos/configuration.linux.nix index 129df79..011d838 100644 --- a/nixos/configuration.linux.nix +++ b/nixos/configuration.linux.nix @@ -4,7 +4,7 @@ # # vim: et:ts=2:sw=2: # -{ pkgs, deprekages, lib, ... }: +{ pkgs, deprekages, talon, ... }: { # Ensures the system state isn't changed breakingly (e.g. by updating @@ -90,6 +90,7 @@ services.udev.packages = [ pkgs.minipro + talon.default ]; services.fwupd.enable = true; diff --git a/talon/community/community-cursorless-0.4.0/.gitignore b/talon/community/community-cursorless-0.4.0/.gitignore new file mode 100644 index 0000000..5dd20fc --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/.gitignore @@ -0,0 +1,2 @@ +*.flac +data/ diff --git a/talon/community/community-cursorless-0.4.0/.vscode/settings.json b/talon/community/community-cursorless-0.4.0/.vscode/settings.json new file mode 100644 index 0000000..46966f4 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "python.formatting.provider": "black", + "python.analysis.stubPath": "/Applications/Talon.app/Contents/Resources/python/lib/python3.9/site-packages", + "cSpell.words": [ + "mkfifo" + ] +} \ No newline at end of file diff --git a/talon/user/community/LICENSE b/talon/community/community-cursorless-0.4.0/LICENSE similarity index 100% rename from talon/user/community/LICENSE rename to talon/community/community-cursorless-0.4.0/LICENSE diff --git a/talon/community/community-cursorless-0.4.0/README.md b/talon/community/community-cursorless-0.4.0/README.md new file mode 100644 index 0000000..d2695c9 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/README.md @@ -0,0 +1,328 @@ +# Talon documentation +For up-to-date documentation on Talon's API and features, please visit https://talon.wiki/. + +https://talon.wiki/unofficial_talon_docs/ is a great place to learn about Talon files, actions, and voice command definitions. + +# knausj_talon + +Talon configs for Mac, Windows, and Linux. Very much in progress. This is also intended to work with both Dragon Naturally Speaking and wav2letter.\ + +Notes: +- commands are subject to change. We do our best to minimize changes, but we are moving to an [object][verb] standard slowly but surely. +- @knausj85 makes extensive use of Talon's eye tracking features, so the grammar for certain programs may be much smaller than you may require. +- The repository was mostly developed with Dragon, so commands are mostly still optimized for that speech engine. + +## Linux & Mac setup + +Clone repo into `~/.talon/user` + +```insert code: +cd ~/.talon/user +git clone git@github.com:knausj85/knausj_talon.git knausj_talon +``` + +Alternatively, access the directory by right clicking the Talon icon in taskbar, clicking Scripting>Open ~/talon, and navigating to user. + +The folder structure should look like: + +```insert code: +~/.talon/user/knausj_talon +~/.talon/user/knausj_talon/code +~/.talon/user/knausj_talon/lang +``` + +## Windows setup + +Note: Talon for Windows should be placed in the Program Files directory (or another 'secure' directory): `C:\Program Files\talon` Talon has been signed and utilizes uiAccess for several goodies: this will allow Talon to work with applications that are run as admin. + +Clone repo into `%AppData%\Talon\user` + +```insert code: +cd %AppData%\Talon\user +git clone git@github.com:knausj85/knausj_talon.git knausj_talon +``` + +Alternatively, access the directory by right clicking the Talon icon in taskbar, clicking Scripting>Open ~/talon, and navigating to user. + +The folder structure should look like: + +```insert code: +%AppData%\Talon\user\knausj_talon +%AppData%\Talon\user\knausj_talon\code +%AppData%\Talon\user\knausj_talon\lang +``` + +## Getting started with Talon + +1. `help active` will display the available commands for the active application. + - Available commands can change with the application, or even window title that has focus. + - You may navigate help using the displayed numbers. e.g., `help one one` or `help eleven` to open the 11th item in the help list. + - Without opening help first, you can also search for commands e.g. `help search tab` to display all tab-related commands + - Without opening help first, you can also jump immediately into a particular help context display by recalling the name displayed in help window (based on the name of the .talon file) e.g. `help symbols` or `help visual studio` + - All help-related commands are defined in misc/help.talon and misc/help_open.talon +2. `help alphabet` will display the alphabet +3. `command history` will toggle a display of the recent commands +4. `format help` will display the available formatters with examples. +5. Many useful, basic commands are defined in https://github.com/knausj85/knausj_talon/blob/master/misc/standard.talon#L36 + - `undo that` and `redo that` are the default undo/redo commands. + - `paste that`, `copy that`, and `cut that` for pasting/copy/cutting, respectively. + +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 here +https://github.com/knausj85/knausj_talon/blob/master/code/keys.py#L6 + +`help alphabet` will open a window that displays the alphabet. `help close` to hide the window. + +Try saying e.g. `air bat cap` to insert abc. + + +### Keys +Keys are defined in keys.py from line 83 - 182. The alphabet is used for A-Z. +https://github.com/knausj85/knausj_talon/blob/84c6f637ba8304352aa15e01b030e8fa36f4f1a2/code/keys.py#L83 + +All key commands are defined in keys.talon +https://github.com/knausj85/knausj_talon/blob/master/misc/keys.talon + +On Windows, try commands such as + +`control air` to press `control-a` and select all. + +`super-shift-sun` to press `windows-shift-s` to trigger the screenshot application (Windows 10). Then try `escape` to exit the screenshot application. + + +On Mac, try commands such as + +`command air` to press `command-a` and select all. + +`control shift command 4` to press ` ctrl-shift-cmd-4` to trigger the screenshot application. Then try `escape` to exit the screenshot application. Please note the order of the modifiers doesn't matter. + + +Any combination of the modifiers, symbols, alphabet, numbers and function keys can be executed via voice to execute shorcuts. Out of the box, only the modifier keys (command, shift, alt, super) cannot be triggered by themselves. + +### Symbols +Some symbols are defined in keys.py, so you can say e.g. `control colon` to press those keys. +https://github.com/knausj85/knausj_talon/blob/master/code/keys.py#L93 + +Some other symbols are defined here: +https://github.com/knausj85/knausj_talon/blob/master/text/symbols.talon + +### Formatters +`format help` will display the available formatters with examples of the output. + +Try using formatters by saying e.g. `snake hello world`, which will insert hello_world + +Mutliple formatters can be used togther, e.g. `dubstring snake hello world`. This will insert "hello_world" + +Formatters (snake, dubstring) are defined here +https://github.com/knausj85/knausj_talon/blob/master/code/formatters.py#L146 + +All formatter-related commands are defined here +https://github.com/knausj85/knausj_talon/blob/master/misc/formatters.talon#L2 + + +### Mouse commands +See https://github.com/knausj85/knausj_talon/blob/master/misc/mouse.talon + +### Generic editor +https://github.com/knausj85/knausj_talon/blob/master/text/generic_editor.talon#L7 + +These generic commands are global. Commands such as `go word left` will work in any text box. + +### Repeating commands +For repeating commands, useful voice commands are defined here: +https://github.com/knausj85/knausj_talon/blob/ced46aee4b59e6ec5e8545bb01434e27792c830e/misc/repeater.talon#L2 + +Try saying e.g. `go up fifth` will go up five lines. +Try saying e.g. `select up third` to hit `shift-up` three times to select some lines in a text field. + +### Window management +Global window managment commands are defined here: +https://github.com/knausj85/knausj_talon/blob/master/misc/window_management.talon#L1 + +`running list` will toggle a GUI list of words you can say to switch to running applications. +`focus chrome` will focus the chrome application. +`launch music` will launch the music application. Note this is currently only implemented on Mac OS X. + +### Screenshot commands + +https://github.com/knausj85/knausj_talon/blob/master/misc/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. + +The commands for enabling languages are defined here: +https://github.com/knausj85/knausj_talon/blob/master/modes/language_modes.talon + +By default, title tracking activates coding languages in supported applications such as VSCode, Visual Studio (requires plugin), and Notepad++. + +To enable title tracking for your application: +1. The active filename (including extension) must be included in the editor's title +2. Implement the required Talon-defined actions (filename, file_ext) to correctly extract the filename and extension from the programs's title. See https://github.com/knausj85/knausj_talon/blob/master/apps/vscode/vscode.py#L18 for an example. + +Python, C#, Talon and javascript language support is currently broken up into ~four contexts in an attempt to define a common grammar where possible between languages + +• operators.talon - operator commands + +• comment.talon - commenting commands + +• programming.talon - function, loop commands, etc + +• {your-language-here}.talon - for implementation of the actioIans for the above, and any language-specific stuff + + +## File Manager commands +For the following file manager commands to work, your file manager must display the full folder path in the title bar. https://github.com/knausj85/knausj_talon/blob/baa323fcd34d8a1124658a425abe8eed59cf2ee5/apps/file_manager.talon + + +For Mac OS X's Finder, run this command in terminal to display the full path in the 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 folder 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 `, `file ` and `open ` commands based on the displayed order. + +## Terminal commands + +Many terminal programs are supported out of the box, but you may not want all the commands enabled. + +To disable various commandsets in your terminal, find the relevant talon file and enable/disable the tags for command sets as appropriate. + +``` +tag(): user.file_manager +tag(): user.git +tag(): user.kubectl +tag(): user.tabs +``` + +For instance, kubectl commands (kubernetes) aren't relevant to everyone. + + +## Jetbrains commands + +For Jetbrains commands to work you must install https://plugins.jetbrains.com/plugin/10504-voice-code-idea +into each editor. + +## Settings + +Several options are configurable via a single settings file out of the box. Any setting can be made context specific as needed (e.g., per-OS, per-app, etc). + +https://github.com/knausj85/knausj_talon/blob/master/settings.talon + + +``` +#adjust the scale of the imgui to my liking +imgui.scale = 1.3 +# enable if you'd like the picker gui to automatically appear when explorer has focus +user.file_manager_auto_show_pickers = 0 +#set the max number of command lines per page in help +user.help_max_command_lines_per_page = 50 +# set the max number of contexts display per page in help +user.help_max_contexts_per_page = 20 +# The default amount used when scrolling continuously +user.mouse_continuous_scroll_amount = 80 +#stop continuous scroll/gaze scroll with a pop +user.mouse_enable_pop_stops_scroll = 1 +#enable pop click with 'control mouse' mode +user.mouse_enable_pop_click = 1 +#When enabled, the 'Scroll Mouse' GUI will not be shown. +user.mouse_hide_mouse_gui = 0 +#hide cursor when mouse_wake is called to enable zoom mouse +user.mouse_wake_hides_cursor = 0 +#the amount to scroll up/down (equivalent to mouse wheel on Windows by default) +user.mouse_wheel_down_amount = 120 +``` + +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. + +• Uncomment `tag(): user.mouse_grid_enabled` to enable the mouse grid. + + +# Collaborators + +This repository is now officially a team effort. The following contributors have direct access: +- @dwiel +- @fidgetingbits +- @knausj85 +- @rntz + +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 + +Anyone is welcome to submit PRs and report issues. + +## Guidelines for contributions + +- Any addition to the global grammar will be scrutinized a bit more thoroughly. The more specific a new context, the less scrutiny that is typically applied. + +- New grammars should follow the [subject][verb] standard where-ever possible. + +- For Mac OS X, the bundle id should be used for defining app contexts, rather than the name. + +- For Windows, both the friendly app name and exe name should be used for defining app contexts when they are different. For some people, the MUICache breaks. + +- For new web apps, ensure the domain is used to minimize potential mismatches +https://github.com/knausj85/knausj_talon/blob/master/apps/web/window_titles.md + +- New applications should support the appropriate 'generic' grammars where possible + +``` +generic_browser.talon +find_and_replace.talon +line_commands.talon +multiple_cursors.talon +snippets.talon +splits.talon +tabs.talon +``` + +- New programming languages should support the appropriate 'generic' grammars where possible as well +``` +operators.talon +programming.talon +comment.talon +block_comment.talon +``` + +and should support the lists + +``` +user.code_functions +user.code_libraries +``` + +where appropriate. See e.g. csharp.py/csharp.talon. At least, until we come up with something better 👍 + diff --git a/talon/user/community/apps/1password/1password.talon b/talon/community/community-cursorless-0.4.0/apps/1password/1password.talon similarity index 100% rename from talon/user/community/apps/1password/1password.talon rename to talon/community/community-cursorless-0.4.0/apps/1password/1password.talon diff --git a/talon/user/community/apps/1password/1password_global.talon b/talon/community/community-cursorless-0.4.0/apps/1password/1password_global.talon similarity index 100% rename from talon/user/community/apps/1password/1password_global.talon rename to talon/community/community-cursorless-0.4.0/apps/1password/1password_global.talon diff --git a/talon/community/community-cursorless-0.4.0/apps/1password/password_manager.mac.talon b/talon/community/community-cursorless-0.4.0/apps/1password/password_manager.mac.talon new file mode 100644 index 0000000..0787d5e --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/1password/password_manager.mac.talon @@ -0,0 +1,24 @@ +os: mac + +#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 +- +action(user.password_fill): + key(cmd-\) + +action(user.password_show): + key(cmd-alt-\) + +action(user.password_new): + key(cmd-i) + +action(user.password_duplicate): + key(cmd-d) + +action(user.password_edit): + key(cmd-e) + +action(user.password_delete): + key(cmd-backspace) + diff --git a/talon/user/community/apps/1password/password_manager.py b/talon/community/community-cursorless-0.4.0/apps/1password/password_manager.py similarity index 93% rename from talon/user/community/apps/1password/password_manager.py rename to talon/community/community-cursorless-0.4.0/apps/1password/password_manager.py index 195e07a..b5b073d 100644 --- a/talon/user/community/apps/1password/password_manager.py +++ b/talon/community/community-cursorless-0.4.0/apps/1password/password_manager.py @@ -1,4 +1,4 @@ -from talon import Module +from talon import app, Context, Module mod = Module() diff --git a/talon/community/community-cursorless-0.4.0/apps/1password/password_manager.win.talon b/talon/community/community-cursorless-0.4.0/apps/1password/password_manager.win.talon new file mode 100644 index 0000000..962f76a --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/1password/password_manager.win.talon @@ -0,0 +1,24 @@ +os: windows + +#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 +- +action(user.password_fill): + key(ctrl-\\) + +action(user.password_show): + key(alt-ctrl-\\) + +action(user.password_new): + key(ctrl-n) + +action(user.password_duplicate): + key(ctrl-d) + +action(user.password_edit): + key(ctrl-e) + +action(user.password_delete): + key(ctrl-delete) + diff --git a/talon/user/community/apps/amethyst/amethyst.talon b/talon/community/community-cursorless-0.4.0/apps/amethyst/amethyst.talon similarity index 83% rename from talon/user/community/apps/amethyst/amethyst.talon rename to talon/community/community-cursorless-0.4.0/apps/amethyst/amethyst.talon index d83d1b2..ceb01a3 100644 --- a/talon/user/community/apps/amethyst/amethyst.talon +++ b/talon/community/community-cursorless-0.4.0/apps/amethyst/amethyst.talon @@ -4,10 +4,9 @@ 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") +window reevaluate: key("alt-shift-z") \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/chrome/chrome.mac.talon b/talon/community/community-cursorless-0.4.0/apps/chrome/chrome.mac.talon new file mode 100644 index 0000000..eb47afa --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/chrome/chrome.mac.talon @@ -0,0 +1,72 @@ +os: mac +app: chrome +- +tag(): browser +tag(): user.tabs +#action(browser.address): + +action(browser.bookmark): + key(cmd-d) + +action(browser.bookmark_tabs): + key(cmd-shift-d) + +action(browser.bookmarks): + key(cmd-alt-b) + +action(browser.bookmarks_bar): + key(cmd-shift-b) + +action(browser.focus_address): + key(cmd-l) + +#action(browser.focus_page): + +action(browser.focus_search): + browser.focus_address() + +action(browser.go_blank): + key(cmd-n) + +action(browser.go_back): + key(cmd-[) + +action(browser.go_forward): + key(cmd-]) + +action(browser.go_home): + key(cmd-shift-h) + +action(browser.open_private_window): + key(cmd-shift-n) + +action(browser.reload): + key(cmd-r) + +action(browser.reload_hard): + key(cmd-shift-r) + +#action(browser.reload_hardest): + +action(browser.show_clear_cache): + key(cmd-shift-delete) + +action(browser.show_downloads): + key(cmd-shift-j) + +#action(browser.show_extensions) + +action(browser.show_history): + key(cmd-y) + +action(browser.submit_form): + key(enter) + +#action(browser.title) + +action(browser.toggle_dev_tools): + key(cmd-alt-i) + +action(user.dental_click): key(escape f) + +fill password: user.fill_password() \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/chrome/chrome.py b/talon/community/community-cursorless-0.4.0/apps/chrome/chrome.py new file mode 100644 index 0000000..c003347 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/chrome/chrome.py @@ -0,0 +1,52 @@ +from talon import ctrl, ui, Module, Context, actions, clip, app +from talon.experimental.locate import locate_hover + +ctx = Context() +mod = Module() + +mod.apps.chrome = "app.name: Google Chrome" +mod.apps.chrome = """ +os: windows +and app.name: Google Chrome +os: windows +and app.exe: chrome.exe +""" +mod.apps.chrome = """ +os: mac +and app.bundle: com.google.Chrome +""" +ctx.matches = r""" +app: chrome +""" + + +@ctx.action_class("user") +class user_actions: + def tab_jump(number: int): + if number < 9: + if app.platform == "mac": + actions.key("cmd-{}".format(number)) + else: + actions.key("ctrl-{}".format(number)) + + def tab_final(): + if app.platform == "mac": + actions.key("cmd-9") + else: + actions.key("ctrl-9") + + +@mod.action_class +class Actions: + def fill_password(): + """Move mouse to last pass fill password button""" + locate_hover("templates/fill-password.png") + + +@ctx.action_class("browser") +class browser_actions: + def go(url: str): + actions.browser.focus_address() + actions.sleep("50ms") + actions.insert(url) + actions.key("enter") diff --git a/talon/community/community-cursorless-0.4.0/apps/chrome/chrome.win.talon b/talon/community/community-cursorless-0.4.0/apps/chrome/chrome.win.talon new file mode 100644 index 0000000..908ace2 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/chrome/chrome.win.talon @@ -0,0 +1,68 @@ +os: windows +app: chrome +- +tag(): browser +tag(): user.tabs +#action(browser.address): + +action(browser.bookmark): + key(ctrl-d) + +action(browser.bookmark_tabs): + key(ctrl-shift-d) + +action(browser.bookmarks): + key(ctrl-shift-o) + +action(browser.bookmarks_bar): + key(ctrl-shift-b) + +action(browser.focus_address): + key(ctrl-l) + +#action(browser.focus_page): + +action(browser.focus_search): + browser.focus_address() + +action(browser.go_blank): + key(ctrl-n) + +action(browser.go_back): + key(alt-left) + +action(browser.go_forward): + key(alt-right) + +action(browser.go_home): + key(alt-home) + +action(browser.open_private_window): + key(ctrl-shift-n) + +action(browser.reload): + key(ctrl-r) + +action(browser.reload_hard): + key(ctrl-shift-r) + +#action(browser.reload_hardest): + +action(browser.show_clear_cache): + key(ctrl-shift-delete) + +action(browser.show_downloads): + key(ctrl-j) + +#action(browser.show_extensions) + +action(browser.show_history): + key(ctrl-h) + +action(browser.submit_form): + key(enter) + +#action(browser.title) + +action(browser.toggle_dev_tools): + key(ctrl-shift-i) diff --git a/talon/user/community/apps/conemu/conemu.talon b/talon/community/community-cursorless-0.4.0/apps/conemu.talon similarity index 61% rename from talon/user/community/apps/conemu/conemu.talon rename to talon/community/community-cursorless-0.4.0/apps/conemu.talon index e6cfbc8..e227497 100644 --- a/talon/user/community/apps/conemu/conemu.talon +++ b/talon/community/community-cursorless-0.4.0/apps/conemu.talon @@ -1,6 +1,7 @@ -os: windows -app.exe: /^conemu64\.exe$/i -- - -tag(): terminal -tag(): user.git +os: windows +app.exe: ConEmu64.exe +- + +tag(): terminal +tag(): user.git + diff --git a/talon/community/community-cursorless-0.4.0/apps/discord/discord.mac.talon b/talon/community/community-cursorless-0.4.0/apps/discord/discord.mac.talon new file mode 100644 index 0000000..6da5af1 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/discord/discord.mac.talon @@ -0,0 +1,35 @@ +os: mac +app: discord +- +tag(): user.messaging + +# Navigation: Servers +action(user.messaging_workspace_previous): key(cmd-alt-up) +action(user.messaging_workspace_next): key(cmd-alt-down) + +# Navigation: Channels +action(user.messaging_open_channel_picker): key(cmd-k) +action(user.messaging_channel_previous): key(alt-up) +action(user.messaging_channel_next): key(alt-down) +action(user.messaging_unread_previous): key(alt-shift-up) +action(user.messaging_unread_next): key(alt-shift-down) +action(user.discord_mentions_last): key(cmd-alt-shift-up) +action(user.discord_mentions_next): key(cmd-alt-shift-down) +action(user.discord_oldest_unread): key(shift-pageup) + +# UI +action(user.discord_toggle_pins): key(cmd-p) +action(user.discord_toggle_inbox): key(cmd-i) +action(user.discord_toggle_members): key(cmd-u) +action(user.discord_emoji_picker): key(cmd-e) +action(user.discord_gif_picker): key(cmd-g) + +# Misc +action(user.messaging_mark_workspace_read): key(shift-esc) +action(user.messaging_mark_channel_read): key(esc) +action(user.messaging_upload_file): key(cmd-shift-u) +action(user.discord_mark_inbox_read): key(cmd-shift-e) +action(user.discord_mute): key(cmd-shift-m) +action(user.discord_deafen): key(cmd-shift-d) +action(user.discord_answer_call): key(cmd-enter) +action(user.discord_decline_call): key(esc) diff --git a/talon/user/community/apps/discord/discord.py b/talon/community/community-cursorless-0.4.0/apps/discord/discord.py similarity index 53% rename from talon/user/community/apps/discord/discord.py rename to talon/community/community-cursorless-0.4.0/apps/discord/discord.py index a5836dc..1cbdcb8 100644 --- a/talon/user/community/apps/discord/discord.py +++ b/talon/community/community-cursorless-0.4.0/apps/discord/discord.py @@ -1,28 +1,9 @@ -from talon import Context, Module, actions +from talon import Module 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: @@ -50,9 +31,6 @@ class discord_actions: def discord_gif_picker(): """Toggle gif picker""" - def discord_sticker_picker(): - """Toggle sticker picker""" - def discord_mark_inbox_read(): """Mark top inbox channel read""" @@ -67,19 +45,3 @@ class discord_actions: 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("#", "") diff --git a/talon/user/community/apps/discord/discord.talon b/talon/community/community-cursorless-0.4.0/apps/discord/discord.talon similarity index 60% rename from talon/user/community/apps/discord/discord.talon rename to talon/community/community-cursorless-0.4.0/apps/discord/discord.talon index 3dae6c1..f6e4346 100644 --- a/talon/user/community/apps/discord/discord.talon +++ b/talon/community/community-cursorless-0.4.0/apps/discord/discord.talon @@ -1,34 +1,20 @@ app: discord - -tag(): user.messaging -tag(): user.emoji - -# Navigation: QuickSwitcher -{user.discord_destination} []: - 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() diff --git a/talon/community/community-cursorless-0.4.0/apps/discord/discord.win.talon b/talon/community/community-cursorless-0.4.0/apps/discord/discord.win.talon new file mode 100644 index 0000000..f068907 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/discord/discord.win.talon @@ -0,0 +1,36 @@ +os: windows +os: linux +app: discord +- +tag(): user.messaging + +# Navigation: Servers +action(user.messaging_workspace_previous): key(ctrl-alt-up) +action(user.messaging_workspace_next): key(ctrl-alt-down) + +# Navigation: Channels +action(user.messaging_open_channel_picker): key(ctrl-k) +action(user.messaging_channel_previous): key(alt-up) +action(user.messaging_channel_next): key(alt-down) +action(user.messaging_unread_previous): key(alt-shift-up) +action(user.messaging_unread_next): key(alt-shift-down) +action(user.discord_mentions_last): key(ctrl-alt-shift-up) +action(user.discord_mentions_next): key(ctrl-alt-shift-down) +action(user.discord_oldest_unread): key(shift-paegup) + +# UI +action(user.discord_toggle_pins): key(ctrl-p) +action(user.discord_toggle_inbox): key(ctrl-i) +action(user.discord_toggle_members): key(ctrl-u) +action(user.discord_emoji_picker): key(ctrl-e) +action(user.discord_gif_picker): key(ctrl-g) + +# Misc +action(user.messaging_mark_workspace_read): key(shift-esc) +action(user.messaging_mark_channel_read): key(esc) +action(user.messaging_upload_file): key(ctrl-shift-u) +action(user.discord_mark_inbox_read): key(ctrl-shift-e) +action(user.discord_mute): key(ctrl-shift-m) +action(user.discord_deafen): key(ctrl-shift-d) +action(user.discord_answer_call): key(ctrl-enter) +action(user.discord_decline_call): key(esc) diff --git a/talon/user/community/apps/eclipse/eclipse_win.py b/talon/community/community-cursorless-0.4.0/apps/eclipse/eclipse.py similarity index 53% rename from talon/user/community/apps/eclipse/eclipse_win.py rename to talon/community/community-cursorless-0.4.0/apps/eclipse/eclipse.py index 3346fa0..238d641 100644 --- a/talon/user/community/apps/eclipse/eclipse_win.py +++ b/talon/community/community-cursorless-0.4.0/apps/eclipse/eclipse.py @@ -1,4 +1,7 @@ -from talon import Context, Module, actions +from talon import Context, actions, ui, Module, app, clip +from typing import List, Union + +is_mac = app.platform == "mac" ctx = Context() mod = Module() @@ -13,40 +16,30 @@ app: eclipse """ -@ctx.action_class("app") -class AppActions: - # talon app actions - def tab_close(): - actions.key("ctrl-w") +@ctx.action_class("win") +class win_actions: + def filename(): + title = actions.win.title() + # this doesn't seem to be necessary on VSCode for Mac + # if title == "": + # title = ui.active_window().doc - def tab_next(): - actions.key("ctrl-pagedown") + if " - " in title: + result = title.split(" - ")[1] + else: + result = title - def tab_previous(): - actions.key("ctrl-pageup") + if "." in result: + return result - # 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") + return "" @ctx.action_class("edit") -class EditActions: - def find_next(): - actions.key("enter") - - def find_previous(): - actions.key("shift-enter") +class edit_actions: + def find(text: str): + actions.key("ctrl-f") + actions.insert(text) def line_swap_up(): actions.key("alt-up") @@ -65,60 +58,65 @@ class EditActions: 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: +class user_actions: + # snippet.py support beginHelp close + # def snippet_search(text: str): + # actions.user.vscode("Insert Snippet") + # actions.insert(text) + + # def snippet_insert(text: str): + # """Inserts a snippet""" + # actions.user.vscode("Insert Snippet") + # actions.insert(text) + # actions.key("enter") + + # def snippet_create(): + # """Triggers snippet creation""" + # actions.user.vscode("Preferences: Configure User Snippets") + + # snippet.py support end + + # def tab_jump(number: int): + # if number < 10: + # if is_mac: + # actions.key("ctrl-{}".format(number)) + # else: + # actions.key("alt-{}".format(number)) + + # def tab_final(): + # if is_mac: + # actions.key("ctrl-0") + # else: + # actions.key("alt-0") + # 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) + # def split_number(index: int): + # """Navigates to a the specified split""" + # if index < 9: + # if is_mac: + # actions.key("cmd-{}".format(index)) + # else: + # actions.key("ctrl-{}".format(index)) # splits.py support end # find_and_replace.py support begin + def find(text: str): + """Triggers find in current editor""" + + actions.key("ctrl-f") + if text: + actions.insert(text) + + def find_next(): + actions.key("enter") + + def find_previous(): + actions.key("shift-enter") + def find_everywhere(text: str): """Triggers find across project""" actions.key("ctrl-h") @@ -173,3 +171,4 @@ class UserActions: actions.key("alt-f alt-o esc") # find_and_replace.py support end + diff --git a/talon/community/community-cursorless-0.4.0/apps/eclipse/eclipse.talon b/talon/community/community-cursorless-0.4.0/apps/eclipse/eclipse.talon new file mode 100644 index 0000000..49e8d21 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/eclipse/eclipse.talon @@ -0,0 +1,195 @@ +#custom eclipse commands go here +app: eclipse +- +tag(): user.find_and_replace +tag(): user.line_commands +# tag(): user.multiple_cursors +# tag(): user.snippets +tag(): user.splits +tag(): user.tabs +#talon app actions +action(app.tab_close): key(ctrl-w) +action(app.tab_next): key(ctrl-pagedown) +action(app.tab_previous): key(ctrl-pageup) +#action(app.tab_reopen): +action(app.window_close): key(alt-f4) +action(app.window_open): key(alt-w n) +#talon code actions +action(code.toggle_comment): key(ctrl-7) + +#talon edit actions +action(edit.indent_more): key(tab) +action(edit.indent_less): key(shift-tab) +action(edit.save_all): key(ctrl-shift-s) + +# splits.py support begin +# requires https://marketplace.eclipse.org/content/handysplit +action(user.split_clear_all): key(alt-shift-s f) +action(user.split_clear): key(alt-shift-s f) +# action(user.split_flip): +action(user.split_last): key(alt-shift-s t) +action(user.split_next): key(alt-shift-s t) +action(user.split_window_down): key(alt-shift-s m) +action(user.split_window_horizontally): key(alt-ctrl-s s) +action(user.split_window_right): key(alt-shift-s m) +action(user.split_window_up): key(alt-shift-s m) +action(user.split_window_vertically): key(alt-shift-s s) +action(user.split_window): key(alt-ctrl-s s) +# splits.py support end + +#multiple_cursor.py support begin +#note: vscode has no explicit mode for multiple cursors +# action(user.multi_cursor_add_above): +# action(user.multi_cursor_add_below): +# action(user.multi_cursor_add_to_line_ends): +# action(user.multi_cursor_disable): +# action(user.multi_cursor_enable): +# action(user.multi_cursor_select_all_occurrences): +# action(user.multi_cursor_select_fewer_occurrences): +# action(user.multi_cursor_select_more_occurrences): +#multiple_cursor.py support end + +please []: + key(ctrl-3) + insert(user.text or "") + +# 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 []: + 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) + +#Expand/Shrink AST Selection +# select less: user.vscode("editor.action.smartSelect.shrink") +# select (more|this): user.vscode("editor.action.smartSelect.expand") + + diff --git a/talon/community/community-cursorless-0.4.0/apps/edge/edge.mac.talon b/talon/community/community-cursorless-0.4.0/apps/edge/edge.mac.talon new file mode 100644 index 0000000..3f8046f --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/edge/edge.mac.talon @@ -0,0 +1,68 @@ +os: mac +app: microsoft_edge +- +tag(): browser +tag(): user.tabs +#action(browser.address): + +action(browser.bookmark): + key(ctrl-d) + +action(browser.bookmark_tabs): + key(ctrl-shift-d) + +action(browser.bookmarks): + key(ctrl-shift-o) + +action(browser.bookmarks_bar): + key(ctrl-shift-b) + +action(browser.focus_address): + key(ctrl-l) + +#action(browser.focus_page): + +action(browser.focus_search): + browser.focus_address() + +action(browser.go_blank): + key(ctrl-n) + +action(browser.go_back): + key(alt-left) + +action(browser.go_forward): + key(alt-right) + +action(browser.go_home): + key(alt-home) + +action(browser.open_private_window): + key(ctrl-shift-p) + +action(browser.reload): + key(ctrl-r) + +action(browser.reload_hard): + key(shift-f5) + +#action(browser.reload_hardest): + +action(browser.show_clear_cache): + key(ctrl-shift-delete) + +action(browser.show_downloads): + key(ctrl-j) + +#action(browser.show_extensions) + +action(browser.show_history): + key(ctrl-h) + +action(browser.submit_form): + key(enter) + +#action(browser.title) + +action(browser.toggle_dev_tools): + key(ctrl-shift-i) diff --git a/talon/community/community-cursorless-0.4.0/apps/edge/edge.py b/talon/community/community-cursorless-0.4.0/apps/edge/edge.py new file mode 100644 index 0000000..7aa55fe --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/edge/edge.py @@ -0,0 +1,42 @@ +from talon import ctrl, ui, Module, Context, actions, clip, app + +ctx = Context() +mod = Module() + +mod.apps.microsoft_edge = """ +os: windows +and app.name: msedge.exe +os: windows +and app.name: Microsoft Edge +os: windows +and app.exe: msedge.exe +""" + +ctx.matches = r""" +app: microsoft_edge +""" + + +@ctx.action_class("user") +class user_actions: + def tab_jump(number: int): + if number < 9: + if app.platform == "mac": + actions.key("cmd-{}".format(number)) + else: + actions.key("ctrl-{}".format(number)) + + def tab_final(): + if app.platform == "mac": + actions.key("cmd-9") + else: + actions.key("ctrl-9") + + +@ctx.action_class("browser") +class browser_actions: + def go(url: str): + actions.browser.focus_address() + actions.sleep("50ms") + actions.insert(url) + actions.key("enter") diff --git a/talon/community/community-cursorless-0.4.0/apps/edge/edge.win.talon b/talon/community/community-cursorless-0.4.0/apps/edge/edge.win.talon new file mode 100644 index 0000000..9af3439 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/edge/edge.win.talon @@ -0,0 +1,68 @@ +os: windows +app: microsoft_edge +- +tag(): browser +tag(): user.tabs +#action(browser.address): + +action(browser.bookmark): + key(ctrl-d) + +action(browser.bookmark_tabs): + key(ctrl-shift-d) + +action(browser.bookmarks): + key(ctrl-shift-o) + +action(browser.bookmarks_bar): + key(ctrl-shift-b) + +action(browser.focus_address): + key(ctrl-l) + +#action(browser.focus_page): + +action(browser.focus_search): + browser.focus_address() + +action(browser.go_blank): + key(ctrl-n) + +action(browser.go_back): + key(alt-left) + +action(browser.go_forward): + key(alt-right) + +action(browser.go_home): + key(alt-home) + +action(browser.open_private_window): + key(ctrl-shift-p) + +action(browser.reload): + key(ctrl-r) + +action(browser.reload_hard): + key(shift-f5) + +#action(browser.reload_hardest): + +action(browser.show_clear_cache): + key(ctrl-shift-delete) + +action(browser.show_downloads): + key(ctrl-j) + +#action(browser.show_extensions) + +action(browser.show_history): + key(ctrl-h) + +action(browser.submit_form): + key(enter) + +#action(browser.title) + +action(browser.toggle_dev_tools): + key(ctrl-shift-i) diff --git a/talon/community/community-cursorless-0.4.0/apps/file_manager.talon b/talon/community/community-cursorless-0.4.0/apps/file_manager.talon new file mode 100644 index 0000000..fbe77e3 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/file_manager.talon @@ -0,0 +1,47 @@ +tag: user.file_manager +- +title force: user.file_manager_refresh_title() +manager show: user.file_manager_toggle_pickers() +manager refresh: user.file_manager_update_lists() +go desk: user.file_manager_open_user_directory("Desktop") +go docks: user.file_manager_open_user_directory("Documents") +go downloads: user.file_manager_open_user_directory("Downloads") +go pictures: user.file_manager_open_user_directory("Pictures") +go profile: user.file_manager_open_user_directory("") +go talon home: user.file_manager_open_directory(path.talon_home()) +go talon user: user.file_manager_open_directory(path.talon_user()) +go user: user.file_manager_open_directory(path.user_home()) +go back: user.file_manager_go_back() +go forward: user.file_manager_go_forward() +daddy: user.file_manager_open_parent() +^follow $: + directory = user.file_manager_get_directory_by_index(number - 1) + user.file_manager_open_directory(directory) +^follow {user.file_manager_directories}$: user.file_manager_open_directory(file_manager_directories) +^open $: + file = user.file_manager_get_file_by_index(number - 1) + user.file_manager_open_file(file) +^folder $: + directory = user.file_manager_get_directory_by_index(number - 1) + user.file_manager_select_directory(directory) +^file $: + file = user.file_manager_get_file_by_index(number - 1) + user.file_manager_select_file(file) +^file {user.file_manager_files}$: user.file_manager_select_file(file_manager_files) + +#new folder +folder new : + user.file_manager_new_folder(text) + +#show properties +properties show: user.file_manager_show_properties() + +# open terminal at location +terminal here: user.file_manager_terminal_here() + +folder next: user.file_manager_next_folder_page() +folder last: user.file_manager_previous_folder_page() + +file next: user.file_manager_next_file_page() +file last: user.file_manager_previous_file_page() + diff --git a/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.linux.talon b/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.linux.talon new file mode 100644 index 0000000..9a02593 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.linux.talon @@ -0,0 +1,61 @@ +os: linux +app: Firefox +app: firefox +- +action(app.tab_next): key(ctrl-pagedown) +action(app.tab_previous): key(ctrl-pageup) + +action(browser.bookmark): + key(ctrl-d) + +action(browser.bookmark_tabs): + key(ctrl-shift-d) + +action(browser.bookmarks): + key(ctrl-shift-o) + +action(browser.bookmarks_bar): + key(ctrl-b) + +action(browser.focus_address): + key(ctrl-l) + +#action(browser.focus_page): + +action(browser.go_blank): + key(ctrl-n) + +action(browser.go_back): + key(alt-left) + +action(browser.go_forward): + key(alt-right) + +action(browser.go_home): + key(alt-home) + +action(browser.open_private_window): + key(ctrl-shift-p) + +action(browser.reload): + key(ctrl-r) + +action(browser.reload_hard): + key(ctrl-shift-r) + +#action(browser.reload_hardest): + +action(browser.show_clear_cache): + key(ctrl-shift-del) + +action(browser.show_downloads): + key(ctrl-shift-y) + +action(browser.show_extensions): + key(ctrl-shift-a) + +action(browser.show_history): + key(ctrl-h) + +action(browser.toggle_dev_tools): + key(ctrl-shift-i) diff --git a/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.mac.talon b/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.mac.talon new file mode 100644 index 0000000..d6a79e3 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.mac.talon @@ -0,0 +1,57 @@ +os: mac +app: firefox +- +action(browser.bookmark): + key(cmd-d) + +action(browser.bookmark_tabs): + key(cmd-shift-d) + +action(browser.bookmarks): + key(cmd-alt-b) + +#action(browser.bookmarks_bar): +# key(ctrl-shift-b) + +action(browser.focus_address): + key(cmd-l) + +#action(browser.focus_page): + +action(browser.go_blank): + key(cmd-n) + +action(browser.go_back): + key(cmd-left) + +action(browser.go_forward): + key(cmd-right) + +action(browser.go_home): + key(cmd-shift-h) + +action(browser.open_private_window): + key(cmd-shift-p) + +action(browser.reload): + key(cmd-r) + +action(browser.reload_hard): + key(cmd-shift-r) + +#action(browser.reload_hardest): + +action(browser.show_clear_cache): + key(cmd-shift-delete) + +action(browser.show_downloads): + key(cmd-shift-j) + +action(browser.show_extensions): + key(cmd-shift-a) + +action(browser.show_history): + key(cmd-y) + +action(browser.toggle_dev_tools): + key(cmd-alt-i) diff --git a/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.py b/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.py new file mode 100644 index 0000000..ce8861c --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.py @@ -0,0 +1,46 @@ +from talon import ctrl, ui, Module, Context, actions, clip, app + +ctx = Context() +mod = Module() +apps = mod.apps +apps.firefox = "app.name: Firefox" +apps.firefox = "app.name: firefox" +apps.firefox = """ +os: windows +and app.name: Firefox +os: windows +and app.exe: firefox.exe +""" +apps.firefox = """ +os: mac +and app.bundle: org.mozilla.firefox +""" + +ctx.matches = r""" +app: firefox +""" + + +@ctx.action_class("user") +class user_actions: + def tab_jump(number: int): + if number < 9: + if app.platform == "mac": + actions.key("cmd-{}".format(number)) + else: + actions.key("ctrl-{}".format(number)) + + def tab_final(): + if app.platform == "mac": + actions.key("cmd-9") + else: + actions.key("ctrl-9") + + +@ctx.action_class("browser") +class browser_actions: + def go(url: str): + actions.browser.focus_address() + actions.sleep("50ms") + actions.insert(url) + actions.key("enter") diff --git a/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.talon b/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.talon new file mode 100644 index 0000000..1492d6a --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.talon @@ -0,0 +1,23 @@ +app: firefox +- +tag(): browser +tag(): user.tabs + +# TODO +#action(browser.address): +#action(browser.title): + +action(browser.focus_search): + browser.focus_address() + +action(browser.submit_form): + key(enter) + +tab search: + browser.focus_address() + insert("% ") +tab search $: + browser.focus_address() + insert("% {text}") + key(down) + diff --git a/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.win.talon b/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.win.talon new file mode 100644 index 0000000..5e97056 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/firefox/firefox.win.talon @@ -0,0 +1,64 @@ +os: windows +app: firefox +- +action(app.tab_next): key(ctrl-pagedown) +action(app.tab_previous): key(ctrl-pageup) + +action(browser.bookmark): + key(ctrl-d) + +action(browser.bookmark_tabs): + key(ctrl-shift-d) + +action(browser.bookmarks): + key(ctrl-shift-b) + +action(browser.bookmarks_bar): + key(alt-v) + sleep(50ms) + key(t) + sleep(50ms) + key(b) + +action(browser.focus_address): + key(ctrl-l) + +#action(browser.focus_page): + +action(browser.go_blank): + key(ctrl-n) + +action(browser.go_back): + key(alt-left) + +action(browser.go_forward): + key(alt-right) + +action(browser.go_home): + key(alt-home) + +action(browser.open_private_window): + key(ctrl-shift-p) + +action(browser.reload): + key(ctrl-r) + +action(browser.reload_hard): + key(ctrl-shift-r) + +#action(browser.reload_hardest): + +action(browser.show_clear_cache): + key(ctrl-shift-delete) + +action(browser.show_downloads): + key(ctrl-j) + +action(browser.show_extensions): + key(ctrl-shift-a) + +action(browser.show_history): + key(ctrl-h) + +action(browser.toggle_dev_tools): + key(ctrl-shift-i) diff --git a/talon/community/community-cursorless-0.4.0/apps/gdb/gdb.py b/talon/community/community-cursorless-0.4.0/apps/gdb/gdb.py new file mode 100644 index 0000000..b3c4a07 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/gdb/gdb.py @@ -0,0 +1,19 @@ +from talon import Context, Module, actions, ui + +ctx = Context() + +ctx.matches = r""" +mode: user.gdb +""" + + +@ctx.action_class("user") +class user_actions: + 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") diff --git a/talon/community/community-cursorless-0.4.0/apps/gdb/gdb.talon b/talon/community/community-cursorless-0.4.0/apps/gdb/gdb.talon new file mode 100644 index 0000000..2008688 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/gdb/gdb.talon @@ -0,0 +1,156 @@ +os: linux +# XXX - this matches .gdb files atm +#win.title: /gdb/ +tag: terminal +mode: user.gdb +- +tag(): user.gdb +tag(): user.debugger + +## +# Generic debugger actions +## + +# Code execution +action(user.debugger_step_into): "stepi\n" +action(user.debugger_step_over): "nexti\n" +action(user.debugger_step_line): "step\n" +action(user.debugger_step_over_line): "next\n" +action(user.debugger_step_out): "finish\n" +until : "until {number}" +action(user.debugger_continue): "c\n" +action(user.debugger_stop): key("ctrl-c") +action(user.debugger_start): "run\n" +action(user.debugger_restart): "run\n" +# XXX - +action(user.debugger_detach): "" + +# Registers +action(user.debugger_show_registers): "info registers\n" +action(user.debugger_get_register): "r " +action(user.debugger_set_register): + insert("set $=") + edit.left() + +# Breakpoints +action(user.debugger_show_breakpoints): "info breakpoints\n" +action(user.debugger_add_sw_breakpoint): "break " +# XXX - +action(user.debugger_add_hw_breakpoint): "" +action(user.debugger_break_now): key("ctrl-c") +action(user.debugger_break_here): "break\n" +action(user.debugger_clear_all_breakpoints): "d br\n" +force clear all break points: + insert("d br\n") + insert("y\n") +action(user.debugger_clear_breakpoint): + insert("d br ") +action(user.debugger_enable_all_breakpoints): + insert("enable br\n") +action(user.debugger_enable_breakpoint): + insert("enable br ") +action(user.debugger_disable_all_breakpoints): + insert("disable br\n") +action(user.debugger_disable_breakpoint): + insert("disable br ") + +break [on] clipboard: + insert("break ") + key(ctrl-shift-v) + key(enter) + +# Memory inspection + +# Type inspection + +## +# gdb specific functionality +## + +# information +list [source]: "list\n" +info source: "info source\n" + +print: "p " +print [variable] : "p {text}" +print hex: "p/x " +print hex [variable] : "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 bytes: "x/{number}bx " +hex dump (half|short) words: "x/{number}hx " +hex dump (d|long) words: "x/{number}dx " +hex dump 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 : "enable display {number_small}\n" +disable display : "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 $: "inferior {number_small}\n" +inferior: "inferior " +resume main (inf|inferior): + insert("inferior 1\n") + insert("c\n") +resume [from] (inf|inferior) $: + 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 : "set listsize {number_small}\n" + +# misc +clear screen: "shell clear\n" diff --git a/talon/community/community-cursorless-0.4.0/apps/generic_browser.talon b/talon/community/community-cursorless-0.4.0/apps/generic_browser.talon new file mode 100644 index 0000000..8eac5e9 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/generic_browser.talon @@ -0,0 +1,64 @@ +tag: browser +- +(address bar | go address | go url): browser.focus_address() +go home: browser.go_home() +[go] forward: browser.go_forward() +go (back | backward): browser.go_back() + +go private: browser.open_private_window() + +bookmark show: browser.bookmarks() +bookmark bar: browser.bookmarks_bar() +bookmark it: browser.bookmark() +bookmark tabs: browser.bookmark_tabs() + +(refresh | reload) it: browser.reload() +(refresh | reload) it hard: browser.reload_hard() + +show downloads: browser.show_downloads() +show extensions: browser.show_extensions() +show history: browser.show_history() +show cache: browser.show_clear_cache() + +dev tools: browser.toggle_dev_tools() + +#todo - port to apps +# navigating current page +# help: key(?) +# scroll tiny down: key(j) +# scroll tiny up: key(k) +# scroll left: key(h) +# scroll right: key(l) +# scroll (pop | spring): key(z H) +# scroll push: key(z L) +# scroll top: key(gg) +# scroll (bottom | end): key(G) +# (scroll half down | mini page): key(d) +# scroll half up: key(u) +# [open] link: key(f) +# [open] link new: key(F) +# copy link: key(y f) +# copy (address | url): key(escape y y) +# (refresh | reload): browser.reload() +# view source: key(g s) +# insert mode: key(i) +# next frame: key(g f) +# main frame: key(g F) + # navigating to new pages +# (open | go) (url | history): key(o) +# (open | go) (url | history) new: key(O) +# (open | go) bookmark: key(b) +# (open | go) bookmark new: key(B) + # using find +# find mode: key(/) +# next match: key(n) +# previous match: key(N) + # navigating history +# history back: key(H) +# history forward: key(L) + # manipulating tabs +# last visited: key(^) +# dupe tab: key(y t) +# restore: key(X) +# search tabs: key(T) +# move to window: key(W) \ No newline at end of file diff --git a/talon/user/community/tags/debugger/debugger.talon b/talon/community/community-cursorless-0.4.0/apps/generic_debugger.talon similarity index 66% rename from talon/user/community/tags/debugger/debugger.talon rename to talon/community/community-cursorless-0.4.0/apps/generic_debugger.talon index d893448..2e721fd 100644 --- a/talon/user/community/tags/debugger/debugger.talon +++ b/talon/community/community-cursorless-0.4.0/apps/generic_debugger.talon @@ -1,4 +1,4 @@ -tag: user.debugger +tag: debugger - # Code execution @@ -29,26 +29,27 @@ set register: user.debugger_set_register() # Breakpoints break now: user.debugger_break_now() break here: user.debugger_break_here() -(list | show) (breaks | break points): user.debugger_show_breakpoints() -(set | add) (break | break point): user.debugger_add_sw_breakpoint() -(set | add) hardware (break | break point): user.debugger_add_hw_breakpoint() -clear all (breaks | break points): user.debugger_clear_all_breakpoints() -clear (break | break point): user.debugger_clear_breakpoint() -clear (break | break point) : +(list|show) (breaks|break points): user.debugger_show_breakpoints() +(set|add) (break|break point): user.debugger_add_sw_breakpoint() +(set|add) hardware (break|break point): user.debugger_add_hw_breakpoint() +clear all (breaks|break points): user.debugger_clear_all_breakpoints() +clear (break|break point): user.debugger_clear_breakpoint() +clear (break|break point) : user.debugger_clear_breakpoint_id(number_small) -disable all (breaks | break points): user.debugger_disable_all_breakpoints() -disable (break | break point): user.debugger_disable_breakpoint() -disable (break | break point) : +disable all (breaks|break points): user.debugger_disable_all_breakpoints() +disable (break|break point): user.debugger_disable_breakpoint() +disable (break|break point) : user.debugger_disable_breakpoint_id(number_small) -enable all (breaks | break points): user.debugger_enable_all_breakpoints() -enable (break | break point): user.debugger_enable_breakpoint() -enable (break | break point) : +enable all (breaks|break points): user.debugger_enable_all_breakpoints() +enable (break|break point): user.debugger_enable_breakpoint() +enable (break|break point) : user.debugger_enable_breakpoint_id(number_small) # Navigation + # Memory Inspection -(stack | back) trace: user.debugger_backtrace() +(stack|back) trace: user.debugger_backtrace() disassemble: user.debugger_disassemble() disassemble here: user.debugger_disassemble_here() disassemble clipboard: user.debugger_disassemble_clipboard() diff --git a/talon/user/community/tags/terminal/terminal.py b/talon/community/community-cursorless-0.4.0/apps/generic_terminal/generic_terminal.py similarity index 78% rename from talon/user/community/tags/terminal/terminal.py rename to talon/community/community-cursorless-0.4.0/apps/generic_terminal/generic_terminal.py index 3d5b6e7..697f00c 100644 --- a/talon/user/community/tags/terminal/terminal.py +++ b/talon/community/community-cursorless-0.4.0/apps/generic_terminal/generic_terminal.py @@ -1,6 +1,7 @@ -from talon import Module +from talon import app, Module, Context, actions, ui, imgui, settings, app, registry mod = Module() +mod.tag("generic_terminal", desc="Tag for enabling generic terminal commands") @mod.action_class @@ -23,8 +24,6 @@ class Actions: def terminal_run_last(): """Repeats the last command""" - def terminal_rerun_search(command: str): - """Searches through the previously executed commands""" - def terminal_kill_all(): """kills the running command""" + diff --git a/talon/community/community-cursorless-0.4.0/apps/generic_terminal/generic_terminal.talon b/talon/community/community-cursorless-0.4.0/apps/generic_terminal/generic_terminal.talon new file mode 100644 index 0000000..c544ddf --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/generic_terminal/generic_terminal.talon @@ -0,0 +1,11 @@ +tag: user.generic_terminal +- +lisa: + user.terminal_list_directories() +lisa all: + user.terminal_list_all_directories() +katie []: user.terminal_change_directory(text or "") +katie root: user.terminal_change_directory_root() +clear screen: user.terminal_clear_screen() +run last: user.terminal_run_last() +kill all: user.terminal_kill_all() \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/i3wm/i3wm.py b/talon/community/community-cursorless-0.4.0/apps/i3wm/i3wm.py new file mode 100644 index 0000000..5ebe444 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/i3wm/i3wm.py @@ -0,0 +1,35 @@ +from talon import Context, Module, actions, settings, ui + +mod = Module() + +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", +) + + +@mod.action_class +class Actions: + 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_lock(): + """Trigger the lock screen""" + key = settings.get("user.i3_mod_key") + actions.key(f"{key}-shift-x") diff --git a/talon/community/community-cursorless-0.4.0/apps/i3wm/i3wm.talon b/talon/community/community-cursorless-0.4.0/apps/i3wm/i3wm.talon new file mode 100644 index 0000000..fe1f619 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/i3wm/i3wm.talon @@ -0,0 +1,100 @@ +# NOTE: If you want to use i3wm you must enable the tag settings.talon. ex: `tag(): user.i3wm` +os: linux +tag: user.i3wm +- +settings(): + user.i3_config_path = "~/.i3/config" + user.i3_mod_key = "super" + + +port : user.system_command("i3-msg workspace {number_small}") +port ten: user.system_command("i3-msg workspace 10") +(port flip|flipper): user.system_command("i3-msg workspace back_and_forth") +port right: user.system_command("i3-msg workspace next") +port left: user.system_command("i3-msg workspace prev") + +(win|window) left: user.system_command("i3-msg focus left") +(win|window) right: user.system_command("i3-msg focus right") +(win|window) up: user.system_command("i3-msg focus up") +(win|window) down: user.system_command("i3-msg focus down") +((win|window) kill|murder): user.system_command("i3-msg kill") +(win|window) stacking: user.system_command("i3-msg layout stacking") +(win|window) default: user.system_command("i3-msg layout toggle split") +(win|window) tabbed: user.system_command("i3-msg layout tabbed") + +reload i three config: user.system_command("i3-msg reload") +restart i three: user.system_command("i3-msg restart") + +(full screen|scuba): user.system_command("i3-msg fullscreen") +toggle floating: user.system_command("i3-msg floating toggle") +focus floating: user.system_command("i3-msg focus mode_toggle") +center window: user.system_command("i3-msg move position center") +resize mode: user.system_command('i3-msg mode "resize"') +focus parent: user.system_command("i3-msg focus parent") +focus child: user.system_command("i3-msg focus child") + +# resize helpers +grow window: + user.system_command('i3-msg mode "resize"') + key(right:10) + key(down:10) + # escape resize mode + key(escape) + # center window + sleep(200ms) + user.system_command("i3-msg move position center") + + +# resize helpers +shrink window: + user.system_command('i3-msg mode "resize"') + key(left:10) + key(up:10) + # escape resize mode + key(escape) + # center window + sleep(200ms) + user.system_command("i3-msg move position center") + +horizontal (shell|terminal): + user.system_command("i3-msg split h") + user.i3wm_shell() + +vertical (shell|terminal): + user.system_command("i3-msg 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) : user.system_command("i3-msg move container to workspace {number_small}") +(shuffle|move (win|window) [to] port ten): user.system_command("i3-msg move container to workspace 10") +(shuffle|move (win|window) [to] last port): user.system_command("i3-msg move container to workspace back_and_forth") +(shuffle|move (win|window) left): user.system_command("i3-msg move left") +(shuffle|move (win|window) right): user.system_command("i3-msg move right") +(shuffle|move (win|window) up): user.system_command("i3-msg move up") +(shuffle|move (win|window) down): user.system_command("i3-msg move down") + +(win|window) horizontal: user.system_command("i3-msg split h") +(win|window) vertical: user.system_command("i3-msg split v") + +make scratch: user.system_command("i3-msg move scratchpad") +[(show|hide)] scratch: user.system_command("i3-msg scratchpad show") +next scratch: + user.system_command("i3-msg scratchpad show") + user.system_command("i3-msg scratchpad show") + +# these rely on the user settings for the mod key. see i3wm.py Actions class +launch: user.i3wm_launch() +launch : + user.i3wm_launch() + sleep(100ms) + insert("{text}") +lock screen: user.i3wm_launch() + +(launch shell|koopa): user.i3wm_shell() + +new scratch (shell|window): + user.i3wm_shell() + sleep(200ms) + user.system_command("i3-msg move scratchpad") + user.system_command("i3-msg scratchpad show") diff --git a/talon/community/community-cursorless-0.4.0/apps/jetbrains/jetbrains.py b/talon/community/community-cursorless-0.4.0/apps/jetbrains/jetbrains.py new file mode 100644 index 0000000..ba35839 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/jetbrains/jetbrains.py @@ -0,0 +1,206 @@ +import os +import os.path +import requests +import time +from pathlib import Path +from talon import ctrl, ui, Module, Context, actions, clip +import tempfile + +# Courtesy of https://github.com/anonfunc/talon-user/blob/master/apps/jetbrains.py + +extendCommands = [] + +# 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-rubymine": 8661, + "jetbrains-rubymine-eap": 8661, + "jetbrains-studio": 8652, + "jetbrains-webstorm": 8663, + "RubyMine": 8661, + "RubyMine-EAP": 8661, + "PyCharm": 8658, + "pycharm64.exe": 8658, + "webstorm64.exe": 8663, +} + + +def _get_nonce(port, file_prefix): + file_name = file_prefix + str(port) + try: + with open(os.path.join(tempfile.gettempdir(), file_name), "r") as fh: + return fh.read() + except FileNotFoundError as e: + try: + home = str(Path.home()) + with open(os.path.join(home, file_name), "r") as fh: + return fh.read() + except FileNotFoundError as eb: + print(f"Could not find {file_name} in tmp or home") + return None + except IOError as e: + print(e) + return None + + +def send_idea_command(cmd): + print("Sending {}".format(cmd)) + active_app = ui.active_app() + bundle = active_app.bundle or active_app.name + port = port_mapping.get(bundle, None) + nonce = _get_nonce(port, ".vcidea_") or _get_nonce(port, "vcidea_") + print(f"sending {bundle} {port} {nonce}") + if port and nonce: + response = requests.get( + "http://localhost:{}/{}/{}".format(port, nonce, cmd), timeout=(0.05, 3.05) + ) + response.raise_for_status() + return response.text + + +def get_idea_location(): + return send_idea_command("location").split() + + +def idea_commands(commands): + command_list = commands.split(",") + print("executing jetbrains", commands) + global extendCommands + extendCommands = command_list + for cmd in command_list: + if cmd: + send_idea_command(cmd.strip()) + time.sleep(0.1) + + +ctx = Context() +mod = Module() + +mod.apps.jetbrains = "app.name: /jetbrains/" +mod.apps.jetbrains = "app.name: IntelliJ IDEA" +mod.apps.jetbrains = "app.name: PyCharm" +mod.apps.jetbrains = "app.name: RubyMine" +mod.apps.jetbrains = "app.name: RubyMine-EAP" + +# windows +mod.apps.jetbrains = "app.name: idea64.exe" +mod.apps.jetbrains = "app.name: PyCharm64.exe" +mod.apps.jetbrains = "app.name: pycharm64.exe" +mod.apps.jetbrains = "app.name: webstorm64.exe" +mod.apps.jetbrains = """ +os: mac +and app.bundle: com.jetbrains.pycharm +""" + + +@mod.action_class +class Actions: + def idea(commands: str): + """Send a command to Jetbrains product""" + idea_commands(commands) + + 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("goto {} {}".format(original_line, original_column)) + send_idea_command("action EditorPaste") + finally: + clip.set(old_clip) + global extendCommands + extendCommands = [] + + +ctx.matches = r""" +app: jetbrains +""" + + +@ctx.action_class("win") +class win_actions: + def filename(): + title = actions.win.title() + result = title.split(" ") + + for word in result: + if "." in word: + return word + + return "" + +@ctx.action_class("edit") +class edit_actions: + def jump_line(n: int): + actions.user.idea("goto {} 0".format(n)) + # 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("user") +class user_actions: + def tab_jump(number: int): + # depends on plugin GoToTabs + if number < 10: + actions.user.idea("action GoToTab{}".format(number)) + + def extend_until_line(line: int): + actions.user.idea("extend {}".format(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("goto {} 0".format(line_start)) + actions.user.idea("action EditorSelectLine"), + else: + actions.user.idea("range {} {}".format(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 line_clone(line: int): + actions.user.idea("clone {}".format(line)) diff --git a/talon/user/community/apps/jetbrains/jetbrains.talon b/talon/community/community-cursorless-0.4.0/apps/jetbrains/jetbrains.talon similarity index 66% rename from talon/user/community/apps/jetbrains/jetbrains.talon rename to talon/community/community-cursorless-0.4.0/apps/jetbrains/jetbrains.talon index 72db4a4..d21a3d0 100644 --- a/talon/user/community/apps/jetbrains/jetbrains.talon +++ b/talon/community/community-cursorless-0.4.0/apps/jetbrains/jetbrains.talon @@ -3,24 +3,82 @@ app: jetbrains - tag(): user.line_commands tag(): user.multiple_cursors -tag(): user.splits +tag(): user.splits tag(): user.tabs -tag(): user.command_search + +#talon app actions (+custom tab actions) +action(user.tab_final): user.idea("action GoToLastTab") +action(app.tab_next): user.idea("action NextTab") +action(app.tab_previous): user.idea("action PreviousTab") + +action(app.tab_close): user.idea("action CloseContent") +action(app.tab_reopen): user.idea("action ReopenClosedTab") +#talon code actions +action(code.toggle_comment): user.idea("action CommentByLineComment") + +#talon edit actions +action(edit.copy): user.idea("action EditorCopy") +action(edit.cut): user.idea("action EditorCut") +action(edit.delete): user.idea("action EditorBackSpace") +action(edit.paste): user.idea("action EditorPaste") +action(edit.find_next): user.idea("action FindNext") +action(edit.find_previous): user.idea("action FindPrevious") +action(edit.find): user.idea("action Find") +action(edit.line_clone): user.idea("action EditorDuplicate") +action(edit.line_swap_down): user.idea("action MoveLineDown") +action(edit.line_swap_up): user.idea("action MoveLineUp") +action(edit.indent_more): user.idea("action EditorIndentLineOrSelection") +action(edit.indent_less): user.idea("action EditorUnindentSelection") +action(edit.select_line): user.idea("action EditorSelectLine") +action(edit.select_word): user.idea("action EditorSelectWord") +action(edit.select_all): user.idea("action $SelectAll") +action(edit.file_start): user.idea("action EditorTextStart") +action(edit.file_end): user.idea("action EditorTextEnd") +action(edit.extend_file_start): user.idea("action EditorTextStartWithSelection") +action(edit.extend_file_end): user.idea("action EditorTextEndWithSelection") + +# splits.py support begin +action(user.split_clear_all): user.idea("action UnsplitAll") +action(user.split_clear): user.idea("action Unsplit") +action(user.split_flip): user.idea("action ChangeSplitOrientation") +action(user.split_last): user.idea("action LastSplitter") +action(user.split_next): user.idea("action NextSplitter") +action(user.split_window_down): user.idea("action MoveTabDown") +action(user.split_window_horizontally): user.idea("action SplitHorizontally") +#action(user.split_window_left): user.idea("action MoveTabLeft") +action(user.split_window_right): user.idea("action MoveTabRight") +#action(user.split_window_up): user.idea("action MoveTabUp") +action(user.split_window_vertically): user.idea("action SplitVertically") +action(user.split_window): user.idea("action EditSourceInNewWindow") +# splits.py support end + +# multiple_cursors.py support begin +action(user.multi_cursor_add_above): user.idea("action EditorCloneCaretAbove") +action(user.multi_cursor_add_below): user.idea("action EditorCloneCaretBelow") +action(user.multi_cursor_disable): key(escape) +action(user.multi_cursor_enable): key(shift-alt-insert) +action(user.multi_cursor_select_all_occurrences): user.idea("action SelectAllOccurrences") +action(user.multi_cursor_select_fewer_occurrences): user.idea("action UnselectPreviousOccurrence") +action(user.multi_cursor_select_more_occurrences): user.idea("action SelectNextOccurrence") # 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") +(done | finish): user.idea("action EditorCompleteStatement") # Copying grab : user.idea_grab(number) -action []: user.deprecate_command("2024-09-02", "action", "please") +# Actions +(action | please): user.idea("action GotoAction") +(action | please) : + user.idea("action GotoAction") + insert(text) # Refactoring refactor: user.idea("action Refactorings.QuickListPopupAction") refactor : - user.idea("action Refactorings.QuickListPopupAction") - insert(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") @@ -44,9 +102,9 @@ go forward: user.idea("action Forward") # Search find (everywhere | all): user.idea("action SearchEverywhere") find (everywhere | all) [over]: - user.idea("action SearchEverywhere") - sleep(500ms) - insert(text) + 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") @@ -70,7 +128,7 @@ insert template [over]: idea("action InsertLiveTemplate") sleep(500ms) insert(text) -create (template | snippet): user.idea("action SaveAsTemplate") +create (template|snippet): user.idea("action SaveAsTemplate") # Recording toggle recording: user.idea("action StartStopMacroRecording") change (recording | recordings): user.idea("action EditMacros") @@ -109,9 +167,9 @@ create sibling [over]: insert(text) create file: user.idea("action NewElement") create file [over]: - user.idea("action NewElement") - sleep(500ms) - insert(text) + 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") @@ -126,8 +184,7 @@ 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 (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: @@ -175,7 +232,7 @@ toggle presentation [mode]: user.idea("action TogglePresentationMode") toggle comment: code.toggle_comment() # Quick popups change scheme: user.idea("action QuickChangeScheme") -# Always javadoc + # Always javadoc (toggle | pop) (doc | documentation): user.idea("action QuickJavaDoc") (pop deaf | toggle definition): user.idea("action QuickImplementations") pop type: user.idea("action ExpressionTypeInfo") @@ -201,81 +258,45 @@ continue: user.idea("action Resume") # 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") +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") +select (more|this): user.idea("action EditorSelectWord") #jet brains-specific line commands. see line_commands.talon for generic ones expand until : - user.select_range(number_1, number_2) - user.idea("action ExpandRegion") + user.select_range(number_1, number_2) + user.idea("action ExpandRegion") collapse until : - user.select_range(number_1, number_2) - user.idea("action CollapseRegion") + user.select_range(number_1, number_2) + user.idea("action CollapseRegion") paste until : - user.select_range(number_1, number_2) - user.idea("action EditorPaste") + user.select_range(number_1, number_2) + user.idea("action EditorPaste") refactor until : - user.select_range(number_1, number_2) - user.idea("action Refactorings.QuickListPopupAction") + user.select_range(number_1, number_2) + user.idea("action Refactorings.QuickListPopupAction") clone : user.line_clone(number) #find/replace clear last [over]: user.idea("find prev {text}, action EditorBackSpace") clear next [over]: user.idea("find next {text}, action EditorBackSpace") -comment last [over]: - user.idea("find prev {text}, action CommentByLineComment") -comment next [over]: - user.idea("find next {text}, action CommentByLineComment") +comment last [over]: user.idea("find prev {text}, action CommentByLineComment") +comment next [over]: user.idea("find next {text}, action CommentByLineComment") go last [over]: user.idea("find prev {text}, action EditorRight") go next [over]: user.idea("find next {text}, action EditorRight") -go [over]: - user.idea("goto {number} 0,find next {text}, action EditorRight") -paste last [over]: - user.idea("find prev {text}, action EditorRight, action EditorPaste") -paste next [over]: - user.idea("find next {text}, action EditorRight, action EditorPaste") -refactor [over]: - user.idea("goto {number} 0,find next {text}, action Refactorings.QuickListPopupAction") -refactor last [over]: - user.idea("find prev {text}, action Refactorings.QuickListPopupAction") -refactor next [over]: - user.idea("find next {text}, action Refactorings.QuickListPopupAction") -rename [over]: - user.idea("goto {number} 0,find next {text}, action RenameElement") -rename next [over]: user.idea("find next {text}, action RenameElement") -rename last [over]: user.idea("find prev {text}, action RenameElement") -complete [over]: - user.idea("goto {number} 0,find next {text},action CodeCompletion") -complete next [over]: user.idea("find next {text},action CodeCompletion") -complete last [over]: user.idea("find prev {text},action CodeCompletion") -quick fix [over]: - user.idea("goto {number} 0,find next {text},action ShowIntentionActions") -quick fix next [over]: - user.idea("find next {text},action ShowIntentionActions") -quick fix last [over]: - user.idea("find prev {text},action ShowIntentionActions") +paste last [over]: user.idea("find prev {text}, action EditorRight, action EditorPaste") +paste next [over]: user.idea("find next {text}, action EditorRight, action EditorPaste") +refactor last [over]: user.idea("find prev {text}, action Refactorings.QuickListPopupAction") +refactor next [over]: user.idea("find next {text}, action Refactorings.QuickListPopupAction") replace last [over]: user.idea("find prev {text}, action EditorPaste") replace next [over]: user.idea("find next {text}, action EditorPaste") - -follow [over]: - user.idea("goto {number} 0,find next {text},action GotoDeclaration") -follow next [over]: user.idea("find next {text},action GotoDeclaration") -follow last [over]: user.idea("find prev {text},action GotoDeclaration") - -reference [over]: - user.idea("goto {number} 0,find next {text},action FindUsages") -reference next [over]: user.idea("find next {text},action FindUsages") -reference last [over]: user.idea("find prev {text},action FindUsages") - select last [over]: user.idea("find prev {text}") select next [over]: user.idea("find next {text}") -select [over]: user.idea("goto {number} 0,find next {text}") select camel left: user.extend_camel_left() select camel right: user.extend_camel_right() diff --git a/talon/user/community/apps/kubectl/kubectl.py b/talon/community/community-cursorless-0.4.0/apps/kubectl/kubectl.py similarity index 94% rename from talon/user/community/apps/kubectl/kubectl.py rename to talon/community/community-cursorless-0.4.0/apps/kubectl/kubectl.py index f5246f1..1cd721b 100644 --- a/talon/user/community/apps/kubectl/kubectl.py +++ b/talon/community/community-cursorless-0.4.0/apps/kubectl/kubectl.py @@ -1,4 +1,4 @@ -from talon import Context, Module +from talon import Module, Context mod = Module() mod.tag("kubectl", desc="tag for enabling kubectl commands in your terminal") diff --git a/talon/community/community-cursorless-0.4.0/apps/kubectl/kubectl.talon b/talon/community/community-cursorless-0.4.0/apps/kubectl/kubectl.talon new file mode 100644 index 0000000..a26ca07 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/kubectl/kubectl.talon @@ -0,0 +1,68 @@ +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: + insert("kubectl exec -it -- /bin/bash") + key("left:13") diff --git a/talon/community/community-cursorless-0.4.0/apps/linear.py b/talon/community/community-cursorless-0.4.0/apps/linear.py new file mode 100644 index 0000000..3483f06 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/linear.py @@ -0,0 +1,11 @@ +from talon import ctrl, ui, Module, Context, actions, clip, app + +ctx = Context() +mod = Module() +apps = mod.apps +apps.linear = """ +app.name: Linear +""" +ctx.matches = r""" +app: linear +""" diff --git a/talon/community/community-cursorless-0.4.0/apps/linear.talon b/talon/community/community-cursorless-0.4.0/apps/linear.talon new file mode 100644 index 0000000..3ae4db5 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/linear.talon @@ -0,0 +1,7 @@ +app: linear +- +show cycle active: "gv" +show cycle upcoming: "gw" +(please | issue hunt) []: + key(cmd-k) + insert(user.text or "") \ No newline at end of file diff --git a/talon/user/community/apps/dunst/dunst.talon b/talon/community/community-cursorless-0.4.0/apps/linux/dunst.talon similarity index 66% rename from talon/user/community/apps/dunst/dunst.talon rename to talon/community/community-cursorless-0.4.0/apps/linux/dunst.talon index 27df9ea..802df00 100644 --- a/talon/user/community/apps/dunst/dunst.talon +++ b/talon/community/community-cursorless-0.4.0/apps/linux/dunst.talon @@ -2,8 +2,8 @@ os: linux - show notifications: key(ctrl-`) -dismiss [notifications]: user.system_command("dunstctl close") -dismiss all [notifications]: user.system_command("dunstctl close-all") +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"') diff --git a/talon/community/community-cursorless-0.4.0/apps/linux/gnome-terminal.talon b/talon/community/community-cursorless-0.4.0/apps/linux/gnome-terminal.talon new file mode 100644 index 0000000..ac4fad7 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/linux/gnome-terminal.talon @@ -0,0 +1,5 @@ +os: linux +app.name: Gnome-terminal +- +tag(): terminal +tag(): user.git diff --git a/talon/community/community-cursorless-0.4.0/apps/linux/guake.talon b/talon/community/community-cursorless-0.4.0/apps/linux/guake.talon new file mode 100644 index 0000000..90fb217 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/linux/guake.talon @@ -0,0 +1,19 @@ +os: linux +app: Guake +- +#comment or remove tags for command sets you don't want +#quake doesn't support the file_manager stuff +#tag(): user.file_manager +tag(): user.git +tag(): user.kubectl +tag(): user.tabs +tag(): terminal + +action(app.tab_open): + key(ctrl-shift-t) +action(app.tab_close): + key(ctrl-shift-w) +action(app.tab_next): + key(ctrl-pagedown) +action(app.tab_previous): + key(ctrl-pageup) diff --git a/talon/user/community/apps/keepassx/keepassx_linux.talon b/talon/community/community-cursorless-0.4.0/apps/linux/keepassx.talon similarity index 76% rename from talon/user/community/apps/keepassx/keepassx_linux.talon rename to talon/community/community-cursorless-0.4.0/apps/linux/keepassx.talon index 0100efd..bcf8a08 100644 --- a/talon/user/community/apps/keepassx/keepassx_linux.talon +++ b/talon/community/community-cursorless-0.4.0/apps/linux/keepassx.talon @@ -10,12 +10,12 @@ quit: key(ctrl-q) # Entries [add] new entry: key(ctrl-n) clone entry: key(ctrl-k) -(view | edit) entry: key(ctrl-e) +(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) +open (earl|url|link): key(ctrl-u) +copy (earl|url|link): key(ctrl-alt-u) find: key(ctrl-f) find : key(ctrl-f) diff --git a/talon/user/community/apps/signal/signal_linux.talon b/talon/community/community-cursorless-0.4.0/apps/linux/signal.talon similarity index 70% rename from talon/user/community/apps/signal/signal_linux.talon rename to talon/community/community-cursorless-0.4.0/apps/linux/signal.talon index 0c6d1f3..e0d74bc 100644 --- a/talon/user/community/apps/signal/signal_linux.talon +++ b/talon/community/community-cursorless-0.4.0/apps/linux/signal.talon @@ -5,24 +5,24 @@ show shortcuts: key("ctrl-/") # Note that the order below matches Keyboard Shortcuts listings # Navigation -(next | nav | navigate) [by] (sec | section): key("ctrl-t") -(prev | previous) (chat | conversation): key("alt-down") -next (chat | conversation): key("alt-up") -(prev | previous) unread: key("alt-shift-down") +(next|nav|navigate) [by] (sec|section): key("ctrl-t") +(prev|previous) (chat|conversation): key("alt-down") +next (chat|conversation): key("alt-up") +(prev|previous) unread: key("alt-shift-down") next unread: key("alt-shift-up") -[open] (pref | preferences): key("ctrl-,") +[open] (pref|preferences): key("ctrl-,") open conversation menu: key("ctrl-shift-l") search: key("ctrl-f") search chat: key("ctrl-shift-f") -focus (chat | composer): key("ctrl-shift-t") +focus (chat|composer): key("ctrl-shift-t") open media: key("ctrl-shift-m") open emoji: key("ctrl-shift-j") open sticker: key("ctrl-shift-s") record [voice] message: key("ctrl-shift-v") archive chat: key("ctrl-shift-a") unarchive chat: key("ctrl-shift-u") -(first | top) message: key("ctrl-up") -(last | bottom) message: key("ctrl-down") +(first|top) message: key("ctrl-up") +(last|bottom) message: key("ctrl-down") close chat: key("ctrl-shift-c") # Messages @@ -39,3 +39,4 @@ expand chat: key("ctrl-shift-x") attach [file]: key("ctrl-u") remove [link] preview: key("ctrl-p") remove [link] attachment: key("ctrl-shift-p") + diff --git a/talon/user/community/apps/taskwarrior/taskwarrior_linux.talon b/talon/community/community-cursorless-0.4.0/apps/linux/taskwarrior.talon similarity index 95% rename from talon/user/community/apps/taskwarrior/taskwarrior_linux.talon rename to talon/community/community-cursorless-0.4.0/apps/linux/taskwarrior.talon index 0a6ca9e..3868c7d 100644 --- a/talon/user/community/apps/taskwarrior/taskwarrior_linux.talon +++ b/talon/community/community-cursorless-0.4.0/apps/linux/taskwarrior.talon @@ -20,7 +20,7 @@ task add: "task add " task add : "task add {text}\n" task undo: "task undo\n" -(tasks | task next): "task next\n" +(tasks|task next): "task next\n" # task editing task edit$: "task {number} edit" diff --git a/talon/community/community-cursorless-0.4.0/apps/linux/terminal.talon b/talon/community/community-cursorless-0.4.0/apps/linux/terminal.talon new file mode 100644 index 0000000..66980d4 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/linux/terminal.talon @@ -0,0 +1,46 @@ +os: linux +tag: terminal +- +tag(): user.file_manager +#todo: generic tab commands +#tag(): tabs +action(edit.page_down): + key(shift-pagedown) +action(edit.page_up): + key(shift-pageup) +action(edit.paste): + key(ctrl-shift-v) +action(edit.copy): + key(ctrl-shift-c) + +run last: + key(up) + key(enter) +rerun : + key(ctrl-r) + insert(text) +rerun search: + key(ctrl-r) +kill all: + key(ctrl-c) + +# XXX - these are specific to certain terminals only and should move into their +# own .talon file +action(edit.find): + key(ctrl-shift-f) +action(edit.word_left): + key(ctrl-w left) +action(edit.word_right): + key(ctrl-w right) +action(app.tab_open): + key(ctrl-shift-t) +action(app.tab_close): + key(ctrl-shift-w) +action(app.tab_next): + key(ctrl-pagedown) +action(app.tab_previous): + key(ctrl-pageup) +action(app.window_open): + key(ctrl-shift-n) +go tab : + key("alt-{number}") diff --git a/talon/user/community/apps/termite/termite.talon b/talon/community/community-cursorless-0.4.0/apps/linux/termite.talon similarity index 93% rename from talon/user/community/apps/termite/termite.talon rename to talon/community/community-cursorless-0.4.0/apps/linux/termite.talon index 134302d..143e508 100644 --- a/talon/user/community/apps/termite/termite.talon +++ b/talon/community/community-cursorless-0.4.0/apps/linux/termite.talon @@ -6,7 +6,7 @@ and not win.title: /VIM/ #tag(): user.file_manager tag(): user.kubectl tag(): user.git -tag(): user.taskwarrior +tag(): user.taskwarrior tag(): terminal # Selection mode diff --git a/talon/user/community/apps/tmux/tmux_linux.talon b/talon/community/community-cursorless-0.4.0/apps/linux/tmux.talon similarity index 87% rename from talon/user/community/apps/tmux/tmux_linux.talon rename to talon/community/community-cursorless-0.4.0/apps/linux/tmux.talon index 98cdd0b..a43706f 100644 --- a/talon/user/community/apps/tmux/tmux_linux.talon +++ b/talon/community/community-cursorless-0.4.0/apps/linux/tmux.talon @@ -4,20 +4,22 @@ tag: user.tmux mux: "tmux " #session management -mux new session: insert("tmux new ") +mux new session: + insert('tmux new ') mux sessions: key(ctrl-b) key(s) mux name session: key(ctrl-b) key($) -mux kill session: insert("tmux kill-session -t ") +mux kill session: + insert('tmux kill-session -t ') #window management mux new window: key(ctrl-b) key(c) mux window : - key(ctrl-b) + key(ctrl-b ) key('{number}') mux previous window: key(ctrl-b) diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/app.talon b/talon/community/community-cursorless-0.4.0/apps/mac/app.talon new file mode 100644 index 0000000..fafc33e --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/app.talon @@ -0,0 +1,40 @@ +os: mac +- +action(app.preferences): + key(cmd-,) + +action(app.tab_close): + key(cmd-w) + +#action(app.tab_detach): +# Move the current tab to a new window + +action(app.tab_next): + key(cmd-alt-right) + +action(app.tab_open): + key(cmd-t) + +action(app.tab_previous): + key(cmd-alt-left) + +action(app.tab_reopen): + key(cmd-shift-t) + +action(app.window_close): + key(cmd-w) + +action(app.window_hide): + key(cmd-m) + +action(app.window_hide_others): + key(cmd-alt-h) + +action(app.window_next): + key(cmd-`) + +action(app.window_open): + key(cmd-n) + +action(app.window_previous): + key(cmd-shift-`) diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/datagrip.talon b/talon/community/community-cursorless-0.4.0/apps/mac/datagrip.talon new file mode 100644 index 0000000..c1883b1 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/datagrip.talon @@ -0,0 +1,17 @@ +app: datagrip +- +run: key(cmd-enter) +run it: + key(cmd-enter) + sleep(50ms) + key(enter) +back: + key(alt-left) +fwack: + key(alt-right) +erase: + key(alt-backspace) +move up: + key(cmd-shift-up) +move down: + key(cmd-shift-down) diff --git a/talon/user/community/plugin/desktops/desktops_mac.py b/talon/community/community-cursorless-0.4.0/apps/mac/desktops.py similarity index 50% rename from talon/user/community/plugin/desktops/desktops_mac.py rename to talon/community/community-cursorless-0.4.0/apps/mac/desktops.py index 0d5ff76..1528d54 100644 --- a/talon/user/community/plugin/desktops/desktops_mac.py +++ b/talon/community/community-cursorless-0.4.0/apps/mac/desktops.py @@ -1,7 +1,26 @@ import contextlib import time -from talon import Context, actions, ctrl, ui +from talon import actions, ctrl, Module, ui, Context + + +mod = Module() + + +@mod.action_class +class ModuleActions: + def desktop(number: int): + "change the current desktop" + + def window_move_desktop_left(): + """move the current window to the desktop to the left""" + + def window_move_desktop_right(): + """move the current window to the desktop to the right""" + + def window_move_desktop(desktop_number: int): + """move the current window to a different desktop""" + ctx = Context() ctx.matches = r""" @@ -14,45 +33,33 @@ def _drag_window_mac(win=None): if win is None: win = ui.active_window() fs = win.children.find(AXSubrole="AXFullScreenButton")[0] - rect = fs.AXFrame - x = rect.x + rect.width + 5 - y = rect.y + rect.height / 2 - previous_position = ctrl.mouse_pos() + rect = fs.AXFrame["$rect2d"] + x = rect["x"] + rect["width"] + 5 + y = rect["y"] + rect["height"] / 2 ctrl.mouse_move(x, y) ctrl.mouse_click(button=0, down=True) yield time.sleep(0.1) ctrl.mouse_click(button=0, up=True) - ctrl.mouse_move(*previous_position) -@ctx.action_class("user") +@ctx.action_class("self") class MacActions: def desktop(number: int): if number < 10: - actions.key(f"ctrl-{number}") - - def desktop_next(): - actions.key("ctrl-right") - - def desktop_last(): - actions.key("ctrl-left") - - def desktop_show(): - actions.key("ctrl-up") + actions.key("ctrl-{}".format(number)) def window_move_desktop_left(): with _drag_window_mac(): - actions.user.desktop_last() + actions.key("ctrl-cmd-alt-left") def window_move_desktop_right(): with _drag_window_mac(): - actions.user.desktop_next() + actions.key("ctrl-cmd-alt-right") def window_move_desktop(desktop_number: int): - # TODO: amethyst stuff should be pulled out into a separate file if ui.apps(bundle="com.amethyst.Amethyst"): actions.key(f"ctrl-alt-shift-{desktop_number}") else: with _drag_window_mac(): - actions.user.desktop(desktop_number) + actions.key(f"ctrl-{desktop_number}") diff --git a/talon/user/community/plugin/desktops/desktops.talon b/talon/community/community-cursorless-0.4.0/apps/mac/desktops.talon similarity index 54% rename from talon/user/community/plugin/desktops/desktops.talon rename to talon/community/community-cursorless-0.4.0/apps/mac/desktops.talon index d6bd7c4..340393c 100644 --- a/talon/user/community/plugin/desktops/desktops.talon +++ b/talon/community/community-cursorless-0.4.0/apps/mac/desktops.talon @@ -1,7 +1,8 @@ -desk : user.desktop(number_small) -desk next: user.desktop_next() -desk last: user.desktop_last() -desk show: user.desktop_show() +# TODO: Once implementations exist for other platforms, maybe remove this +# restriction. +os: mac +- +desk : user.desktop(number) window move desk : user.window_move_desktop(number) window move desk left: user.window_move_desktop_left() window move desk right: user.window_move_desktop_right() diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/edit.talon b/talon/community/community-cursorless-0.4.0/apps/mac/edit.talon new file mode 100644 index 0000000..c584356 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/edit.talon @@ -0,0 +1,208 @@ +os: mac +- +action(edit.copy): + key(cmd-c) + +action(edit.cut): + key(cmd-x) + +action(edit.delete): + key(backspace) + +action(edit.delete_line): + edit.select_line() + edit.delete() + +#action(edit.delete_paragraph): + +#action(edit.delete_sentence): + +action(edit.delete_word): + edit.select_word() + edit.delete() + +action(edit.down): + key(down) + +#action(edit.extend_again): + +#action(edit.extend_column): + +action(edit.extend_down): + key(shift-down) + +action(edit.extend_file_end): + key(cmd-shift-down) + +action(edit.extend_file_start): + key(cmd-shift-up) + +action(edit.extend_left): + key(shift-left) + +#action(edit.extend_line): + +action(edit.extend_line_down): + key(shift-down) + +action(edit.extend_line_end): + key(cmd-shift-right) + +action(edit.extend_line_start): + key(cmd-shift-left) + +action(edit.extend_line_up): + key(shift-up) + +action(edit.extend_page_down): + key(cmd-shift-pagedown) + +action(edit.extend_page_up): + key(cmd-shift-pageup) + +#action(edit.extend_paragraph_end): +#action(edit.extend_paragraph_next()): +#action(edit.extend_paragraph_previous()): +#action(edit.extend_paragraph_start()): + +action(edit.extend_right): + key(shift-right) + +#action(edit.extend_sentence_end): +#action(edit.extend_sentence_next): +#action(edit.extend_sentence_previous): +#action(edit.extend_sentence_start): + +action(edit.extend_up): + key(shift-up) + +action(edit.extend_word_left): + key(shift-alt-left) + +action(edit.extend_word_right): + key(shift-alt-right) + +action(edit.file_end): + key(cmd-down cmd-left) + +action(edit.file_start): + key(cmd-up cmd-left) + +action(edit.find): + key(cmd-f) + #actions.insert(text) + +action(edit.find_next): + key(cmd-g) + +action(edit.find_previous): + key(cmd-shift-g) + +action(edit.indent_less): + key(cmd-left delete) + +action(edit.indent_more): + key(cmd-left tab) + +#action(edit.jump_column(n: int) +#action(edit.jump_line(n: int) + +action(edit.left): + key(left) + +action(edit.line_down): + key(down home) + +action(edit.line_end): + key(cmd-right) + +action(edit.line_insert_down): + key(end enter) + +action(edit.line_insert_up): + key(cmd-left enter up) + +action(edit.line_start): + key(cmd-left) + +action(edit.line_up): + key(up cmd-left) + +#action(edit.move_again): + +action(edit.page_down): + key(pagedown) + +action(edit.page_up): + key(pageup) + +#action(edit.paragraph_end): +#action(edit.paragraph_next): +#action(edit.paragraph_previous): +#action(edit.paragraph_start): + +action(edit.paste): + key(cmd-v) + +action(edit.paste_match_style): + key(cmd-alt-shift-v) + +action(edit.print): + key(cmd-p) + +action(edit.redo): + key(cmd-shift-z) + +action(edit.right): + key(right) + +action(edit.save): + key(cmd-s) + +action(edit.save_all): + key(cmd-shift-s) + +action(edit.select_all): + key(cmd-a) + +action(edit.select_line): + key(cmd-right cmd-shift-left) + +#action(edit.select_lines(a: int, b: int)): + +action(edit.select_none): + key(right) + +#action(edit.select_paragraph): +#action(edit.select_sentence): + +action(edit.select_word): + edit.word_left() + edit.extend_word_right() + +#action(edit.selected_text): -> str +#action(edit.sentence_end): +#action(edit.sentence_next): +#action(edit.sentence_previous): +#action(edit.sentence_start): + +action(edit.undo): + key(cmd-z) + +action(edit.up): + key(up) + +action(edit.word_left): + key(alt-left) + +action(edit.word_right): + key(alt-right) + +action(edit.zoom_in): + key(cmd-=) + +action(edit.zoom_out): + key(cmd--) + +action(edit.zoom_reset): + key(cmd-0) diff --git a/talon/user/community/apps/finder/finder.py b/talon/community/community-cursorless-0.4.0/apps/mac/finder/finder.py similarity index 86% rename from talon/user/community/apps/finder/finder.py rename to talon/community/community-cursorless-0.4.0/apps/mac/finder/finder.py index fc83818..5d9e70c 100644 --- a/talon/user/community/apps/finder/finder.py +++ b/talon/community/community-cursorless-0.4.0/apps/mac/finder/finder.py @@ -1,8 +1,10 @@ -import os - -from talon import Context, actions, ui +from talon import Context, Module, actions, imgui, settings, ui +from talon.experimental.locate import locate from talon.mac import applescript +import os + +mod = Module() ctx = Context() ctx.matches = r""" app: finder @@ -12,10 +14,7 @@ directories_to_exclude = {} @ctx.action_class("user") -class UserActions: - def file_manager_open_parent(): - actions.key("cmd-up") - +class user_actions: def file_manager_current_path(): title = ui.active_window().title @@ -75,3 +74,10 @@ class UserActions: """selects the file""" actions.key("home") actions.insert(path) + + +@mod.action_class +class Actions: + def resize_finder_column(): + """Move mouse to resize a finder column""" + print(locate("templates/finder-resize.png")) diff --git a/talon/user/community/apps/finder/finder.talon b/talon/community/community-cursorless-0.4.0/apps/mac/finder/finder.talon similarity index 73% rename from talon/user/community/apps/finder/finder.talon rename to talon/community/community-cursorless-0.4.0/apps/mac/finder/finder.talon index 8e406b5..bddf5b0 100644 --- a/talon/user/community/apps/finder/finder.talon +++ b/talon/community/community-cursorless-0.4.0/apps/mac/finder/finder.talon @@ -2,9 +2,20 @@ os: mac app: finder - tag(): user.file_manager -tag(): user.tabs + +action(user.file_manager_open_parent): + key(cmd-up) + +action(user.file_manager_go_forward): + key("cmd-]") + +action(user.file_manager_go_back): + key("cmd-[") + +re size column: user.resize_finder_column() + preferences: key(cmd-,) -options: key(cmd-j) +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 diff --git a/talon/user/community/apps/iterm/iterm.py b/talon/community/community-cursorless-0.4.0/apps/mac/iterm/iterm.py similarity index 67% rename from talon/user/community/apps/iterm/iterm.py rename to talon/community/community-cursorless-0.4.0/apps/mac/iterm/iterm.py index 87d5a1b..766662f 100644 --- a/talon/user/community/apps/iterm/iterm.py +++ b/talon/community/community-cursorless-0.4.0/apps/mac/iterm/iterm.py @@ -1,31 +1,16 @@ -from talon import Context, Module, actions +from talon import Context, Module, actions, imgui, settings, ui +import os 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: +class user_actions: # def file_manager_current_path(): # title = ui.active_window().title @@ -70,12 +55,32 @@ class UserActions: # """selects the file""" # actions.insert(path) - def tab_jump(number: int): - actions.key(f"cmd-{number}") + def terminal_list_directories(): + actions.insert("ls") + actions.key("enter") - def tab_final(): - actions.key("cmd-9") + def terminal_list_all_directories(): + actions.insert("ls -a") + actions.key("enter") + + def terminal_change_directory(path: str): + actions.insert("cd {}".format(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") diff --git a/talon/user/community/apps/iterm/iterm.talon b/talon/community/community-cursorless-0.4.0/apps/mac/iterm/iterm.talon similarity index 64% rename from talon/user/community/apps/iterm/iterm.talon rename to talon/community/community-cursorless-0.4.0/apps/mac/iterm/iterm.talon index f779f05..b367f20 100644 --- a/talon/user/community/apps/iterm/iterm.talon +++ b/talon/community/community-cursorless-0.4.0/apps/mac/iterm/iterm.talon @@ -4,8 +4,7 @@ app: iterm2 tag(): terminal # todo: filemanager support #tag(): user.file_manager -tag(): user.generic_unix_shell +tag(): user.generic_terminal tag(): user.git tag(): user.kubectl -tag(): user.tabs -tag(): user.readline +tag(): user.tabs \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/preview.py b/talon/community/community-cursorless-0.4.0/apps/mac/preview.py new file mode 100644 index 0000000..0ba0f70 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/preview.py @@ -0,0 +1,11 @@ +from talon import ctrl, ui, Module, Context, actions, clip, app + +ctx = Context() +mod = Module() +apps = mod.apps +apps.preview = """ +app.name: Preview +""" +ctx.matches = r""" +app: preview +""" diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/preview.talon b/talon/community/community-cursorless-0.4.0/apps/mac/preview.talon new file mode 100644 index 0000000..c3d7fbd --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/preview.talon @@ -0,0 +1,9 @@ +app: preview +- + page : + key(cmd-alt-g) + "{number}" + key(enter) +add text []: + key(cmd-ctrl-t:2) + insert(text or "") \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/rstudio.talon b/talon/community/community-cursorless-0.4.0/apps/mac/rstudio.talon new file mode 100644 index 0000000..21b3e51 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/rstudio.talon @@ -0,0 +1,127 @@ +os: mac +app: RStudio +- + +run that: key("cmd-enter") +run document: key("cmd-alt-r") +run from top: key("cmd-alt-b") +run to end: key("cmd-alt-e") +run (function|funk): key("cmd-alt-f") +run section: key("cmd-alt-t") +run previous chunks: key("cmd-alt-p") +run chunk: key("cmd-alt-c") +run next chunk: key("cmd-alt-n") +run all: key("cmd-shift-s") +run knitter: key("cmd-shift-k") +run profiler: key("cmd-shift-alt-p") + +# Moving around and formatting +jump back: key("cmd-f9") +jump forward: key("cmd-f10") +close all tabs: key("cmd-shift-w") +indent lines: key("cmd-i") +toggle comment: key("cmd-shift-c") +reformat comment: key("cmd-shift-/") +reformat R code: key("cmd-shift-a") +line up: key("alt-up") +line down: key("alt-down") +duplicate line up: key("cmd-alt-up") +duplicate line [down]: key("cmd-alt-down") +select to paren: key("ctrl-shift-e") +select to matching paren: key("ctrl-shift-alt-e") +jump to matching: key("ctrl-p") +expand selection: key("shift-alt-cmd-up") +reduce selection: key("shift-alt-cmd-down") +add cursor up: key("ctrl-alt-up") +add cursor down: key("ctrl-alt-down") +move active cursor up: key("ctrl-alt-shift-up") +move active cursor down: key("ctrl-alt-shift-down") +delete line: key("cmd-d") +delete word left: key("alt-backspace") +delete word right: key("alt-delete") +assign that: key("alt--") +pipe that: key("cmd-shift-m") +insert knitter chunk: key("cmd-alt-i") + +# Folding +fold that: key("cmd-alt-l") +unfold that: key("cmd-shift-alt-l") +fold all: key("cmd-alt-o") +unfold all: key("cmd-shift-alt-o") + +# Find and replace +find and replace: key("cmd-f") +find next: key("cmd-g") +find previous: key("cmd-shift-g") +find with selection: key("cmd-e") +find in files: key("cmd-shift-f") +run replace: key("cmd-shift-j") +run spell check: key("f7") + +# Navigation and panels +go to source: key("ctrl-1") +go to console: key("ctrl-2") +go to help: key("ctrl-3") +go to history: key("ctrl-4") +go to files: key("ctrl-5") +go to (plots|plot): key("ctrl-6") +go to packages: key("ctrl-7") +go to environment: key("ctrl-8") +go to git: key("ctrl-9") +go to build: key("ctrl-0") +go to terminal: key("alt-shift-t") +go to omni: key("ctrl-.") +go to line: key("cmd-shift-alt-g") +go to section: key("cmd-shift-alt-j") +go to tab: key("ctrl-shift-.") +go to previous tab: key("ctrl-f11") +go to next tab: key("ctrl-f12") +go to first tab: key("ctrl-shift-f11") +go to last tab: key("ctrl-shift-f12") + +zoom source: key("ctrl-shift-1") +(zoom|show) all: key("ctrl-shift-0") + +help that: key("f1") +define that: key("f2") +previous plot: key("cmd-alt-f11") +next plot: key("cmd-alt-f12") + +# devtools, package development, and session management +restart R session: key("cmd-shift-f10") +dev tools build: key("cmd-shift-b") +dev tools load all: key("cmd-shift-l") +dev tools test: key("cmd-shift-t") +dev tools check: key("cmd-shift-e") +dev tools document: key("cmd-shift-d") + +# Debugging +toggle breakpoint: key("shift-f9") +debug next: key("f10") +debug step into (function|funk): key("shift-f4") +debug finish (function|funk): key("shift-f6") +debug continue: key("shift-f5") +debug stop: key("shift-f8") + +# Git/SVN +run git diff: key("ctrl-alt-d") +run git commit: key("ctrl-alt-m") + +# Other shortcuts that could be enabled +# run line and stay: key("alt-enter") +# run and echo all: key("cmd-shift-enter") +# extract (function|funk): key("cmd-alt-x") +# extract variable: key("cmd-alt-v") +# new terminal: key("shift-alt-t") +# rename current terminal: key("shift-alt-r") +# clear current terminal: key("ctrl-shift-l") +# previous terminal: key("ctrl-alt-f11") +# next terminal: key("ctrl-alt-f12") +# clear console: key("ctrl-l") +# popup history: key("cmd-up") +# change working directory: key("ctrl-shift-h") +# new document: key("cmd-shift-n") +# new document (chrome only): key("cmd-shift-alt-n") +# insert code section: key("cmd-shift-r") +# scroll diff view: key("ctrl-up/down") +# sync editor & pdf preview: key("cmd-f8") diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/safari.py b/talon/community/community-cursorless-0.4.0/apps/mac/safari.py new file mode 100644 index 0000000..2e3c869 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/safari.py @@ -0,0 +1,32 @@ +from talon import ctrl, ui, Module, Context, actions, clip, app + +ctx = Context() +mod = Module() +apps = mod.apps +mod.apps.safari = """ +os: mac +and app.bundle: com.apple.Safari +""" + +ctx.matches = r""" +app: safari +""" + + +@ctx.action_class("user") +class user_actions: + def tab_jump(number: int): + if number < 9: + actions.key("cmd-{}".format(number)) + + def tab_final(): + actions.key("cmd-9") + + +@ctx.action_class("browser") +class browser_actions: + def go(url: str): + actions.browser.focus_address() + actions.sleep("50ms") + actions.insert(url) + actions.key("enter") diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/safari.talon b/talon/community/community-cursorless-0.4.0/apps/mac/safari.talon new file mode 100644 index 0000000..a640afe --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/safari.talon @@ -0,0 +1,68 @@ +app: safari +- +tag(): browser +tag(): user.tabs +#action(browser.address): + +action(browser.bookmark): + key(cmd-d) + +action(browser.bookmark_tabs): + key(cmd-shift-d) + +action(browser.bookmarks): + key(cmd-alt-b) + +#action(browser.bookmarks_bar): +# key(ctrl-shift-b) + +action(browser.focus_address): + key(cmd-l) + +#action(browser.focus_page): + +action(browser.focus_search): + browser.focus_address() + +action(browser.go_blank): + key(cmd-n) + +action(browser.go_back): + key(cmd-left) + +action(browser.go_forward): + key(cmd-right) + +action(browser.go_home): + key(cmd-shift-h) + +action(browser.open_private_window): + key(cmd-shift-n) + +action(browser.reload): + key(cmd-r) + +action(browser.reload_hard): + key(cmd-shift-r) + +#action(browser.reload_hardest): + +#action(browser.show_clear_cache): +# key(cmd-shift-delete) + +action(browser.show_downloads): + key(cmd-shift-j) + +action(browser.show_extensions): + key(ctrl-shift-a) + +action(browser.show_history): + key(cmd-y) + +action(browser.submit_form): + key(enter) + +#action(browser.title) + +action(browser.toggle_dev_tools): + key(cmd-alt-i) diff --git a/talon/user/community/apps/apple_terminal/apple_terminal.py b/talon/community/community-cursorless-0.4.0/apps/mac/terminal/terminal.py similarity index 59% rename from talon/user/community/apps/apple_terminal/apple_terminal.py rename to talon/community/community-cursorless-0.4.0/apps/mac/terminal/terminal.py index 4d10e6d..50c98a6 100644 --- a/talon/user/community/apps/apple_terminal/apple_terminal.py +++ b/talon/community/community-cursorless-0.4.0/apps/mac/terminal/terminal.py @@ -1,9 +1,6 @@ +from talon import Context, Module, actions, imgui, settings, ui import os -from talon import Context, actions, ui - -# TODO: fit this to terminal.py - ctx = Context() ctx.matches = r""" app: apple_terminal @@ -12,21 +9,11 @@ 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: +class user_actions: 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) @@ -44,16 +31,10 @@ class UserActions: def file_manager_open_directory(path: str): """opens the directory that's already visible in the view""" actions.insert("cd ") - path = f'"{path}"' + path = '"{}"'.format(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") + actions.user.file_manager_refresh_title() def file_manager_select_directory(path: str): """selects the directory""" @@ -61,7 +42,7 @@ class UserActions: 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}"' + name = '"{}"'.format(name) actions.insert("mkdir " + name) @@ -74,17 +55,33 @@ class UserActions: """selects the file""" actions.insert(path) - def file_manager_refresh_title(): - return + def terminal_list_directories(): + actions.insert("ls") + actions.key("enter") + def terminal_list_all_directories(): + actions.insert("ls -a") + actions.key("enter") -@ctx.action_class("app") -class app_actions: - # other tab functions should already be implemented in - # code/platforms/mac/app.py + def terminal_change_directory(path: str): + actions.insert("cd {}".format(path)) + if path: + actions.key("enter") - def tab_previous(): - actions.key("ctrl-shift-tab") + 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") - def tab_next(): - actions.key("ctrl-tab") diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/terminal/terminal.talon b/talon/community/community-cursorless-0.4.0/apps/mac/terminal/terminal.talon new file mode 100644 index 0000000..24a6409 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/terminal/terminal.talon @@ -0,0 +1,35 @@ +app: apple_terminal +- +#comment or remove tags for command sets you don't want +tag(): user.file_manager +tag(): user.generic_terminal +tag(): user.git +tag(): user.kubectl +tag(): user.tabs +tag(): terminal + +action(user.file_manager_open_parent): + insert("cd ..") + key(enter) +action(app.tab_open): + key(cmd-t) +action(app.tab_close): + key(cmd-w) +action(app.tab_next): + key(ctrl-tab) +action(app.tab_previous): + key(ctrl-shift-tab) +action(app.window_open): + key(cmd-n) +action(edit.page_down): + key(command-pagedown) +action(edit.page_up): + key(command-pageup) +rerun search: + key(ctrl-r) +suspend: + key(ctrl-z) +resume: + insert("fg") + key(enter) + diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/things3.py b/talon/community/community-cursorless-0.4.0/apps/mac/things3.py new file mode 100644 index 0000000..7bf599c --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/things3.py @@ -0,0 +1,143 @@ +from typing import Dict, Optional +import webbrowser +from talon import ctrl, ui, Module, Context, actions, clip, app +from dataclasses import dataclass +from things3.things3 import Things3 +from ...code.create_spoken_forms import create_spoken_forms +import io +import csv + +ctx = Context() +mod = Module() +apps = mod.apps +apps.things3 = """ +app.name: Things +""" +ctx.matches = r""" +app: things3 +""" + +mod.list("things_tag", desc="Tags in Things") +mod.list("things_tag_with_shortcut", desc="Tags in Things within an assigned shortcut") +mod.list("things_project", desc="Areas and projects in Things") + +things = Things3() + + +@dataclass +class Tag: + uuid: str + title: str + shortcut: Optional[str] + + +@dataclass +class Project: + uuid: str + title: str + + +raw_areas = things.get_areas() +raw_projects = things.get_projects() +sql_query = f""" +SELECT TAG.title, TAG.shortcut, TAG.uuid +FROM {things.TABLE_TAG} AS TAG +""" +tags = [Tag(**raw_tag) for raw_tag in things.execute_query(sql_query)] +projects = [ + Project(uuid=raw_project["uuid"], title=raw_project["title"]) + for raw_project in raw_areas + raw_projects +] + +tag_map: Dict[str, Tag] = {tag.uuid: tag for tag in tags} +project_map = {project.uuid: project for project in projects} + +ctx.lists["self.things_tag"] = { + spoken_form: tag.uuid + for tag in tags + for spoken_form in create_spoken_forms(tag.title) +} +ctx.lists["self.things_tag_with_shortcut"] = { + spoken_form: tag.uuid + for tag in tags + if tag.shortcut is not None + for spoken_form in create_spoken_forms(tag.title) +} +things_projects = { + spoken_form: project.uuid + for project in projects + for spoken_form in create_spoken_forms(project.title) +} +print(things_projects) +ctx.lists["self.things_project"] = things_projects + + +@mod.action_class +class Actions: + def tag_todo(things_tags: str): + """Tag todo with a list of tags""" + tag_list = [tag_map[tag_uuid] for tag_uuid in things_tags.split(",")] + tags_with_shortcuts = [tag for tag in tag_list if tag.shortcut is not None] + tags_without_shortcuts = [tag for tag in tag_list if tag.shortcut is None] + + for tag in tags_with_shortcuts: + actions.key(f"ctrl-{tag.shortcut}") + + for tag in tags_without_shortcuts: + actions.key(f"cmd-shift-t") + actions.insert(tag.title) + actions.key("enter") + + def filter_by_tag(things_tags: str): + """Tag todo with a list of tags""" + tag_list = [tag_map[tag_uuid] for tag_uuid in things_tags.split(",")] + + for tag in tag_list: + if tag.shortcut is None: + raise Exception("Can only filter by tags with assigned shortcuts") + actions.key(f"ctrl-alt-{tag.shortcut}") + + def show_tag(things_tag: str): + """Show a particular tag in things""" + tag = tag_map[things_tag] + webbrowser.open(f"things:///show?id={tag.uuid}") + + def show_things_list(things_project: str): + """Show a list in things""" + project = project_map[things_project] + webbrowser.open(f"things:///show?id={project.uuid}") + + def move_todo(project: str): + """Move todo to a particular list""" + try: + project = project_map[project].title + except KeyError: + pass + actions.key("cmd-shift-m") + actions.insert(project) + actions.key("enter") + + +@mod.capture(rule="{self.things_tag}+") +def things_tags(m) -> str: + "One or more Things tags" + return ",".join(m.things_tag_list) + + +@mod.capture(rule="{self.things_tag_with_shortcut}+") +def things_tags_with_shortcut(m) -> str: + "One or more Things tags" + return ",".join(m.things_tag_with_shortcut_list) + + +@mod.capture(rule="{self.things_project}+") +def things_projects(m) -> str: + "One or more Things projects" + return to_csv_row_string(m.things_project_list) + + +def to_csv_row_string(row_elements: str) -> str: + output = io.StringIO() + writer = csv.writer(output, quoting=csv.QUOTE_MINIMAL) + writer.writerow(row_elements) + return output.getvalue() diff --git a/talon/community/community-cursorless-0.4.0/apps/mac/things3.talon b/talon/community/community-cursorless-0.4.0/apps/mac/things3.talon new file mode 100644 index 0000000..7f83ac7 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/mac/things3.talon @@ -0,0 +1,51 @@ +app: things3 +- +tag(): user.todo_list + +new task: key(cmd-n) +action(user.mark_complete): key(cmd-.) +action(user.mark_cancelled): key(cmd-alt-.) + +action(user.show_inbox): key(cmd-1) +action(user.show_today): key(cmd-2) +action(user.show_upcoming): key(cmd-3) +action(user.show_anytime): key(cmd-4) +action(user.show_someday): key(cmd-5) +action(user.show_logbook): key(cmd-6) +show tag {user.things_tag}: user.show_tag(things_tag) +show list {user.things_project}: user.show_things_list(things_project) + +follow link: key(cmd-alt-enter) + +tag this : user.tag_todo(things_tags) +move this [to] {user.things_project}: user.move_todo(things_project) +move this [to] inbox: user.move_todo("Inbox") + +do this today: key(cmd-t) +do this evening: key(cmd-e) +do this (anytime | any time): key(cmd-r) +do this someday: key(cmd-o) +do this : + key(cmd-s) + insert(text) + key(enter) + +deadline : + key(cmd-shift-d) + insert(text) + key(enter) + +clear deadline: + key(cmd-shift-d backspace enter) + +filter [tag] : user.filter_by_tag(things_tags) +clear filter: key(ctrl-escape) + +action(user.dental_click): key(cmd-enter) + +add checklist: key(cmd-shift-c) + +drag [this] way up: key(cmd-alt-up) +drag [this] way down: key(cmd-alt-down) +drag [this] up: key(cmd-up) +drag [this] down: key(cmd-down) \ No newline at end of file diff --git a/talon/user/community/apps/slack/slack_mac.talon b/talon/community/community-cursorless-0.4.0/apps/slack/slack.mac.talon similarity index 53% rename from talon/user/community/apps/slack/slack_mac.talon rename to talon/community/community-cursorless-0.4.0/apps/slack/slack.mac.talon index 70437a4..c994744 100644 --- a/talon/user/community/apps/slack/slack_mac.talon +++ b/talon/community/community-cursorless-0.4.0/apps/slack/slack.mac.talon @@ -2,66 +2,64 @@ os: mac app: slack - tag(): user.messaging -tag(): user.emoji # Workspace workspace : key("cmd-{number}") +action(user.messaging_workspace_previous): key(cmd-shift-[) +action(user.messaging_workspace_next): key(cmd-shift-]) # Channel (slack | lack) [channel] info: key(cmd-shift-i) -# Navigation -focus (move | next): key(ctrl-`) -(section | zone) [next]: key(f6) -(section | zone) (previous | last): key(shift-f6) +action(user.messaging_open_channel_picker): key(cmd-k) +action(user.messaging_channel_previous): key(alt-up) +action(user.messaging_channel_next): key(alt-down) +action(user.messaging_unread_previous): key(alt-shift-up) +action(user.messaging_unread_next): key(alt-shift-down) + # Navigation +(move | next) focus: key(ctrl-`) +[next] (section | zone): key(f6) +(previous | last) (section | zone): key(shift-f6) (slack | lack) [direct] messages: key(cmd-shift-k) (slack | lack) threads: key(cmd-shift-t) (slack | lack) (history [next] | back | backward): key(cmd-[) (slack | lack) forward: key(cmd-]) -(element | bit) [next]: key(tab) -(element | bit) (previous | last): key(shift-tab) +[next] (element | bit): key(tab) +(previous | last) (element | bit): key(shift-tab) (slack | lack) (my stuff | activity): key(cmd-shift-m) (slack | lack) directory: key(cmd-shift-e) (slack | lack) (starred [items] | stars): key(cmd-shift-s) -(slack | lack) unread [messages]: key(cmd-shift-a) +(slack | lack) unread [messages]: key(cmd-j) (go | undo | toggle) full: key(ctrl-cmd-f) -# Messaging +action(user.messaging_open_search): key(cmd-f) + # Messaging grab left: key(shift-up) grab right: key(shift-down) add line: key(shift-enter) (slack | lack) (slap | slaw | slapper): key(cmd-right shift-enter) -(slack | lack) (react | reaction): key(cmd-shift-\) +(slack | lack) (react | reaction): key(cmd-shift-\\) (insert command | commandify): key(cmd-shift-c) -insert link: key(cmd-shift-u) -insert code: key(cmd-shift-alt-c) +insert code: + insert("``````") + key(left left left) + key(shift-enter) + key(shift-enter) + key(up) (slack | lack) (bull | bullet | bulleted) [list]: key(cmd-shift-8) (slack | lack) (number | numbered) [list]: key(cmd-shift-7) (slack | lack) (quotes | quotation): key(cmd-shift->) bold: key(cmd-b) (italic | italicize): key(cmd-i) (strike | strikethrough): key(cmd-shift-x) +action(user.messaging_mark_workspace_read): key(shift-esc) +action(user.messaging_mark_channel_read): key(esc) +(clear | scrap | scratch): key(cmd-a backspace) + # Files and Snippets +action(user.messaging_upload_file): key(cmd-u) (slack | lack) snippet: key(cmd-shift-enter) -# Calls + # Calls ([toggle] mute | unmute): key(m) -(slack | lack) huddle: key(cmd-shift-h) (slack | lack) ([toggle] video): key(v) (slack | lack) invite: key(a) -# Miscellaneous + # Miscellaneous (slack | lack) shortcuts: key(cmd-/) emote : "{text}" toggle left sidebar: key(cmd-shift-d) toggle right sidebar: key(cmd-.) - -# DEPRECATED -(move | next) focus: - app.notify("please use the voice command 'focus next' instead of 'next focus'") - key(ctrl-`) -[next] (section | zone): - app.notify("please use the voice command 'section next' instead of 'next section'") - key(f6) -(previous | last) (section | zone): - app.notify("please use the voice command 'section last' instead of 'last section'") - key(shift-f6) -[next] (element | bit): - app.notify("please use the voice command 'element next' instead of 'next element'") - key(tab) -(previous | last) (element | bit): - app.notify("please use the voice command 'element last' instead of 'last element'") - key(shift-tab) diff --git a/talon/community/community-cursorless-0.4.0/apps/slack/slack.py b/talon/community/community-cursorless-0.4.0/apps/slack/slack.py new file mode 100644 index 0000000..3254e86 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/slack/slack.py @@ -0,0 +1,15 @@ +from talon import Module + +mod = Module() +apps = mod.apps +apps.slack = "app.name: Slack" +mod.apps.slack = """ +os: windows +and app.name: slack.exe +os: windows +and app.exe: slack.exe +""" +apps.slack = """ +os: mac +and app.bundle: com.tinyspeck.slackmacgap +""" diff --git a/talon/user/community/apps/slack/slack_win.talon b/talon/community/community-cursorless-0.4.0/apps/slack/slack.win.talon similarity index 54% rename from talon/user/community/apps/slack/slack_win.talon rename to talon/community/community-cursorless-0.4.0/apps/slack/slack.win.talon index fd23aed..897f1c2 100644 --- a/talon/user/community/apps/slack/slack_win.talon +++ b/talon/community/community-cursorless-0.4.0/apps/slack/slack.win.talon @@ -4,63 +4,64 @@ app: slack #todo: some sort of plugin, consolidate with teams or something? - tag(): user.messaging -tag(): user.emoji # Workspaces workspace : key("ctrl-{number}") +action(user.messaging_workspace_previous): key(ctrl-shift-tab) +action(user.messaging_workspace_next): key(ctrl-tab) # Channel (slack | lack) [channel] info: key(ctrl-shift-i) +action(user.messaging_open_channel_picker): key(ctrl-k) +action(user.messaging_channel_previous): key(alt-up) +action(user.messaging_channel_next): key(alt-down) +action(user.messaging_unread_previous): key(alt-shift-up) +action(user.messaging_unread_next): key(alt-shift-down) # Navigation -focus (move | next): key(ctrl-`) -(section | zone) [next]: key(f6) -(section | zone) (previous | last): key(shift-f6) +(move | next) focus: key(ctrl-`) +[next] (section | zone): key(f6) +(previous | last) (section | zone): key(shift-f6) (slack | lack) [direct] messages: key(ctrl-shift-k) (slack | lack) threads: key(ctrl-shift-t) (slack | lack) (history [next] | back | backward): key(alt-left) (slack | lack) forward: key(alt-right) -(element | bit) [next]: key(tab) -(element | bit) (previous | last): key(shift-tab) +[next] (element | bit): key(tab) +(previous | last) (element | bit): key(shift-tab) (slack | lack) (my stuff | activity): key(ctrl-shift-m) (slack | lack) directory: key(ctrl-shift-e) (slack | lack) (starred [items] | stars): key(ctrl-shift-s) -(slack | lack) unread [messages]: key(ctrl-shift-a) +(slack | lack) unread [messages]: key(ctrl-j) +#(go | undo | toggle) full: key(ctrl-cmd-f) +action(user.messaging_open_search): key(ctrl-f) # Messaging grab left: key(shift-up) grab right: key(shift-down) add line: key(shift-enter) #"(slack | lack) (slap | slaw | slapper): [key(cmd-right) key(shift-enter")], -(slack | lack) (react | reaction): key(ctrl-shift-\) +(slack | lack) (react | reaction): key(ctrl-shift-\\) (insert command | commandify): key(ctrl-shift-c) -insert code: insert("```") +insert code: + insert("``````") + key(left left left) + key(shift-enter) + key(shift-enter) + key(up) (slack | lack) (bull | bullet | bulleted) [list]: key(ctrl-shift-8) (slack | lack) (number | numbered) [list]: key(ctrl-shift-7) (slack | lack) (quotes | quotation): key(ctrl-shift-9) bold: key(ctrl-b) (italic | italicize): key(ctrl-i) (strike | strikethrough): key(ctrl-shift-x) +action(user.messaging_mark_workspace_read): key(shift-esc) +action(user.messaging_mark_channel_read): key(esc) +(clear | scrap | scratch): key(ctrl-a backspace) + # Files and Snippets +action(user.messaging_upload_file): key(ctrl-u) (slack | lack) snippet: key(ctrl-shift-enter) -# Calls + # Calls ([toggle] mute | unmute): key(m) (slack | lack) ([toggle] video): key(v) (slack | lack) invite: key(a) -# Miscellaneous + # Miscellaneous (slack | lack) shortcuts: key(ctrl-/) emote : "{text}" toggle left sidebar: key(ctrl-shift-d) toggle right sidebar: key(ctrl-.) - -# DEPRECATED -(move | next) focus: - app.notify("please use the voice command 'focus next' instead of 'next focus'") - key(ctrl-`) -[next] (section | zone): - app.notify("please use the voice command 'section next' instead of 'next section'") - key(f6) -(previous | last) (section | zone): - app.notify("please use the voice command 'section last' instead of 'last section'") - key(shift-f6) -[next] (element | bit): - app.notify("please use the voice command 'element next' instead of 'next element'") - key(tab) -(previous | last) (element | bit): - app.notify("please use the voice command 'element last' instead of 'last element'") - key(shift-tab) diff --git a/talon/user/community/apps/teams/teams.py b/talon/community/community-cursorless-0.4.0/apps/teams/teams.py similarity index 100% rename from talon/user/community/apps/teams/teams.py rename to talon/community/community-cursorless-0.4.0/apps/teams/teams.py diff --git a/talon/user/community/apps/teams/teams.talon b/talon/community/community-cursorless-0.4.0/apps/teams/teams.talon similarity index 78% rename from talon/user/community/apps/teams/teams.talon rename to talon/community/community-cursorless-0.4.0/apps/teams/teams.talon index ea8b7c2..5301743 100644 --- a/talon/user/community/apps/teams/teams.talon +++ b/talon/community/community-cursorless-0.4.0/apps/teams/teams.talon @@ -1,6 +1,4 @@ app: microsoft_teams -os: windows -os: linux - # Shortcut reference @@ -12,12 +10,19 @@ show shortcuts: key(ctrl-.) show commands: key(ctrl-/) open filter: key(ctrl-shift-f) go to: key(ctrl-g) -open (apps | applications): key(ctrl-`) +open (apps|applications): key(ctrl-`) [start] new chat: key(ctrl-n) open settings: key(ctrl-,) open help: key(f1) close: key(escape) +#zoom in: key(ctrl-=) +#zoom out: key(ctrl--) +#reset zoom: key(ctrl-0) +action(edit.zoom_in): key(ctrl-=) +action(edit.zoom_out): key(ctrl--) +action(edit.zoom_reset): key(ctrl-0) + # navigations open activity: key(ctrl-1) open chat: key(ctrl-2) @@ -26,11 +31,11 @@ open calendar: key(ctrl-4) open planner: key(ctrl-5) open calls: key(ctrl-6) open files: key(ctrl-7) -[go] [to] (prev | previous) [list] item: key(alt-up) +[go] [to] (prev|previous) [list] item: key(alt-up) [go] [to] next [list] item: key(alt-down) move [selected] team up: key(ctrl-shift-up) move [selected] team down: key(ctrl-shift-down) -[go] [to] (prev | previous) section: key(ctrl-shift-f6) +[go] [to] (prev|previous) section: key(ctrl-shift-f6) [go] [to] next section: key(ctrl-f6) # messaging @@ -55,11 +60,11 @@ decline screen share: key(ctrl-shift-d) accept screen share: key(ctrl-shift-a) schedule [a] meeting: key(alt-shift-n) go to current time: key(alt-.) -go to (prev | previous) (day | week): key(ctrl-alt-left) -go to next (day | week): key(ctrl-alt-right) +go to (prev|previous) (day|week): key(ctrl-alt-left) +go to next (day|week): key(ctrl-alt-right) view day: key(ctrl-alt-1) view work week: key(ctrl-alt-2) view week: key(ctrl-alt-3) -(safe | send) meeting request: key(ctrl-s) +(safe|send) meeting request: key(ctrl-s) join [from] meeting [details]: key(alt-shift-j) go to suggested time: key(alt-shift-s) diff --git a/talon/user/community/apps/visualstudio/visual_studio.py b/talon/community/community-cursorless-0.4.0/apps/visualstudio/visual_studio.py similarity index 72% rename from talon/user/community/apps/visualstudio/visual_studio.py rename to talon/community/community-cursorless-0.4.0/apps/visualstudio/visual_studio.py index 7362e8b..4bab331 100644 --- a/talon/user/community/apps/visualstudio/visual_studio.py +++ b/talon/community/community-cursorless-0.4.0/apps/visualstudio/visual_studio.py @@ -1,6 +1,5 @@ # vs title tracking requires an extension # https://marketplace.visualstudio.com/items?itemName=mayerwin.RenameVisualStudioWindowTitle -# https://github.com/mayerwin/vs-customize-window-title (VS 2022 support in releases) # I currently configure the extension as below # Document (no solution) open: [documentName] - [ideName] # No document or solution open: [idleName] @@ -8,7 +7,8 @@ # Solution in design mode: [documentName] - [parentPath]\[solutionName] - [ideName] # Solution in running mode: [documentName] - [parentPath]\[solutionName] (Running) - [ideName] -from talon import Context, Module, actions +from talon import Context, actions, ui, Module, app, clip +from typing import List, Union # is_mac = app.platform == "mac" @@ -18,8 +18,6 @@ mod = Module() apps = mod.apps apps.visual_studio = """ os: windows -and app.name: Microsoft Visual Studio 2022 -os: windows and app.name: Microsoft Visual Studio 2019 os: windows and app.name: devenv.exe @@ -30,55 +28,29 @@ ctx.matches = r""" app: visual_studio """ -from talon import Context, actions -ctx = Context() -ctx.matches = r""" -os: windows -app: visual_studio -""" +@ctx.action_class("win") +class win_actions: + def filename(): + title = actions.win.title() + # this doesn't seem to be necessary on VSCode for Mac + # if title == "": + # title = ui.active_window().doc + result = title.split("-")[0].rstrip() -@ctx.action_class("app") -class AppActions: - # talon app actions - def tab_close(): - actions.key("ctrl-f4") + if "." in result: + # print(result) + return result - def tab_next(): - actions.key("ctrl-tab") - - def tab_previous(): - actions.key("ctrl-shift-tab") - - def tab_reopen(): - actions.key("ctrl-1 ctrl-r enter") - - -@ctx.action_class("code") -class CodeActions: - # talon code actions - def toggle_comment(): - actions.key("ctrl-k ctrl-/") + return "" @ctx.action_class("edit") -class EditActions: - # talon edit actions - def indent_more(): - actions.key("tab") - - def indent_less(): - actions.key("shift-tab") - - def save_all(): - actions.key("ctrl-shift-s") - - def find_next(): - actions.key("enter") - - def find_previous(): - actions.key("shift-enter") +class edit_actions: + def find(text: str): + actions.key("ctrl-f") + actions.insert(text) def line_swap_up(): actions.key("alt-up") @@ -96,25 +68,21 @@ class EditActions: actions.key("enter") -@ctx.action_class("win") -class WinActions: - def filename(): - title = actions.win.title() - # this doesn't seem to be necessary on VSCode for Mac - # if title == "": - # title = ui.active_window().doc - - result = title.split("-")[0].rstrip() - - if "." in result: - # print(result) - return result - - return "" - - @ctx.action_class("user") -class UserActions: +class user_actions: + # snippet.py support beginHelp close + def snippet_search(text: str): + """TEST""" + actions.key("ctrl-k ctrl-x") + + # def snippet_insert(text: str): + # """Inserts a snippet""" + + # def snippet_create(): + # """Triggers snippet creation""" + + # snippet.py support end + # def select_word(verb: str): # actions.key("ctrl-w") # actions.user.perform_selection_action(verb) @@ -169,6 +137,19 @@ class UserActions: # find_and_replace.py support begin + def find(text: str): + """Triggers find in current editor""" + actions.key("ctrl-f") + + if text: + actions.insert(text) + + def find_next(): + actions.key("enter") + + def find_previous(): + actions.key("shift-enter") + def find_everywhere(text: str): """Triggers find across project""" actions.key("ctrl-shift-f") @@ -223,26 +204,3 @@ class UserActions: # find_and_replace.py support end - # multiple_cursor.py support begin - # note: visual studio has no explicit mode for multiple cursors; requires https://marketplace.visualstudio.com/items?itemName=VaclavNadrasky.MultiCaretBooster - def multi_cursor_add_above(): - actions.key("shift-alt-up") - - def multi_cursor_add_below(): - actions.key("shift-alt-down") - - # action(user.multi_cursor_add_to_line_ends): does not exist :( - def multi_cursor_disable(): - actions.key("escape") - - def multi_cursor_enable(): - actions.skip() - - def multi_cursor_select_all_occurrences(): - actions.key("shift-alt-;") - - def multi_cursor_select_fewer_occurrences(): - actions.key("shift-alt-k") - - def multi_cursor_select_more_occurrences(): - actions.key("shift-alt->") diff --git a/talon/user/community/apps/visualstudio/visual_studio.talon b/talon/community/community-cursorless-0.4.0/apps/visualstudio/visual_studio.talon similarity index 58% rename from talon/user/community/apps/visualstudio/visual_studio.talon rename to talon/community/community-cursorless-0.4.0/apps/visualstudio/visual_studio.talon index 35545df..31ff1af 100644 --- a/talon/user/community/apps/visualstudio/visual_studio.talon +++ b/talon/community/community-cursorless-0.4.0/apps/visualstudio/visual_studio.talon @@ -1,10 +1,37 @@ os: windows -app: visual_studio +app: Microsoft Visual Studio 2019 +app: devenv.exe - tag(): user.tabs tag(): user.line_commands tag(): user.find_and_replace +tag(): user.snippets tag(): user.multiple_cursors + +#talon app actions +action(app.tab_close): key(ctrl-f4) +action(app.tab_next): key(ctrl-tab) +action(app.tab_previous): key(ctrl-shift-tab) +action(app.tab_reopen): key(ctrl-1 ctrl-r enter) + +#talon code actions +action(code.toggle_comment): key(ctrl-k ctrl-/) + +#talon edit actions +action(edit.indent_more): key(tab) +action(edit.indent_less): key(shift-tab) +action(edit.save_all): key(ctrl-shift-s) + +#multiple_cursor.py support begin +#note: visual studio has no explicit mode for multiple cursors; requires https://marketplace.visualstudio.com/items?itemName=VaclavNadrasky.MultiCaretBooster +action(user.multi_cursor_add_above): key(shift-alt-up) +action(user.multi_cursor_add_below): key(shift-alt-down) +#action(user.multi_cursor_add_to_line_ends): does not exist :( +action(user.multi_cursor_disable): key(escape) +action(user.multi_cursor_enable): skip() +action(user.multi_cursor_select_all_occurrences): key(shift-alt-;) +action(user.multi_cursor_select_fewer_occurrences): key(shift-alt-k) +action(user.multi_cursor_select_more_occurrences): key(shift-alt->) #multiple_cursor.py support end # Panels @@ -19,19 +46,19 @@ panel breakpoints: key(ctrl-alt-b) # Settings show settings: key(alt-t o) -#show shortcuts: -#show snippets: +#show shortcuts: +#show snippets: # Display fullscreen switch: key(shift-alt-enter) wrap switch: key(ctrl-e ctrl-w) # File Commands -file hunt []: +file hunt []: key(ctrl-shift-t) insert(text or "") file create: key(ctrl-n) -#file open folder: +#file open folder: file rename: key(ctrl-[ s f2) file reveal: key(ctrl-[ s) @@ -44,9 +71,9 @@ format that: key(ctrl-k ctrl-d) format selection: key(ctrl-k ctrl-f) imports fix: key(ctrl-r ctrl-g) -# problem next: -# problem last: -# problem fix: +# problem next: +# problem last: +# problem fix: refactor field: key(ctrl-r ctrl-e) refactor interface: key(ctrl-r ctrl-i) refactor method: key(ctrl-r ctrl-m) @@ -59,15 +86,15 @@ refactor that: key(ctrl-r ctrl-r) go back: key(ctrl--) go forward: key(ctrl-shift--) go implementation: key(f12) -go recent []: +go recent []: key(ctrl-1 ctrl-r) sleep(100ms) insert(text or "") -go type []: +go type []: key(ctrl-1 ctrl-t) sleep(100ms) insert(text or "") -go member []: +go member []: key(alt-\) sleep(100ms) insert(text or "") @@ -92,3 +119,4 @@ debug step out [of]: key(f10) debug start: key(f5) debug stopper: key(shift-f5) debug continue: key(f5) +#debug restart: \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/README.md b/talon/community/community-cursorless-0.4.0/apps/vscode/README.md new file mode 100644 index 0000000..553f40a --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/README.md @@ -0,0 +1,5 @@ +# VSCode support + +Assumes that you have the [Run Command by +Id](https://marketplace.visualstudio.com/items?itemName=pokey.run-command-by-id) +extension installed. diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/command_client.py b/talon/community/community-cursorless-0.4.0/apps/vscode/command_client.py new file mode 100644 index 0000000..3cf3c45 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/command_client.py @@ -0,0 +1,167 @@ +import requests +import time +import json +from typing import Any +from talon import Module, actions, app +from pathlib import Path +from tempfile import gettempdir + +is_mac = app.platform == "mac" + +mod = Module() + + +class NotSet: + def __repr__(self): + return "" + + +def run_vscode_command( + command: str, + *args: str, + wait_for_finish: bool = False, + expect_response: bool = False, + decode_json_arguments: bool = False, +): + """Execute command via vscode command server.""" + # NB: This is a hack to work around the fact that talon doesn't support + # variable argument lists + args = list( + filter( + lambda x: x is not NotSet, + args, + ) + ) + if decode_json_arguments: + args = [json.loads(arg) for arg in args] + + port_file_path = Path(gettempdir()) / "vscode-port" + original_contents = port_file_path.read_text() + + # Issue command to VSCode telling it to update the port file. Because only + # the active VSCode instance will accept keypresses, we can be sure that + # the active VSCode instance will be the one to write the port. + if is_mac: + actions.key("cmd-shift-alt-p") + else: + actions.key("ctrl-shift-alt-p") + + # Wait for the VSCode instance to update the port file. This generally + # happens within the first millisecond, but we give it 3 seconds just in + # case. + start_time = time.monotonic() + new_contents = port_file_path.read_text() + sleep_time = 0.0005 + while True: + if new_contents != original_contents: + try: + decoded_contents = json.loads(new_contents) + # If we're successful, we break out of the loop + break + except ValueError: + # If we're not successful, we keep waiting; we assume it was a + # partial write from VSCode + pass + time.sleep(sleep_time) + sleep_time *= 2 + if time.monotonic() - start_time > 3.0: + raise Exception("Timed out waiting for VSCode to update port file") + new_contents = port_file_path.read_text() + + port = decoded_contents["port"] + + response = requests.post( + f"http://localhost:{port}/execute-command", + json={ + "commandId": command, + "args": args, + "waitForFinish": wait_for_finish, + "expectResponse": expect_response, + }, + timeout=(0.05, 3.05), + ) + response.raise_for_status() + + actions.sleep("25ms") + + if expect_response: + return response.json() + + +@mod.action_class +class Actions: + def vscode( + command: str, + arg1: Any = NotSet, + arg2: Any = NotSet, + arg3: Any = NotSet, + arg4: Any = NotSet, + arg5: Any = NotSet, + ): + """Execute command via vscode command server.""" + run_vscode_command( + command, + arg1, + arg2, + arg3, + arg4, + arg5, + ) + + def vscode_and_wait( + command: str, + arg1: Any = NotSet, + arg2: Any = NotSet, + arg3: Any = NotSet, + arg4: Any = NotSet, + arg5: Any = NotSet, + ): + """Execute command via vscode command server and wait for command to finish.""" + run_vscode_command( + command, + arg1, + arg2, + arg3, + arg4, + arg5, + wait_for_finish=True, + ) + + def vscode_json_and_wait( + command: str, + arg1: Any = NotSet, + arg2: Any = NotSet, + arg3: Any = NotSet, + arg4: Any = NotSet, + arg5: Any = NotSet, + ): + """Execute command via vscode command server and wait for command to finish.""" + run_vscode_command( + command, + arg1, + arg2, + arg3, + arg4, + arg5, + wait_for_finish=True, + decode_json_arguments=True, + ) + + def vscode_get( + command: str, + arg1: Any = NotSet, + arg2: Any = NotSet, + arg3: Any = NotSet, + arg4: Any = NotSet, + arg5: Any = NotSet, + ) -> Any: + """Execute command via vscode command server and return command output.""" + return run_vscode_command( + command, + arg1, + arg2, + arg3, + arg4, + arg5, + expect_response=True, + ) diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/actions.py b/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/actions.py new file mode 100644 index 0000000..06f21ab --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/actions.py @@ -0,0 +1,77 @@ +from user.pokey_talon.code.terms import SELECT, TELEPORT, DELETE, FIND +from talon import Context, actions, ui, Module, app, clip + +mod = Module() + +ctx = Context() +ctx.matches = r""" +app: vscode +""" + +# TODO A lot of these could be supported by supporting a proper "pop back" +# Would basically use the same logic that is used for updating token ranges +mod.list("simple_cursorless_action", desc="Supported actions for cursorless navigation") +ctx.lists["self.simple_cursorless_action"] = { + # Accepts any single extent + "spring": "setSelection", + SELECT: "setSelection", + "pree": "setSelectionBefore", + "post": "setSelectionAfter", + DELETE: "delete", + "clear": "clear", + # "sort": "sortLines", + # "join": "joinLines", + # "float": "insertEmptyLineBelow", + # "drop": "insertEmptyLineBelow", + # Sort children?? (would need to sort only named ones to avoid commas) + # Reverse children + # "cut": "cut", + # "copy": "copy", + # FIND: "findInFile", + # f"{FIND} last": "findBackwardsInFile", + # f"{FIND} all": "findAll", + # "fold": "fold", + # "stack": "addCursorAt", + # "cursor all": "addCursorToAllLines", + # "remove cursor": "removeCursor", + # "tab": "indent", + # "retab": "dedent", + # "comment": "comment", + # "github open": "openInGithub", + # "smear": "cloneLineDown", + # # Accepts only single token + # "rename": "rename", + # "ref show": "showReferences", + # "def show": "showDefinition", + # "hover show": "showHover", + # "act up": "scrollToTop", + # "act eat": "scrollToMid", + # "act down": "scrollToBottom", + # "breakpoint": "addBreakPoint", + # # Accepts position + # "paste": "paste", +} + +{ + # Require 2 extents of any kind, but prob best to assume second extend is + # same type as first, and need to explicitly say "token" if you want to use + # a token for the second one if the first is not + # Note: these should actually be of the form "swap with" + "swap": "swap", + # Require 1 extent of any kind and 1 position, but prob best to assume + # the position is same type as extent, and need to explicitly say "token" + # if you want to use a token for the second one if the first is not + "use": "use", + # Require 1 extent of any kind, and 1 format string (eg "camel foo bar", + # "phrase hello world" etc, "spell air bat cap") + # Note: these should actually be of the form "replace with" + # Could also except a second extent, and that would replace it with that + # extent + "replace with": "replaceWith", + # Require 1 extent of any kind, and 1 format type (eg camel, uppercase, + # phrase etc) + # Note: these should actually be of the form "reformat as" + "reformat as": "reformatAs", +} +"" +"phones" \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/command.py b/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/command.py new file mode 100644 index 0000000..3955be6 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/command.py @@ -0,0 +1,27 @@ +from talon import Context, actions, ui, Module, app, clip +import json +from typing import Any +from ..command_client import NotSet + + +mod = Module() + + +@mod.action_class +class Actions: + def cursorless_single_target_command( + action: str, + target: str, + arg1: Any = NotSet, + arg2: Any = NotSet, + arg3: Any = NotSet, + ): + """Execute single-target cursorlses command""" + actions.user.vscode_and_wait( + "cursorless.command", + action, + [json.loads(target)], + arg1, + arg2, + arg3, + ) diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/cursorless.talon b/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/cursorless.talon new file mode 100644 index 0000000..05cf49c --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/cursorless.talon @@ -0,0 +1,35 @@ +app: vscode +- +{self.simple_cursorless_action} : + user.cursorless_single_target_command(simple_cursorless_action, cursorless_arg) + +drink : + user.cursorless_single_target_command("setSelection", cursorless_arg) + user.new_line_above() + +pour : + user.cursorless_single_target_command("setSelection", cursorless_arg) + user.new_line_below() + +funk wrap with : + user.cursorless_single_target_command("wrap", cursorless_arg, "{code_functions}(", ")") + +square wrap : + user.cursorless_single_target_command("wrap", cursorless_arg, "[", "]") + +round wrap : + user.cursorless_single_target_command("wrap", cursorless_arg, "(", ")") + +curly wrap : + user.cursorless_single_target_command("wrap", cursorless_arg, "{", "}") + +(diamond | angle) wrap : + user.cursorless_single_target_command("wrap", cursorless_arg, "<", ">") + +quad wrap : + user.cursorless_single_target_command("wrap", cursorless_arg, "\"", "\"") + +twin wrap : + user.cursorless_single_target_command("wrap", cursorless_arg, "'", "'") + +action(user.dental_click): user.vscode("cursorless.toggleDecorations") \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/range.py b/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/range.py new file mode 100644 index 0000000..b6d0ffe --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/range.py @@ -0,0 +1,42 @@ +import json +from .target import BASE_TARGET, cursor_mark +from talon import Context, actions, ui, Module, app, clip + +mod = Module() + + +@mod.capture( + rule=( + " | " + "through | " + "[range] through " + ) +) +def cursorless_range(m) -> str: + if "through" in m: + end = json.loads(m[-1]) + if m[0] == "through": + start = BASE_TARGET.copy() + else: + start = json.loads(m.cursorless_target_list[0]) + return json.dumps( + { + "type": "range", + "start": start, + "end": end, + } + ) + + return m[0] + + +@mod.capture(rule=(" (and )*")) +def cursorless_arg(m) -> str: + if len(m.cursorless_range_list) == 1: + return m.cursorless_range + return json.dumps( + { + "type": "list", + "elements": [json.loads(match) for match in m.cursorless_range_list], + } + ) diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/target.py b/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/target.py new file mode 100644 index 0000000..db6589f --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/cursorless/target.py @@ -0,0 +1,344 @@ +from ....code.terms import SELECT, TELEPORT, DELETE, FIND + +from enum import Enum +from dataclasses import dataclass +from ....code.keys import symbol_key_words +import json + +from talon import Context, actions, ui, Module, app, clip + +ctx = Context() +mod = Module() + +ctx.matches = r""" +app: vscode +""" + +mod.list("symbol_color", desc="Supported symbol colors for token jumping") +ctx.lists["self.symbol_color"] = { + "gray": "default", + "blue": "blue", + "green": "green", + "rose": "red", + "squash": "yellow", + "plum": "mauve", +} + + +CONNECTIVES = {"at", "of", "in", "containing"} + +BASE_TARGET = {"type": "primitive"} + + +@mod.capture( + rule=( + "[{user.cursorless_position}] " + "[{user.cursorless_pair_surround_type}] " + "[{user.cursorless_selection_type} [of | in | containing]] " + "[] " + "[ [at]]" + "( | {user.cursorless_mark} | {user.unmarked_core} | | | )" + "[ | {user.cursorless_matching}]" + ) +) +def cursorless_target(m) -> str: + """Supported extents for cursorless navigation""" + object = BASE_TARGET.copy() + for capture in m: + if capture in CONNECTIVES: + continue + for key, value in json.loads(capture).items(): + if ( + key in object + and key == SELECTION_TYPE_KEY + and ranked_selection_types[value] < ranked_selection_types[object[key]] + ): + continue + object[key] = value + + return json.dumps(object) + + +@dataclass +class ModifierTerm: + term: str + info: dict + + @property + def value(self): + return json.dumps(self.info) + + +def make_simple_transformation(type: str): + return {"transformation": {"type": type}} + + +matching_transformation = ModifierTerm( + "matching", make_simple_transformation("matchingPairSymbol") +) + +mod.list("cursorless_matching", desc="Supported symbol extent types") +ctx.lists["self.cursorless_matching"] = { + matching_transformation.term: matching_transformation.value +} + +containing_scope_type_map = { + "arg": "argumentOrParameter", + "arrow": "arrowFunction", + "class": "class", + "funk": "namedFunction", + "if": "ifStatement", + "lambda": "arrowFunction", + "map": "dictionary", + "pair": "pair", + "value": "pairValue", + "key": "pairKey", +} + +containing_scope_types = { + term: { + "transformation": { + "type": "containingScope", + "scopeType": containing_scope_type, + } + } + for term, containing_scope_type in containing_scope_type_map.items() +} + +mod.list("containing_scope_type", desc="Supported symbol extent types") +ctx.lists["self.containing_scope_type"] = { + key: json.dumps(value) for key, value in containing_scope_types.items() +} + +SELECTION_TYPE_KEY = "selectionType" + + +@dataclass +class SelectionType: + singular: str + plural: str + json_name: str + rank: int + + @property + def json_repr(self): + return {SELECTION_TYPE_KEY: self.json_name} + + +TOKEN = SelectionType("token", "tokens", "token", 0) +LINE = SelectionType("line", "lines", "line", 1) +BLOCK = SelectionType("block", "blocks", "block", 2) + +SELECTION_TYPES = [ + TOKEN, + LINE, + BLOCK, +] + +ranked_selection_types = { + selection_type.json_name: selection_type.rank for selection_type in SELECTION_TYPES +} + +cursor_mark = {"mark": {"type": "cursor"}} + +marks = { + "here": cursor_mark, + "this": cursor_mark, + **{ + f"this {selection_type.singular}": {**selection_type.json_repr, **cursor_mark} + for selection_type in SELECTION_TYPES + }, + **{ + f"these {selection_type.plural}": {**selection_type.json_repr, **cursor_mark} + for selection_type in SELECTION_TYPES + }, + "change": {"mark": {"type": "lastEditRange"}}, + "last cursor": {"mark": {"type": "lastCursorPosition"}}, + **{ + f"this {containing_scope_type}": {**cursor_mark, **value} + for containing_scope_type, value in containing_scope_types.items() + }, +} + +mod.list("cursorless_mark", desc="Types of marks") +ctx.lists["self.cursorless_mark"] = { + key: json.dumps(value) for key, value in marks.items() +} + +unmarked_cores = { + **containing_scope_types, + **{ + selection_type.singular: selection_type.json_repr + for selection_type in SELECTION_TYPES + }, + matching_transformation.term: matching_transformation.info, +} + +mod.list("unmarked_core", desc="Core terms whose mark must be inferred") +ctx.lists["self.unmarked_core"] = { + key: json.dumps(value) for key, value in unmarked_cores.items() +} + + +positions = { + "after": {"position": "after"}, + "before": {"position": "before"}, + "start of": {"position": "before", "insideOutsideType": "inside"}, + "end of": {"position": "after", "insideOutsideType": "inside"}, + "above": {"position": "before", **LINE.json_repr}, + "below": {"position": "after", **LINE.json_repr}, +} + +mod.list("cursorless_position", desc="Types of positions") +ctx.lists["self.cursorless_position"] = { + key: json.dumps(value) for key, value in positions.items() +} + +selection_type_map = {} + +for selection_type in SELECTION_TYPES: + selection_type_map[selection_type.singular] = selection_type.json_repr + selection_type_map[selection_type.plural] = selection_type.json_repr + +mod.list("cursorless_selection_type", desc="Types of selection_types") +ctx.lists["self.cursorless_selection_type"] = { + key: json.dumps(value) for key, value in selection_type_map.items() +} + + +@mod.capture(rule="[at] [{user.symbol_color}] ") +def decorated_symbol(m) -> str: + """A decorated symbol""" + try: + symbol_color = m.symbol_color + except AttributeError: + symbol_color = "default" + + character = m.any_alphanumeric_key + + return json.dumps( + { + "mark": { + "type": "decoratedSymbol", + "symbolColor": symbol_color, + "character": character, + } + } + ) + + +mod.list("cursorless_sub_component_type", desc="Supported subcomponent types") +ctx.lists["self.cursorless_sub_component_type"] = { + "small": "subtoken", + "subtoken": "subtoken", + "subtokens": "subtoken", + "subword": "subtoken", + "subwords": "subtoken", + "car": "character", + "cars": "character", + "character": "character", + "characters": "character", + "letter": "character", + "letters": "character", +} + + +@mod.capture( + rule=( + " [through ] {user.cursorless_sub_component_type}" + ) +) +def cursorless_indexer(m) -> str: + """Supported extents for cursorless navigation""" + return json.dumps( + { + "transformation": { + "type": "subpiece", + "pieceType": m.cursorless_sub_component_type, + "startIndex": m.ordinals_list[0], + "endIndex": m.ordinals_list[-1] + 1, + } + } + ) + + +pair_symbols = { + "[": "squareBrackets", + "]": "squareBrackets", + "{": "curlyBrackets", + "}": "curlyBrackets", + "<": "angleBrackets", + ">": "angleBrackets", + "(": "parentheses", + ")": "parentheses", + '"': "doubleQuotes", + "'": "singleQuotes", +} + +mod.list("pair_symbol", desc="A pair symbol") +ctx.lists["self.pair_symbol"] = { + phrase: pair_symbols[character] + for phrase, character in symbol_key_words.items() + if character in pair_symbols +} + +cursorless_pair_surround_types = { + "out": {"insideOutsideType": "outside"}, + "outer": {"insideOutsideType": "outside"}, + "outside": {"insideOutsideType": "outside"}, + "in": {"insideOutsideType": "inside"}, + "inner": {"insideOutsideType": "inside"}, + "inside": {"insideOutsideType": "inside"}, +} + +mod.list("cursorless_pair_surround_type", desc="Supported pair surround types") +ctx.lists["self.cursorless_pair_surround_type"] = { + key: json.dumps(value) for key, value in cursorless_pair_surround_types.items() +} + + +@mod.capture(rule=("{user.cursorless_pair_surround_type} {user.pair_symbol}")) +def cursorless_surrounding_pair(m) -> str: + """Supported extents for cursorless navigation""" + return json.dumps( + { + "transformation": { + "type": "surroundingPair", + "delimiter": m.pair_symbol, + } + ** json.loads(m.cursorless_pair_surround_type), + } + ) + + +simple_transformations = [ + matching_transformation, +] + +mod.list("cursorless_simple_transformations", desc="simple transformations") +ctx.lists["self.cursorless_simple_transformations"] = { + transformation.term: transformation.value + for transformation in simple_transformations +} + + +@mod.capture(rule=("[every] {user.containing_scope_type} [containing]")) +def cursorless_containing_scope(m) -> str: + """Supported extents for cursorless navigation""" + if m[0] in ["every"]: + current_target = json.loads(m.containing_scope_type) + current_target["transformation"]["includeSiblings"] = True + return json.dumps(current_target) + return m.containing_scope_type + + +@mod.capture( + rule=( + " |" + "{user.cursorless_simple_transformations} |" + "" + ) +) +def cursorless_range_transformation(m) -> str: + """Supported positions for cursorless navigation""" + return str(m) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/decorative_navigation.talon b/talon/community/community-cursorless-0.4.0/apps/vscode/decorative_navigation.talon new file mode 100644 index 0000000..da1859d --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/decorative_navigation.talon @@ -0,0 +1,19 @@ +app: vscode +- +# ( | spring) [{user.symbol_color}] : +# user.vscode_and_wait("decorative-navigation.selectToken", symbol_color or "default", any_alphanumeric_key) +# sleep(50ms) + +# pree [{user.symbol_color}] : +# user.vscode("decorative-navigation.selectToken", symbol_color or "default", any_alphanumeric_key) +# key(left) + +# post [{user.symbol_color}] : +# user.vscode("decorative-navigation.selectToken", symbol_color or "default", any_alphanumeric_key) +# key(right) + +# def show [{user.symbol_color}] : +# user.vscode("decorative-navigation.selectToken", symbol_color or "default", any_alphanumeric_key) +# user.vscode("editor.action.revealDefinition") + +# action(user.dental_click): user.vscode("decorative-navigation.toggleDecorations") \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/metago.talon b/talon/community/community-cursorless-0.4.0/apps/vscode/metago.talon new file mode 100644 index 0000000..5613442 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/metago.talon @@ -0,0 +1,49 @@ +app: vscode +- + +# line up: user.vscode("metaGo.selectLineUp") +# line: user.vscode("metaGo.selectLineDown") + +bracket switch: user.vscode("metaGo.changeSurroundingPair") + +# to: user.vscode("metaGo.gotoSmart") +# word : user.jump("metaGo.gotoSmart", word) +# before: user.vscode("metaGo.gotoBefore") +# after: user.vscode("metaGo.gotoAfter") +razor: user.vscode("metaGo.gotoEmptyLineUp") +dropper: user.vscode("metaGo.gotoEmptyLineDown") +# bracket: user.vscode("metaGo.jumpToBracket") +# very: +# key(cmd-shift-. enter) + +# to: user.vscode("metaGo.selectSmart") +# tasty: user.vscode("metaGo.selectBefore") +# toasty: user.vscode("metaGo.selectAfter") + upper: user.vscode("metaGo.selectEmptyLineUp") + downer: user.vscode("metaGo.selectEmptyLineDown") + downer: + user.vscode("metaGo.selectEmptyLineDown") + key(backspace) + upper: + user.vscode("metaGo.selectEmptyLineUp") + key(backspace) +# outside: user.vscode("metaGo.inSurroundingPairSelectionWithPairs") +# inside : +# user.vscode("metaGo.inSurroundingPairSelection") +# key(symbol_key) +# sleep(25ms) + +# cursor add: user.vscode("metaGo.addCursorSmart") +# cursor add before: user.vscode("metaGo.addCursorBefore") +# cursor add after: user.vscode("metaGo.addCursorAfter") + +act up: user.vscode("metaGo.scrollCurrentLineToTop") +act eat: user.vscode("metaGo.scrollCurrentLineToMiddle") +act down: user.vscode("metaGo.scrollCurrentLineToBottom") + + right big: user.vscode("metaGo.cursorSpaceWordRightSelect") + left big: user.vscode("metaGo.cursorSpaceWordLeftSelect") +tug big: user.vscode("metaGo.cursorSpaceWordLeft") +push big: user.vscode("metaGo.cursorSpaceWordRight") + +swap anchor: user.vscode("metaGo.selectionSwitchActiveWithAnchor") \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/snippets/csharp_snippets.py b/talon/community/community-cursorless-0.4.0/apps/vscode/snippets/csharp_snippets.py new file mode 100644 index 0000000..ed50447 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/snippets/csharp_snippets.py @@ -0,0 +1,43 @@ +from talon import Context, actions, ui, Module, app + +# from user.knausj_talon.code.snippet_watcher import snippet_watcher +import os + +ctx = Context() +ctx.matches = r""" +app: vscode +mode: user.csharp +mode: command +and code.language: csharp +""" +# short name -> ide clip name +ctx.lists["user.snippets"] = { + "class": "class", + "else": "else", + "for each": "foreach", + "if": "if", + "try except": "try", + "try finally": "tryf", + "while": "while", + # "class funky": "def(class method)", + # "class static funky": "def(class static method)", + # "for": "for", + # "funky": "def", +} + + +# def update_list(watch_list): +# ctx.lists["user.snippets"] = watch_list + + +# # there's probably a way to do this without +# snippet_path = None +# if app.platform == "windows": +# snippet_path = os.path.expandvars(r"%AppData%\Code\User\snippets") +# elif app.platform == "mac": +# snippet_path = os.path.expanduser( +# "~/Library/Application Support/Code/User/snippets" +# ) +# if snippet_path: +# watcher2 = snippet_watcher({snippet_path: ["csharp.json",],}, update_list,) +# print("reloaded!") diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/snippets/python_snippets.py b/talon/community/community-cursorless-0.4.0/apps/vscode/snippets/python_snippets.py new file mode 100644 index 0000000..af84d70 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/snippets/python_snippets.py @@ -0,0 +1,44 @@ +from talon import Context, actions, ui, Module, app + +# from user.knausj_talon.code.snippet_watcher import snippet_watcher +import os + +ctx = Context() +ctx.matches = r""" +app: vscode +mode: user.python +mode: command +and code.language: python +""" +# short name -> ide clip name +ctx.lists["user.snippets"] = { + "class funky": "def(class method)", + "class static funky": "def(class static method)", + "class": "class", + "else if": "elif", + "for": "for", + "funky": "def", + "if else": "if/else", + "if": "if", + "lambda": "lambda", + "try except": "try/except", + "while": "while", + "with": "with", +} + + +# def update_list(watch_list): +# ctx.lists["user.snippets"] = watch_list + + +# # there's probably a way to do this without +# snippet_path = None +# if app.platform == "windows": +# snippet_path = os.path.expandvars(r"%AppData%\Code\User\snippets") +# elif app.platform == "mac": +# snippet_path = os.path.expanduser( +# "~/Library/Application Support/Code/User/snippets" +# ) +# if snippet_path: +# watcher = snippet_watcher({snippet_path: ["python.json",],}, update_list,) + diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/vscode.py b/talon/community/community-cursorless-0.4.0/apps/vscode/vscode.py new file mode 100644 index 0000000..e150321 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/vscode.py @@ -0,0 +1,223 @@ +from talon import Context, actions, ui, Module, app, clip + +is_mac = app.platform == "mac" + +ctx = Context() +mod = Module() +mod.apps.vscode = """ +os: mac +and app.bundle: com.microsoft.VSCode +""" +mod.apps.vscode = """ +os: linux +and app.name: Code +os: linux +and app.name: code-oss +""" +mod.apps.vscode = """ +os: windows +and app.name: Visual Studio Code +os: windows +and app.exe: Code.exe +""" + +ctx.matches = r""" +app: vscode +""" + + +@ctx.action_class("win") +class win_actions: + def filename(): + title = actions.win.title() + # this doesn't seem to be necessary on VSCode for Mac + # if title == "": + # title = ui.active_window().doc + + if is_mac: + result = title.split(" — ")[0] + else: + result = title.split(" - ")[0] + + if "." in result: + return result + + return "" + + +@ctx.action_class("edit") +class edit_actions: + def find(text: str): + if is_mac: + actions.key("cmd-f") + else: + actions.key("ctrl-f") + + actions.insert(text) + + def line_swap_up(): + actions.key("alt-up") + + def line_swap_down(): + actions.key("alt-down") + + def line_clone(): + actions.key("shift-alt-down") + + def jump_line(n: int): + actions.user.vscode("workbench.action.gotoLine") + actions.insert(str(n)) + actions.key("enter") + actions.edit.line_start() + + +@mod.action_class +class Actions: + def vscode_terminal(number: int): + """Activate a terminal by number""" + actions.user.vscode(f"workbench.action.terminal.focusAtIndex{number}") + + +@ctx.action_class("user") +class user_actions: + # snippet.py support beginHelp close + def snippet_search(text: str): + actions.user.vscode("editor.action.insertSnippet") + actions.insert(text) + + def snippet_insert(text: str): + """Inserts a snippet""" + actions.user.vscode("editor.action.insertSnippet") + actions.insert(text) + actions.key("enter") + + def snippet_create(): + """Triggers snippet creation""" + actions.user.vscode("workbench.action.openSnippets") + + # snippet.py support end + + def tab_jump(number: int): + if number < 10: + if is_mac: + actions.key("ctrl-{}".format(number)) + else: + actions.key("alt-{}".format(number)) + + def tab_final(): + if is_mac: + actions.key("ctrl-0") + else: + actions.key("alt-0") + + # splits.py support begin + def split_number(index: int): + """Navigates to a the specified split""" + if index < 9: + if is_mac: + actions.key("cmd-{}".format(index)) + else: + actions.key("ctrl-{}".format(index)) + + # splits.py support end + + # find_and_replace.py support begin + + def find(text: str): + """Triggers find in current editor""" + if is_mac: + actions.key("cmd-f") + else: + actions.key("ctrl-f") + + if text: + actions.insert(text) + + def find_next(): + actions.user.vscode("editor.action.nextMatchFindAction") + + def find_previous(): + actions.user.vscode("editor.action.previousMatchFindAction") + + def find_everywhere(text: str): + """Triggers find across project""" + if is_mac: + actions.key("cmd-shift-f") + else: + actions.key("ctrl-shift-f") + + if text: + actions.insert(text) + + def find_toggle_match_by_case(): + """Toggles find match by case sensitivity""" + if is_mac: + actions.key("alt-cmd-c") + else: + actions.key("alt-c") + + def find_toggle_match_by_word(): + """Toggles find match by whole words""" + if is_mac: + actions.key("cmd-alt-w") + else: + actions.key("alt-w") + + def find_toggle_match_by_regex(): + """Toggles find match by regex""" + if is_mac: + actions.key("cmd-alt-r") + else: + actions.key("alt-r") + + def replace(text: str): + """Search and replaces in the active editor""" + if is_mac: + actions.key("alt-cmd-f") + else: + actions.key("ctrl-h") + + if text: + actions.insert(text) + + def replace_everywhere(text: str): + """Search and replaces in the entire project""" + if is_mac: + actions.key("cmd-shift-h") + else: + actions.key("ctrl-shift-h") + + if text: + actions.insert(text) + + def replace_confirm(): + """Confirm replace at current position""" + if is_mac: + actions.key("shift-cmd-1") + else: + actions.key("ctrl-shift-1") + + def replace_confirm_all(): + """Confirm replace all""" + if is_mac: + actions.key("cmd-enter") + else: + actions.key("ctrl-alt-enter") + + def select_previous_occurrence(text: str): + actions.edit.find(text) + actions.sleep("100ms") + actions.key("shift-enter esc") + + def select_next_occurrence(text: str): + actions.edit.find(text) + actions.sleep("100ms") + actions.key("esc") + + def select_next_token(): + actions.edit.find("") + actions.key("enter") + actions.key("enter") + actions.key("esc") + + # find_and_replace.py support end diff --git a/talon/community/community-cursorless-0.4.0/apps/vscode/vscode.talon b/talon/community/community-cursorless-0.4.0/apps/vscode/vscode.talon new file mode 100644 index 0000000..d06a151 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/vscode/vscode.talon @@ -0,0 +1,387 @@ +#custom vscode commands go here +app: vscode +- +tag(): user.find_and_replace +tag(): user.line_commands +tag(): user.multiple_cursors +tag(): user.snippets +tag(): user.splits +tag(): user.tabs + +# TODO remove this once implemented in cursorless +cut line: + key(cmd-x) + +action(edit.select_line): + key(ctrl-e cmd-shift-left) + + line: + user.vscode_and_wait("editor.action.deleteLines") + +action(user.new_line_below): + user.vscode_and_wait("editor.action.insertLineAfter") + +action(user.new_line_above): + user.vscode_and_wait("editor.action.insertLineBefore") + +settings(): + key_wait = 1 + +#talon app actions +action(app.tab_close): user.vscode("workbench.action.closeActiveEditor") +action(app.tab_next): user.vscode("workbench.action.nextEditorInGroup") +action(app.tab_previous): user.vscode("workbench.action.previousEditorInGroup") +action(app.tab_reopen): user.vscode("workbench.action.reopenClosedEditor") +action(app.window_close): user.vscode("workbench.action.closeWindow") +action(app.window_open): user.vscode("workbench.action.newWindow") + last: user.vscode("workbench.action.openPreviousRecentlyUsedEditorInGroup") + next: user.vscode("workbench.action.openNextRecentlyUsedEditorInGroup") + +#talon code actions +action(code.toggle_comment): user.vscode("editor.action.commentLine") + +#talon edit actions +action(edit.indent_more): user.vscode("editor.action.indentLines") +action(edit.indent_less): user.vscode("editor.action.outdentLines") +action(edit.save_all): user.vscode("workbench.action.files.saveAll") + +# splits.py support begin +action(user.split_clear_all): + user.vscode("workbench.action.editorLayoutSingle") +action(user.split_clear): + user.vscode("workbench.action.joinTwoGroups") +action(user.split_flip): + user.vscode("workbench.action.toggleEditorGroupLayout") +action(user.split_last): + user.vscode("workbench.action.focusLeftGroup") +action(user.split_next): + user.vscode_and_wait("workbench.action.focusRightGroup") +action(user.split_window_down): + user.vscode("workbench.action.moveEditorToBelowGroup") +action(user.split_window_horizontally): + user.vscode("workbench.action.splitEditorOrthogonal") +action(user.split_window_left): + user.vscode("workbench.action.moveEditorToLeftGroup") +action(user.split_window_right): + user.vscode("workbench.action.moveEditorToRightGroup") +action(user.split_window_up): + user.vscode("workbench.action.moveEditorToAboveGroup") +action(user.split_window_vertically): + user.vscode("workbench.action.splitEditor") +action(user.split_window): + user.vscode("workbench.action.splitEditor") +cross: + # user.vscode("vscode-neovim.escape") + # sleep(25ms) + user.split_next() + # key(a) +# splits.py support end + +#multiple_cursor.py support begin +#note: vscode has no explicit mode for multiple cursors +action(user.multi_cursor_add_above): + user.vscode("editor.action.insertCursorAbove") +action(user.multi_cursor_add_below): + user.vscode("editor.action.insertCursorBelow") +action(user.multi_cursor_add_to_line_ends): + user.vscode("editor.action.insertCursorAtEndOfEachLineSelected") +action(user.multi_cursor_disable): key(escape) +action(user.multi_cursor_enable): skip() +action(user.multi_cursor_select_all_occurrences): + user.vscode("editor.action.selectHighlights") +action(user.multi_cursor_select_fewer_occurrences): + user.vscode("cursorUndo") +action(user.multi_cursor_select_more_occurrences): + user.vscode("editor.action.addSelectionToNextFindMatch") +#multiple_cursor.py support end + +please []: + user.vscode("workbench.action.showCommands") + insert(user.text or "") + +# Sidebar +bar explore: user.vscode("workbench.view.explorer") +bar extensions: user.vscode("workbench.view.extensions") +bar outline: user.vscode("outline.focus") +bar run: user.vscode("workbench.view.debug") +bar source: user.vscode("workbench.view.scm") +side dog: user.vscode("workbench.action.toggleSidebarVisibility") +search next: user.vscode("search.action.focusNextSearchResult") +search last: user.vscode("search.action.focusPreviousSearchResult") + +symbol hunt []: + user.vscode("workbench.action.gotoSymbol") + sleep(50ms) + insert(text or "") + +symbol last: user.vscode("gotoNextPreviousMember.previousMember") +symbol next: user.vscode("gotoNextPreviousMember.nextMember") +# symbol: user.vscode_and_wait("semantic-movement.jumpToContainingSymbol") +# funk: user.vscode_and_wait("semantic-movement.jumpToContainingFunction") +# named funk: user.vscode_and_wait("semantic-movement.jumpToContainingNamedFunction") +# class: user.vscode_and_wait("semantic-movement.jumpToContainingClass") +# symbol: user.vscode_and_wait("semantic-movement.selectContainingSymbol") +# funk: user.vscode_and_wait("semantic-movement.selectContainingFunction") +# named funk: user.vscode_and_wait("semantic-movement.selectContainingNamedFunction") +# class: user.vscode_and_wait("semantic-movement.selectContainingClass") + +# Panels +panel control: user.vscode("workbench.panel.repl.view.focus") +panel output: user.vscode("workbench.panel.output.focus") +panel problems: user.vscode("workbench.panel.markers.view.focus") +low dog: user.vscode("workbench.action.togglePanel") +panel terminal: user.vscode("workbench.panel.terminal.focus") +pan edit: user.vscode("workbench.action.focusActiveEditorGroup") + +# Settings +show settings: user.vscode("workbench.action.openGlobalSettings") +show shortcuts: user.vscode("workbench.action.openGlobalKeybindings") +show snippets: user.vscode("workbench.action.openSnippets") + +# Display +centered switch: user.vscode("workbench.action.toggleCenteredLayout") +fullscreen switch: user.vscode("workbench.action.toggleFullScreen") +theme switch: user.vscode("workbench.action.selectTheme") +wrap switch: user.vscode("editor.action.toggleWordWrap") +zen switch: user.vscode("workbench.action.toggleZenMode") + +# File Commands + dock [] [{user.file_extension}]: + user.vscode("workbench.action.quickOpen") + sleep(50ms) + insert(text or "") + insert(file_extension or "") + sleep(300ms) + dock [] [{user.file_extension}]: + user.vscode("workbench.action.quickOpen") + sleep(50ms) + insert(text or "") + insert(file_extension or "") + sleep(300ms) + key(enter) +file copy path: + user.vscode("copyFilePath") +file create sibling * [] [{user.file_extension}]: + user.vscode_and_wait("explorer.newFile") + sleep(500ms) + user.insert_many(format_text_list or "") + user.insert_formatted(user.word or "", "NOOP") + insert(file_extension or "") +file create: user.vscode("workbench.action.files.newUntitledFile") +file rename: + user.vscode("fileutils.renameFile") + sleep(150ms) +file move: + user.vscode("fileutils.moveFile") + sleep(150ms) +file open folder: + user.vscode("revealFileInOS") +file reveal: user.vscode("workbench.files.action.showActiveFileInExplorer") +save ugly: + user.vscode("workbench.action.files.saveWithoutFormatting") +file clone: + user.vscode("fileutils.duplicateFile") + sleep(150ms) +action(edit.save): + key(cmd-s) + sleep(50ms) + +# Language Features +suggest show: user.vscode("editor.action.triggerSuggest") +hint show: user.vscode("editor.action.triggerParameterHints") +def show: user.vscode("editor.action.revealDefinition") +definition peek: user.vscode("editor.action.peekDefinition") +definition side: user.vscode("editor.action.revealDefinitionAside") +references show: user.vscode("editor.action.goToReferences") +ref show: user.vscode("references-view.find") +format that: user.vscode("editor.action.formatDocument") +format selection: user.vscode("editor.action.formatSelection") +imports fix: user.vscode("editor.action.organizeImports") +problem next: user.vscode("editor.action.marker.nextInFiles") +problem last: user.vscode("editor.action.marker.prevInFiles") +problem fix: user.vscode("problems.action.showQuickFixes") +rename that: user.vscode("editor.action.rename") +refactor that: user.vscode("editor.action.refactor") +whitespace trim: user.vscode("editor.action.trimTrailingWhitespace") +language switch: user.vscode("workbench.action.editor.changeLanguageMode") +refactor rename: user.vscode("editor.action.rename") +refactor this: user.vscode("editor.action.refactor") +ref next: + user.vscode("references-view.tree.focus") + key(down enter) +ref last: + user.vscode("references-view.tree.focus") + key(up enter) + +#code navigation +( declaration | follow): + user.vscode("editor.action.revealDefinition") +spring back: + user.vscode("workbench.action.navigateBack") +spring forward: user.vscode("workbench.action.navigateForward") + implementation: + user.vscode("editor.action.goToImplementation") + type: + user.vscode("editor.action.goToTypeDefinition") + usage: + user.vscode("references-view.find") + +# Bookmarks. Requires Bookmarks plugin + sesh []: + user.vscode("workbench.action.openRecent") + sleep(50ms) + insert(text or "") + sleep(250ms) + sesh []: + user.vscode("workbench.action.openRecent") + sleep(50ms) + insert(text or "") + key(enter) + sleep(250ms) + + marks: user.vscode("workbench.view.extension.bookmarks") +toggle mark: user.vscode("bookmarks.toggle") + next mark: user.vscode("bookmarks.jumpToNext") + last mark: user.vscode("bookmarks.jumpToPrevious") + +# Folding +fold that: user.vscode("editor.fold") +unfold that: user.vscode("editor.unfold") +fold those: user.vscode("editor.foldAllMarkerRegions") +unfold those: user.vscode("editor.unfoldRecursively") +fold all: user.vscode("editor.foldAll") +unfold all: user.vscode("editor.unfoldAll") +fold comments: user.vscode("editor.foldAllBlockComments") + +# Git / Github (not using verb-noun-adjective pattern, mirroring terminal commands.) +git branch: user.vscode("git.branchFrom") +git branch this: user.vscode("git.branch") +git checkout []: + user.vscode("git.checkout") + sleep(50ms) + insert(text or "") +git commit []: + user.vscode("git.commitStaged") + sleep(100ms) + user.insert_formatted(text or "", "CAPITALIZE_FIRST_WORD") +git commit undo: user.vscode("git.undoCommit") +git commit ammend: user.vscode("git.commitStagedAmend") +git diff: user.vscode("git.openChange") +git ignore: user.vscode("git.ignore") +git merge: user.vscode("git.merge") +git output: user.vscode("git.showOutput") +git pull: user.vscode("git.pullRebase") +git push: user.vscode("git.push") +git push focus: user.vscode("git.pushForce") +git rebase abort: user.vscode("git.rebaseAbort") +git reveal: user.vscode("git.revealInExplorer") +git revert: user.vscode("git.revertChange") +git stash: user.vscode("git.stash") +git stash pop: user.vscode("git.stashPop") +git status: user.vscode("workbench.scm.focus") +git stage: user.vscode("git.stage") +git stage all: user.vscode("git.stageAll") +git unstage: user.vscode("git.unstage") +git unstage all: user.vscode("git.unstageAll") +pull request: user.vscode("pr.create") +change next: key(alt-f5) +change last: key(shift-alt-f5) + +#Debugging +break point: user.vscode("editor.debug.action.toggleBreakpoint") +step over: user.vscode("workbench.action.debug.stepOver") +debug step into: user.vscode("workbench.action.debug.stepInto") +debug step out [of]: user.vscode("workbench.action.debug.stepOut") +debug start: user.vscode("workbench.action.debug.start") +debug pause: user.vscode("workbench.action.debug.pause") +debug stopper: user.vscode("workbench.action.debug.stop") +debug continue: user.vscode("workbench.action.debug.continue") +debug restart: user.vscode("workbench.action.debug.restart") +debug console: user.vscode("workbench.debug.action.toggleRepl") + +# Terminal +term external: user.vscode("workbench.action.terminal.openNativeConsole") +term new: user.vscode("workbench.action.terminal.new") +term next: user.vscode("workbench.action.terminal.focusNext") +term last:user.vscode("workbench.action.terminal.focusPrevious") +term split: user.vscode("workbench.action.terminal.split") +term zoom: user.vscode("workbench.action.toggleMaximizedPanel") +term trash: user.vscode("workbench.action.terminal.kill") +term dog: user.vscode_and_wait("workbench.action.terminal.toggleTerminal") +term scroll up: user.vscode("workbench.action.terminal.scrollUp") +term scroll down: user.vscode("workbench.action.terminal.scrollDown") +term : user.vscode_terminal(number_small) + +#TODO: should this be added to linecommands? +copy line down: user.vscode("editor.action.copyLinesDownAction") +copy line up: user.vscode("editor.action.copyLinesUpAction") + +#Expand/Shrink AST Selection + less: user.vscode("editor.action.smartSelect.shrink") + (more|this): user.vscode("editor.action.smartSelect.expand") + +minimap: user.vscode("editor.action.toggleMinimap") +maximize: user.vscode("workbench.action.minimizeOtherEditors") +restore: user.vscode("workbench.action.evenEditorWidths") + +replace here: + user.replace("") + key(cmd-alt-l) + +hover show: user.vscode("editor.action.showHover") + +edit last: user.vscode("editsHistory.moveCursorToPreviousEdit") +edit next: user.vscode("editsHistory.moveCursorToNextEdit") +edit add: user.vscode("editsHistory.createEditAtCursor") +edit last here: user.vscode("editsHistory.moveCursorToPreviousEditInSameFile") +edit next here: user.vscode("editsHistory.moveCursorToNextEditInSameFile") + +join lines: user.vscode("editor.action.joinLines") + +commode: + user.vscode("vscode-neovim.escape") + sleep(25ms) + +insert: + key(i) + sleep(25ms) + +replace smart: + key(:) + sleep(50ms) + key(S) + key(/) + +swap this: user.vscode("extension.swap") + +full screen: user.vscode("workbench.action.toggleFullScreen") +reload window: user.vscode("workbench.action.reloadWindow") +close window: user.vscode("workbench.action.closeWindow") + +curse undo: user.vscode("cursorUndo") + + word: user.vscode("editor.action.addSelectionToNextFindMatch") +skip word: user.vscode("editor.action.moveSelectionToNextFindMatch") + +Github open: user.vscode("openInGithub.openInGitHubFile") + +stage on: + user.vscode_and_wait("git.stage") + key(cmd-w) + user.vscode_and_wait("workbench.scm.focus") + key(down:100) + sleep(100ms) + key(enter) + +# jupyter +cell next: user.vscode("jupyter.gotoNextCellInFile") +cell last: user.vscode("jupyter.gotoPrevCellInFile") +cell run above: user.vscode("jupyter.runallcellsabove.palette") +cell run: user.vscode("jupyter.runcurrentcell") + +jest: key(ctrl-space) +yes: + sleep(100ms) + key(tab) + +install local: user.vscode("workbench.extensions.action.installVSIX") \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/web/github.talon b/talon/community/community-cursorless-0.4.0/apps/web/github.talon new file mode 100644 index 0000000..e254513 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/web/github.talon @@ -0,0 +1,57 @@ +# https://help.github.com/en/github/getting-started-with-github/keyboard-shortcuts +tag: browser +win.title: /github.com/ +- + +# site wide shortcuts +focus search: key(s) +go to notifications: insert("gn") +go to dashboard: insert("gd") +show keyboard shortcuts: key(?) +move selection down: key(j) +move selection up: key(k) +toggle selection: key(x) +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") + +# network graph + +# source code browsing +find file: key(t) +jump to line: key(l) +switch (branch|tag): key(w) +expand url: key(y) +(show|hide) [all] in line notes: key(i) + +# issues +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) +submit comment: key(ctrl-enter) +preview comment: key(ctrl-shift-p) +#go fullscreen: key(ctrl-shift-l) +git hub full screen: key(ctrl-shift-l) + +# browsing commit +#submit comment: key(ctrl-enter) +close form: key(escape) +parent commit: key(p) +other parent commit: key(o) + +# commit list +#expand url: key(y) + +# notifications +mark as read: key(y) +mute thread: key(shift-m) + +# pull request list +open issue: key(o) diff --git a/talon/user/community/apps/gitlab/gitlab.talon b/talon/community/community-cursorless-0.4.0/apps/web/gitlab.talon similarity index 87% rename from talon/user/community/apps/gitlab/gitlab.talon rename to talon/community/community-cursorless-0.4.0/apps/web/gitlab.talon index a138b53..950f05f 100644 --- a/talon/user/community/apps/gitlab/gitlab.talon +++ b/talon/community/community-cursorless-0.4.0/apps/web/gitlab.talon @@ -1,7 +1,8 @@ # Shortcuts taken from: https://docs.gitlab.com/ee/user/shortcuts.html # + tag: browser -browser.host: /gitlab\.com/ +win.title: /gitlab.com/ #win.title: /GitLab/ - @@ -16,7 +17,7 @@ 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) +(show|hide) performance bar: key(p) edit last comment: key(1) toggle mark down [preview]: key(ctrl-shift-p) @@ -27,9 +28,9 @@ 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 (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") diff --git a/talon/community/community-cursorless-0.4.0/apps/web/gmail.talon b/talon/community/community-cursorless-0.4.0/apps/web/gmail.talon new file mode 100644 index 0000000..983ffca --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/web/gmail.talon @@ -0,0 +1,12 @@ +tag: browser +win.title: /gmail.com/ +- + +show inbox: key(escape i g i escape) +email sign off: + edit.line_end() + key(enter:2) + insert("Best,") + key(enter) + insert("Pokey") +forward this: key(escape i f) \ No newline at end of file diff --git a/talon/user/community/apps/outlook/outlook_web.talon b/talon/community/community-cursorless-0.4.0/apps/web/outlook.talon similarity index 73% rename from talon/user/community/apps/outlook/outlook_web.talon rename to talon/community/community-cursorless-0.4.0/apps/web/outlook.talon index b8d9ce3..92bf15d 100644 --- a/talon/user/community/apps/outlook/outlook_web.talon +++ b/talon/community/community-cursorless-0.4.0/apps/web/outlook.talon @@ -1,6 +1,7 @@ # https://support.office.com/en-us/article/keyboard-shortcuts-for-outlook-3cdeb221-7ae5-4c1d-8c1d-9e63216c1efd#PickTab=Web # the shortcuts below our based half of the bill in short cut menu, but the # link above has significantly more that could so be added + os: linux tag: browser win.title: /Outlook/ @@ -17,7 +18,7 @@ discard [draft]: key(esc) insert [a] [hyper] link: key(ctrl-k) # email list -(select | unselect) [this] message: key(ctrl-space) +(select|unselect) [this] message: key(ctrl-space) select all [messages]: key(ctrl-a) clear all [messages]: key(esc) select first [message]: key(home) @@ -27,11 +28,11 @@ select last [message]: key(and) open [this] message: key(o) open [this] message [in] [a] new window: key(shift-enter) close [this] message: key(esc) -[open] [the] next (item | message): key(ctrl-.) -[open] [the] (prev | previous) item: key(ctrl-,) -next reading [pane] (item | message): key(.) -(prev | previous) [pane] (item | message): key(,) -(expand | collapse) [conversation]: key(x) +[open] [the] next (item|message): key(ctrl-.) +[open] [the] (prev|previous) item: key(ctrl-,) +next reading [pane] (item|message): key(.) +(prev|previous) [pane] (item|message): key(,) +(expand|collapse) [conversation]: key(x) # go to go [to] mail: key(ctrl-shift-1) @@ -53,11 +54,11 @@ show help: key(?) # email actions undo [last] [action]: key(ctrl-z) delete [this] [message]: key(delete) -(perm | permanently) delete [this] [message]: key(shift+delete) +(perm|permanently) delete [this] [message]: key(shift+delete) new folder: key(shift-e) -mark [this] [(item | message)] as read: key(q) -mark [this] [(item | message)] as unread: key(u) -flag [this] [(item | message)]: key(insert) +mark [this] [(item|message)] as read: key(q) +mark [this] [(item|message)] as unread: key(u) +flag [this] [(item|message)]: key(insert) archive: key(e) mark [this] [message] [as] junk: key(j) moved to [a] folder: key(v) diff --git a/talon/user/community/apps/protonmail/protonmail.talon b/talon/community/community-cursorless-0.4.0/apps/web/protonmail.talon similarity index 62% rename from talon/user/community/apps/protonmail/protonmail.talon rename to talon/community/community-cursorless-0.4.0/apps/web/protonmail.talon index 358a0ee..e106ae1 100644 --- a/talon/user/community/apps/protonmail/protonmail.talon +++ b/talon/community/community-cursorless-0.4.0/apps/web/protonmail.talon @@ -16,35 +16,35 @@ save message: key(ctrl-s) # Mail ## Jumping -(go | jump) [to] inbox: +(go|jump) [to] inbox: key(g) key(i) -(go | jump) [to] draft: +(go|jump) [to] draft: key(g) key(d) -(go | jump) [to] sent: +(go|jump) [to] sent: key(g) key(s) -(go | jump) [to] starred: +(go|jump) [to] starred: key(g) key(.) -(go | jump) [to] archive: +(go|jump) [to] archive: key(g) key(a) -(go | jump) [to] spam: +(go|jump) [to] spam: key(g) key(x) -(go | jump) [to] trash: +(go|jump) [to] trash: key(g) key(t) ## Navigation -(prev | previous) message: key(up) +(prev|previous) message: key(up) next message: key(down) exit message: key(left) enter message: key(right) -(show | display) newer [message]: key(k) -(show | display) older [message]: key(j) +(show|display) newer [message]: key(k) +(show|display) older [message]: key(j) open message: key(enter) go back: key(escape) @@ -52,26 +52,26 @@ go back: key(escape) select all: key(*) key(a) -(deselect | unselect) all: +(deselect|unselect) all: key(*) key(n) -select [the] (message | conversation): key(x) +select [the] (message|conversation): key(x) mark [as] read: key(r) mark [as] unread: key(u) -star (message | conversation): key(.) +star (message|conversation): key(.) move to inbox: key(i) move to trash: key(t) move to archive: key(a) move to spam: key(s) ## Actions -reply to (message | conversation): key(shift-r) -reply all [to] (message | conversation): key(shift-a) -forward (message | conversation): key(shift-f) +reply to (message|conversation): key(shift-r) +reply all [to] (message|conversation): key(shift-a) +forward (message|conversation): key(shift-f) # Contacts ## Contact List -(prev | previous) contact: key(up) +(prev|previous) contact: key(up) next contact: key(down) enter contact: key(right) delete contact: key(t) diff --git a/talon/user/community/apps/twitter/twitter.talon b/talon/community/community-cursorless-0.4.0/apps/web/twitter.talon similarity index 91% rename from talon/user/community/apps/twitter/twitter.talon rename to talon/community/community-cursorless-0.4.0/apps/web/twitter.talon index 4e7feb6..2bae5b6 100644 --- a/talon/user/community/apps/twitter/twitter.talon +++ b/talon/community/community-cursorless-0.4.0/apps/web/twitter.talon @@ -1,10 +1,10 @@ tag: browser -browser.host: twitter.com +win.title: /twitter.com/ #win.title: /Twitter/ - # navigation -(show shortcuts | shortcuts help): key(?) +(show shortcuts|shortcuts help): key(?) next tweet: key(j) previous tweet: key(k) page down: key(space) diff --git a/talon/community/community-cursorless-0.4.0/apps/web/window_titles.md b/talon/community/community-cursorless-0.4.0/apps/web/window_titles.md new file mode 100644 index 0000000..c1e8cdb --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/web/window_titles.md @@ -0,0 +1,9 @@ +These talon files mostly match based on the url of the target site, however by +default talon cannot find these URLs. To get the URL listed in the tab name, +and thus the win.title, you need to use a plug in: +Firefox: https://addons.mozilla.org/en-US/firefox/addon/domain-in-title/ +Chrome: https://chrome.google.com/webstore/detail/url-in-title/ignpacbgnbnkaiooknalneoeladjnfgb?hl=en + +Alternatively if you are comfortable with using a name that's possibly more +prone to false positives, you can optionally use the commented out name-based +match that is present in many of the files. diff --git a/talon/community/community-cursorless-0.4.0/apps/win/app.talon b/talon/community/community-cursorless-0.4.0/apps/win/app.talon new file mode 100644 index 0000000..564736e --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/win/app.talon @@ -0,0 +1,42 @@ +os: windows +os: linux +- +#app.preferences() + +action(app.tab_close): + key(ctrl-w) + +#action(app.tab_detach): +# Move the current tab to a new window + +action(app.tab_next): + key(ctrl-tab) + +action(app.tab_open): + key(ctrl-t) + +action(app.tab_previous): + key(ctrl-shift-tab) + +action(app.tab_reopen): + key(ctrl-shift-t) + +action(app.window_close): + key(alt-f4) + +action(app.window_hide): + key(alt-space n) + +action(app.window_hide_others): + key(win-d alt-tab) + +#requires easy window switcher or equivalent (built into most Linux) +action(app.window_next): + key(alt-`) + +action(app.window_open): + key(ctrl-n) + +#requires easy window switcher or equivalent (built into most Linux) +action(app.window_previous): + key(alt-shift-`) diff --git a/talon/user/community/apps/windows_command_processor/command_processor_win.py b/talon/community/community-cursorless-0.4.0/apps/win/command_processor/command_processor.py similarity index 78% rename from talon/user/community/apps/windows_command_processor/command_processor_win.py rename to talon/community/community-cursorless-0.4.0/apps/win/command_processor/command_processor.py index 389178c..383217c 100644 --- a/talon/user/community/apps/windows_command_processor/command_processor_win.py +++ b/talon/community/community-cursorless-0.4.0/apps/win/command_processor/command_processor.py @@ -1,11 +1,11 @@ -import os +from talon import Context, Module, actions, imgui, settings, ui, app -from talon import Context, actions, ui +import os ctx = Context() ctx.matches = r""" app: windows_command_processor -app: windows_terminal +app: windows_terminal and win.title: /Command Prompt/ """ @@ -13,26 +13,9 @@ user_path = os.path.expanduser("~") directories_to_remap = {} directories_to_exclude = {} -ctx.tags = ["user.file_manager", "user.git", "user.kubectl", "terminal"] - - -@ctx.action_class("edit") -class EditActions: - def delete_line(): - actions.key("esc") - @ctx.action_class("user") -class UserActions: - def file_manager_refresh_title(): - actions.insert("title Command Prompt: %CD%") - actions.key("enter") - - def file_manager_open_parent(): - actions.insert("cd ..") - actions.key("enter") - actions.user.file_manager_refresh_title() - +class user_actions: def file_manager_current_path(): path = ui.active_window().title path = path.replace("Administrator: ", "").replace("Command Prompt: ", "") @@ -54,17 +37,17 @@ class UserActions: def file_manager_open_directory(path: str): """opens the directory that's already visible in the view""" - actions.insert(f'cd "{path}"') + actions.insert('cd "{}"'.format(path)) actions.key("enter") actions.user.file_manager_refresh_title() def file_manager_select_directory(path: str): """selects the directory""" - actions.insert(f'"{path}"') + actions.insert('"{}"'.format(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.insert(f'mkdir "{name}"') + actions.insert('mkdir "{}"'.format(name)) def file_manager_open_file(path: str): """opens the file""" @@ -89,7 +72,7 @@ class UserActions: actions.key("enter") def terminal_change_directory(path: str): - actions.insert(f"cd {path}") + actions.insert("cd {}".format(path)) # if path: # actions.key("enter") diff --git a/talon/community/community-cursorless-0.4.0/apps/win/command_processor/command_processor.talon b/talon/community/community-cursorless-0.4.0/apps/win/command_processor/command_processor.talon new file mode 100644 index 0000000..95c63e9 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/win/command_processor/command_processor.talon @@ -0,0 +1,27 @@ +app: windows_command_processor +app: windows_terminal +and win.title: /Command Prompt/ +- +# comment or remove tags for command sets you don't want +tag(): user.file_manager +tag(): user.generic_terminal +tag(): user.git +tag(): user.kubectl +tag(): terminal + +action(user.file_manager_refresh_title): + insert("title Command Prompt: %CD%") + key(enter) + +#action(user.file_manager_go_back): +# key("alt-left") + +#action(user.file_manager_go_forward): +# key("alt-right") + +action(user.file_manager_open_parent): + insert("cd ..") + key(enter) + user.file_manager_refresh_title() + +action(edit.delete_line): key(esc) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/win/edit.talon b/talon/community/community-cursorless-0.4.0/apps/win/edit.talon new file mode 100644 index 0000000..fe341ef --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/win/edit.talon @@ -0,0 +1,205 @@ +os: windows +os: linux +- +action(edit.copy): + key(ctrl-c) + +action(edit.cut): + key(ctrl-x) + +action(edit.delete): + key(backspace) + +action(edit.delete_line): + edit.select_line() + edit.delete() + +#action(edit.delete_paragraph): + +#action(edit.delete_sentence): + +action(edit.delete_word): + edit.select_word() + edit.delete() + +action(edit.down): + key(down) + +#action(edit.extend_again): + +#action(edit.extend_column): + +action(edit.extend_down): + key(shift-down) + +action(edit.extend_file_end): + key(shift-ctrl-end) + +action(edit.extend_file_start): + key(shift-ctrl-home) + +action(edit.extend_left): + key(shift-left) + +#action(edit.extend_line): + +action(edit.extend_line_down): + key(shift-down) + +action(edit.extend_line_end): + key(shift-end) + +action(edit.extend_line_start): + key(shift-home) + +action(edit.extend_line_up): + key(shift-up) + +action(edit.extend_page_down): + key(shift-pagedown) + +action(edit.extend_page_up): + key(shift-pageup) + +#action(edit.extend_paragraph_end): +#action(edit.extend_paragraph_next()): +#action(edit.extend_paragraph_previous()): +#action(edit.extend_paragraph_start()): + +action(edit.extend_right): + key(shift-right) + +#action(edit.extend_sentence_end): +#action(edit.extend_sentence_next): +#action(edit.extend_sentence_previous): +#action(edit.extend_sentence_start): + +action(edit.extend_up): + key(shift-up) + +action(edit.extend_word_left): + key(ctrl-shift-left) + +action(edit.extend_word_right): + key(ctrl-shift-right) + +action(edit.file_end): + key(ctrl-end) + +action(edit.file_start): + key(ctrl-home) + +action(edit.find): + key(ctrl-f) + actions.insert(text) + +action(edit.find_next): + key(f3) +#action(edit.find_previous): + +action(edit.indent_less): + key(home delete) + +action(edit.indent_more): + key(home tab) + +#action(edit.jump_column(n: int) +#action(edit.jump_line(n: int) + +action(edit.left): + key(left) + +action(edit.line_down): + key(down home) + +action(edit.line_end): + key(end) + +action(edit.line_insert_down): + key(end enter) + +action(edit.line_insert_up): + key(home enter up) + +action(edit.line_start): + key(home) + +action(edit.line_up): + key(up home) + +#action(edit.move_again): + +action(edit.page_down): + key(pagedown) + +action(edit.page_up): + key(pageup) + +#action(edit.paragraph_end): +#action(edit.paragraph_next): +#action(edit.paragraph_previous): +#action(edit.paragraph_start): + +action(edit.paste): + key(ctrl-v) + +#action(paste_match_style): + +action(edit.print): + key(ctrl-p) + +action(edit.redo): + key(ctrl-y) + +action(edit.right): + key(right) + +action(edit.save): + key(ctrl-s) + +action(edit.save_all): + key(ctrl-shift-s) + +action(edit.select_all): + key(ctrl-a) + +action(edit.select_line): + key(end shift-home) + +#action(edit.select_lines(a: int, b: int)): + +action(edit.select_none): + key(right) + +#action(edit.select_paragraph): +#action(edit.select_sentence): + +action(edit.select_word): + key(ctrl-left ctrl-shift-right) + +#action(edit.selected_text): -> str +#action(edit.sentence_end): +#action(edit.sentence_next): +#action(edit.sentence_previous): +#action(edit.sentence_start): + +action(edit.undo): + key(ctrl-z) + +action(edit.up): + key(up) + +action(edit.word_left): + key(ctrl-left) + +action(edit.word_right): + key(ctrl-right) + +action(edit.zoom_in): + key(ctrl-+) + +action(edit.zoom_out): + key(ctrl--) + +action(edit.zoom_reset): + key(ctrl-0) diff --git a/talon/user/community/apps/windows_explorer/windows_explorer.py b/talon/community/community-cursorless-0.4.0/apps/win/explorer/explorer.py similarity index 92% rename from talon/user/community/apps/windows_explorer/windows_explorer.py rename to talon/community/community-cursorless-0.4.0/apps/win/explorer/explorer.py index 88cd59b..2d75b50 100644 --- a/talon/user/community/apps/windows_explorer/windows_explorer.py +++ b/talon/community/community-cursorless-0.4.0/apps/win/explorer/explorer.py @@ -1,143 +1,139 @@ -import os - -from talon import Context, Module, actions, app, ui - -mod = Module() -apps = mod.apps - -apps.windows_explorer = r""" -os: windows -and app.name: Windows Explorer -os: windows -and app.name: Windows-Explorer -os: windows -and app.exe: /^explorer\.exe$/i -""" - -# many commands should work in most save/open dialog. -# note the "show options" stuff won't work unless work -# unless the path is displayed in the title, which is rare for those -apps.windows_file_browser = """ -os: windows -and app.name: /.*/ -and title: /(Save|Open|Browse|Select)/ -""" - -ctx = Context() -ctx.matches = r""" -app: windows_explorer -app: windows_file_browser -""" - -user_path = os.path.expanduser("~") -directories_to_remap = {} -directories_to_exclude = {} - -if app.platform == "windows": - is_windows = True - import ctypes - - GetUserNameEx = ctypes.windll.secur32.GetUserNameExW - NameDisplay = 3 - - size = ctypes.pointer(ctypes.c_ulong(0)) - GetUserNameEx(NameDisplay, None, size) - - nameBuffer = ctypes.create_unicode_buffer(size.contents.value) - GetUserNameEx(NameDisplay, nameBuffer, size) - one_drive_path = os.path.expanduser(os.path.join("~", "OneDrive")) - - # this is probably not the correct way to check for onedrive, quick and dirty - if os.path.isdir(os.path.expanduser(os.path.join("~", r"OneDrive\Desktop"))): - directories_to_remap = { - "Desktop": os.path.join(one_drive_path, "Desktop"), - "Documents": os.path.join(one_drive_path, "Documents"), - "Downloads": os.path.join(user_path, "Downloads"), - "Music": os.path.join(user_path, "Music"), - "OneDrive": one_drive_path, - "Pictures": os.path.join(one_drive_path, "Pictures"), - "Videos": os.path.join(user_path, "Videos"), - } - else: - # todo use expanduser for cross platform support - directories_to_remap = { - "Desktop": os.path.join(user_path, "Desktop"), - "Documents": os.path.join(user_path, "Documents"), - "Downloads": os.path.join(user_path, "Downloads"), - "Music": os.path.join(user_path, "Music"), - "OneDrive": one_drive_path, - "Pictures": os.path.join(user_path, "Pictures"), - "Videos": os.path.join(user_path, "Videos"), - } - - if nameBuffer.value: - directories_to_remap[nameBuffer.value] = user_path - - directories_to_exclude = [ - "", - "Run", - "Task Switching", - "Task View", - "This PC", - "File Explorer", - "Program Manager", - ] - - -@ctx.action_class("user") -class UserActions: - - def file_manager_open_parent(): - actions.key("alt-up") - - def file_manager_current_path(): - path = ui.active_window().title - - if path in directories_to_remap: - path = directories_to_remap[path] - - if path in directories_to_exclude: - actions.user.file_manager_hide_pickers() - path = "" - - return path - - def file_manager_terminal_here(): - actions.key("ctrl-l") - actions.insert("cmd.exe") - actions.key("enter") - - def file_manager_show_properties(): - """Shows the properties for the file""" - actions.key("alt-enter") - - def file_manager_open_directory(path: str): - """opens the directory that's already visible in the view""" - actions.key("ctrl-l") - 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("home") - actions.key("ctrl-shift-n") - actions.insert(name) - - def file_manager_open_file(path: str): - """opens the file""" - actions.key("home") - actions.insert(path) - actions.key("enter") - - def file_manager_select_file(path: str): - """selects the file""" - actions.key("home") - actions.insert(path) - - def file_manager_open_volume(volume: str): - """file_manager_open_volume""" - actions.user.file_manager_open_directory(volume) +from talon import Context, Module, actions, imgui, settings, ui, app + +import os + +mod = Module() +apps = mod.apps + +apps.windows_explorer = """ +os: windows +and app.name: Windows Explorer +os: windows +and app.exe: explorer.exe +""" + +# many commands should work in most save/open dialog. +# note the "show options" stuff won't work unless work +# unless the path is displayed in the title, which is rare for those +apps.windows_file_browser = """ +os: windows +and app.name: /.*/ +and title: /(Save|Open|Browse|Select)/ +""" + +ctx = Context() +ctx.matches = r""" +app: windows_explorer +app: windows_file_browser +""" + +user_path = os.path.expanduser("~") +directories_to_remap = {} +directories_to_exclude = {} + +if app.platform == "windows": + is_windows = True + import ctypes + + GetUserNameEx = ctypes.windll.secur32.GetUserNameExW + NameDisplay = 3 + + size = ctypes.pointer(ctypes.c_ulong(0)) + GetUserNameEx(NameDisplay, None, size) + + nameBuffer = ctypes.create_unicode_buffer(size.contents.value) + GetUserNameEx(NameDisplay, nameBuffer, size) + one_drive_path = os.path.expanduser(os.path.join("~", "OneDrive")) + + # this is probably not the correct way to check for onedrive, quick and dirty + if os.path.isdir(os.path.expanduser(os.path.join("~", r"OneDrive\Desktop"))): + default_folder = os.path.join("~", "Desktop") + + directories_to_remap = { + "Desktop": os.path.join(one_drive_path, "Desktop"), + "Documents": os.path.join(one_drive_path, "Documents"), + "Downloads": os.path.join(user_path, "Downloads"), + "Music": os.path.join(user_path, "Music"), + "OneDrive": one_drive_path, + "Pictures": os.path.join(one_drive_path, "Pictures"), + "Videos": os.path.join(user_path, "Videos"), + } + else: + # todo use expanduser for cross platform support + directories_to_remap = { + "Desktop": os.path.join(user_path, "Desktop"), + "Documents": os.path.join(user_path, "Documents"), + "Downloads": os.path.join(user_path, "Downloads"), + "Music": os.path.join(user_path, "Music"), + "OneDrive": one_drive_path, + "Pictures": os.path.join(user_path, "Pictures"), + "Videos": os.path.join(user_path, "Videos"), + } + + if nameBuffer.value: + directories_to_remap[nameBuffer.value] = user_path + + directories_to_exclude = [ + "", + "Run", + "Task Switching", + "Task View", + "This PC", + "File Explorer", + "Program Manager", + ] + + +@ctx.action_class("user") +class user_actions: + def file_manager_current_path(): + path = ui.active_window().title + + if path in directories_to_remap: + path = directories_to_remap[path] + + if path in directories_to_exclude: + actions.user.file_manager_hide_pickers() + path = "" + + return path + + def file_manager_terminal_here(): + actions.key("ctrl-l") + actions.insert("cmd.exe") + actions.key("enter") + + def file_manager_show_properties(): + """Shows the properties for the file""" + actions.key("alt-enter") + + def file_manager_open_directory(path: str): + """opens the directory that's already visible in the view""" + actions.key("ctrl-l") + 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("home") + actions.key("ctrl-shift-n") + actions.insert(name) + + def file_manager_open_file(path: str): + """opens the file""" + actions.key("home") + actions.insert(path) + actions.key("enter") + + def file_manager_select_file(path: str): + """selects the file""" + actions.key("home") + actions.insert(path) + + def file_manager_open_volume(volume: str): + """file_manager_open_volume""" + actions.user.file_manager_open_directory(volume) diff --git a/talon/user/community/apps/windows_explorer/windows_explorer.talon b/talon/community/community-cursorless-0.4.0/apps/win/explorer/explorer.talon similarity index 60% rename from talon/user/community/apps/windows_explorer/windows_explorer.talon rename to talon/community/community-cursorless-0.4.0/apps/win/explorer/explorer.talon index ef7e83e..37e3ff7 100644 --- a/talon/user/community/apps/windows_explorer/windows_explorer.talon +++ b/talon/community/community-cursorless-0.4.0/apps/win/explorer/explorer.talon @@ -2,6 +2,13 @@ app: windows_explorer app: windows_file_browser - tag(): user.file_manager +action(user.file_manager_go_back): + key("alt-left") +action(user.file_manager_go_forward): + key("alt-right") +action(user.file_manager_open_parent): + key("alt-up") + ^go $: user.file_manager_open_volume("{letter}:") go app data: user.file_manager_open_directory("%AppData%") go program files: user.file_manager_open_directory("%programfiles%") diff --git a/talon/user/community/apps/mintty/mintty_win.py b/talon/community/community-cursorless-0.4.0/apps/win/mintty/mintty.py similarity index 75% rename from talon/user/community/apps/mintty/mintty_win.py rename to talon/community/community-cursorless-0.4.0/apps/win/mintty/mintty.py index 783a30a..8ac393a 100644 --- a/talon/user/community/apps/mintty/mintty_win.py +++ b/talon/community/community-cursorless-0.4.0/apps/win/mintty/mintty.py @@ -1,144 +1,115 @@ -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") +from talon import Context, Module, actions, imgui, settings, ui +import os +import subprocess + +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 +""" +directories_to_remap = {} +directories_to_exclude = {} + +setting_cyg_path = mod.setting( + "cygpath", + type=str, + default="C:\\cygwin64\\bin\\cygpath.exe", + desc="Path to cygpath.exe", +) + + +def get_win_path(cyg_path): + path = "" + try: + path = ( + subprocess.check_output([setting_cyg_path.get(), "-w", cyg_path]) + .strip(b"\n") + .decode() + ) + except: + path = "" + return path + + +@ctx.action_class("user") +class user_actions: + 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 = '"{}"'.format(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 = '"{}"'.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 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("cd {}".format(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") diff --git a/talon/community/community-cursorless-0.4.0/apps/win/mintty/mintty.talon b/talon/community/community-cursorless-0.4.0/apps/win/mintty/mintty.talon new file mode 100644 index 0000000..3ce1e31 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/win/mintty/mintty.talon @@ -0,0 +1,17 @@ +app: mintty +- +tag(): terminal +tag(): user.file_manager +tag(): user.generic_terminal +tag(): user.git +tag(): user.kubectl + +action(user.file_manager_open_parent): + insert("cd ..") + key(enter) + +action(edit.paste): key(shift-insert) +action(edit.copy): key(ctrl-insert) + +action(edit.delete_line): key(ctrl-u) + diff --git a/talon/user/community/apps/notepad++/notepad++_win.py b/talon/community/community-cursorless-0.4.0/apps/win/notepad++/notepad++.py similarity index 73% rename from talon/user/community/apps/notepad++/notepad++_win.py rename to talon/community/community-cursorless-0.4.0/apps/win/notepad++/notepad++.py index 78aa4d4..10a3aaa 100644 --- a/talon/user/community/apps/notepad++/notepad++_win.py +++ b/talon/community/community-cursorless-0.4.0/apps/win/notepad++/notepad++.py @@ -1,68 +1,20 @@ -from talon import Context, Module, actions +from talon import Context, actions, ui, Module mod = Module() ctx = Context() apps = mod.apps -apps.notepad_plus_plus = r""" +apps.notepad_plus_plus = """ os: windows and app.name: Notepad++ : a free (GNU) source code editor os: windows -and app.name: Notepad++ : a free (GPL) source code editor -os: windows -and app.exe: /^notepad\+\+\.exe$/i +and app.name: notepad++.exe """ ctx.matches = r""" app: notepad_plus_plus """ -ctx.tags = ["user.find_and_replace", "user.line_commands", "user.tabs"] - - -@ctx.action_class("app") -class AppActions: - def tab_previous(): - actions.key("ctrl-pageup") - - def tab_next(): - actions.key("ctrl-pagedown") - - -@ctx.action_class("code") -class CodeActions: - def toggle_comment(): - actions.key("ctrl-q") - - -@ctx.action_class("edit") -class EditActions: - def line_clone(): - actions.key("ctrl-d") - - def line_swap_up(): - actions.key("ctrl-shift-up") - - def line_swap_down(): - actions.key("ctrl-shift-down") - - def indent_more(): - actions.key("tab") - - def indent_less(): - actions.key("shift-tab") - - def jump_line(n: int): - actions.key("ctrl-g") - actions.insert(str(n)) - actions.key("enter") - - def find_next(): - actions.key("enter") - - def find_previous(): - actions.key("shift-enter") - @ctx.action_class("win") class win_actions: @@ -75,8 +27,20 @@ class win_actions: return "" +@ctx.action_class("edit") +class edit_actions: + def jump_line(n: int): + actions.key("ctrl-g") + actions.insert(str(n)) + actions.key("enter") + + def find(text: str): + actions.key("ctrl-f") + actions.insert(text) + + @ctx.action_class("user") -class UserActions: +class user_actions: def select_next_occurrence(text: str): actions.edit.find(text) actions.sleep("100ms") @@ -91,7 +55,7 @@ class UserActions: def tab_jump(number: int): if number < 10: - actions.key(f"ctrl-keypad_{number}") + actions.key("ctrl-keypad_{}".format(number)) def tab_final(): """Jumps to the final tab""" @@ -100,6 +64,19 @@ class UserActions: # find_and_replace.py support begin + def find(text: str): + """Triggers find in current editor""" + actions.key("ctrl-f") + + if text: + actions.insert(text) + + def find_next(): + actions.key("enter") + + def find_previous(): + actions.key("shift-enter") + def find_everywhere(text: str): """Triggers find across project""" @@ -143,3 +120,4 @@ class UserActions: actions.key("alt-a") # find_and_replace.py support end + diff --git a/talon/community/community-cursorless-0.4.0/apps/win/notepad++/notepad++.talon b/talon/community/community-cursorless-0.4.0/apps/win/notepad++/notepad++.talon new file mode 100644 index 0000000..f12ab63 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/win/notepad++/notepad++.talon @@ -0,0 +1,19 @@ +app: notepad_plus_plus +- +tag(): user.find_and_replace +tag(): user.line_commands +tag(): user.tabs +action(app.tab_previous): + key(ctrl-pageup) +action(app.tab_next): + key(ctrl-pagedown) +action(code.toggle_comment): + key(ctrl-q) +action(edit.line_clone): + key(ctrl-d) +action(edit.line_swap_up): + key(ctrl-shift-up) +action(edit.line_swap_down): + key(ctrl-shift-down) +action(edit.indent_more): key(tab) +action(edit.indent_less): key(shift-tab) \ No newline at end of file diff --git a/talon/user/community/apps/outlook/outlook_win.talon b/talon/community/community-cursorless-0.4.0/apps/win/outlook/outlook.talon similarity index 85% rename from talon/user/community/apps/outlook/outlook_win.talon rename to talon/community/community-cursorless-0.4.0/apps/win/outlook/outlook.talon index 295ebac..746e60c 100644 --- a/talon/user/community/apps/outlook/outlook_win.talon +++ b/talon/community/community-cursorless-0.4.0/apps/win/outlook/outlook.talon @@ -8,4 +8,4 @@ inbox: key(ctrl-1) Reply: key(ctrl-r) Reply all: key(ctrl-shift-r) Forward: key(ctrl-f) -accept: key(shift-f10 c c enter) +accept: key(shift-f10 c c enter) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/apps/win/powershell/power_shell.py b/talon/community/community-cursorless-0.4.0/apps/win/powershell/power_shell.py new file mode 100644 index 0000000..cbd17e4 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/win/powershell/power_shell.py @@ -0,0 +1,87 @@ +from talon import Context, Module, actions, imgui, settings, ui, app + +import os + +ctx = Context() +mod = Module() +ctx.matches = r""" +app: windows_power_shell +app: windows_terminal +and win.title: /PowerShell/ +""" + +user_path = os.path.expanduser("~") +directories_to_remap = {} +directories_to_exclude = {} + + +@ctx.action_class("user") +class user_actions: + def file_manager_current_path(): + path = ui.active_window().title + path = path.replace("Administrator: ", "").replace("Windows PowerShell: ", "") + + if path in directories_to_remap: + path = directories_to_remap[path] + + if path in directories_to_exclude: + path = "" + return path + + def file_manager_open_directory(path: str): + """opens the directory that's already visible in the view""" + actions.insert('cd "{}"'.format(path)) + actions.key("enter") + actions.user.file_manager_refresh_title() + + def file_manager_select_directory(path: str): + """selects the directory""" + actions.insert('"{}"'.format(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.insert('mkdir "{}"'.format(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 -force") + actions.key("enter") + + def terminal_change_directory(path: str): + actions.insert("cd {}".format(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.insert("cls") + actions.key("enter") + + def terminal_run_last(): + actions.key("up enter") + + def terminal_kill_all(): + actions.key("ctrl-c") + actions.insert("y") + actions.key("enter") diff --git a/talon/community/community-cursorless-0.4.0/apps/win/powershell/power_shell.talon b/talon/community/community-cursorless-0.4.0/apps/win/powershell/power_shell.talon new file mode 100644 index 0000000..b9f3e72 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/win/powershell/power_shell.talon @@ -0,0 +1,26 @@ +app: windows_power_shell +app: windows_terminal +and win.title: /PowerShell/ +- +tag(): user.file_manager +tag(): user.git +tag(): user.generic_terminal +tag(): user.kubectl +tag(): terminal + +action(user.file_manager_refresh_title): + insert("$Host.UI.RawUI.WindowTitle = 'Windows PowerShell: ' + $(get-location)") + key(enter) + +#action(user.file_manager_go_back): +# key("alt-left") + +#action(user.file_manager_go_forward): +# key("alt-right") + +action(user.file_manager_open_parent): + insert("cd ..") + key(enter) + user.file_manager_refresh_title() + +action(edit.delete_line): key(esc) \ No newline at end of file diff --git a/talon/user/community/apps/powershell/powershell_win.py b/talon/community/community-cursorless-0.4.0/apps/win/windows_terminal/windows_terminal.py similarity index 62% rename from talon/user/community/apps/powershell/powershell_win.py rename to talon/community/community-cursorless-0.4.0/apps/win/windows_terminal/windows_terminal.py index a592e58..93b9698 100644 --- a/talon/user/community/apps/powershell/powershell_win.py +++ b/talon/community/community-cursorless-0.4.0/apps/win/windows_terminal/windows_terminal.py @@ -1,39 +1,27 @@ -from talon import Context, Module, actions, ui +from talon import Context, Module, actions, imgui, settings, ui, app + +import os ctx = Context() mod = Module() ctx.matches = r""" -app: windows_power_shell app: windows_terminal -and win.title: /PowerShell/ """ +user_path = os.path.expanduser("~") directories_to_remap = {} directories_to_exclude = {} -@ctx.action_class("edit") -class EditActions: - def delete_line(): - actions.key("esc") - - @ctx.action_class("user") -class UserActions: - def file_manager_refresh_title(): - actions.insert( - "$Host.UI.RawUI.WindowTitle = 'Windows PowerShell: ' + $(get-location)" - ) - actions.key("enter") - - def file_manager_open_parent(): - actions.insert("cd ..") - actions.key("enter") - actions.user.file_manager_refresh_title() - +class user_actions: def file_manager_current_path(): path = ui.active_window().title - path = path.replace("Administrator: ", "").replace("Windows PowerShell: ", "") + path = ( + path.replace("Administrator: ", "") + .replace("Windows PowerShell: ", "") + .replace("Command Prompt: ", "") + ) if path in directories_to_remap: path = directories_to_remap[path] @@ -42,19 +30,28 @@ class UserActions: path = "" return path + # def file_manager_terminal_here(): + # actions.key("ctrl-l") + # actions.insert("cmd.exe") + # actions.key("enter") + + # def file_manager_show_properties(): + # """Shows the properties for the file""" + # actions.key("alt-enter") + def file_manager_open_directory(path: str): """opens the directory that's already visible in the view""" - actions.insert(f'cd "{path}"') + actions.insert('cd "{}"'.format(path)) actions.key("enter") actions.user.file_manager_refresh_title() def file_manager_select_directory(path: str): """selects the directory""" - actions.insert(f'"{path}"') + actions.insert('"{}"'.format(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.insert(f'mkdir "{name}"') + actions.insert('mkdir "{}"'.format(name)) def file_manager_open_file(path: str): """opens the file""" @@ -68,3 +65,8 @@ class UserActions: def file_manager_open_volume(volume: str): """file_manager_open_volume""" actions.user.file_manager_open_directory(volume) + actions.user.file_manager_refresh_title() + + def tab_jump(number: int): + actions.key("ctrl-alt-{}".format(number)) + diff --git a/talon/community/community-cursorless-0.4.0/apps/win/windows_terminal/windows_terminal.talon b/talon/community/community-cursorless-0.4.0/apps/win/windows_terminal/windows_terminal.talon new file mode 100644 index 0000000..19aefe1 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/win/windows_terminal/windows_terminal.talon @@ -0,0 +1,30 @@ +app: windows_terminal +- +tag(): user.tabs +tag(): terminal + +# commands that work regardless of active terminal should go here. + +# windows terminal config based on mrob95's +# https://github.com/mrob95/WindowsTerminal-config/blob/master/settings.json + +action(edit.paste): key(ctrl-shift-v) +action(edit.copy): key(ctrl-shift-c) +action(app.tab_close): key(ctrl-shift-w) +action(app.tab_open): key(ctrl-shift-t) + +settings open : key(ctrl-,) +focus left: key(ctrl-alt-shift-left) +focus right: key(ctrl-alt-shift-right) +focus up: key(ctrl-alt-shift-up) +focus down: key(ctrl-alt-shift-down) +split right: key(ctrl-shift-h) +split down: key(ctrl-h) +term menu: key(ctrl-shift-f1) + +run last: key(up enter) +kill all: + key(ctrl-c) + insert("y") + key(enter) + diff --git a/talon/community/community-cursorless-0.4.0/apps/win/wsl/ubuntu.talon b/talon/community/community-cursorless-0.4.0/apps/win/wsl/ubuntu.talon new file mode 100644 index 0000000..2405d6d --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/win/wsl/ubuntu.talon @@ -0,0 +1,16 @@ +app: ubuntu +app: windows_terminal +and win.title: /Ubuntu/ +- +tag(): user.file_manager +tag(): user.generic_terminal +tag(): user.git +tag(): user.kubectl +tag(): terminal + +action(user.file_manager_refresh_title): skip() +action(user.file_manager_open_parent): + insert("cd ..") + key(enter) + +^go $: user.file_manager_open_volume("/mnt/{letter}") diff --git a/talon/community/community-cursorless-0.4.0/apps/win/wsl/wsl.py b/talon/community/community-cursorless-0.4.0/apps/win/wsl/wsl.py new file mode 100644 index 0000000..fc83011 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/win/wsl/wsl.py @@ -0,0 +1,205 @@ +from talon import Context, Module, actions, imgui, settings, ui, app +import os +import subprocess + +mod = Module() +mod.apps.ubuntu = """ +os: windows +and app.name: ubuntu.exe +""" + +ctx = Context() +ctx.matches = r""" +app: ubuntu +app: windows_terminal +and win.title: /Ubuntu/ +""" +directories_to_remap = {} +directories_to_exclude = {} + +user_path = os.path.expanduser("~") +if app.platform == "windows": + is_windows = True + import ctypes + + GetUserNameEx = ctypes.windll.secur32.GetUserNameExW + NameDisplay = 3 + + size = ctypes.pointer(ctypes.c_ulong(0)) + GetUserNameEx(NameDisplay, None, size) + + nameBuffer = ctypes.create_unicode_buffer(size.contents.value) + GetUserNameEx(NameDisplay, nameBuffer, size) + one_drive_path = os.path.expanduser(os.path.join("~", "OneDrive")) + + # this is probably not the correct way to check for onedrive, quick and dirty + if os.path.isdir(os.path.expanduser(os.path.join("~", r"OneDrive\Desktop"))): + default_folder = os.path.join("~", "Desktop") + + directories_to_remap = { + "Desktop": os.path.join(one_drive_path, "Desktop"), + "Documents": os.path.join(one_drive_path, "Documents"), + "Downloads": os.path.join(user_path, "Downloads"), + "Music": os.path.join(user_path, "Music"), + "OneDrive": one_drive_path, + "Pictures": os.path.join(one_drive_path, "Pictures"), + "Videos": os.path.join(user_path, "Videos"), + } + else: + # todo use expanduser for cross platform support + directories_to_remap = { + "Desktop": os.path.join(user_path, "Desktop"), + "Documents": os.path.join(user_path, "Documents"), + "Downloads": os.path.join(user_path, "Downloads"), + "Music": os.path.join(user_path, "Music"), + "OneDrive": one_drive_path, + "Pictures": os.path.join(user_path, "Pictures"), + "Videos": os.path.join(user_path, "Videos"), + } + + +def get_win_path(wsl_path): + path = "" + try: + path = ( + subprocess.check_output(["wsl", "wslpath", "-w", wsl_path]) + .strip(b"\n") + .decode() + ) + except: + path = "" + + return path + + +def get_usr_path(): + path = "" + try: + path = ( + subprocess.check_output(["wsl", "wslpath", "-a", "~"]).strip(b"\n").decode() + ) + except: + path = "" + + return path + + +def get_wsl_path(win_path): + path = "" + try: + path = ( + subprocess.check_output(["wsl", "wslpath", "-u", "'{}'".format(win_path)]) + .strip(b"\n") + .decode() + ) + except: + path = "" + + return path + + +@ctx.action_class("user") +class user_actions: + def file_manager_refresh_title(): + actions.skip() + + def file_manager_current_path(): + path = ui.active_window().title + try: + path = path.split(":")[1].lstrip() + except: + path = "" + + # print("current: " + path) + if "~" in path: + # the only way I could find to correctly support the user folder: + # get absolute path of ~, and strip /mnt/x from the string + abs_usr_path = get_usr_path() + abs_usr_path = abs_usr_path[abs_usr_path.find("/home") : len(abs_usr_path)] + path = path.replace("~", abs_usr_path) + + path = get_win_path(path) + + if path in directories_to_remap: + path = directories_to_remap[path] + + if path in directories_to_exclude: + path = "" + + return path + + # def file_manager_terminal_here(): + # actions.key("ctrl-l") + # actions.insert("cmd.exe") + # actions.key("enter") + + # def file_manager_show_properties(): + # """Shows the properties for the file""" + # actions.key("alt-enter") + def file_manager_open_user_directory(path: str): + """expands and opens the user directory""" + if path in directories_to_remap: + path = directories_to_remap[path] + + path = os.path.expanduser(os.path.join("~", path)) + if ":" in path: + path = get_wsl_path(path) + + actions.user.file_manager_open_directory(path) + + def file_manager_open_directory(path: str): + """opens the directory that's already visible in the view""" + if ":" in str(path): + path = get_wsl_path(path) + + actions.insert('cd "{}"'.format(path)) + actions.key("enter") + actions.user.file_manager_refresh_title() + + def file_manager_select_directory(path: str): + """selects the directory""" + actions.insert('"{}"'.format(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.insert('mkdir "{}"'.format(name)) + + def file_manager_open_file(path: str): + actions.insert(path) + # actions.key("enter") + + def file_manager_select_file(path: str): + actions.insert(path) + + def file_manager_open_volume(volume: str): + 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("cd {}".format(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") diff --git a/talon/community/community-cursorless-0.4.0/apps/windbg/windbg.py b/talon/community/community-cursorless-0.4.0/apps/windbg/windbg.py new file mode 100644 index 0000000..9ac44ed --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/windbg/windbg.py @@ -0,0 +1,32 @@ +from talon import Context, Module, actions, ui + +mod = Module() +ctx = Context() + +ctx.matches = r""" +mode: user.windbg +""" + +ctx.lists["self.windows_dlls"] = { + "core": "ntdll", + "en tea": "ntdll", + "user": "user32", +} + + +@mod.capture(rule="{self.windows_dlls}") +def windows_dlls(m) -> str: + "Return an register" + return m.windows_dlls + + +@ctx.action_class("user") +class user_actions: + def debugger_clear_breakpoint_id(number_small: int): + actions.insert(f"bc {number_small}\n") + + def debugger_disable_breakpoint_id(number_small: int): + actions.insert(f"bd {number_small}\n") + + def debugger_enable_breakpoint_id(number_small: int): + actions.insert(f"be {number_small}\n") diff --git a/talon/community/community-cursorless-0.4.0/apps/windbg/windbg.talon b/talon/community/community-cursorless-0.4.0/apps/windbg/windbg.talon new file mode 100644 index 0000000..2c1eaec --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/apps/windbg/windbg.talon @@ -0,0 +1,157 @@ +# XXX - trigger alt-1 to hit command window for necessary commands? +# ex: user.windbg_insert_in_cmd() +# edit.left() + +mode: user.windbg +- +tag(): user.debugger +tag(): user.windbg + +## +# Generic debugger actions +## + +# Code execution +action(user.debugger_step_into): + key(f8) +action(user.debugger_step_over): + key(f10) +# XXX - +action(user.debugger_step_line): "" +action(user.debugger_step_over_line): "" + +action(user.debugger_step_out): + key(shift-f11) +action(user.debugger_continue): + key(f5) +action(user.debugger_stop): + key(shift-f5) +action(user.debugger_restart): + key(ctrl-shift-f5) +action(user.debugger_detach): + insert(".detach") + +# Registers +action(user.debugger_show_registers): + key(r enter) +action(user.debugger_get_register): + insert("r @") +action(user.debugger_set_register): + insert("set $@=") + edit.left() + +# Breakpoints +action(user.debugger_show_breakpoints): + insert("bl\n") +action(user.debugger_add_sw_breakpoint): + insert("bp ") +action(user.debugger_add_hw_breakpoint): + insert("ba e 1 ") +action(user.debugger_break_now): + key(ctrl-break) +action(user.debugger_clear_all_breakpoints): + insert("bc *\n") +action(user.debugger_clear_breakpoint): + insert("bc ") +action(user.debugger_enable_all_breakpoints): + insert("be *\n") +action(user.debugger_enable_breakpoint): + insert("be ") +action(user.debugger_disable_all_breakpoints): + insert("bd *\n") +action(user.debugger_disable_breakpoint): + insert("bd ") + +# Navigation +action(user.debugger_goto_address): + insert("ctrl-g") +action(user.debugger_goto_clipboard): + insert("ctrl-g") + edit.paste() + key(enter) +action(user.debugger_goto_highlighted): + insert("ctrl-g") + edit.copy() + edit.paste() + key(enter) + + +# Memory inspection +action(user.debugger_backtrace): + key(k enter) +action(user.debugger_disassemble): + key(u space) +action(user.debugger_disassemble_here): + key(u enter) +action(user.debugger_disassemble_clipboard): + key(u space) + edit.paste() + key(enter) +action(user.debugger_dump_ascii_string): + insert("da ") +action(user.debugger_dump_unicode_string): + insert("du ") +action(user.debugger_dump_pointers): + insert("dps ") + +action(user.debugger_list_modules): + insert("lm\n") + + +# Registers XXX +register : + key(@) + insert("{registers}") + +# Type inspection +action(user.debugger_inspect_type): + insert("dt ") + +# Convenience +action(user.debugger_clear_line): + key("ctrl-a backspace") +## +# Windbg specific functionality +## + +open help: insert(".hh\n") + +# xxx - add window switching + +add microsoft symbols: + insert("srv*C:\\symbols*http://msdl.microsoft.com/download/symbols;\n") +force reload symbols: + insert(".reload /f\n") +reload symbols: + insert(".reload\n") +loaded modules: + insert("lm l\n") + +display pointers: + insert("dps ") + +# XXX - should be generic +dereference pointer: + insert("poi()") + edit.left() + +show version: key(ctrl-alt-w) + +## +# Windows +## + +view command: key(alt-1) +view watch: key(alt-2) +view locals: key(alt-3) +view registers: key(alt-4) +view memory: key(alt-5) +view call stack: key(alt-6) +view disassembly: key(alt-7) +view scratch pad: key(alt-8) +view (processes|threads): key(alt-9) + +# XXX - temp +dump function params: "r @rcx,@rdx,@r8,@r9\n" + +(lib|library) : "{windows_dlls}" diff --git a/talon/user/community/plugin/mouse/Resources/HiddenCursor.cur b/talon/community/community-cursorless-0.4.0/code/Resources/HiddenCursor.cur similarity index 100% rename from talon/user/community/plugin/mouse/Resources/HiddenCursor.cur rename to talon/community/community-cursorless-0.4.0/code/Resources/HiddenCursor.cur diff --git a/talon/community/community-cursorless-0.4.0/code/abbreviate.py b/talon/community/community-cursorless-0.4.0/code/abbreviate.py new file mode 100644 index 0000000..844251d --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/abbreviate.py @@ -0,0 +1,218 @@ +# XXX - would be nice to be able pipe these through formatters + +from talon import Context, Module + +mod = Module() +mod.list("abbreviation", desc="Common abbreviation") + +abbreviations = { + "address": "addr", + "administrator": "admin", + "administrators": "admins", + "advance": "adv", + "advanced": "adv", + "alberta": "ab", + "alternative": "alt", + "application": "app", + "applications": "apps", + "argument": "arg", + "arguments": "args", + "as far as i can tell": "afaict", + "as far as i know": "afaik", + "assembly": "asm", + "at the moment": "atm", + "attribute": "attr", + "attributes": "attrs", + "authenticate": "auth", + "authentication": "auth", + "away from keyboard": "afk", + "binary": "bin", + "boolean": "bool", + "british columbia": "bc", + "button": "btn", + "canada": "ca", + "centimeter": "cm", + "char": "chr", + "character": "char", + "class": "cls", + "client": "cli", + "command": "cmd", + "comment": "cmt", + "compare": "cmp", + "conference": "conf", + "config": "cfg", + "configuration": "cfg", + "context": "ctx", + "control": "ctrl", + "constant": "const", + "coordinate": "coord", + "coordinates": "coords", + "copy": "cpy", + "count": "cnt", + "counter": "ctr", + "database": "db", + "declare": "decl", + "declaration": "decl", + "decode": "dec", + "decrement": "dec", + "debug": "dbg", + "define": "def", + "definition": "def", + "description": "desc", + "develop": "dev", + "development": "dev", + "device": "dev", + "dictation": "dict", + "dictionary": "dict", + "direction": "dir", + "directory": "dir", + "distribution": "dist", + "document": "doc", + "documents": "docs", + "double": "dbl", + "dupe": "dup", + "duplicate": "dup", + "dynamic": "dyn", + "encode": "enc", + "entry": "ent", + "enumerate": "enum", + "environment": "env", + "escape": "esc", + "etcetera": "etc", + "example": "ex", + "exception": "exc", + "execute": "exec", + "expression": "exp", + "extend": "ext", + "extension": "ext", + "file system": "fs", + "framework": "fw", + "function": "func", + "funny": "lol", + "generic": "gen", + "generate": "gen", + "hypertext": "http", + "history": "hist", + "image": "img", + "import table": "iat", + "import address table": "iat", + "increment": "inc", + "information": "info", + "initialize": "init", + "initializer": "init", + "in real life": "irl", + "instance": "inst", + "integer": "int", + "interrupt": "int", + "iterate": "iter", + "java archive": "jar", + "javascript": "js", + "jason": "json", + "jump": "jmp", + "keyboard": "kbd", + "keyword arguments": "kwargs", + "keyword": "kw", + "kilogram": "kg", + "kilometer": "km", + "language": "lng", + "length": "len", + "library": "lib", + "manitoba": "mb", + "markdown": "md", + "message": "msg", + "meta sploit": "msf", + "meta sploit framework": "msf", + "microphone": "mic", + "milligram": "mg", + "millisecond": "ms", + "miscellaneous": "misc", + "module": "mod", + "mount": "mnt", + "nano second": "ns", + "neo vim": "nvim", + "new brunswick": "nb", + "nova scotia": "ns", + "number": "num", + "object": "obj", + "okay": "ok", + "ontario": "on", + "option": "opt", + "operating system": "os", + "original": "orig", + "package": "pkg", + "parameter": "param", + "parameters": "params", + "pico second": "ps", + "pixel": "px", + "point": "pt", + "pointer": "ptr", + "position": "pos", + "position independent code": "pic", + "position independent executable": "pie", + "previous": "prev", + "property": "prop", + "public": "pub", + "python": "py", + "quebec": "qc", + "query string": "qs", + "random": "rnd", + "receipt": "rcpt", + "reference": "ref", + "references": "refs", + "register": "reg", + "registery": "reg", + "regular expression": "regex", + "regular expressions": "regex", + "repel": "repl", + "represent": "repr", + "representation": "repr", + "request": "req", + "return": "ret", + "revision": "rev", + "ruby": "rb", + "saskatchewan": "sk", + "service pack": "sp", + "session id": "sid", + "shell": "sh", + "shellcode": "sc", + "source": "src", + "special": "spec", + "specific": "spec", + "specification": "spec", + "specify": "spec", + "standard in": "stdin", + "standard out": "stdout", + "standard": "std", + "string": "str", + "structure": "struct", + "synchronize": "sync", + "synchronous": "sync", + "system": "sys", + "table of contents": "toc", + "table": "tbl", + "taiwan": "tw", + "technology": "tech", + "temperature": "temp", + "temporary": "tmp", + "temp": "tmp", + "text": "txt", + "time of check time of use": "toctou", + "token": "tok", + "ultimate": "ulti", + "unique id": "uuid", + "user": "usr", + "utilities": "utils", + "utility": "util", + "value": "val", + "variable": "var", + "verify": "vrfy", + "versus": "vs", + "visual": "vis", + "visual studio": "msvc", + "web": "www", + "what the fuck": "wtf", + "window": "win", +} + +ctx = Context() +ctx.lists["user.abbreviation"] = abbreviations diff --git a/talon/community/community-cursorless-0.4.0/code/app_names/app_name_overrides.linux.csv b/talon/community/community-cursorless-0.4.0/code/app_names/app_name_overrides.linux.csv new file mode 100644 index 0000000..2f58165 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/app_names/app_name_overrides.linux.csv @@ -0,0 +1,2 @@ +grip, DataGrip +py, jetbrains-pycharm-ce \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/code/app_names/app_name_overrides.mac.csv b/talon/community/community-cursorless-0.4.0/code/app_names/app_name_overrides.mac.csv new file mode 100644 index 0000000..e0125a9 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/app_names/app_name_overrides.mac.csv @@ -0,0 +1,3 @@ +grip, DataGrip +term, iTerm2 +one note, ONENOTE \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/code/app_names/app_name_overrides.windows.csv b/talon/community/community-cursorless-0.4.0/code/app_names/app_name_overrides.windows.csv new file mode 100644 index 0000000..72b45b3 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/app_names/app_name_overrides.windows.csv @@ -0,0 +1,8 @@ +grip, DataGrip +term, iTerm2 +one note, ONENOTE +lock, slack.exe +app, slack.exe +lockapp, slack.exe +pycharm, pycharm64.exe +webstorm, webstorm64.exe \ No newline at end of file diff --git a/talon/user/community/core/app_running.py b/talon/community/community-cursorless-0.4.0/code/app_running.py similarity index 100% rename from talon/user/community/core/app_running.py rename to talon/community/community-cursorless-0.4.0/code/app_running.py diff --git a/talon/user/community/core/application_matches.py b/talon/community/community-cursorless-0.4.0/code/application_matches.py similarity index 74% rename from talon/user/community/core/application_matches.py rename to talon/community/community-cursorless-0.4.0/code/application_matches.py index 9c524b6..e207ad0 100644 --- a/talon/user/community/core/application_matches.py +++ b/talon/community/community-cursorless-0.4.0/code/application_matches.py @@ -1,4 +1,4 @@ -from talon import Module +from talon import Context, Module mod = Module() @@ -26,6 +26,11 @@ os: mac and app.bundle: com.apple.Terminal """ +apps.iterm2 = """ +os: mac +and app.bundle: com.googlecode.iterm2 +""" + # linux specific apps apps.keepass = """ os: linux @@ -54,23 +59,20 @@ os: linux and app.name: /termite/ """ -apps.windows_command_processor = r""" +apps.windows_command_processor = """ os: windows and app.name: Windows Command Processor os: windows -and app.exe: /^cmd\.exe$/i +and app.exe: cmd.exe """ -apps.windows_terminal = r""" +apps.windows_terminal = """ os: windows -and app.exe: /^windowsterminal\.exe$/i +and app.exe: WindowsTerminal.exe """ -mod.apps.windows_power_shell = r""" +mod.apps.windows_power_shell = """ os: windows -and app.exe: /^powershell\.exe$/i +and app.exe: powershell.exe """ -apps.vim = """ -win.title:/VIM/ -""" diff --git a/talon/community/community-cursorless-0.4.0/code/code.py b/talon/community/community-cursorless-0.4.0/code/code.py new file mode 100644 index 0000000..f4cca36 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/code.py @@ -0,0 +1,511 @@ +from talon import Context, Module, actions, app, imgui, registry, settings + +ctx = Context() +mod = Module() +mod.list("code_functions", desc="List of functions for active language") +mod.list("code_types", desc="List of types for active language") +mod.list("code_libraries", desc="List of libraries for active language") + +setting_private_function_formatter = mod.setting("code_private_function_formatter", str) +setting_protected_function_formatter = mod.setting( + "code_protected_function_formatter", str +) +setting_public_function_formatter = mod.setting("code_public_function_formatter", str) +setting_private_variable_formatter = mod.setting("code_private_variable_formatter", str) +setting_protected_variable_formatter = mod.setting( + "code_protected_variable_formatter", str +) +setting_public_variable_formatter = mod.setting("code_public_variable_formatter", str) + +mod.tag("code_comment", desc="Tag for enabling generic comment commands") +mod.tag("code_block_comment", desc="Tag for enabling generic block comment commands") +mod.tag("code_operators", desc="Tag for enabling generic operator commands") +mod.tag( + "code_generic", + desc="Tag for enabling other basic programming commands (loops, functions, etc)", +) + +key = actions.key +function_list = [] +library_list = [] +extension_lang_map = { + ".elm": "elm", + ".asm": "assembly", + ".bat": "batch", + ".c": "c", + ".cmake": "cmake", + ".cpp": "cplusplus", + ".cs": "csharp", + ".gdb": "gdb", + ".go": "go", + ".h": "c", + ".hpp": "cplusplus", + ".java": "java", + ".js": "javascript", + ".jsx": "javascript", + ".json": "json", + ".lua": "lua", + ".md": "markdown", + ".pl": "perl", + ".ps1": "powershell", + ".py": "python", + ".r": "r", + ".rb": "ruby", + ".s": "assembly", + ".sh": "bash", + ".snippets": "snippets", + ".talon": "talon", + ".ts": "typescript", + ".tsx": "typescript", + ".vba": "vba", + ".vim": "vimscript", + ".vimrc": "vimscript", +} + +# flag indicates whether or not the title tracking is enabled +forced_language = False + + +@mod.capture(rule="{user.code_functions}") +def code_functions(m) -> str: + """Returns a function name""" + return m.code_functions + + +@mod.capture(rule="{user.code_types}") +def code_types(m) -> str: + """Returns a type""" + return m.code_types + + +@mod.capture(rule="{user.code_libraries}") +def code_libraries(m) -> str: + """Returns a type""" + return m.code_libraries + + +@ctx.action_class("code") +class code_actions: + def language(): + result = "" + if not forced_language: + file_extension = actions.win.file_ext() + + if file_extension and file_extension in extension_lang_map: + result = extension_lang_map[file_extension] + + # print("code.language: " + result) + return result + + +# create a mode for each defined language +for __, lang in extension_lang_map.items(): + mod.mode(lang) + + +@mod.action_class +class Actions: + def code_set_language_mode(language: str): + """Sets the active language mode, and disables extension matching""" + global forced_language + actions.user.code_clear_language_mode() + actions.mode.enable("user.{}".format(language)) + # app.notify("Enabled {} mode".format(language)) + forced_language = True + + def code_clear_language_mode(): + """Clears the active language mode, and re-enables code.language: extension matching""" + global forced_language + forced_language = False + + for __, lang in extension_lang_map.items(): + actions.mode.disable("user.{}".format(lang)) + # app.notify("Cleared language modes") + + def code_operator_indirection(): + """code_operator_indirection""" + + def code_operator_address_of(): + """code_operator_address_of (e.g., C++ & op)""" + + def code_operator_structure_dereference(): + """code_operator_structure_dereference (e.g., C++ -> op)""" + + def code_operator_lambda(): + """code_operator_lambda""" + + def code_operator_subscript(): + """code_operator_subscript (e.g., C++ [])""" + + def code_operator_assignment(): + """code_operator_assignment""" + + def code_operator_subtraction(): + """code_operator_subtraction""" + + def code_operator_subtraction_assignment(): + """code_operator_subtraction_equals""" + + def code_operator_addition(): + """code_operator_addition""" + + def code_operator_addition_assignment(): + """code_operator_addition_assignment""" + + def code_operator_multiplication(): + """code_operator_multiplication""" + + def code_operator_multiplication_assignment(): + """code_operator_multiplication_assignment""" + + def code_operator_exponent(): + """code_operator_exponent""" + + def code_operator_division(): + """code_operator_division""" + + def code_operator_division_assignment(): + """code_operator_division_assignment""" + + def code_operator_modulo(): + """code_operator_modulo""" + + def code_operator_modulo_assignment(): + """code_operator_modulo_assignment""" + + def code_operator_equal(): + """code_operator_equal""" + + def code_operator_not_equal(): + """code_operator_not_equal""" + + def code_operator_greater_than(): + """code_operator_greater_than""" + + def code_operator_greater_than_or_equal_to(): + """code_operator_greater_than_or_equal_to""" + + def code_operator_less_than(): + """code_operator_less_than""" + + def code_operator_less_than_or_equal_to(): + """code_operator_less_than_or_equal_to""" + + def code_operator_in(): + """code_operator_less_than_or_equal_to""" + + def code_operator_and(): + """codee_operator_and""" + + def code_operator_or(): + """code_operator_or""" + + def code_operator_bitwise_and(): + """code_operator_bitwise_and""" + + def code_operator_bitwise_and_assignment(): + """code_operator_and""" + + def code_operator_bitwise_or(): + """code_operator_bitwise_or""" + + def code_operator_bitwise_or_assignment(): + """code_operator_or_assignment""" + + def code_operator_bitwise_exclusive_or(): + """code_operator_bitwise_exclusive_or""" + + def code_operator_bitwise_exclusive_or_assignment(): + """code_operator_bitwise_exclusive_or_assignment""" + + def code_operator_bitwise_left_shift(): + """code_operator_bitwise_left_shift""" + + def code_operator_bitwise_left_shift_assignment(): + """code_operator_bitwise_left_shift_assigment""" + + def code_operator_bitwise_right_shift(): + """code_operator_bitwise_right_shift""" + + def code_operator_bitwise_right_shift_assignment(): + """code_operator_bitwise_right_shift_assignment""" + + def code_block(): + """Inserts equivalent of {\n} for the active language, and places the cursor appropriately""" + + def code_self(): + """Inserts the equivalent of "this" in C++ or self in python""" + + def code_null(): + """inserts null equivalent""" + + def code_is_null(): + """inserts check for == null""" + + def code_is_not_null(): + """inserts check for == null""" + + def code_state_in(): + """Inserts python "in" equivalent""" + + def code_state_if(): + """Inserts if statement""" + + def code_state_else_if(): + """Inserts else if statement""" + + def code_state_else(): + """Inserts else statement""" + + def code_state_do(): + """Inserts do statement""" + + def code_state_switch(): + """Inserts switch statement""" + + def code_state_case(): + """Inserts case statement""" + + def code_state_for(): + """Inserts for statement""" + + def code_state_for_each(): + """Inserts for each equivalent statement""" + + def code_state_go_to(): + """inserts go-to statement""" + + def code_state_while(): + """Inserts while statement""" + + def code_state_return(): + """Inserts return statement""" + + def code_break(): + """Inserts break statement""" + + def code_next(): + """Inserts next statement""" + + def code_true(): + """Insert True value""" + + def code_false(): + """Insert False value""" + + def code_try_catch(): + """Inserts try/catch. If selection is true, does so around the selecion""" + + def code_default_function(text: str): + """Inserts function declaration""" + actions.user.code_private_function(text) + + def code_private_function(text: str): + """Inserts private function declaration""" + + def code_private_static_function(text: str): + """Inserts private static function""" + + def code_protected_function(text: str): + """Inserts protected function declaration""" + + def code_protected_static_function(text: str): + """Inserts public function""" + + def code_public_function(text: str): + """Inserts public function""" + + def code_public_static_function(text: str): + """Inserts public function""" + + def code_private_function_formatter(name: str): + """Inserts private function name with formatter""" + actions.insert( + actions.user.formatted_text( + name, settings.get("user.code_private_function_formatter") + ) + ) + + def code_protected_function_formatter(name: str): + """inserts properly formatted private function name""" + actions.insert( + actions.user.formatted_text( + name, settings.get("user.code_protected_function_formatter") + ) + ) + + def code_public_function_formatter(name: str): + """inserts properly formatted private function name""" + actions.insert( + actions.user.formatted_text( + name, settings.get("user.code_public_function_formatter") + ) + ) + + def code_private_variable_formatter(name: str): + """inserts properly formatted private function name""" + actions.insert( + actions.user.formatted_text( + name, settings.get("user.code_private_variable_formatter") + ) + ) + + def code_protected_variable_formatter(name: str): + """inserts properly formatted private function name""" + actions.insert( + actions.user.formatted_text( + name, settings.get("user.code_protected_variable_formatter") + ) + ) + + def code_public_variable_formatter(name: str): + """inserts properly formatted private function name""" + actions.insert( + actions.user.formatted_text( + name, settings.get("user.code_public_variable_formatter") + ) + ) + + def code_comment(): + """Inserts comment at current cursor location""" + + def code_block_comment(): + """Block comment""" + + def code_block_comment_prefix(): + """Block comment start syntax""" + + def code_block_comment_suffix(): + """Block comment end syntax""" + + def code_type_definition(): + """code_type_definition (typedef)""" + + def code_typedef_struct(): + """code_typedef_struct (typedef)""" + + def code_type_class(): + """code_type_class""" + + def code_type_struct(): + """code_type_struct""" + + def code_include(): + """code_include""" + + def code_include_system(): + """code_include_system""" + + def code_include_local(): + """code_include_local""" + + def code_import(): + """import/using equivalent""" + + def code_from_import(): + """from import python equivalent""" + + def code_toggle_functions(): + """GUI: List functions for active language""" + global function_list + if gui_libraries.showing: + gui_libraries.hide() + if gui_functions.showing: + function_list = [] + gui_functions.hide() + else: + update_function_list_and_freeze() + + def code_select_function(number: int, selection: str): + """Inserts the selected function when the imgui is open""" + if gui_functions.showing and number < len(function_list): + actions.user.code_insert_function( + registry.lists["user.code_functions"][0][function_list[number]], + selection, + ) + + def code_insert_function(text: str, selection: str): + """Inserts a function and positions the cursor appropriately""" + + def code_toggle_libraries(): + """GUI: List libraries for active language""" + global library_list + if gui_functions.showing: + gui_functions.hide() + if gui_libraries.showing: + library_list = [] + gui_libraries.hide() + else: + update_library_list_and_freeze() + + def code_select_library(number: int, selection: str): + """Inserts the selected library when the imgui is open""" + if gui_libraries.showing and number < len(library_list): + actions.user.code_insert_library( + registry.lists["user.code_libraries"][0][library_list[number]], + selection, + ) + + def code_insert_library(text: str, selection: str): + """Inserts a library and positions the cursor appropriately""" + + def code_document_string(): + """Inserts a document string and positions the cursor appropriately""" + + +def update_library_list_and_freeze(): + global library_list + if "user.code_libraries" in registry.lists: + library_list = sorted(registry.lists["user.code_libraries"][0].keys()) + else: + library_list = [] + + gui_libraries.show() + + +def update_function_list_and_freeze(): + global function_list + if "user.code_functions" in registry.lists: + function_list = sorted(registry.lists["user.code_functions"][0].keys()) + else: + function_list = [] + + gui_functions.show() + + +@imgui.open() +def gui_functions(gui: imgui.GUI): + gui.text("Functions") + gui.line() + + # print(str(registry.lists["user.code_functions"])) + for i, entry in enumerate(function_list, 1): + if entry in registry.lists["user.code_functions"][0]: + gui.text( + "{}. {}: {}".format( + i, entry, registry.lists["user.code_functions"][0][entry] + ) + ) + + +@imgui.open() +def gui_libraries(gui: imgui.GUI): + gui.text("Libraries") + gui.line() + + for i, entry in enumerate(library_list, 1): + gui.text( + "{}. {}: {}".format( + i, entry, registry.lists["user.code_libraries"][0][entry] + ) + ) + + +def commands_updated(_): + if gui_functions.showing: + update_function_list_and_freeze() + if gui_libraries.showing: + update_library_list_and_freeze() + + +registry.register("update_commands", commands_updated) diff --git a/talon/community/community-cursorless-0.4.0/code/create_spoken_forms.py b/talon/community/community-cursorless-0.4.0/code/create_spoken_forms.py new file mode 100644 index 0000000..fdeba88 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/create_spoken_forms.py @@ -0,0 +1,86 @@ +from typing import List, Optional +import itertools +from talon import registry +import re + +from .extensions import file_extensions +from .numbers import digits_map +from .abbreviate import abbreviations + +# TODO: 'Whats application': 'WhatsApp' (Should keep "whats app" as well?) +# TODO: 'V O X': 'VOX' (should keep "VOX" as well?) +# Could handle by handling all alternatives for these, or by having hardcoded list of things that we want to handle specially + +DEFAULT_MINIMUM_TERM_LENGTH = 3 + +SMALL_WORD = r"[A-Z]?[a-z]+" +# TODO: We want "AXEvery" to be ["AX", "Every"] +UPPERCASE_WORD = r"[A-Z]+" +FILE_EXTENSIONS_REGEX = "|".join( + re.escape(file_extension) for file_extension in file_extensions.values() +) +DIGITS_REGEX = r"\d" +FULL_REGEX = re.compile( + "|".join( + [ + DIGITS_REGEX, + FILE_EXTENSIONS_REGEX, + SMALL_WORD, + UPPERCASE_WORD, + ] + ) +) + +REVERSE_PRONUNCIATION_MAP = { + **{value: key for key, value in abbreviations.items()}, + **{value: key for key, value in file_extensions.items()}, + **{str(value): key for key, value in digits_map.items()}, +} + + +def create_single_spoken_form(source: str): + normalized_source = source.lower() + try: + mapped_source = REVERSE_PRONUNCIATION_MAP[normalized_source] + except KeyError: + mapped_source = source + if mapped_source.isupper(): + mapped_source = " ".join(mapped_source) + return mapped_source + + +def create_spoken_forms( + source: str, + words_to_exclude: Optional[List[str]] = None, + minimum_term_length=DEFAULT_MINIMUM_TERM_LENGTH, +) -> List[str]: + if words_to_exclude is None: + words_to_exclude = [] + + pieces = list(FULL_REGEX.finditer(source)) + # print([piece.group(0) for piece in pieces]) + + term_sequence = " ".join( + [create_single_spoken_form(piece.group(0)) for piece in pieces] + ).split(" ") + # print(term_sequence) + + terms = list( + { + term.strip() + for term in ( + term_sequence + + list(itertools.accumulate([f"{term} " for term in term_sequence])) + + [source] + ) + } + ) + + terms = [ + term + for term in terms + if term not in words_to_exclude and len(term) >= minimum_term_length + ] + # print(terms) + + return terms \ No newline at end of file diff --git a/talon/user/community/tags/debugger/debugger.py b/talon/community/community-cursorless-0.4.0/code/debugger.py similarity index 100% rename from talon/user/community/tags/debugger/debugger.py rename to talon/community/community-cursorless-0.4.0/code/debugger.py diff --git a/talon/user/community/core/delayed_speech_off.py b/talon/community/community-cursorless-0.4.0/code/delayed_speech_off.py similarity index 93% rename from talon/user/community/core/delayed_speech_off.py rename to talon/community/community-cursorless-0.4.0/code/delayed_speech_off.py index 11e8480..2ea916c 100644 --- a/talon/user/community/core/delayed_speech_off.py +++ b/talon/community/community-cursorless-0.4.0/code/delayed_speech_off.py @@ -1,4 +1,4 @@ -from talon import Module, actions, speech_system +from talon import Context, Module, actions, app, speech_system delay_mod = Module() diff --git a/talon/community/community-cursorless-0.4.0/code/dictation.py b/talon/community/community-cursorless-0.4.0/code/dictation.py new file mode 100644 index 0000000..cffb63f --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/dictation.py @@ -0,0 +1,229 @@ +# Descended from https://github.com/dwiel/talon_community/blob/master/misc/dictation.py +from talon import Module, Context, ui, actions, clip, app, grammar +from typing import Optional, Tuple, Literal + +mod = Module() + +setting_context_sensitive_dictation = mod.setting( + "context_sensitive_dictation", + type=bool, + default=False, + desc="Look at surrounding text to improve auto-capitalization/spacing in dictation mode. By default, this works by selecting that text & copying it to the clipboard, so it may be slow or fail in some applications.", +) + +@mod.capture(rule="({user.vocabulary} | )") +def word(m) -> str: + """A single word, including user-defined vocabulary.""" + try: + return m.vocabulary + except AttributeError: + return " ".join(actions.dictate.replace_words(actions.dictate.parse_words(m.word))) + +@mod.capture(rule="({user.vocabulary} | )+") +def text(m) -> str: + """A sequence of words, including user-defined vocabulary.""" + return format_phrase(m) + +@mod.capture(rule="({user.vocabulary} | {user.punctuation} | )+") +def prose(m) -> str: + """Mixed words and punctuation, auto-spaced & capitalized.""" + text, _state = auto_capitalize(format_phrase(m)) + return text + + +# ---------- FORMATTING ---------- # +def format_phrase(m): + words = capture_to_words(m) + result = "" + for i, word in enumerate(words): + if i > 0 and needs_space_between(words[i-1], word): + result += " " + result += word + return result + +def capture_to_words(m): + words = [] + for item in m: + words.extend( + actions.dictate.replace_words(actions.dictate.parse_words(item)) + if isinstance(item, grammar.vm.Phrase) else + item.split(" ")) + return words + +no_space_before = set("\n .,!?;:-/%)]}\"") +no_space_after = set("\n -/#@([{$£€¥₩₽₹\"") +def needs_space_between(before: str, after: str) -> bool: + return (before != "" and after != "" + and before[-1] not in no_space_after + and after[0] not in no_space_before) + +def auto_capitalize(text, state = None): + """ + Auto-capitalizes text. `state` argument means: + + - None: Don't capitalize initial word. + - "sentence start": Capitalize initial word. + - "after newline": Don't capitalize initial word, but we're after a newline. + Used for double-newline detection. + + Returns (capitalized text, updated state). + """ + output = "" + # Imagine a metaphorical "capitalization charge" travelling through the + # string left-to-right. + charge = state == "sentence start" + newline = state == "after newline" + for c in text: + # Sentence endings & double newlines create a charge. + if c in ".!?" or (newline and c == "\n"): + charge = True + # Alphanumeric characters and commas/colons absorb charge & try to + # capitalize (for numbers & punctuation this does nothing, which is what + # we want). + elif charge and (c.isalnum() or c in ",:"): + charge = False + c = c.capitalize() + # Otherwise the charge just passes through. + output += c + newline = c == "\n" + return output, ("sentence start" if charge else + "after newline" if newline else None) + + +# ---------- DICTATION AUTO FORMATTING ---------- # +class DictationFormat: + def __init__(self): + self.reset() + + def reset(self): + self.before = "" + self.state = "sentence start" + + def update_context(self, before): + if before is None: return + self.reset() + self.pass_through(before) + + def pass_through(self, text): + _, self.state = auto_capitalize(text, self.state) + self.before = text or self.before + + def format(self, text): + if needs_space_between(self.before, text): + text = " " + text + text, self.state = auto_capitalize(text, self.state) + self.before = text or self.before + return text + +dictation_formatter = DictationFormat() +ui.register("app_deactivate", lambda app: dictation_formatter.reset()) +ui.register("win_focus", lambda win: dictation_formatter.reset()) + +@mod.action_class +class Actions: + def dictation_format_reset(): + """Resets the dictation formatter""" + return dictation_formatter.reset() + + def dictation_insert_raw(text: str): + """Inserts text as-is, without invoking the dictation formatter.""" + dictation_formatter.pass_through(text) + actions.insert(text) + + def dictation_insert(text: str) -> str: + """Inserts dictated text, formatted appropriately.""" + # do_the_dance = whether we should try to be context-sensitive. Since + # whitespace is not affected by formatter state, if text.isspace() is + # True we don't need context-sensitivity. + do_the_dance = (setting_context_sensitive_dictation.get() + and not text.isspace()) + if do_the_dance: + dictation_formatter.update_context( + actions.user.dictation_peek_left(clobber=True)) + text = dictation_formatter.format(text) + actions.user.add_phrase_to_history(text) + actions.insert(text) + # Add a space after cursor if necessary. + if not do_the_dance or not text or text[-1] in no_space_after: + return + char = actions.user.dictation_peek_right() + if char is not None and needs_space_between(text, char): + actions.insert(" ") + actions.edit.left() + + def dictation_peek_left(clobber: bool = False) -> Optional[str]: + """ + Tries to get some text before the cursor, ideally a word or two, for the + purpose of auto-spacing & -capitalization. Results are not guaranteed; + dictation_peek_left() may return None to indicate no information. (Note + that returning the empty string "" indicates there is nothing before + cursor, ie. we are at the beginning of the document.) + + If there is currently a selection, dictation_peek_left() must leave it + unchanged unless `clobber` is true, in which case it may clobber it. + """ + # Get rid of the selection if it exists. + if clobber: actions.user.clobber_selection_if_exists() + # Otherwise, if there's a selection, fail. + elif "" != actions.edit.selected_text(): return None + + # In principle the previous word should suffice, but some applications + # have a funny concept of what the previous word is (for example, they + # may only take the "`" at the end of "`foo`"). To be double sure we + # take two words left. I also tried taking a line up + a word left, but + # edit.extend_up() = key(shift-up) doesn't work consistently in the + # Slack webapp (sometimes escapes the text box). + actions.edit.extend_word_left() + actions.edit.extend_word_left() + text = actions.edit.selected_text() + # if we're at the beginning of the document/text box, we may not have + # selected any text, in which case we shouldn't move the cursor. + if text: + # Unfortunately, in web Slack, if our selection ends at newline, + # this will go right over the newline. Argh. + actions.edit.right() + return text + + def clobber_selection_if_exists(): + """Deletes the currently selected text if it exists; otherwise does nothing.""" + actions.key("space backspace") + # This space-backspace trick is fast and reliable but has the + # side-effect of cluttering the undo history. Other options: + # + # 1. Call edit.cut() inside a clip.revert() block. This assumes + # edit.cut() is supported AND will be a no-op if there's no + # selection. Unfortunately, sometimes one or both of these is false, + # eg. the notion webapp makes ctrl-x cut the current block by default + # if nothing is selected. + # + # 2. Test whether a selection exists by asking whether + # edit.selected_text() is empty; if it does, use edit.delete(). This + # usually uses the clipboard, which can be quite slow. Also, not sure + # how this would interact with switching edit.selected_text() to use + # the selection clipboard on linux, which can be nonempty even if no + # text is selected in the current application. + # + # Perhaps this ought to be configurable by a setting. + + def dictation_peek_right() -> Optional[str]: + """ + Tries to get the character after the cursor for auto-spacing purposes. + Results are not guaranteed; dictation_peek_right() may return None to + indicate no information. (Note that returning the empty string "" + indicates there is nothing after cursor, ie. we are at the end of the + document.) + """ + actions.edit.extend_right() + char = actions.edit.selected_text() + if char: actions.edit.left() + return char + +# Use the dictation formatter in dictation mode. +dictation_ctx = Context() +dictation_ctx.matches = r""" +mode: dictation +""" + +@dictation_ctx.action_class("main") +class main_action: + def auto_insert(text): actions.user.dictation_insert(text) diff --git a/talon/community/community-cursorless-0.4.0/code/edit.py b/talon/community/community-cursorless-0.4.0/code/edit.py new file mode 100644 index 0000000..485e945 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/edit.py @@ -0,0 +1,91 @@ +import time +from talon import Context, Module, actions, clip, ui + +ctx = Context() +mod = Module() + + +@ctx.action_class("edit") +class edit_actions: + def selected_text() -> str: + with clip.capture() as s: + actions.edit.copy() + try: + return s.get() + except clip.NoChange: + return "" + + +@mod.action_class +class Actions: + def paste(text: str): + """Pastes text and preserves clipboard""" + + with clip.revert(): + clip.set_text(text) + actions.edit.paste() + # sleep here so that clip.revert doesn't revert the clipboard too soon + actions.sleep("150ms") + + def new_line_below(): + """Create new line below current line""" + actions.edit.line_end() + actions.key("enter") + + def new_line_above(): + """Create new line above current line""" + actions.edit.line_start() + actions.key("left") + actions.key("enter") + + def down_n(n: int): + """Goes down n lines""" + for _ in range(n): + actions.edit.down() + actions.sleep("10ms") + + def up_n(n: int): + """Goes up n lines""" + for _ in range(n): + actions.edit.up() + actions.sleep("10ms") + + def left_n(n: int): + """Goes left n lines""" + for _ in range(n): + actions.edit.left() + + def delete_left_n(n: int): + """Goes left n lines""" + actions.key(f"backspace:{n}") + + def delete_right_n(n: int): + """Goes left n lines""" + actions.key(f"delete:{n}") + + def right_n(n: int): + """Goes right n lines""" + for _ in range(n): + actions.edit.right() + + def word_right_n(n: int): + """Goes right n words""" + for _ in range(n): + actions.edit.word_right() + + def word_left_n(n: int): + """Goes left n words""" + for _ in range(n): + actions.edit.word_left() + + def delete_word_right_n(n: int): + """Delete right n words""" + for _ in range(n): + actions.edit.extend_word_right() + actions.edit.delete() + + def delete_word_left_n(n: int): + """Delete left n words""" + for _ in range(n): + actions.edit.extend_word_left() + actions.edit.delete() \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/code/engine.py b/talon/community/community-cursorless-0.4.0/code/engine.py new file mode 100644 index 0000000..66d7a29 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/engine.py @@ -0,0 +1,19 @@ +from talon import Context, Module +from talon import speech_system + +mod = Module() + + +@mod.action_class +class Actions: + def engine_sleep(): + """Sleep the engine""" + speech_system.engine_mimic("go to sleep"), + + def engine_wake(): + """Wake the engine""" + speech_system.engine_mimic("wake up"), + + def engine_mimic(cmd: str): + """Sends phrase to engine""" + speech_system.engine_mimic(cmd) diff --git a/talon/user/community/core/system_command.py b/talon/community/community-cursorless-0.4.0/code/exec.py similarity index 100% rename from talon/user/community/core/system_command.py rename to talon/community/community-cursorless-0.4.0/code/exec.py diff --git a/talon/user/community/core/file_extension/file_extension.py b/talon/community/community-cursorless-0.4.0/code/extensions.py similarity index 55% rename from talon/user/community/core/file_extension/file_extension.py rename to talon/community/community-cursorless-0.4.0/code/extensions.py index 340790e..0b0ad06 100644 --- a/talon/user/community/core/file_extension/file_extension.py +++ b/talon/community/community-cursorless-0.4.0/code/extensions.py @@ -1,13 +1,11 @@ -from talon import Context, Module - -from ..user_settings import track_csv_list +from .user_settings import get_list_from_csv +from talon import Module, Context, actions, app mod = Module() mod.list("file_extension", desc="A file extension, such as .py") _file_extensions_defaults = { "dot pie": ".py", - "dot elixir": ".ex", "dot talon": ".talon", "dot mark down": ".md", "dot shell": ".sh", @@ -19,7 +17,6 @@ _file_extensions_defaults = { "dot org": ".org", "dot us": ".us", "dot U S": ".us", - "dot co dot UK": ".co.uk", "dot exe": ".exe", "dot bin": ".bin", "dot bend": ".bin", @@ -34,35 +31,13 @@ _file_extensions_defaults = { "tot csv": ".csv", "dot cassie": ".csv", "dot text": ".txt", - "dot julia": ".jl", - "dot J L": ".jl", - "dot html": ".html", - "dot css": ".css", - "dot sass": ".sass", - "dot svg": ".svg", - "dot png": ".png", - "dot wave": ".wav", - "dot flack": ".flac", - "dot doc": ".doc", - "dot doc x": ".docx", - "dot pdf": ".pdf", - "dot tar": ".tar", - "dot g z": ".gz", - "dot g zip": ".gzip", - "dot zip": ".zip", - "dot toml": ".toml", - "dot java": ".java", - "dot class": ".class", - "dot log": ".log", } -ctx = Context() - - -@track_csv_list( +file_extensions = get_list_from_csv( "file_extensions.csv", headers=("File extension", "Name"), default=_file_extensions_defaults, ) -def on_update(values): - ctx.lists["self.file_extension"] = values + +ctx = Context() +ctx.lists["self.file_extension"] = file_extensions \ No newline at end of file diff --git a/talon/user/community/tags/file_manager/file_manager.py b/talon/community/community-cursorless-0.4.0/code/file_manager.py similarity index 60% rename from talon/user/community/tags/file_manager/file_manager.py rename to talon/community/community-cursorless-0.4.0/code/file_manager.py index 5257901..6d46b66 100644 --- a/talon/user/community/tags/file_manager/file_manager.py +++ b/talon/community/community-cursorless-0.4.0/code/file_manager.py @@ -1,420 +1,427 @@ -import math -from itertools import islice -from pathlib import Path - -from talon import Context, Module, actions, app, imgui, registry, settings, ui - -mod = Module() -ctx = Context() - -mod.tag("file_manager", desc="Tag for enabling generic file management commands") -mod.list("file_manager_directories", desc="List of subdirectories for the current path") -mod.list("file_manager_files", desc="List of files at the root of the current path") - -words_to_exclude = [ - "and", - "zero", - "one", - "two", - "three", - "for", - "four", - "five", - "six", - "seven", - "eight", - "nine", - "microsoft", - "windows", - "Windows", - "dot", - "exe", -] - -mod.setting( - "file_manager_auto_show_pickers", - type=bool, - default=False, - desc="Enable to show the file/directories pickers automatically", -) -mod.setting( - "file_manager_folder_limit", - type=int, - default=1000, - desc="Maximum number of files/folders to iterate", -) -mod.setting( - "file_manager_file_limit", - type=int, - default=1000, - desc="Maximum number of files to iterate", -) -mod.setting( - "file_manager_imgui_limit", - type=int, - default=20, - desc="Maximum number of files/folders to display in the imgui", -) -mod.setting( - "file_manager_string_limit", - type=int, - default=20, - desc="Maximum like of string to display in the imgui", -) -cached_path = None -file_selections = folder_selections = [] -current_file_page = current_folder_page = 1 - -ctx.lists["self.file_manager_directories"] = [] -ctx.lists["self.file_manager_files"] = [] - - -@mod.action_class -class Actions: - def file_manager_current_path() -> str: - """Returns the current path for the active file manager.""" - return "" - - def file_manager_open_parent(): - """file_manager_open_parent""" - return - - def file_manager_open_volume(volume: str): - """file_manager_open_volume""" - - def file_manager_open_directory(path: str): - """opens the directory that's already visible in the view""" - - def file_manager_select_directory(path: str): - """selects the directory""" - - def file_manager_new_folder(name: str): - """Creates a new folder in a gui filemanager or inserts the command to do so for terminals""" - - def file_manager_show_properties(): - """Shows the properties for the file""" - - def file_manager_terminal_here(): - """Opens terminal at current location""" - - def file_manager_open_file(path: str): - """opens the file""" - - def file_manager_select_file(path: str): - """selects the file""" - - def file_manager_refresh_title(): - """Refreshes the title to match current directory. this is for e.g. windows command prompt that will need to do some magic.""" - return - - def file_manager_update_lists(): - """Forces an update of the lists (e.g., when file or folder created)""" - update_lists() - - def file_manager_toggle_pickers(): - """Shows the pickers""" - if gui_files.showing: - gui_files.hide() - gui_folders.hide() - else: - gui_files.show() - gui_folders.show() - - def file_manager_hide_pickers(): - """Hides the pickers""" - if gui_files.showing: - gui_files.hide() - gui_folders.hide() - - def file_manager_get_directory_by_index(index: int) -> str: - """Returns the requested directory for the imgui display by index""" - index = (current_folder_page - 1) * settings.get( - "user.file_manager_imgui_limit" - ) + index - assert index < len(folder_selections) - return folder_selections[index] - - def file_manager_get_file_by_index(index: int) -> str: - """Returns the requested directory for the imgui display by index""" - index = (current_file_page - 1) * settings.get( - "user.file_manager_imgui_limit" - ) + index - assert index < len(file_selections) - return file_selections[index] - - def file_manager_next_file_page(): - """next_file_page""" - global current_file_page - if gui_files.showing: - if current_file_page != total_file_pages: - current_file_page += 1 - else: - current_file_page = 1 - gui_files.show() - - def file_manager_previous_file_page(): - """previous_file_page""" - global current_file_page - if gui_files.showing: - if current_file_page != 1: - current_file_page -= 1 - else: - current_file_page = total_file_pages - - gui_files.show() - - def file_manager_next_folder_page(): - """next_folder_page""" - global current_folder_page - if gui_folders.showing: - if current_folder_page != total_folder_pages: - current_folder_page += 1 - else: - current_folder_page = 1 - - gui_folders.show() - - def file_manager_previous_folder_page(): - """previous_folder_page""" - global current_folder_page - if gui_folders.showing: - if current_folder_page != 1: - current_folder_page -= 1 - else: - current_folder_page = total_folder_pages - - gui_folders.show() - - -def is_dir(f): - try: - return f.is_dir() - except: - return False - - -def is_file(f): - try: - return f.is_file() - except: - return False - - -def get_directory_map(current_path): - directories = [ - f.name - for f in islice( - current_path.iterdir(), settings.get("user.file_manager_folder_limit", 1000) - ) - if is_dir(f) - ] - directories.sort(key=str.casefold) - return actions.user.create_spoken_forms_from_list( - directories, words_to_exclude=words_to_exclude - ) - - -def get_file_map(current_path): - files = [ - f.name - for f in islice( - current_path.iterdir(), settings.get("user.file_manager_file_limit", 1000) - ) - if is_file(f) - ] - files.sort(key=str.casefold) - return actions.user.create_spoken_forms_from_list( - files, words_to_exclude=words_to_exclude - ) - - -@imgui.open(y=10, x=900) -def gui_folders(gui: imgui.GUI): - global current_folder_page, total_folder_pages - total_folder_pages = math.ceil( - len(ctx.lists["self.file_manager_directories"]) - / settings.get("user.file_manager_imgui_limit") - ) - gui.text(f"Select a directory ({current_folder_page}/{total_folder_pages})") - gui.line() - - index = 1 - current_index = (current_folder_page - 1) * settings.get( - "user.file_manager_imgui_limit" - ) - - while index <= settings.get( - "user.file_manager_imgui_limit" - ) and current_index < len(folder_selections): - name = ( - ( - folder_selections[current_index][ - : settings.get("user.file_manager_string_limit") - ] - + ".." - ) - if len(folder_selections[current_index]) - > settings.get("user.file_manager_string_limit") - else folder_selections[current_index] - ) - gui.text(f"{index}: {name} ") - current_index += 1 - index = index + 1 - - # if total_folder_pages > 1: - # gui.spacer() - - # if gui.button('Next...'): - # actions.user.file_manager_next_folder_page() - - # if gui.button("Previous..."): - # actions.user.file_manager_previous_folder_page() - - gui.spacer() - if gui.button("Manager close"): - actions.user.file_manager_hide_pickers() - - -@imgui.open(y=10, x=1300) -def gui_files(gui: imgui.GUI): - global file_selections, current_file_page, total_file_pages - total_file_pages = math.ceil( - len(file_selections) / settings.get("user.file_manager_imgui_limit") - ) - - gui.text(f"Select a file ({current_file_page}/{total_file_pages})") - gui.line() - index = 1 - current_index = (current_file_page - 1) * settings.get( - "user.file_manager_imgui_limit" - ) - - while index <= settings.get( - "user.file_manager_imgui_limit" - ) and current_index < len(file_selections): - name = ( - ( - file_selections[current_index][ - : settings.get("user.file_manager_string_limit") - ] - + ".." - ) - if len(file_selections[current_index]) - > settings.get("user.file_manager_string_limit") - else file_selections[current_index] - ) - - gui.text(f"{index}: {name} ") - current_index = current_index + 1 - index = index + 1 - - # if total_file_pages > 1: - # gui.spacer() - - # if gui.button('Next...'): - # actions.user.file_manager_next_file_page() - - # if gui.button("Previous..."): - # actions.user.file_manager_previous_file_page() - - -def clear_lists(): - global folder_selections, file_selections, current_folder_page, current_file_page - if ( - len(ctx.lists["self.file_manager_directories"]) > 0 - or len(ctx.lists["self.file_manager_files"]) > 0 - ): - current_folder_page = current_file_page = 1 - ctx.lists.update( - { - "self.file_manager_directories": [], - "self.file_manager_files": [], - } - ) - folder_selections = [] - file_selections = [] - - -def update_gui(): - if gui_folders.showing or settings.get("user.file_manager_auto_show_pickers"): - gui_folders.show() - gui_files.show() - - -def update_lists(path=None): - global folder_selections, file_selections, current_folder_page, current_file_page - is_valid_path = False - if not path: - path = actions.user.file_manager_current_path() - directories = {} - files = {} - folder_selections = [] - file_selections = [] - # print(path) - try: - current_path = Path(path) - is_valid_path = current_path.is_dir() - except: - is_valid_path = False - - if is_valid_path: - # print("valid..." + str(current_path)) - try: - directories = get_directory_map(current_path) - files = get_file_map(current_path) - except: - # print("invalid path...") - - directories = {} - files = {} - - current_folder_page = current_file_page = 1 - ctx.lists.update( - { - "self.file_manager_directories": directories, - "self.file_manager_files": files, - } - ) - - folder_selections = list(set(directories.values())) - folder_selections.sort(key=str.casefold) - file_selections = list(set(files.values())) - file_selections.sort(key=str.casefold) - - update_gui() - - -def win_event_handler(window): - global cached_path - - # on windows, we get events from the clock - # and such, so this check is important - if not window.app.exe or window != ui.active_window(): - return - - if "user.file_manager" not in registry.tags: - actions.user.file_manager_hide_pickers() - clear_lists() - cached_path = None - return - - path = actions.user.file_manager_current_path() - - if path: - if cached_path != path: - update_lists(path) - elif cached_path: - clear_lists() - actions.user.file_manager_hide_pickers() - - cached_path = path - - -def register_events(): - ui.register("win_title", win_event_handler) - ui.register("win_focus", win_event_handler) - - -# prevent scary errors in the log by waiting for talon to be fully loaded -# before registering the events -app.register("ready", register_events) +from talon import app, Module, Context, actions, ui, imgui, settings, app, registry +from os.path import expanduser +from subprocess import Popen +from pathlib import Path +from typing import List, Union +import os +import math +import re +from itertools import islice + +mod = Module() +ctx = Context() + +mod.tag("file_manager", desc="Tag for enabling generic file management commands") +mod.list("file_manager_directories", desc="List of subdirectories for the current path") +mod.list("file_manager_files", desc="List of files at the root of the current path") + + +setting_auto_show_pickers = mod.setting( + "file_manager_auto_show_pickers", + type=int, + default=0, + desc="Enable to show the file/directories pickers automatically", +) +setting_folder_limit = mod.setting( + "file_manager_folder_limit", + type=int, + default=1000, + desc="Maximum number of files/folders to iterate", +) +setting_file_limit = mod.setting( + "file_manager_file_limit", + type=int, + default=1000, + desc="Maximum number of files to iterate", +) +setting_imgui_limit = mod.setting( + "file_manager_imgui_limit", + type=int, + default=20, + desc="Maximum number of files/folders to display in the imgui", +) +setting_imgui_string_limit = mod.setting( + "file_manager_string_limit", + type=int, + default=20, + desc="Maximum like of string to display in the imgui", +) +cached_path = None +file_selections = folder_selections = [] +current_file_page = current_folder_page = 1 + +ctx.lists["self.file_manager_directories"] = [] +ctx.lists["self.file_manager_files"] = [] + +directories_to_remap = {} +user_path = os.path.expanduser("~") +if app.platform == "windows": + is_windows = True + import ctypes + + GetUserNameEx = ctypes.windll.secur32.GetUserNameExW + NameDisplay = 3 + + size = ctypes.pointer(ctypes.c_ulong(0)) + GetUserNameEx(NameDisplay, None, size) + + nameBuffer = ctypes.create_unicode_buffer(size.contents.value) + GetUserNameEx(NameDisplay, nameBuffer, size) + one_drive_path = os.path.expanduser(os.path.join("~", "OneDrive")) + + # this is probably not the correct way to check for onedrive, quick and dirty + if os.path.isdir(os.path.expanduser(os.path.join("~", r"OneDrive\Desktop"))): + default_folder = os.path.join("~", "Desktop") + + directories_to_remap = { + "Desktop": os.path.join(one_drive_path, "Desktop"), + "Documents": os.path.join(one_drive_path, "Documents"), + "Downloads": os.path.join(user_path, "Downloads"), + "Music": os.path.join(user_path, "Music"), + "OneDrive": one_drive_path, + "Pictures": os.path.join(one_drive_path, "Pictures"), + "Videos": os.path.join(user_path, "Videos"), + } + else: + # todo use expanduser for cross platform support + directories_to_remap = { + "Desktop": os.path.join(user_path, "Desktop"), + "Documents": os.path.join(user_path, "Documents"), + "Downloads": os.path.join(user_path, "Downloads"), + "Music": os.path.join(user_path, "Music"), + "OneDrive": one_drive_path, + "Pictures": os.path.join(user_path, "Pictures"), + "Videos": os.path.join(user_path, "Videos"), + } + + +@mod.action_class +class Actions: + def file_manager_current_path() -> str: + """Returns the current path for the active file manager.""" + return "" + + def file_manager_open_parent(): + """file_manager_open_parent""" + return + + def file_manager_go_forward(): + """file_manager_go_forward_directory""" + + def file_manager_go_back(): + """file_manager_go_forward_directory""" + + def file_manager_open_volume(volume: str): + """file_manager_open_volume""" + + def file_manager_open_directory(path: str): + """opens the directory that's already visible in the view""" + + def file_manager_select_directory(path: str): + """selects the directory""" + + def file_manager_new_folder(name: str): + """Creates a new folder in a gui filemanager or inserts the command to do so for terminals""" + + def file_manager_show_properties(): + """Shows the properties for the file""" + + def file_manager_terminal_here(): + """Opens terminal at current location""" + + def file_manager_open_file(path: str): + """opens the file""" + + def file_manager_select_file(path: str): + """selects the file""" + + def file_manager_refresh_title(): + """Refreshes the title to match current directory. this is for e.g. windows command prompt that will need to do some magic. """ + return + + def file_manager_update_lists(): + """Forces an update of the lists (e.g., when file or folder created)""" + update_lists() + + def file_manager_toggle_pickers(): + """Shows the pickers""" + if gui_files.showing: + gui_files.hide() + gui_folders.hide() + else: + gui_files.show() + gui_folders.show() + + def file_manager_hide_pickers(): + """Hides the pickers""" + if gui_files.showing: + gui_files.hide() + gui_folders.hide() + + def file_manager_open_user_directory(path: str): + """expands and opens the user directory""" + # this functionality exists mostly for windows. + # since OneDrive does strange stuff... + if path in directories_to_remap: + path = directories_to_remap[path] + + path = os.path.expanduser(os.path.join("~", path)) + actions.user.file_manager_open_directory(path) + + def file_manager_get_directory_by_index(index: int) -> str: + """Returns the requested directory for the imgui display by index""" + index = (current_folder_page - 1) * setting_imgui_limit.get() + index + assert index < len(folder_selections) + return folder_selections[index] + + def file_manager_get_file_by_index(index: int) -> str: + """Returns the requested directory for the imgui display by index""" + index = (current_file_page - 1) * setting_imgui_limit.get() + index + assert index < len(file_selections) + return file_selections[index] + + def file_manager_next_file_page(): + """next_file_page""" + global current_file_page + if gui_files.showing: + if current_file_page != total_file_pages: + current_file_page += 1 + else: + current_file_page = 1 + gui_files.show() + + def file_manager_previous_file_page(): + """previous_file_page""" + global current_file_page + if gui_files.showing: + if current_file_page != 1: + current_file_page -= 1 + else: + current_file_page = total_file_pages + + gui_files.show() + + def file_manager_next_folder_page(): + """next_folder_page""" + global current_folder_page + if gui_folders.showing: + if current_folder_page != total_folder_pages: + current_folder_page += 1 + else: + current_folder_page = 1 + + gui_folders.show() + + def file_manager_previous_folder_page(): + """previous_folder_page""" + global current_folder_page + if gui_folders.showing: + if current_folder_page != 1: + current_folder_page -= 1 + else: + current_folder_page = total_folder_pages + + gui_folders.show() + + +pattern = re.compile(r"[A-Z][a-z]*|[a-z]+|\d") + + +def create_spoken_forms(symbols, max_len=30): + return [" ".join(list(islice(pattern.findall(s), max_len))) for s in symbols] + + +def is_dir(f): + try: + return f.is_dir() + except: + return False + + +def is_file(f): + try: + return f.is_file() + except: + return False + + +def get_directory_map(current_path): + directories = [ + f.name + for f in islice( + current_path.iterdir(), settings.get("user.file_manager_folder_limit", 1000) + ) + if is_dir(f) + ] + # print(len(directories)) + spoken_forms = create_spoken_forms(directories) + return dict(zip(spoken_forms, directories)) + + +def get_file_map(current_path): + files = [ + f.name + for f in islice( + current_path.iterdir(), settings.get("user.file_manager_file_limit", 1000) + ) + if is_file(f) + ] + # print(str(files)) + spoken_forms = create_spoken_forms([p for p in files]) + return dict(zip(spoken_forms, [f for f in files])) + + +@imgui.open(y=10, x=900) +def gui_folders(gui: imgui.GUI): + global current_folder_page, total_folder_pages + total_folder_pages = math.ceil( + len(ctx.lists["self.file_manager_directories"]) / setting_imgui_limit.get() + ) + gui.text( + "Select a directory ({}/{})".format(current_folder_page, total_folder_pages) + ) + gui.line() + + index = 1 + current_index = (current_folder_page - 1) * setting_imgui_limit.get() + + while index <= setting_imgui_limit.get() and current_index < len(folder_selections): + name = ( + ( + folder_selections[current_index][: setting_imgui_string_limit.get()] + + ".." + ) + if len(folder_selections[current_index]) > setting_imgui_string_limit.get() + else folder_selections[current_index] + ) + gui.text("{}: {} ".format(index, name)) + current_index += 1 + index = index + 1 + + # if total_folder_pages > 1: + # gui.spacer() + + # if gui.button('Next...'): + # actions.user.file_manager_next_folder_page() + + # if gui.button("Previous..."): + # actions.user.file_manager_previous_folder_page() + + +@imgui.open(y=10, x=1300) +def gui_files(gui: imgui.GUI): + global file_selections, current_file_page, total_file_pages + total_file_pages = math.ceil(len(file_selections) / setting_imgui_limit.get()) + + gui.text("Select a file ({}/{})".format(current_file_page, total_file_pages)) + gui.line() + index = 1 + current_index = (current_file_page - 1) * setting_imgui_limit.get() + + while index <= setting_imgui_limit.get() and current_index < len(file_selections): + name = ( + (file_selections[current_index][: setting_imgui_string_limit.get()] + "..") + if len(file_selections[current_index]) > setting_imgui_string_limit.get() + else file_selections[current_index] + ) + + gui.text("{}: {} ".format(index, name)) + current_index = current_index + 1 + index = index + 1 + + # if total_file_pages > 1: + # gui.spacer() + + # if gui.button('Next...'): + # actions.user.file_manager_next_file_page() + + # if gui.button("Previous..."): + # actions.user.file_manager_previous_file_page() + + +def clear_lists(): + global folder_selections, file_selections + if ( + len(ctx.lists["self.file_manager_directories"]) > 0 + or len(ctx.lists["self.file_manager_files"]) > 0 + ): + current_folder_page = current_file_page = 1 + ctx.lists["self.file_manager_directories"] = [] + ctx.lists["self.file_manager_files"] = [] + folder_selections = [] + file_selections = [] + + +def update_gui(): + if gui_folders.showing or setting_auto_show_pickers.get() >= 1: + gui_folders.show() + gui_files.show() + + +def update_lists(): + global folder_selections, file_selections, current_folder_page, current_file_page + is_valid_path = False + path = actions.user.file_manager_current_path() + directories = {} + files = {} + folder_selections = [] + file_selections = [] + # print(path) + try: + current_path = Path(path) + is_valid_path = current_path.is_dir() + except: + is_valid_path = False + + if is_valid_path: + # print("valid..." + str(current_path)) + try: + directories = get_directory_map(current_path) + files = get_file_map(current_path) + except: + # print("invalid path...") + + directories = {} + files = {} + + current_folder_page = current_file_page = 1 + ctx.lists["self.file_manager_directories"] = directories + ctx.lists["self.file_manager_files"] = files + folder_selections = sorted(directories.values(), key=str.casefold) + file_selections = sorted(files.values(), key=str.casefold) + + update_gui() + + +def win_event_handler(window): + global cached_path + + # on windows, we get events from the clock + # and such, so this check is important + if not window.app.exe or window != ui.active_window(): + return + + path = actions.user.file_manager_current_path() + + if not "user.file_manager" in registry.tags: + actions.user.file_manager_hide_pickers() + clear_lists() + elif path: + if cached_path != path: + update_lists() + elif cached_path: + clear_lists() + actions.user.file_manager_hide_pickers() + + cached_path = path + + +def register_events(): + ui.register("win_title", win_event_handler) + ui.register("win_focus", win_event_handler) + + +# prevent scary errors in the log by waiting for talon to be fully loaded +# before registering the events +app.register("ready", register_events) + diff --git a/talon/user/community/tags/find_and_replace/find_and_replace.py b/talon/community/community-cursorless-0.4.0/code/find_and_replace.py similarity index 71% rename from talon/user/community/tags/find_and_replace/find_and_replace.py rename to talon/community/community-cursorless-0.4.0/code/find_and_replace.py index 00c1556..3428852 100644 --- a/talon/user/community/tags/find_and_replace/find_and_replace.py +++ b/talon/community/community-cursorless-0.4.0/code/find_and_replace.py @@ -1,4 +1,5 @@ -from talon import Module +from talon import Context, actions, ui, Module, app +from typing import Union mod = Module() mod.tag("find_and_replace", desc="Tag for enabling generic find and replace commands") @@ -6,6 +7,15 @@ mod.tag("find_and_replace", desc="Tag for enabling generic find and replace comm @mod.action_class class Actions: + def find(text: str): + """Finds text in current editor""" + + def find_next(): + """Navigates to the next occurrence""" + + def find_previous(): + """Navigates to the previous occurrence""" + def find_everywhere(text: str): """Finds text across project""" @@ -35,3 +45,6 @@ class Actions: def select_next_occurrence(text: str): """Selects the next occurrence of the text, and suppresses any find/replace dialogs.""" + + def select_next_token(): + """Selects the next occurrence of the token under the cursor, and suppresses any find/replace dialogs.""" diff --git a/talon/community/community-cursorless-0.4.0/code/formatters.py b/talon/community/community-cursorless-0.4.0/code/formatters.py new file mode 100644 index 0000000..3f27fc7 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/formatters.py @@ -0,0 +1,294 @@ +from talon import Module, Context, actions, ui, imgui, app +from talon.grammar import Phrase +from typing import List, Union +import logging +import re + +ctx = Context() +key = actions.key +edit = actions.edit + +words_to_keep_lowercase = ( + "a,an,the,at,by,for,in,is,of,on,to,up,and,as,but,or,nor".split(",") +) + +# The last phrase spoken, without & with formatting. Used for reformatting. +last_phrase = "" +last_phrase_formatted = "" + + +def surround(by): + def func(i, word, last): + if i == 0: + word = by + word + if last: + word += by + return word + + return func + + +def format_phrase(m: Union[str, Phrase], fmtrs: str): + global last_phrase, last_phrase_formatted + last_phrase = m + words = [] + if isinstance(m, str): + words = m.split(" ") + else: + # TODO: is this still necessary, and if so why? + if m.words[-1] == "over": + m.words = m.words[:-1] + + words = actions.dictate.parse_words(m) + words = actions.dictate.replace_words(words) + + result = last_phrase_formatted = format_phrase_no_history(words, fmtrs) + actions.user.add_phrase_to_history(result) + # Arguably, we shouldn't be dealing with history here, but somewhere later + # down the line. But we have a bunch of code that relies on doing it this + # way and I don't feel like rewriting it just now. -rntz, 2020-11-04 + return result + + +def format_phrase_no_history(word_list, fmtrs: str): + fmtr_list = fmtrs.split(",") + words = [] + spaces = True + for i, w in enumerate(word_list): + for name in reversed(fmtr_list): + smash, func = all_formatters[name] + w = func(i, w, i == len(word_list) - 1) + spaces = spaces and not smash + words.append(w) + sep = " " if spaces else "" + return sep.join(words) + + +NOSEP = True +SEP = False + + +def words_with_joiner(joiner): + """Pass through words unchanged, but add a separator between them.""" + + def formatter_function(i, word, _): + return word if i == 0 else joiner + word + + return (NOSEP, formatter_function) + + +def first_vs_rest(first_func, rest_func=lambda w: w): + """Supply one or two transformer functions for the first and rest of + words respectively. + + Leave second argument out if you want all but the first word to be passed + through unchanged. + Set first argument to None if you want the first word to be passed + through unchanged.""" + if first_func is None: + first_func = lambda w: w + + def formatter_function(i, word, _): + return first_func(word) if i == 0 else rest_func(word) + + return formatter_function + + +def every_word(word_func): + """Apply one function to every word.""" + + def formatter_function(i, word, _): + return word_func(word) + + return formatter_function + + +formatters_dict = { + "NOOP": (SEP, lambda i, word, _: word), + "DOUBLE_UNDERSCORE": (NOSEP, first_vs_rest(lambda w: "__%s__" % w)), + "PRIVATE_CAMEL_CASE": (NOSEP, first_vs_rest(lambda w: w, lambda w: w.capitalize())), + "PROTECTED_CAMEL_CASE": ( + NOSEP, + first_vs_rest(lambda w: w, lambda w: w.capitalize()), + ), + "PUBLIC_CAMEL_CASE": (NOSEP, every_word(lambda w: w.capitalize())), + "SNAKE_CASE": ( + NOSEP, + first_vs_rest(lambda w: w.lower(), lambda w: "_" + w.lower()), + ), + "NO_SPACES": (NOSEP, every_word(lambda w: w)), + "DASH_SEPARATED": words_with_joiner("-"), + "TERMINAL_DASH_SEPARATED": ( + NOSEP, + first_vs_rest(lambda w: " --" + w.lower(), lambda w: "-" + w.lower()), + ), + "DOUBLE_COLON_SEPARATED": words_with_joiner("::"), + "ALL_CAPS": (SEP, every_word(lambda w: w.upper())), + "ALL_LOWERCASE": (SEP, every_word(lambda w: w.lower())), + "DOUBLE_QUOTED_STRING": (SEP, surround('"')), + "SINGLE_QUOTED_STRING": (SEP, surround("'")), + "SPACE_SURROUNDED_STRING": (SEP, surround(" ")), + "DOT_SEPARATED": words_with_joiner("."), + "DOT_SNAKE": (NOSEP, lambda i, word, _: "." + word if i == 0 else "_" + word), + "SLASH_SEPARATED": (NOSEP, words_with_joiner("/")), + "CAPITALIZE_FIRST_WORD": (SEP, first_vs_rest(lambda w: w.capitalize())), + "CAPITALIZE_ALL_WORDS": ( + SEP, + lambda i, word, _: word.capitalize() + if i == 0 or word not in words_to_keep_lowercase + else word, + ), + "FIRST_THREE": (NOSEP, lambda i, word, _: word[0:3]), + "FIRST_FOUR": (NOSEP, lambda i, word, _: word[0:4]), + "FIRST_FIVE": (NOSEP, lambda i, word, _: word[0:5]), +} + +# This is the mapping from spoken phrases to formatters +formatters_words = { + "allcaps": formatters_dict["ALL_CAPS"], + "alldown": formatters_dict["ALL_LOWERCASE"], + "camel": formatters_dict["PRIVATE_CAMEL_CASE"], + "dotted": formatters_dict["DOT_SEPARATED"], + "dubstring": formatters_dict["DOUBLE_QUOTED_STRING"], + "dunder": formatters_dict["DOUBLE_UNDERSCORE"], + "hammer": formatters_dict["PUBLIC_CAMEL_CASE"], + "kebab": formatters_dict["DASH_SEPARATED"], + # "packed": formatters_dict["DOUBLE_COLON_SEPARATED"], + "padded": formatters_dict["SPACE_SURROUNDED_STRING"], + # "say": formatters_dict["NOOP"], + # "sentence": formatters_dict["CAPITALIZE_FIRST_WORD"], + "slasher": formatters_dict["SLASH_SEPARATED"], + "smash": formatters_dict["NO_SPACES"], + "snake": formatters_dict["SNAKE_CASE"], + # "speak": formatters_dict["NOOP"], + "string": formatters_dict["SINGLE_QUOTED_STRING"], + "title": formatters_dict["CAPITALIZE_ALL_WORDS"], + # disable a few formatters for now + # "tree": formatters_dict["FIRST_THREE"], + # "quad": formatters_dict["FIRST_FOUR"], + # "fiver": formatters_dict["FIRST_FIVE"], +} + +all_formatters = {} +all_formatters.update(formatters_dict) +all_formatters.update(formatters_words) + +mod = Module() +mod.list("formatters", desc="list of formatters") +mod.list( + "prose_formatter", + desc="words to start dictating prose, and the formatter they apply", +) + + +@mod.capture(rule="{self.formatters}+") +def formatters(m) -> str: + "Returns a comma-separated string of formatters e.g. 'SNAKE,DUBSTRING'" + return ",".join(m.formatters_list) + + +@mod.capture( + # Note that if the user speaks something like "snake dot", it will + # insert "dot" - otherwise, they wouldn't be able to insert punctuation + # words directly. + rule=" ( | )*" +) +def format_text(m) -> str: + "Formats the text and returns a string" + out = "" + formatters = m[0] + for chunk in m[1:]: + if isinstance(chunk, ImmuneString): + out += chunk.string + else: + out += format_phrase(chunk, formatters) + return out + + +class ImmuneString(object): + """Wrapper that makes a string immune from formatting.""" + + def __init__(self, string): + self.string = string + + +@mod.capture( + # Add anything else into this that you want to be able to speak during a + # formatter. + rule="( | numb )" +) +def formatter_immune(m) -> ImmuneString: + """Text that can be interspersed into a formatter, e.g. characters. + + It will be inserted directly, without being formatted. + + """ + if hasattr(m, "number"): + value = m.number + else: + value = m[0] + return ImmuneString(str(value)) + + +@mod.action_class +class Actions: + def formatted_text(phrase: Union[str, Phrase], formatters: str) -> str: + """Formats a phrase according to formatters. formatters is a comma-separated string of formatters (e.g. 'CAPITALIZE_ALL_WORDS,DOUBLE_QUOTED_STRING')""" + return format_phrase(phrase, formatters) + + def insert_formatted(phrase: Union[str, Phrase], formatters: str): + """Inserts a phrase formatted according to formatters. Formatters is a comma separated list of formatters (e.g. 'CAPITALIZE_ALL_WORDS,DOUBLE_QUOTED_STRING')""" + actions.insert(format_phrase(phrase, formatters)) + + def formatters_help_toggle(): + """Lists all formatters""" + if gui.showing: + gui.hide() + else: + gui.show() + + def formatters_reformat_last(formatters: str) -> str: + """Clears and reformats last formatted phrase""" + global last_phrase, last_phrase_formatted + if actions.user.get_last_phrase() != last_phrase_formatted: + # The last thing we inserted isn't the same as the last thing we + # formatted, so abort. + logging.warning( + "formatters_reformat_last(): Last phrase wasn't a formatter!" + ) + return + actions.user.clear_last_phrase() + actions.user.insert_formatted(last_phrase, formatters) + + def formatters_reformat_selection(formatters: str) -> str: + """Reformats the current selection.""" + selected = edit.selected_text() + unformatted = re.sub(r"[^a-zA-Z0-9]+", " ", selected).lower() + # TODO: Separate out camelcase & studleycase vars + + # Delete separately for compatibility with programs that don't overwrite + # selected text (e.g. Emacs) + edit.delete() + text = actions.self.formatted_text(unformatted, formatters) + actions.insert(text) + return text + + def insert_many(strings: List[str]) -> None: + """Insert a list of strings, sequentially.""" + for string in strings: + actions.insert(string) + + +ctx.lists["self.formatters"] = formatters_words.keys() +ctx.lists["self.prose_formatter"] = { + "speak": "NOOP", + "sense": "CAPITALIZE_FIRST_WORD", +} + + +@imgui.open() +def gui(gui: imgui.GUI): + gui.text("List formatters") + gui.line() + for name in sorted(set(formatters_words.keys())): + gui.text(f"{name} | {format_phrase_no_history(['one', 'two', 'three'], name)}") diff --git a/talon/user/community/core/help/help.py b/talon/community/community-cursorless-0.4.0/code/help.py similarity index 52% rename from talon/user/community/core/help/help.py rename to talon/community/community-cursorless-0.4.0/code/help.py index b77920b..cb96928 100644 --- a/talon/user/community/core/help/help.py +++ b/talon/community/community-cursorless-0.4.0/code/help.py @@ -1,51 +1,41 @@ +from collections import defaultdict import itertools import math -import re -from collections import defaultdict -from functools import cmp_to_key -from itertools import islice -from typing import Any, Iterable, Tuple +from typing import Dict, List, Iterable, Set, Tuple, Union -from talon import Context, Module, actions, imgui, registry, settings +from talon import Module, Context, actions, imgui, Module, registry, ui, app +from talon.grammar import Phrase mod = Module() mod.list("help_contexts", desc="list of available contexts") -mod.tag("help_open", "tag for commands that are available only when help is visible") -mod.setting( +mod.mode("help", "mode for commands that are available only when help is visible") +setting_help_max_contexts_per_page = mod.setting( "help_max_contexts_per_page", type=int, default=20, desc="Max contexts to display per page in help", ) -mod.setting( +setting_help_max_command_lines_per_page = mod.setting( "help_max_command_lines_per_page", type=int, default=50, desc="Max lines of command to display per page in help", ) -mod.setting( - "help_sort_contexts_by_specificity", - type=bool, - default=True, - desc="If true contexts are sorted by specificity before alphabetically. If false, contexts are just sorted alphabetically.", -) ctx = Context() # context name -> commands context_command_map = {} # rule word -> Set[(context name, rule)] -rule_word_map: dict[str, set[tuple[str, str]]] = defaultdict(set) +rule_word_map: Dict[str, Set[Tuple[str, str]]] = defaultdict(set) search_phrase = None # context name -> actual context context_map = {} current_context_page = 1 -# sorted list of diplay names -sorted_display_list = [] -# display names -> context name -display_name_to_context_name_map = {} +sorted_context_map_keys = [] + selected_context = None selected_context_page = 1 @@ -54,52 +44,45 @@ total_page_count = 1 cached_active_contexts_list = [] live_update = True +cached_window_title = None show_enabled_contexts_only = False -selected_list = None -current_list_page = 1 - def update_title(): global live_update global show_enabled_contexts_only + global cached_window_title if live_update: if gui_context_help.showing: - if selected_context is None: + if selected_context == None: refresh_context_command_map(show_enabled_contexts_only) else: - update_active_contexts_cache(registry.last_active_contexts) + update_active_contexts_cache(registry.active_contexts()) +# todo: dynamic rect? @imgui.open(y=0) -def gui_formatters(gui: imgui.GUI): - global formatters_words - if formatters_reformat: - gui.text("re-formatters help") - else: - gui.text("formatters help") +def gui_alphabet(gui: imgui.GUI): + global alphabet + gui.text("Alphabet help") gui.line() - for key, val in formatters_words.items(): - gui.text(f"{val}: {key}") + for key, val in alphabet.items(): + gui.text("{}: {}".format(val, key)) gui.spacer() - gui.text("* prose formatter") - gui.spacer() - if gui.button("Help close"): - gui_formatters.hide() + if gui.button("close"): + gui_alphabet.hide() def format_context_title(context_name: str) -> str: global cached_active_contexts_list return "{} [{}]".format( context_name, - ( - "ACTIVE" - if context_map.get(context_name, None) in cached_active_contexts_list - else "INACTIVE" - ), + "ACTIVE" + if context_map.get(context_name, None) in cached_active_contexts_list + else "INACTIVE", ) @@ -111,40 +94,37 @@ def format_context_button(index: int, context_label: str, context_name: str) -> return "{}. {}{}".format( index, context_label, - ( - "*" - if context_map.get(context_name, None) in cached_active_contexts_list - else "" - ), + "*" + if context_map.get(context_name, None) in cached_active_contexts_list + else "", ) else: - return f"{index}. {context_label} " + return "{}. {} ".format(index, context_label) # translates 1-based index -> actual index in sorted_context_map_keys def get_context_page(index: int) -> int: - return math.ceil(index / settings.get("user.help_max_contexts_per_page")) + return math.ceil(index / setting_help_max_contexts_per_page.get()) def get_total_context_pages() -> int: return math.ceil( - len(sorted_display_list) / settings.get("user.help_max_contexts_per_page") + len(sorted_context_map_keys) / setting_help_max_contexts_per_page.get() ) def get_current_context_page_length() -> int: - start_index = (current_context_page - 1) * settings.get( - "user.help_max_contexts_per_page" - ) + start_index = (current_context_page - 1) * setting_help_max_contexts_per_page.get() return len( - sorted_display_list[ - start_index : start_index + settings.get("user.help_max_contexts_per_page") + sorted_context_map_keys[ + start_index : start_index + setting_help_max_contexts_per_page.get() ] ) -def get_command_line_count(command: tuple[str, str]) -> int: - """This should be kept in sync with draw_commands""" +def get_command_line_count(command: Tuple[str, str]) -> int: + """This should be kept in sync with draw_commands + """ _, body = command lines = len(body.split("\n")) if lines == 1: @@ -153,7 +133,7 @@ def get_command_line_count(command: tuple[str, str]) -> int: return lines + 1 -def get_pages(item_line_counts: list[int]) -> list[int]: +def get_pages(item_line_counts: List[int]) -> List[int]: """Given some set of indivisible items with given line counts, return the page number each item should appear on. @@ -166,8 +146,9 @@ def get_pages(item_line_counts: list[int]) -> list[int]: current_page = 1 pages = [] for line_count in item_line_counts: - if line_count + current_page_line_count > settings.get( - "user.help_max_command_lines_per_page" + if ( + line_count + current_page_line_count + > setting_help_max_command_lines_per_page.get() ): if current_page_line_count == 0: # Special case, render a larger page. @@ -190,7 +171,7 @@ def gui_context_help(gui: imgui.GUI): global current_context_page global selected_context global selected_context_page - global sorted_display_list + global sorted_context_map_keys global show_enabled_contexts_only global cached_active_contexts_list global total_page_count @@ -202,7 +183,9 @@ def gui_context_help(gui: imgui.GUI): if not show_enabled_contexts_only: gui.text( - f"Help: All ({current_context_page}/{total_page_count}) (* = active)" + "Help: All ({}/{}) (* = active)".format( + current_context_page, total_page_count + ) ) else: gui.text( @@ -215,35 +198,29 @@ def gui_context_help(gui: imgui.GUI): current_item_index = 1 current_selection_index = 1 - current_group = "" - for display_name, group, _ in sorted_display_list: - target_page = get_context_page(current_item_index) - context_name = display_name_to_context_name_map[display_name] - if current_context_page == target_page: - if current_group != group: - if current_group: - gui.line() - gui.text(f"{group}:") - current_group = group + for key in sorted_context_map_keys: + if key in ctx.lists["self.help_contexts"]: + target_page = get_context_page(current_item_index) - button_name = format_context_button( - current_selection_index, - display_name, - context_name, - ) + if current_context_page == target_page: + button_name = format_context_button( + current_selection_index, + key, + ctx.lists["self.help_contexts"][key], + ) - if gui.button(button_name): - selected_context = context_name - current_selection_index = current_selection_index + 1 + if gui.button(button_name): + selected_context = ctx.lists["self.help_contexts"][key] + current_selection_index = current_selection_index + 1 - current_item_index += 1 + current_item_index += 1 if total_page_count > 1: gui.spacer() - if gui.button("Help next"): + if gui.button("Next..."): actions.user.help_next() - if gui.button("Help previous"): + if gui.button("Previous..."): actions.user.help_previous() # if there's a selected context, draw the commands for it @@ -255,19 +232,19 @@ def gui_context_help(gui: imgui.GUI): gui.spacer() if total_page_count > 1: - if gui.button("Help next"): + if gui.button("Next..."): actions.user.help_next() - if gui.button("Help previous"): + if gui.button("Previous..."): actions.user.help_previous() - if gui.button("Help return"): + if gui.button("Return"): actions.user.help_return() - if gui.button("Help refresh"): + if gui.button("Refresh"): actions.user.help_refresh() - if gui.button("Help close"): + if gui.button("Close"): actions.user.help_hide() @@ -327,7 +304,7 @@ def draw_search_commands(gui: imgui.GUI): gui.spacer() -def get_search_commands(phrase: str) -> dict[str, tuple[str, str]]: +def get_search_commands(phrase: str) -> Dict[str, Tuple[str, str]]: global rule_word_map tokens = search_phrase.split(" ") @@ -347,41 +324,37 @@ def draw_commands_title(gui: imgui.GUI, title: str): global selected_context_page global total_page_count - gui.text(f"{title} ({selected_context_page}/{total_page_count})") + gui.text("{} ({}/{})".format(title, selected_context_page, total_page_count)) gui.line() -def draw_commands(gui: imgui.GUI, commands: Iterable[tuple[str, str]]): +def draw_commands(gui: imgui.GUI, commands: Iterable[Tuple[str, str]]): for key, val in commands: val = val.split("\n") if len(val) > 1: - gui.text(f"{key}:") + gui.text("{}:".format(key)) for line in val: - gui.text(f" {line}") + gui.text(" {}".format(line)) else: - gui.text(f"{key}: {val[0]}") + gui.text("{}: {}".format(key, val[0])) def reset(): global current_context_page - global sorted_display_list + global sorted_context_map_keys global selected_context global search_phrase global selected_context_page + global cached_window_title global show_enabled_contexts_only - global display_name_to_context_name_map - global selected_list - global current_list_page current_context_page = 1 - sorted_display_list = [] + sorted_context_map_keys = None selected_context = None search_phrase = None selected_context_page = 1 + cached_window_title = None show_enabled_contexts_only = False - display_name_to_context_name_map = {} - selected_list = None - current_list_page = 1 def update_active_contexts_cache(active_contexts): @@ -391,126 +364,79 @@ def update_active_contexts_cache(active_contexts): # example usage todo: make a list definable in .talon -# overrides = {"generic browser": "broswer"} +# overrides = {"generic browser" : "broswer"} overrides = {} def refresh_context_command_map(enabled_only=False): - active_contexts = registry.last_active_contexts - - local_context_map = {} - local_display_name_to_context_name_map = {} - local_context_command_map = {} - cached_short_context_names = {} - - for context_name, context in registry.contexts.items(): - splits = context_name.split(".") - - if "talon" == splits[-1]: - display_name = splits[-2].replace("_", " ") - - short_names = actions.user.create_spoken_forms( - display_name, - generate_subsequences=False, - ) - - if short_names[0] in overrides: - short_names = [overrides[short_names[0]]] - elif len(short_names) == 2 and short_names[1] in overrides: - short_names = [overrides[short_names[1]]] - - if enabled_only and context in active_contexts or not enabled_only: - local_context_command_map[context_name] = {} - for command_alias, val in context.commands.items(): - if command_alias in registry.commands or not enabled_only: - local_context_command_map[context_name][ - str(val.rule.rule) - ] = val.target.code - if len(local_context_command_map[context_name]) == 0: - local_context_command_map.pop(context_name) - else: - for short_name in short_names: - cached_short_context_names[short_name] = context_name - - # the last entry will contain no symbols - local_display_name_to_context_name_map[display_name] = context_name - local_context_map[context_name] = context - - # Update all the global state after we've performed our calculations - global context_map - global context_command_map - global sorted_display_list - global show_enabled_contexts_only - global display_name_to_context_name_map global rule_word_map + global context_command_map + global context_map + global sorted_context_map_keys + global show_enabled_contexts_only + global cached_window_title + global context_map - context_map = local_context_map - context_command_map = local_context_command_map - sorted_display_list = get_sorted_display_keys( - local_context_map, - local_display_name_to_context_name_map, - ) + context_map = {} + cached_short_context_names = {} show_enabled_contexts_only = enabled_only - display_name_to_context_name_map = local_display_name_to_context_name_map - rule_word_map = refresh_rule_word_map(local_context_command_map) - - ctx.lists["self.help_contexts"] = cached_short_context_names + cached_window_title = ui.active_window().title + active_contexts = registry.active_contexts() + # print(str(active_contexts)) update_active_contexts_cache(active_contexts) + context_command_map = {} + for context_name, context in registry.contexts.items(): + splits = context_name.split(".") + index = -1 + if "talon" in splits[index]: + index = -2 + short_name = splits[index].replace("_", " ") + else: + short_name = splits[index].replace("_", " ") -def get_sorted_display_keys( - context_map: dict[str, Any], - display_name_to_context_name_map: dict[str, str], -): - if settings.get("user.help_sort_contexts_by_specificity"): - return get_sorted_keys_by_context_specificity( - context_map, - display_name_to_context_name_map, - ) - return [ - (display_name, "", 0) - for display_name in sorted(display_name_to_context_name_map.keys()) - ] + if "mac" == short_name or "win" == short_name or "linux" == short_name: + index = index - 1 + short_name = splits[index].replace("_", " ") + # print("short name: " + short_name) + if short_name in overrides: + short_name = overrides[short_name] -def get_sorted_keys_by_context_specificity( - context_map: dict[str, Any], - display_name_to_context_name_map: dict[str, str], -) -> list[Tuple[str, str, int]]: - def get_group(display_name) -> Tuple[str, str, int]: - try: - context_name = display_name_to_context_name_map[display_name] - context = context_map[context_name] - keys = context._match.keys() - if any(key for key in keys if key.startswith("app.")): - return (display_name, "Application-specific", 2) - if keys: - return (display_name, "Context-dependent", 1) - return (display_name, "Global", 0) - except Exception as ex: - return (display_name, "", 0) + if enabled_only and context in active_contexts or not enabled_only: + context_command_map[context_name] = {} + for command_alias, val in context.commands.items(): + # print(str(val)) + if command_alias in registry.commands: + # print(str(val.rule.rule) + ": " + val.target.code) + context_command_map[context_name][ + str(val.rule.rule) + ] = val.target.code + # print(short_name) + # print("length: " + str(len(context_command_map[context_name]))) + if len(context_command_map[context_name]) == 0: + context_command_map.pop(context_name) + else: + cached_short_context_names[short_name] = context_name + context_map[context_name] = context - grouped_list = [ - get_group(display_name) - for display_name in display_name_to_context_name_map.keys() - ] - return sorted( - grouped_list, - key=lambda item: (-item[2], item[0]), - ) + refresh_rule_word_map(context_command_map) + + ctx.lists["self.help_contexts"] = cached_short_context_names + # print(str(ctx.lists["self.help_contexts"])) + sorted_context_map_keys = sorted(cached_short_context_names) def refresh_rule_word_map(context_command_map): + global rule_word_map rule_word_map = defaultdict(set) for context_name, commands in context_command_map.items(): for rule in commands: - tokens = {token for token in re.split(r"\W+", rule) if token.isalpha()} + tokens = set(token for token in rule.split(" ") if token.isalpha()) for token in tokens: rule_word_map[token].add((context_name, rule)) - return rule_word_map - events_registered = False @@ -528,114 +454,43 @@ def register_events(register: bool): registry.unregister("update_commands", commands_updated) -def hide_all_help_guis(): - gui_context_help.hide() - gui_formatters.hide() - gui_list_help.hide() - - -def paginate_list(data, SIZE=None): - chunk_size = SIZE or settings.get("user.help_max_command_lines_per_page") - it = iter(data) - for i in range(0, len(data), chunk_size): - yield {k: data[k] for k in islice(it, chunk_size)} - - -def draw_list_commands(gui: imgui.GUI): - global selected_list - global total_page_count - global selected_context_page - - talon_list = registry.lists[selected_list][-1] - # numpages = math.ceil(len(talon_list) / SIZE) - - pages_list = [] - - for item in paginate_list(talon_list): - pages_list.append(item) - # print(pages_list) - - total_page_count = len(pages_list) - return pages_list - - -@imgui.open(y=0) -def gui_list_help(gui: imgui.GUI): - global total_page_count - global current_list_page - global selected_list - - pages_list = draw_list_commands(gui) - total_page_count = len(pages_list) - # print(pages_list[current_page]) - - gui.text(f"{selected_list} {current_list_page}/{total_page_count}") - - gui.line() - - for key, value in pages_list[current_list_page - 1].items(): - gui.text(f"{value}: {key}") - - gui.spacer() - - if total_page_count > 1: - if gui.button("Help next"): - actions.user.help_next() - - if gui.button("Help previous"): - actions.user.help_previous() - - if gui.button("Help return"): - actions.user.help_return() - - if gui.button("Help refresh"): - actions.user.help_refresh() - - if gui.button("Help close"): - actions.user.help_hide() - - @mod.action_class class Actions: - def help_list(ab: str): - """Provides the symbol dictionary""" + def help_alphabet(ab: dict): + """Provides the alphabet dictionary""" # what you say is stored as a trigger - global selected_list + global alphabet + alphabet = ab reset() - selected_list = ab - gui_list_help.show() - register_events(True) - ctx.tags = ["user.help_open"] - - def help_formatters(ab: dict, reformat: bool): - """Provides the list of formatter keywords""" - # what you say is stored as a trigger - global formatters_words, formatters_reformat - formatters_words = ab - formatters_reformat = reformat - reset() - hide_all_help_guis() - gui_formatters.show() + # print("help_alphabet - alphabet gui_alphabet: {}".format(gui_alphabet.showing)) + # print( + # "help_alphabet - gui_context_help showing: {}".format( + # gui_context_help.showing + # ) + # ) + gui_context_help.hide() + gui_alphabet.hide() + gui_alphabet.show() register_events(False) - ctx.tags = ["user.help_open"] + actions.mode.enable("user.help") def help_context_enabled(): """Display contextual command info""" reset() refresh_context_command_map(enabled_only=True) - hide_all_help_guis() + gui_alphabet.hide() gui_context_help.show() register_events(True) - ctx.tags = ["user.help_open"] + actions.mode.enable("user.help") def help_context(): """Display contextual command info""" reset() refresh_context_command_map() - hide_all_help_guis() + gui_alphabet.hide() gui_context_help.show() register_events(True) - ctx.tags = ["user.help_open"] + actions.mode.enable("user.help") def help_search(phrase: str): """Display command info for search phrase""" @@ -644,10 +499,10 @@ class Actions: reset() search_phrase = phrase refresh_context_command_map() - hide_all_help_guis() + gui_alphabet.hide() gui_context_help.show() register_events(True) - ctx.tags = ["user.help_open"] + actions.mode.enable("user.help") def help_selected_context(m: str): """Display command info for selected context""" @@ -659,13 +514,13 @@ class Actions: refresh_context_command_map() else: selected_context_page = 1 - update_active_contexts_cache(registry.last_active_contexts) + update_active_contexts_cache(registry.active_contexts()) selected_context = m - hide_all_help_guis() + gui_alphabet.hide() gui_context_help.show() register_events(True) - ctx.tags = ["user.help_open"] + actions.mode.enable("user.help") def help_next(): """Navigates to next page""" @@ -674,8 +529,6 @@ class Actions: global selected_context_page global total_page_count - global current_list_page - if gui_context_help.showing: if selected_context is None and search_phrase is None: if current_context_page != total_page_count: @@ -688,29 +541,22 @@ class Actions: else: selected_context_page = 1 - if gui_list_help.showing: - if current_list_page != total_page_count: - current_list_page += 1 - else: - current_list_page = 1 - def help_select_index(index: int): """Select the context by a number""" - global sorted_display_list, selected_context + global sorted_context_map_keys, selected_context if gui_context_help.showing: - if index < settings.get("user.help_max_contexts_per_page") and ( - (current_context_page - 1) - * settings.get("user.help_max_contexts_per_page") + if index < setting_help_max_contexts_per_page.get() and ( + (current_context_page - 1) * setting_help_max_contexts_per_page.get() + index - < len(sorted_display_list) + < len(sorted_context_map_keys) ): if selected_context is None: - selected_context = display_name_to_context_name_map[ - sorted_display_list[ + selected_context = ctx.lists["self.help_contexts"][ + sorted_context_map_keys[ (current_context_page - 1) - * settings.get("user.help_max_contexts_per_page") + * setting_help_max_contexts_per_page.get() + index - ][0] + ] ] def help_previous(): @@ -720,8 +566,6 @@ class Actions: global selected_context_page global total_page_count - global current_list_page - if gui_context_help.showing: if selected_context is None and search_phrase is None: if current_context_page != 1: @@ -735,12 +579,6 @@ class Actions: else: selected_context_page = total_page_count - if gui_list_help.showing: - if current_list_page != total_page_count: - current_list_page -= 1 - else: - current_list_page = 1 - def help_return(): """Returns to the main help window""" global selected_context @@ -758,10 +596,10 @@ class Actions: global selected_context if gui_context_help.showing: - if selected_context is None: + if selected_context == None: refresh_context_command_map(show_enabled_contexts_only) else: - update_active_contexts_cache(registry.last_active_contexts) + update_active_contexts_cache(registry.active_contexts()) def help_hide(): """Hides the help""" @@ -772,11 +610,16 @@ class Actions: # "help_hide - gui_context_help showing: {}".format(gui_context_help.showing) # ) - hide_all_help_guis() + gui_alphabet.hide() + gui_context_help.hide() refresh_context_command_map() register_events(False) - ctx.tags = [] + actions.mode.disable("user.help") def commands_updated(_): update_title() + + +app.register("ready", refresh_context_command_map) + diff --git a/talon/community/community-cursorless-0.4.0/code/history.py b/talon/community/community-cursorless-0.4.0/code/history.py new file mode 100644 index 0000000..2d98221 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/history.py @@ -0,0 +1,76 @@ +from talon import imgui, Module, speech_system, actions, app + +# We keep command_history_size lines of history, but by default display only +# command_history_display of them. +mod = Module() +setting_command_history_size = mod.setting("command_history_size", int, default=50) +setting_command_history_display = mod.setting( + "command_history_display", int, default=10 +) + +hist_more = False +history = [] + + +def parse_phrase(word_list): + return " ".join(word.split("\\")[0] for word in word_list) + + +def on_phrase(j): + global history + + try: + val = parse_phrase(getattr(j["parsed"], "_unmapped", j["phrase"])) + except: + val = parse_phrase(j["phrase"]) + + if val != "": + history.append(val) + history = history[-setting_command_history_size.get() :] + + +# todo: dynamic rect? +@imgui.open(x=1300, y=915) +def gui(gui: imgui.GUI): + global history + text = ( + history[:] if hist_more else history[-setting_command_history_display.get() :] + ) + for line in text: + gui.text(line) + + +speech_system.register("phrase", on_phrase) + + +@mod.action_class +class Actions: + def history_toggle(): + """Toggles viewing the history""" + if gui.showing: + gui.hide() + else: + gui.show() + + def history_enable(): + """Enables the history""" + gui.show() + + def history_disable(): + """Disables the history""" + gui.hide() + + def history_clear(): + """Clear the history""" + global history + history = [] + + def history_more(): + """Show more history""" + global hist_more + hist_more = True + + def history_less(): + """Show less history""" + global hist_more + hist_more = False diff --git a/talon/user/community/core/homophones/homophones.csv b/talon/community/community-cursorless-0.4.0/code/homophones.csv similarity index 96% rename from talon/user/community/core/homophones/homophones.csv rename to talon/community/community-cursorless-0.4.0/code/homophones.csv index 8cc249d..e5a4cf1 100644 --- a/talon/user/community/core/homophones/homophones.csv +++ b/talon/community/community-cursorless-0.4.0/code/homophones.csv @@ -23,12 +23,11 @@ adds,ads,adz adherence,adherents ado,adieu aerial,ariel -Aaron,Erin +arrow,aero affected,effected afterward,afterword aid,aide ale,ail -align,a line,aline all,awl alluded,eluded illusion,allusion @@ -42,6 +41,7 @@ ascent,assent assistance,assistants augur,auger aunt,ant +auto,otto oral,aural oriole,aureole away,aweigh @@ -51,17 +51,15 @@ axle,axel eyes,ayes bah,baa Babel,babble -bad,bade,bed +bad,bade bait,bate bald,balled,bawled bail,bale,baal band,banned barred,bard baron,barren -barrel,beryl basil,basal base,bass -baseline,bassline basis,bases basque,bask based,baste @@ -86,7 +84,6 @@ block,bloc bore,boar boulder,bolder bomb,balm,bombe -bought,bot booty,bootie border,boarder board,bored @@ -106,7 +103,7 @@ brewed,brood browse,brows brute,brut build,billed -bullion,bouillon,boolean +bullion,bouillon boy,buoy burger,burgher borough,burrow,burro @@ -157,7 +154,6 @@ complacent,complaisant complement,compliment conceded,conceited consonants,consonance -consul,console,consol continents,continence coup,coo coulee,coolie @@ -216,7 +212,6 @@ eve,eave exceed,accede except,accept exercise,exorcise -end,and I'd,eyed faint,feint fair,fare @@ -316,7 +311,6 @@ I'll,aisle,isle islet,eyelet its,it's jam,jamb -json,jason jinx,jinks kernel,colonel nap,knap @@ -384,7 +378,7 @@ mill,mil mince,mints mind,mined minor,miner -Mrs,misses +Mrs.,misses missile,missal missed,mist might,mite @@ -413,7 +407,6 @@ or,ore,oar owed,ode oh,owe our,hour -ours,hours outcast,outcaste overdue,overdo overseas,oversees @@ -423,6 +416,7 @@ pale,pail pain,pane palette,palate,pallet pair,pear,pare +pairs,pears parley,parlay past,passed patients,patience @@ -469,12 +463,12 @@ pros,prose perl,pearl,purl pervade,purveyed quartz,quarts +queue,cue,cu quince,quints choir,quire rabbit,rabbet rain,reign,rein wrap,rap -wrapper,rapper wrapped,rapped,rapt raise,rays,raze read,red @@ -513,7 +507,6 @@ sense,cents,scents skull,scull seer,sear,sere see,sea -scalar,scaler cedar,seeder seem,seam sees,seize,seas @@ -540,7 +533,6 @@ sore,soar sorry,sari soul,sole so,sow,sew -sewing,sowing spade,spayed stayed,staid stare,stair @@ -624,7 +616,6 @@ wax,whacks way,weigh,whey we,wee where,wear,ware -were,whir,we're whether,weather,wether we'd,weed week,weak @@ -634,10 +625,9 @@ wet,whet we've,weave Wales,whales,wails we'll,wheel -wheeled,wield -with,width which,witch while,wile +were,whir word,whirred hoop,whoop whose,who's @@ -649,10 +639,10 @@ one,won want,wont would,wood war,wore -ward,word world,whirled,whorled worn,warn rack,wrack +rapper,wrapper wrote,rote rye,wry you,yew,ewe diff --git a/talon/community/community-cursorless-0.4.0/code/homophones.py b/talon/community/community-cursorless-0.4.0/code/homophones.py new file mode 100644 index 0000000..35a49ef --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/homophones.py @@ -0,0 +1,168 @@ +from talon import Context, Module, app, clip, cron, imgui, actions, ui, fs +import os + +######################################################################## +# global settings +######################################################################## + +# a list of homophones where each line is a comma separated list +# e.g. where,wear,ware +# a suitable one can be found here: +# https://github.com/pimentel/homophones +cwd = os.path.dirname(os.path.realpath(__file__)) +homophones_file = os.path.join(cwd, "homophones.csv") +# if quick_replace, then when a word is selected and only one homophone exists, +# replace it without bringing up the options +quick_replace = True +show_help = False +######################################################################## + +ctx = Context() +mod = Module() +mod.mode("homophones") +mod.list("homophones_canonicals", desc="list of words ") + + +main_screen = ui.main_screen() + + +def update_homophones(name, flags): + if name != homophones_file: + return + + phones = {} + canonical_list = [] + with open(homophones_file, "r") as f: + for line in f: + words = line.rstrip().split(",") + canonical_list.append(words[0]) + for word in words: + word = word.lower() + old_words = phones.get(word, []) + phones[word] = sorted(set(old_words + words)) + + global all_homophones + all_homophones = phones + ctx.lists["self.homophones_canonicals"] = canonical_list + + +update_homophones(homophones_file, None) +fs.watch(cwd, update_homophones) +active_word_list = None +is_selection = False + + +def close_homophones(): + gui.hide() + actions.mode.disable("user.homophones") + + +def raise_homophones(word, forced=False, selection=False): + global quick_replace + global active_word_list + global show_help + global force_raise + global is_selection + + force_raise = forced + is_selection = selection + + if is_selection: + word = word.strip() + + is_capitalized = word == word.capitalize() + is_upper = word.isupper() + + word = word.lower() + + if word not in all_homophones: + app.notify("homophones.py", '"%s" not in homophones list' % word) + return + + active_word_list = all_homophones[word] + if ( + is_selection + and len(active_word_list) == 2 + and quick_replace + and not force_raise + ): + if word == active_word_list[0].lower(): + new = active_word_list[1] + else: + new = active_word_list[0] + + if is_capitalized: + new = new.capitalize() + elif is_upper: + new = new.upper() + + clip.set(new) + actions.edit.paste() + + return + + actions.mode.enable("user.homophones") + show_help = False + gui.show() + + +@imgui.open(x=main_screen.x + main_screen.width / 2.6, y=main_screen.y) +def gui(gui: imgui.GUI): + global active_word_list + if show_help: + gui.text("Homephone help - todo") + else: + gui.text("Select a homophone") + gui.line() + index = 1 + for word in active_word_list: + gui.text("Choose {}: {} ".format(index, word)) + index = index + 1 + + +def show_help_gui(): + global show_help + show_help = True + gui.show() + + +@mod.capture(rule="{self.homophones_canonicals}") +def homophones_canonical(m) -> str: + "Returns a single string" + return m.homophones_canonicals + + +@mod.action_class +class Actions: + def homophones_hide(): + """Hides the homophones display""" + close_homophones() + + def homophones_show(m: str): + """Sentence formatter""" + print(m) + raise_homophones(m, False, False) + + def homophones_show_selection(): + """Sentence formatter""" + raise_homophones(actions.edit.selected_text(), False, True) + + def homophones_force_show(m: str): + """Sentence formatter""" + raise_homophones(m, True, False) + + def homophones_force_show_selection(): + """Sentence formatter""" + raise_homophones(actions.edit.selected_text(), True, True) + + def homophones_select(number: int) -> str: + """selects the homophone by number""" + if number <= len(active_word_list) and number > 0: + return active_word_list[number - 1] + + error = "homophones.py index {} is out of range (1-{})".format( + number, len(active_word_list) + ) + app.notify(error) + raise error + diff --git a/talon/community/community-cursorless-0.4.0/code/keeper.py b/talon/community/community-cursorless-0.4.0/code/keeper.py new file mode 100644 index 0000000..8ec548c --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/keeper.py @@ -0,0 +1,11 @@ +from talon import actions, speech_system + + +def fn(d): + words = d["parsed"]._unmapped + if words[0] == "keeper": + actions.insert(" ".join(words[1:])) + d["parsed"]._sequence = [] + + +speech_system.register("pre:phrase", fn) diff --git a/talon/user/community/core/keys/keys.py b/talon/community/community-cursorless-0.4.0/code/keys.py similarity index 53% rename from talon/user/community/core/keys/keys.py rename to talon/community/community-cursorless-0.4.0/code/keys.py index 59855b4..baeb3d8 100644 --- a/talon/user/community/core/keys/keys.py +++ b/talon/community/community-cursorless-0.4.0/code/keys.py @@ -1,14 +1,29 @@ -from talon import Context, Module, actions, app +from typing import Set +from user.pokey_talon.code.terms import DELETE + +from talon import Module, Context, actions, app +import sys + +default_alphabet = "air bat cap drum each fine gust harp sit jane crunch look made near odd pit quench red sun trap urge vest whale plex yank zip".split( + " " +) +letters_string = "abcdefghijklmnopqrstuvwxyz" + +default_digits = "zero one two three four five six seven eight nine".split(" ") +numbers = [str(i) for i in range(10)] +default_f_digits = ( + "one two three four five six seven eight nine ten eleven twelve".split(" ") +) mod = Module() mod.list("letter", desc="The spoken phonetic alphabet") mod.list("symbol_key", desc="All symbols from the keyboard") +mod.list("immune_symbol_key", desc="Symbols that can appear in a formatter") mod.list("arrow_key", desc="All arrow keys") mod.list("number_key", desc="All number keys") mod.list("modifier_key", desc="All modifier keys") mod.list("function_key", desc="All function keys") mod.list("special_key", desc="All special keys") -mod.list("keypad_key", desc="All keypad keys") mod.list("punctuation", desc="words for inserting punctuation into text") @@ -36,12 +51,6 @@ def number_key(m) -> str: return m.number_key -@mod.capture(rule="{self.keypad_key}") -def keypad_key(m) -> str: - "One keypad key" - return m.keypad_key - - @mod.capture(rule="{self.letter}") def letter(m) -> str: "One letter key" @@ -60,6 +69,12 @@ def symbol_key(m) -> str: return m.symbol_key +@mod.capture(rule="{self.immune_symbol_key}") +def immune_symbol_key(m) -> str: + "A symbol key that is allowed to appear within a format string" + return m.immune_symbol_key + + @mod.capture(rule="{self.function_key}") def function_key(m) -> str: "One function key" @@ -74,7 +89,7 @@ def any_alphanumeric_key(m) -> str: @mod.capture( rule="( | | " - "| | | | )" + "| | | )" ) def unmodified_key(m) -> str: "A single key with no modifiers" @@ -104,9 +119,25 @@ def letters(m) -> str: ctx = Context() +modifier_keys = { + # If you find 'alt' is often misrecognized, try using 'alter'. + "alt": "alt", #'alter': 'alt', + "many": "cmd", + "troll": "ctrl", #'troll': 'ctrl', + "option": "alt", + "ship": "shift", #'sky': 'shift', + "super": "super", +} +if app.platform == "mac": + modifier_keys["command"] = "cmd" + modifier_keys["option"] = "alt" +ctx.lists["self.modifier_key"] = modifier_keys +alphabet = dict(zip(default_alphabet, letters_string)) +ctx.lists["self.letter"] = alphabet -# `punctuation_words` is for words you want available BOTH in dictation and as key names in command mode. -# `symbol_key_words` is for key names that should be available in command mode, but NOT during dictation. +# `punctuation_words` is for words you want available BOTH in dictation and as +# key names in command mode. `symbol_key_words` is for key names that should be +# available in command mode, but NOT during dictation. punctuation_words = { # TODO: I'm not sure why we need these, I think it has something to do with # Dragon. Possibly it has been fixed by later improvements to talon? -rntz @@ -114,16 +145,14 @@ punctuation_words = { ",": ",", # <== these things "back tick": "`", "comma": ",", - # Workaround for issue with conformer b-series; see #946 - "coma": ",", "period": ".", - "full stop": ".", - "semicolon": ";", + "semi": ";", "colon": ":", "forward slash": "/", "question mark": "?", "exclamation mark": "!", "exclamation point": "!", + "dollar sign": "$", "asterisk": "*", "hash sign": "#", "number sign": "#", @@ -131,73 +160,94 @@ punctuation_words = { "at sign": "@", "and sign": "&", "ampersand": "&", - # Currencies - "dollar sign": "$", - "pound sign": "£", - "hyphen": "-", - "L paren": "(", - "left paren": "(", - "R paren": ")", - "right paren": ")", } -symbol_key_words = { + +immune_symbol_key_words = { "dot": ".", - "point": ".", - "quote": "'", - "question": "?", - "apostrophe": "'", - "L square": "[", - "left square": "[", - "brack": "[", - "bracket": "[", - "left bracket": "[", - "square": "[", - "R square": "]", - "right square": "]", - "r brack": "]", - "r bracket": "]", - "right bracket": "]", - "slash": "/", - "backslash": "\\", - "minus": "-", "dash": "-", - "equals": "=", +} + +symbol_key_words = { + "brick": "`", + "stroke": "/", + "backstroke": "\\", + "minus": "-", + "equal": "=", "plus": "+", - "grave": "`", "tilde": "~", "bang": "!", + "dollar": "$", "down score": "_", - "underscore": "_", - "paren": "(", - "brace": "{", - "left brace": "{", - "curly bracket": "{", - "left curly bracket": "{", - "r brace": "}", - "right brace": "}", - "r curly bracket": "}", - "right curly bracket": "}", + "question": "?", + "single": "'", + "double": '"', + "leper": "(", + "repper": ")", + "lack": "[", + "rack": "]", + "lace": "{", + "race": "}", "angle": "<", - "left angle": "<", "less than": "<", - "rangle": ">", - "R angle": ">", - "right angle": ">", + "wrangle": ">", "greater than": ">", - "star": "*", - "hash": "#", - "percent": "%", - "caret": "^", + "snow": "*", + "pound": "#", + "percy": "%", + "tangle": "^", "amper": "&", "pipe": "|", - "dub quote": '"', - "double quote": '"', - # Currencies - "dollar": "$", - "pound": "£", } # make punctuation words also included in {user.symbol_keys} symbol_key_words.update(punctuation_words) +symbol_key_words.update(immune_symbol_key_words) ctx.lists["self.punctuation"] = punctuation_words ctx.lists["self.symbol_key"] = symbol_key_words +ctx.lists["self.immune_symbol_key"] = immune_symbol_key_words +ctx.lists["self.number_key"] = dict(zip(default_digits, numbers)) +ctx.lists["self.arrow_key"] = { + "down": "down", + "left": "left", + "right": "right", + "up": "up", +} + +simple_keys = [ + # "end", + # "home", + # "insert", + "pagedown", + "pageup", + "tab", +] + +alternate_keys = { + "clap": "enter", + "deli": "delete", + "drill": "delete", + "chuck that": "backspace", + "wipe": "backspace", + "scrape": "escape", + "void": "space", + "page up": "pageup", + "page down": "pagedown", +} +# mac apparently doesn't have the menu key. +if app.platform in ("windows", "linux"): + alternate_keys["menu key"] = "menu" + alternate_keys["print screen"] = "printscr" + +keys = {k: k for k in simple_keys} +keys.update(alternate_keys) +ctx.lists["self.special_key"] = keys +ctx.lists["self.function_key"] = { + f"fun {default_f_digits[i]}": f"f{i + 1}" for i in range(12) +} + + +@mod.action_class +class Actions: + def get_alphabet() -> dict: + """Provides the alphabet dictionary""" + return alphabet diff --git a/talon/user/community/tags/line_commands/line_commands.py b/talon/community/community-cursorless-0.4.0/code/line_commands.py similarity index 87% rename from talon/user/community/tags/line_commands/line_commands.py rename to talon/community/community-cursorless-0.4.0/code/line_commands.py index 11d1cff..45d1a49 100644 --- a/talon/user/community/tags/line_commands/line_commands.py +++ b/talon/community/community-cursorless-0.4.0/code/line_commands.py @@ -1,4 +1,10 @@ -from talon import Module, actions +import os +import os.path +import requests +import time +from pathlib import Path +from talon import ctrl, ui, Module, Context, actions, clip +import tempfile mod = Module() @@ -37,5 +43,4 @@ class Actions: def line_clone(line: int): """Clones specified line at current position""" - actions.edit.jump_line(line) - actions.edit.line_clone() + diff --git a/talon/community/community-cursorless-0.4.0/code/macro.py b/talon/community/community-cursorless-0.4.0/code/macro.py new file mode 100644 index 0000000..c5ad7a2 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/macro.py @@ -0,0 +1,51 @@ +from talon import actions, Module, speech_system + +mod = Module() + +macro = [] +recording = False + + +@mod.action_class +class Actions: + def macro_record(): + """record a new macro""" + global macro + global recording + + macro = [] + recording = True + + def macro_stop(): + """stop recording""" + global recording + recording = False + + def macro_insert_wait(number_small: int): + """Insert wait for given number of seconds""" + if not number_small: + actions.sleep("250ms") + else: + actions.sleep(f"{number_small}s") + + def macro_play(): + """player recorded macro""" + actions.user.macro_stop() + + # :-1 because we don't want to replay `macro play` + for words in macro[:-1]: + print(words) + actions.mimic(words) + + +def fn(d): + if not recording: + return + + if "parsed" not in d: + return + + macro.append(d["parsed"]._unmapped) + + +speech_system.register("pre:phrase", fn) diff --git a/talon/user/community/tags/messaging/messaging.py b/talon/community/community-cursorless-0.4.0/code/messaging.py similarity index 95% rename from talon/user/community/tags/messaging/messaging.py rename to talon/community/community-cursorless-0.4.0/code/messaging.py index 7cbef73..a1e34f3 100644 --- a/talon/user/community/tags/messaging/messaging.py +++ b/talon/community/community-cursorless-0.4.0/code/messaging.py @@ -1,4 +1,4 @@ -from talon import Module +from talon import Context, actions, ui, Module, app mod = Module() mod.tag("messaging", desc="Tag for generic multi-channel messaging apps") diff --git a/talon/community/community-cursorless-0.4.0/code/microphone_selection.py b/talon/community/community-cursorless-0.4.0/code/microphone_selection.py new file mode 100644 index 0000000..bf7b9e5 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/microphone_selection.py @@ -0,0 +1,69 @@ +from talon import actions +from talon import Module, actions, imgui, scripting, app +from talon.microphone import manager +from talon.lib import cubeb +from talon import scripting + +ctx = cubeb.Context() +mod = Module() + + +def devices_changed(device_type): + update_microphone_list() + + +microphone_device_list = [] + + +def update_microphone_list(): + global microphone_device_list + microphone_device_list = [] + for device in ctx.inputs(): + if str(device.state) == "DeviceState.ENABLED": + microphone_device_list.append(device) + + +@imgui.open() +def gui(gui: imgui.GUI): + gui.text("Select a Microphone") + gui.line() + for index, item in enumerate(microphone_device_list, 1): + if gui.button("{}. {}".format(index, item.name)): + actions.user.microphone_select(index) + + +@mod.action_class +class Actions: + def microphone_selection_toggle(): + """""" + if gui.showing: + gui.hide() + else: + gui.show() + + def microphone_select(index: int): + """Selects a micropohone""" + # print(str(index) + " " + str(len(microphone_device_list))) + if 1 <= index and index <= len(microphone_device_list): + microphone = microphone_device_list[index - 1].name + for item in manager.menu.items: + # print(item.name + " " + microphone) + if microphone in item.name: + # manager.menu_click(item) + actions.speech.set_microphone(item.name) + app.notify("Activating {}".format(item.name)) + + break + + gui.hide() + + +ctx.register("devices_changed", devices_changed) + + +def on_ready(): + update_microphone_list() + + +app.register("ready", on_ready) + diff --git a/talon/community/community-cursorless-0.4.0/code/momentary.py b/talon/community/community-cursorless-0.4.0/code/momentary.py new file mode 100644 index 0000000..2e53ffa --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/momentary.py @@ -0,0 +1,28 @@ +from talon import Module, actions, speech_system, scope + +flag = False +def on_phrase_post(j): + global flag + if flag: + flag = False + checked_modes = list(scope.get('mode').intersection({'sleep', 'dictation'})) + # make sure we're in sleep or dictation mode: + if len(checked_modes) == 1: + mode = checked_modes[0] + actions.mode.enable('command') + actions.mode.disable(mode) + try: + # NOTE: the following API is completely private and subject to change with no notice + speech_system._on_audio_frame(j['samples']) + finally: + actions.mode.disable('command') + actions.mode.enable(mode) +speech_system.register('post:phrase', on_phrase_post) + +mod = Module() +@mod.action_class +class Actions: + def momentary(): + """Wake up Talon and re-run the entire current audio""" + global flag + flag = True diff --git a/talon/community/community-cursorless-0.4.0/code/mouse.py b/talon/community/community-cursorless-0.4.0/code/mouse.py new file mode 100644 index 0000000..7d1af1d --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/mouse.py @@ -0,0 +1,373 @@ +import os +import pathlib +import subprocess + +from talon import ( + Context, + Module, + actions, + app, + cron, + ctrl, + clip, + imgui, + noise, + settings, + ui, +) +from talon_plugins import eye_mouse, eye_zoom_mouse, speech +from talon_plugins.eye_mouse import config, toggle_camera_overlay, toggle_control + +key = actions.key +self = actions.self +scroll_amount = 0 +click_job = None +scroll_job = None +gaze_job = None +cancel_scroll_on_pop = True + +default_cursor = { + "AppStarting": r"%SystemRoot%\Cursors\aero_working.ani", + "Arrow": r"%SystemRoot%\Cursors\aero_arrow.cur", + "Hand": r"%SystemRoot%\Cursors\aero_link.cur", + "Help": r"%SystemRoot%\Cursors\aero_helpsel.cur", + "No": r"%SystemRoot%\Cursors\aero_unavail.cur", + "NWPen": r"%SystemRoot%\Cursors\aero_pen.cur", + "Person": r"%SystemRoot%\Cursors\aero_person.cur", + "Pin": r"%SystemRoot%\Cursors\aero_pin.cur", + "SizeAll": r"%SystemRoot%\Cursors\aero_move.cur", + "SizeNESW": r"%SystemRoot%\Cursors\aero_nesw.cur", + "SizeNS": r"%SystemRoot%\Cursors\aero_ns.cur", + "SizeNWSE": r"%SystemRoot%\Cursors\aero_nwse.cur", + "SizeWE": r"%SystemRoot%\Cursors\aero_ew.cur", + "UpArrow": r"%SystemRoot%\Cursors\aero_up.cur", + "Wait": r"%SystemRoot%\Cursors\aero_busy.ani", + "Crosshair": "", + "IBeam": "", +} + +# todo figure out why notepad++ still shows the cursor sometimes. +hidden_cursor = os.path.join( + os.path.dirname(os.path.realpath(__file__)), r"Resources\HiddenCursor.cur" +) + +mod = Module() +mod.list( + "mouse_button", desc="List of mouse button words to mouse_click index parameter" +) +setting_mouse_enable_pop_click = mod.setting( + "mouse_enable_pop_click", + type=int, + default=0, + desc="Enable pop to click when control mouse is enabled.", +) +setting_mouse_enable_pop_stops_scroll = mod.setting( + "mouse_enable_pop_stops_scroll", + type=int, + default=0, + desc="When enabled, pop stops continuous scroll modes (wheel upper/downer/gaze)", +) +setting_mouse_wake_hides_cursor = mod.setting( + "mouse_wake_hides_cursor", + type=int, + default=0, + desc="When enabled, mouse wake will hide the cursor. mouse_wake enables zoom mouse.", +) +setting_mouse_hide_mouse_gui = mod.setting( + "mouse_hide_mouse_gui", + type=int, + default=0, + desc="When enabled, the 'Scroll Mouse' GUI will not be shown.", +) +setting_mouse_continuous_scroll_amount = mod.setting( + "mouse_continuous_scroll_amount", + type=int, + default=80, + desc="The default amount used when scrolling continuously", +) +setting_mouse_wheel_down_amount = mod.setting( + "mouse_wheel_down_amount", + type=int, + default=120, + desc="The amount to scroll up/down (equivalent to mouse wheel on Windows by default)", +) + +continuous_scoll_mode = "" + + +@imgui.open(x=700, y=0) +def gui_wheel(gui: imgui.GUI): + gui.text("Scroll mode: {}".format(continuous_scoll_mode)) + gui.line() + if gui.button("Wheel Stop [stop scrolling]"): + actions.user.mouse_scroll_stop() + + +@mod.action_class +class Actions: + def mouse_show_cursor(): + """Shows the cursor""" + show_cursor_helper(True) + + def mouse_hide_cursor(): + """Hides the cursor""" + show_cursor_helper(False) + + def mouse_wake(): + """Enable control mouse, zoom mouse, and disables cursor""" + eye_zoom_mouse.toggle_zoom_mouse(True) + # eye_mouse.control_mouse.enable() + if setting_mouse_wake_hides_cursor.get() >= 1: + show_cursor_helper(False) + + def mouse_calibrate(): + """Start calibration""" + eye_mouse.calib_start() + + def mouse_toggle_control_mouse(): + """Toggles control mouse""" + toggle_control(not config.control_mouse) + + def mouse_toggle_camera_overlay(): + """Toggles camera overlay""" + toggle_camera_overlay(not config.show_camera) + + def mouse_toggle_zoom_mouse(): + """Toggles zoom mouse""" + eye_zoom_mouse.toggle_zoom_mouse(not eye_zoom_mouse.zoom_mouse.enabled) + + def mouse_cancel_zoom_mouse(): + """Cancel zoom mouse if pending""" + if ( + eye_zoom_mouse.zoom_mouse.enabled + and eye_zoom_mouse.zoom_mouse.state != eye_zoom_mouse.STATE_IDLE + ): + eye_zoom_mouse.zoom_mouse.cancel() + + def mouse_trigger_zoom_mouse(): + """Trigger zoom mouse if enabled""" + if eye_zoom_mouse.zoom_mouse.enabled: + eye_zoom_mouse.zoom_mouse.on_pop(eye_zoom_mouse.zoom_mouse.state) + + def mouse_drag(): + """(TEMPORARY) Press and hold/release button 0 depending on state for dragging""" + # todo: fixme temporary fix for drag command + button_down = len(list(ctrl.mouse_buttons_down())) > 0 + print(str(ctrl.mouse_buttons_down())) + if not button_down: + # print("start drag...") + ctrl.mouse_click(button=0, down=True) + # app.notify("drag started") + else: + # print("end drag...") + ctrl.mouse_click(button=0, up=True) + + # app.notify("drag stopped") + + def mouse_sleep(): + """Disables control mouse, zoom mouse, and re-enables cursor""" + eye_zoom_mouse.toggle_zoom_mouse(False) + toggle_control(False) + show_cursor_helper(True) + stop_scroll() + + # todo: fixme temporary fix for drag command + button_down = len(list(ctrl.mouse_buttons_down())) > 0 + if button_down: + ctrl.mouse_click(button=0, up=True) + + def mouse_scroll_down(): + """Scrolls down""" + mouse_scroll(setting_mouse_wheel_down_amount.get())() + + def mouse_scroll_down_continuous(): + """Scrolls down continuously""" + global continuous_scoll_mode + continuous_scoll_mode = "scroll down continuous" + mouse_scroll(setting_mouse_continuous_scroll_amount.get())() + + if scroll_job is None: + start_scroll() + + if setting_mouse_hide_mouse_gui.get() == 0: + gui_wheel.show() + + def mouse_scroll_up(): + """Scrolls up""" + mouse_scroll(-setting_mouse_wheel_down_amount.get())() + + def mouse_scroll_up_continuous(): + """Scrolls up continuously""" + global continuous_scoll_mode + continuous_scoll_mode = "scroll up continuous" + mouse_scroll(-setting_mouse_continuous_scroll_amount.get())() + + if scroll_job is None: + start_scroll() + if setting_mouse_hide_mouse_gui.get() == 0: + gui_wheel.show() + + def mouse_scroll_stop(): + """Stops scrolling""" + stop_scroll() + + def mouse_gaze_scroll(): + """Starts gaze scroll""" + global continuous_scoll_mode + continuous_scoll_mode = "gaze scroll" + start_cursor_scrolling() + if setting_mouse_hide_mouse_gui.get() == 0: + gui_wheel.show() + + def copy_mouse_position(): + """Copy the current mouse position coordinates""" + position = ctrl.mouse_pos() + clip.set_text((repr(position))) + + def mouse_move_center_active_window(): + """move the mouse cursor to the center of the currently active window""" + rect = ui.active_window().rect + ctrl.mouse_move(rect.left + (rect.width / 2), rect.top + (rect.height / 2)) + + +def show_cursor_helper(show): + """Show/hide the cursor""" + if app.platform == "windows": + import ctypes + import winreg + + import win32con + + try: + Registrykey = winreg.OpenKey( + winreg.HKEY_CURRENT_USER, r"Control Panel\Cursors", 0, winreg.KEY_WRITE + ) + + for value_name, value in default_cursor.items(): + if show: + winreg.SetValueEx( + Registrykey, value_name, 0, winreg.REG_EXPAND_SZ, value + ) + else: + winreg.SetValueEx( + Registrykey, value_name, 0, winreg.REG_EXPAND_SZ, hidden_cursor + ) + + winreg.CloseKey(Registrykey) + + ctypes.windll.user32.SystemParametersInfoA( + win32con.SPI_SETCURSORS, 0, None, 0 + ) + + except WindowsError: + print("Unable to show_cursor({})".format(str(show))) + else: + ctrl.cursor_visible(show) + + +def on_pop(active): + pass + # actions.core.repeat_command() + + +noise.register("pop", on_pop) + + +def mouse_scroll(amount): + def scroll(): + global scroll_amount + if (scroll_amount >= 0) == (amount >= 0): + scroll_amount += amount + else: + scroll_amount = amount + actions.mouse_scroll(y=int(amount)) + + return scroll + + +def scroll_continuous_helper(): + global scroll_amount + # print("scroll_continuous_helper") + if scroll_amount and ( + eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_IDLE + ): # or eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_SLEEP): + actions.mouse_scroll(by_lines=False, y=int(scroll_amount / 10)) + + +def start_scroll(): + global scroll_job + scroll_job = cron.interval("60ms", scroll_continuous_helper) + # if eye_zoom_mouse.zoom_mouse.enabled and eye_mouse.mouse.attached_tracker is not None: + # eye_zoom_mouse.zoom_mouse.sleep(True) + + +def gaze_scroll(): + # print("gaze_scroll") + if ( + eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_IDLE + ): # or eye_zoom_mouse.zoom_mouse.state == eye_zoom_mouse.STATE_SLEEP: + x, y = ctrl.mouse_pos() + + # the rect for the window containing the mouse + rect = None + + # on windows, check the active_window first since ui.windows() is not z-ordered + if app.platform == "windows" and ui.active_window().rect.contains(x, y): + rect = ui.active_window().rect + else: + windows = ui.windows() + for w in windows: + if w.rect.contains(x, y): + rect = w.rect + break + + if rect is None: + # print("no window found!") + return + + midpoint = rect.y + rect.height / 2 + amount = int(((y - midpoint) / (rect.height / 10)) ** 3) + actions.mouse_scroll(by_lines=False, y=amount) + + # print(f"gaze_scroll: {midpoint} {rect.height} {amount}") + + +def stop_scroll(): + global scroll_amount, scroll_job, gaze_job + scroll_amount = 0 + if scroll_job: + cron.cancel(scroll_job) + + if gaze_job: + cron.cancel(gaze_job) + + scroll_job = None + gaze_job = None + gui_wheel.hide() + + # if eye_zoom_mouse.zoom_mouse.enabled and eye_mouse.mouse.attached_tracker is not None: + # eye_zoom_mouse.zoom_mouse.sleep(False) + + +def start_cursor_scrolling(): + global scroll_job, gaze_job + stop_scroll() + gaze_job = cron.interval("60ms", gaze_scroll) + # if eye_zoom_mouse.zoom_mouse.enabled and eye_mouse.mouse.attached_tracker is not None: + # eye_zoom_mouse.zoom_mouse.sleep(True) + + +if app.platform == "mac": + from talon import tap + + def on_move(e): + if not config.control_mouse: + buttons = ctrl.mouse_buttons_down() + # print(str(ctrl.mouse_buttons_down())) + if not e.flags & tap.DRAG and buttons: + e.flags |= tap.DRAG + # buttons is a set now + e.button = list(buttons)[0] + e.modify() + + tap.register(tap.MMOVE | tap.HOOK, on_move) diff --git a/talon/user/community/tags/multiple_cursors/multiple_cursors.py b/talon/community/community-cursorless-0.4.0/code/multiple_cursors.py similarity index 86% rename from talon/user/community/tags/multiple_cursors/multiple_cursors.py rename to talon/community/community-cursorless-0.4.0/code/multiple_cursors.py index 0faf527..ce499ac 100644 --- a/talon/user/community/tags/multiple_cursors/multiple_cursors.py +++ b/talon/community/community-cursorless-0.4.0/code/multiple_cursors.py @@ -1,4 +1,4 @@ -from talon import Module +from talon import Context, actions, ui, Module, app mod = Module() mod.tag("multiple_cursors", desc="Tag for enabling generic multiple cursor commands") @@ -24,11 +24,9 @@ class multiple_cursor_actions: def multi_cursor_select_more_occurrences(): """Adds cursor at next occurrence of selection""" - def multi_cursor_skip_occurrence(): - """Skips adding a cursor at next occurrence of selection""" - def multi_cursor_select_all_occurrences(): """Adds cursor at every occurrence of selection""" def multi_cursor_add_to_line_ends(): """Adds cursor at end of every selected line""" + diff --git a/talon/user/community/core/numbers/numbers.py b/talon/community/community-cursorless-0.4.0/code/numbers.py similarity index 59% rename from talon/user/community/core/numbers/numbers.py rename to talon/community/community-cursorless-0.4.0/code/numbers.py index e3e6c32..792df91 100644 --- a/talon/user/community/core/numbers/numbers.py +++ b/talon/community/community-cursorless-0.4.0/code/numbers.py @@ -1,83 +1,34 @@ -import math -from typing import Iterator, Union - -from talon import Context, Module +from talon import Context, Module, actions +from typing import List, Optional, Union, Iterator mod = Module() ctx = Context() -digit_list = "zero one two three four five six seven eight nine".split() -teens = "ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen".split() -tens = "twenty thirty forty fifty sixty seventy eighty ninety".split() +digits = "zero one two three four five six seven eight nine".split() +teens = "eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen".split() +tens = "ten twenty thirty forty fifty sixty seventy eighty ninety".split() scales = "hundred thousand million billion trillion quadrillion quintillion sextillion septillion octillion nonillion decillion".split() -digits_map = {n: i for i, n in enumerate(digit_list)} +digits_map = {n: i for i, n in enumerate(digits)} digits_map["oh"] = 0 -teens_map = {n: i + 10 for i, n in enumerate(teens)} -tens_map = {n: 10 * (i + 2) for i, n in enumerate(tens)} -scales_map = {n: 10 ** (3 * (i + 1)) for i, n in enumerate(scales[1:])} +teens_map = {n: i + 11 for i, n in enumerate(teens)} +tens_map = {n: 10 * (i + 1) for i, n in enumerate(tens)} +scales_map = {n: 10 ** (3 * (i+1)) for i, n in enumerate(scales[1:])} scales_map["hundred"] = 100 -# Maps number words to integers values that are used to compute numeric values. numbers_map = digits_map.copy() numbers_map.update(teens_map) numbers_map.update(tens_map) numbers_map.update(scales_map) - -def get_spoken_form_under_one_hundred( - start, - end, - *, - include_oh_variant_for_single_digits=False, - include_default_variant_for_single_digits=False, - include_double_digits=False, -): - """Helper function to get dictionary of spoken forms for non-negative numbers in the range [start, end] under 100""" - - result = {} - - for value in range(start, end + 1): - digit_index = value % 10 - if value < 10: - # oh prefix digit: "oh five"-> `05` - if include_oh_variant_for_single_digits: - result[f"oh {digit_list[digit_index]}"] = f"0{value}" - # default digit: "five" -> `5` - if include_default_variant_for_single_digits: - result[f"{digit_list[digit_index]}"] = f"{value}" - elif value < 20: - teens_index = value - 10 - result[f"{teens[teens_index]}"] = f"{value}" - elif value < 100: - tens_index = math.floor(value / 10) - 2 - if digit_index > 0: - spoken_form = f"{tens[tens_index]} {digit_list[digit_index]}" - else: - spoken_form = f"{tens[tens_index]}" - - result[spoken_form] = f"{value}" - else: - raise ValueError(f"Value {value} is not in the range [0, 100)") - - # double digits: "five one" -> `51` - if include_double_digits and value > 9: - tens_index = math.floor(value / 10) - spoken_form = f"{digit_list[tens_index]} {digit_list[digit_index]}" - result[spoken_form] = f"{value}" - - return result - - -def parse_number(l: list[str]) -> str: +def parse_number(l: List[str]) -> str: """Parses a list of words into a number/digit string.""" l = list(scan_small_numbers(l)) for scale in scales: l = parse_scale(scale, l) return "".join(str(n) for n in l) - -def scan_small_numbers(l: list[str]) -> Iterator[Union[str, int]]: +def scan_small_numbers(l: List[str]) -> Iterator[Union[str,int]]: """ Takes a list of number words, yields a generator of mixed numbers & strings. Translates small number terms (<100) into corresponding numbers. @@ -93,7 +44,7 @@ def scan_small_numbers(l: list[str]) -> Iterator[Union[str, int]]: while l: n = l.pop() # fuse tens onto digits, eg. "twenty", "one" -> 21 - if n in tens_map and l and digits_map.get(l[-1], 0) != 0: + if n in tens_map and n != "ten" and l and digits_map.get(l[-1], 0) != 0: d = l.pop() yield numbers_map[n] + numbers_map[d] # turn small number terms into corresponding numbers @@ -102,8 +53,7 @@ def scan_small_numbers(l: list[str]) -> Iterator[Union[str, int]]: else: yield n - -def parse_scale(scale: str, l: list[Union[str, int]]) -> list[Union[str, int]]: +def parse_scale(scale: str, l: List[Union[str,int]]) -> List[Union[str,int]]: """Parses a list of mixed numbers & strings for occurrences of the following pattern: @@ -130,7 +80,7 @@ def parse_scale(scale: str, l: list[Union[str, int]]) -> list[Union[str, int]]: # haven't processed yet; this strategy means that "thousand hundred" # gets parsed as 1,100 instead of 100,000, but "hundred thousand" is # parsed correctly as 100,000. - before = 1 # default multiplier + before = 1 # default multiplier if left and isinstance(left[-1], int) and left[-1] != 0: before = left.pop() @@ -140,8 +90,7 @@ def parse_scale(scale: str, l: list[Union[str, int]]) -> list[Union[str, int]]: after = "" while right and isinstance(right[0], int): next = after + str(right[0]) - if len(next) >= scale_digits: - break + if len(next) >= scale_digits: break after = next right.pop(0) after = int(after) if after else 0 @@ -153,20 +102,17 @@ def parse_scale(scale: str, l: list[Union[str, int]]) -> list[Union[str, int]]: return left - def split_list(value, l: list) -> Iterator: """Splits a list by occurrences of a given value.""" start = 0 while True: - try: - i = l.index(value, start) - except ValueError: - break + try: i = l.index(value, start) + except ValueError: break yield l[start:i] - start = i + 1 + start = i+1 yield l[start:] - + # # ---------- TESTS (uncomment to run) ---------- # def test_number(expected, string): # print('testing:', string) @@ -202,72 +148,39 @@ def split_list(value, l: list) -> Iterator: # #test_number(100001010, "one million ten ten") # #test_number(1050006000, "one hundred thousand and five thousand and six thousand") - + # ---------- CAPTURES ---------- -alt_digits = "(" + "|".join(digits_map.keys()) + ")" -alt_teens = "(" + "|".join(teens_map.keys()) + ")" -alt_tens = "(" + "|".join(tens_map.keys()) + ")" -alt_scales = "(" + "|".join(scales_map.keys()) + ")" +alt_digits = "(" + ("|".join(digits_map.keys())) + ")" +alt_teens = "(" + ("|".join(teens_map.keys())) + ")" +alt_tens = "(" + ("|".join(tens_map.keys())) + ")" +alt_scales = "(" + ("|".join(scales_map.keys())) + ")" number_word = "(" + "|".join(numbers_map.keys()) + ")" -# don't allow numbers to start with scale words like "hundred", "thousand", etc -leading_words = numbers_map.keys() - scales_map.keys() -leading_words -= {"oh", "o"} # comment out to enable bare/initial "oh" -number_word_leading = f"({'|'.join(leading_words)})" - - -mod.list("number_small", "List of small (0-99) numbers") -mod.tag("unprefixed_numbers", desc="Dont require prefix when saying a number") -ctx.lists["user.number_small"] = get_spoken_form_under_one_hundred( - 0, - 99, - include_default_variant_for_single_digits=True, - include_double_digits=True, -) - # TODO: allow things like "double eight" for 88 @ctx.capture("digit_string", rule=f"({alt_digits} | {alt_teens} | {alt_tens})+") -def digit_string(m) -> str: - return parse_number(list(m)) - +def digit_string(m) -> str: return parse_number(list(m)) @ctx.capture("digits", rule="") def digits(m) -> int: """Parses a phrase representing a digit sequence, returning it as an integer.""" return int(m.digit_string) - -@mod.capture(rule=f"{number_word_leading} ([and] {number_word})*") +@mod.capture(rule=f"{number_word}+ (and {number_word}+)*") def number_string(m) -> str: """Parses a number phrase, returning that number as a string.""" return parse_number(list(m)) - -@mod.capture(rule=" ((point | dot) )+") -def number_decimal_string(m) -> str: - """Parses a decimal number phrase, returning that number as a string.""" - return ".".join(m.number_string_list) - - @ctx.capture("number", rule="") def number(m) -> int: """Parses a number phrase, returning it as an integer.""" return int(m.number_string) - @ctx.capture("number_signed", rule=f"[negative|minus] ") def number_signed(m): number = m[-1] return -number if (m[0] in ["negative", "minus"]) else number - -@ctx.capture("number_small", rule="{user.number_small}") -def number_small(m) -> int: - return int(m.number_small) - - -@mod.capture(rule=f"[negative|minus] ") -def number_signed_small(m) -> int: - """Parses an integer between -99 and 99.""" - number = m[-1] - return -number if (m[0] in ["negative", "minus"]) else number +@ctx.capture( + "number_small", rule=f"({alt_digits} | {alt_teens} | {alt_tens} [{alt_digits}])" +) +def number_small(m): return int(parse_number(list(m))) diff --git a/talon/user/community/core/numbers/ordinals.py b/talon/community/community-cursorless-0.4.0/code/ordinals.py similarity index 57% rename from talon/user/community/core/numbers/ordinals.py rename to talon/community/community-cursorless-0.4.0/code/ordinals.py index 4a68fee..f5fb3b9 100644 --- a/talon/user/community/core/numbers/ordinals.py +++ b/talon/community/community-cursorless-0.4.0/code/ordinals.py @@ -1,4 +1,20 @@ -from talon import Context, Module +from talon import Context, Module, actions, app, ui + + +def ordinal(n): + """ + Convert an integer into its ordinal representation:: + ordinal(0) => '0th' + ordinal(3) => '3rd' + ordinal(122) => '122nd' + ordinal(213) => '213th' + """ + n = int(n) + suffix = ["th", "st", "nd", "rd", "th"][min(n % 10, 4)] + if 11 <= (n % 100) <= 13: + suffix = "th" + return str(n) + suffix + # The primitive ordinal words in English below a hundred. ordinal_words = { @@ -36,7 +52,6 @@ tens_words = "zero ten twenty thirty forty fifty sixty seventy eighty ninety".sp # ordinal_numbers maps ordinal words into their corresponding numbers. ordinal_numbers = {} ordinal_small = {} - for n in range(1, 100): if n in ordinal_words: word = ordinal_words[n] @@ -45,28 +60,28 @@ for n in range(1, 100): assert 1 < tens < 10, "we have already handled all ordinals < 20" assert 0 < units, "we have already handled all ordinals divisible by ten" word = f"{tens_words[tens]} {ordinal_words[units]}" + if n <= 20: - ordinal_small[word] = str(n) - ordinal_numbers[word] = str(n) + ordinal_small[word] = n + ordinal_numbers[word] = n mod = Module() ctx = Context() +mod.list("ordinals", desc="list of ordinals") +mod.list("ordinals_small", desc="list of ordinals small (1-20)") -mod.list("ordinals", "List of ordinals (1-99)") -mod.list("ordinals_small", "List of small ordinals (1-20)") - -ctx.lists["user.ordinals"] = ordinal_numbers -ctx.lists["user.ordinals_small"] = ordinal_small +ctx.lists["self.ordinals"] = ordinal_numbers.keys() +ctx.lists["self.ordinals_small"] = ordinal_small.keys() -@mod.capture(rule="{user.ordinals}") +@mod.capture(rule="{self.ordinals}") def ordinals(m) -> int: - """Returns a single ordinal as an integer""" - return int(m.ordinals) + """Returns a single ordinal as a digit""" + return int(ordinal_numbers[m[0]]) -@mod.capture(rule="{user.ordinals_small}") +@mod.capture(rule="{self.ordinals_small}") def ordinals_small(m) -> int: - """Returns a single small ordinal as an integer""" - return int(m.ordinals_small) + """Returns a single ordinal as a digit""" + return int(ordinal_numbers[m[0]]) diff --git a/talon/community/community-cursorless-0.4.0/code/parrot.py b/talon/community/community-cursorless-0.4.0/code/parrot.py new file mode 100644 index 0000000..4643926 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/parrot.py @@ -0,0 +1,14 @@ +from talon import Module, Context, actions, app + +mod = Module() + + +@mod.action_class +class Actions: + def dental_click(): + """Responds to an alveolar click""" + app.notify("Dental click") + + def postalveolar_click(): + """Responds to an postalveolar click""" + actions.core.repeat_phrase(1) \ No newline at end of file diff --git a/talon/user/community/core/text/phrase_history.py b/talon/community/community-cursorless-0.4.0/code/phrase_history.py similarity index 66% rename from talon/user/community/core/text/phrase_history.py rename to talon/community/community-cursorless-0.4.0/code/phrase_history.py index 062bf3b..f06f50d 100644 --- a/talon/user/community/core/text/phrase_history.py +++ b/talon/community/community-cursorless-0.4.0/code/phrase_history.py @@ -1,6 +1,5 @@ -import logging - from talon import Module, actions, imgui +import logging mod = Module() @@ -9,7 +8,6 @@ phrase_history = [] phrase_history_length = 40 phrase_history_display_length = 40 - @mod.action_class class Actions: def get_last_phrase() -> str: @@ -18,10 +16,8 @@ class Actions: def get_recent_phrase(number: int) -> str: """Gets the nth most recent phrase""" - try: - return phrase_history[number - 1] - except IndexError: - return "" + try: return phrase_history[number-1] + except IndexError: return "" def clear_last_phrase(): """Clears the last phrase""" @@ -33,8 +29,9 @@ class Actions: if not phrase_history: logging.warning("clear_last_phrase(): No last phrase to clear!") return - for _ in phrase_history.pop(0): - actions.key("backspace") + for _ in phrase_history[0]: + actions.edit.delete() + phrase_history.pop(0) def select_last_phrase(): """Selects the last phrase""" @@ -44,14 +41,6 @@ class Actions: for _ in phrase_history[0]: actions.edit.extend_left() - def before_last_phrase(): - """Moves left before the last phrase""" - try: - for _ in phrase_history.pop(0): - actions.edit.left() - except IndexError: - logging.warning("before_last_phrase(): No last phrase to move before!") - def add_phrase_to_history(text: str): """Adds a phrase to the phrase history""" global phrase_history @@ -60,26 +49,12 @@ class Actions: def toggle_phrase_history(): """Toggles list of recent phrases""" - if gui.showing: - gui.hide() - else: - gui.show() - - def phrase_history_hide(): - """Hides the recent phrases window""" - - gui.hide() - + if gui.showing: gui.hide() + else: gui.show() @imgui.open() def gui(gui: imgui.GUI): gui.text("Recent phrases") - gui.text("Say 'recent repeat ' retype a phrase on this list.") - gui.text("Say 'recent copy ' to copy a phrase from this list.") gui.line() for index, text in enumerate(phrase_history[:phrase_history_display_length], 1): gui.text(f"{index}: {text}") - - gui.spacer() - if gui.button("Recent close"): - actions.user.phrase_history_hide() diff --git a/talon/community/community-cursorless-0.4.0/code/screenshot.py b/talon/community/community-cursorless-0.4.0/code/screenshot.py new file mode 100644 index 0000000..09c5f7d --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/screenshot.py @@ -0,0 +1,83 @@ +from talon import Module, screen, ui, actions, clip, app, settings +from datetime import datetime +import os, subprocess + +active_platform = app.platform +default_command = None +if active_platform == "windows": + + default_folder = os.path.expanduser(os.path.join("~", r"OneDrive\Desktop")) + # this is probably not the correct way to check for onedrive, quick and dirty + if not os.path.isdir(default_folder): + default_folder = os.path.join("~", "Desktop") +elif active_platform == "mac": + default_folder = os.path.join("~", "Desktop") +elif active_platform == "linux": + default_folder = "~" + default_command = "scrot -s" + +mod = Module() +screenshot_folder = mod.setting( + "screenshot_folder", + type=str, + default=default_folder, + desc="Where to save screenshots. Note this folder must exist.", +) +screenshot_selection_command = mod.setting( + "screenshot_selection_command", + type=str, + default=default_command, + desc="Commandline trigger for taking a selection of the screen. By default, only linux uses this.", +) + + +def get_screenshot_path(): + filename = "screenshot-%s.png" % datetime.now().strftime("%Y%m%d%H%M%S") + folder_path = screenshot_folder.get() + path = os.path.expanduser(os.path.join(folder_path, filename)) + return os.path.normpath(path) + + +@mod.action_class +class Actions: + def screenshot(): + """takes a screenshot of the entire screen and saves it to the desktop as screenshot.png""" + img = screen.capture_rect(screen.main_screen().rect) + path = get_screenshot_path() + + img.write_file(path) + app.notify(subtitle="Screenshot: %s" % path) + + def screenshot_window(): + """takes a screenshot of the current window and says it to the desktop as screenshot.png""" + img = screen.capture_rect(ui.active_window().rect) + path = get_screenshot_path() + img.write_file(path) + app.notify(subtitle="Screenshot: %s" % path) + + def screenshot_selection(): + """triggers an application is capable of taking a screenshot of a portion of the screen""" + command = screenshot_selection_command.get() + if command: + path = get_screenshot_path() + command = command.split() + command.append(path) + subprocess.Popen(command) + app.notify(subtitle="Screenshot: %s" % path) + else: + if active_platform == "windows": + actions.key("super-shift-s") + elif active_platform == "mac": + actions.key("ctrl-shift-cmd-4") + # linux is handled by the command by default + # elif active_platform == "linux": + + def screenshot_clipboard(): + """takes a screenshot of the entire screen and saves it to the clipboard""" + img = screen.capture_rect(screen.main_screen().rect) + clip.set_image(img) + + def screenshot_window_clipboard(): + """takes a screenshot of the window and saves it to the clipboard""" + img = screen.capture_rect(ui.active_window().rect) + clip.set_image(img) diff --git a/talon/community/community-cursorless-0.4.0/code/search_engines.py b/talon/community/community-cursorless-0.4.0/code/search_engines.py new file mode 100644 index 0000000..9e313b3 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/search_engines.py @@ -0,0 +1,33 @@ +from .user_settings import get_list_from_csv +from talon import Module, Context +from urllib.parse import quote_plus +import webbrowser + +mod = Module() +mod.list( + "search_engine", + desc="A search engine. Any instance of %s will be replaced by query text", +) + +_search_engine_defaults = { + "amazon": "https://www.amazon.com/s/?field-keywords=%s", + "google": "https://www.google.com/search?q=%s", + "map": "https://maps.google.com/maps?q=%s", + "scholar": "https://scholar.google.com/scholar?q=%s", + "wiki": "https://en.wikipedia.org/w/index.php?search=%s", +} + +ctx = Context() +ctx.lists["self.search_engine"] = get_list_from_csv( + "search_engines.csv", + headers=("URL Template", "Name"), + default=_search_engine_defaults, +) + + +@mod.action_class +class Actions: + def search_with_search_engine(search_template: str, search_text: str): + """Search a search engine for given text""" + url = search_template.replace("%s", quote_plus(search_text)) + webbrowser.open(url) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/code/slow_mode.py b/talon/community/community-cursorless-0.4.0/code/slow_mode.py new file mode 100644 index 0000000..33c1284 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/slow_mode.py @@ -0,0 +1,4 @@ +from talon import Module + +mod = Module() +mod.mode("slow") \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/code/snippet_watcher.py b/talon/community/community-cursorless-0.4.0/code/snippet_watcher.py new file mode 100644 index 0000000..1c9b5e1 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/snippet_watcher.py @@ -0,0 +1,95 @@ +# from talon import app, fs +# import os, csv, re +# from os.path import isfile, join +# from itertools import islice +# from pathlib import Path +# import json +# from jsoncomment import JsonComment + +# parser = JsonComment(json) + +# pattern = re.compile(r"[A-Z][a-z]*|[a-z]+|\d") + +# # todo: should this be an action that lives elsewhere?? +# def create_spoken_form(text, max_len=15): +# return " ".join(list(islice(pattern.findall(text), max_len))) + + +# class snippet_watcher: +# directories = {} +# snippet_dictionary = {} +# callback_function = None +# file_snippet_cache = {} + +# def __notify(self): +# # print("NOTIFY") +# self.snippet_dictionary = {} +# for key, val in self.file_snippet_cache.items(): +# self.snippet_dictionary.update(val) + +# # print(str(self.snippet_dictionary)) +# if self.callback_function: +# self.callback_function(self.snippet_dictionary) + +# def __update_all_snippets(self): +# for directory, file_list in self.directories.items(): +# if os.path.isdir(directory): +# for f in file_list: +# path = os.path.join(directory, f) +# self.__process_file(path) + +# # print(str(self.snippet_dictionary)) +# self.__notify() + +# def __process_file(self, name): +# path_obj = Path(name) +# directory = os.path.normpath(path_obj.parents[0]) +# file_name = path_obj.name +# file_type = path_obj.suffix +# self.file_snippet_cache[str(path_obj)] = {} + +# print("{}, {}, {}, {}".format(name, directory, file_name, file_type)) +# if directory in self.directories and file_name in self.directories[directory]: +# if file_type.lower() == ".json": +# jsonDict = {} + +# if os.path.isfile(name): +# with open(name, "r") as f: +# jsonDict = parser.load(f) +# # else: +# # print("snippet_watcher.py: File {} does not exist".format(directory)) + +# for key, data in jsonDict.items(): +# self.file_snippet_cache[str(path_obj)][ +# create_spoken_form(key) +# ] = data["prefix"] + +# def __on_fs_change(self, name, flags): +# self.__process_file(name) + +# # print(str(self.snippet_dictionary)) +# self.__notify() + +# def __init__(self, dirs, callback): +# self.directories = dirs +# self.callback_function = callback +# self.snippet_dictionary = {} +# self.file_snippet_cache = {} +# # none = process all directories +# self.__update_all_snippets() + +# for directory in self.directories.keys(): +# if os.path.isdir(directory): +# fs.watch(directory, self.__on_fs_change) +# # else: +# # print( +# # "snippet_watcher.py: directory {} does not exist".format(directory) +# # ) + + +# # Test = snippet_watcher( +# # {os.path.expandvars(r"%AppData%\Code\User\snippets"): ["python.json"]}, +# # None +# # # {os.path.expandvars(r"%USERPROFILE%\.vscode\extensions\ms-dotnettools.csharp-1.22.1\snippets": ["csharp.json"]}, +# # ) +# # print(str(Test.directories)) diff --git a/talon/community/community-cursorless-0.4.0/code/snippets.py b/talon/community/community-cursorless-0.4.0/code/snippets.py new file mode 100644 index 0000000..966b1d9 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/snippets.py @@ -0,0 +1,41 @@ +# defines placeholder actions and captures for ide-specific snippet functionality +from talon import Module, actions, app, Context, imgui, registry + +mod = Module() +mod.tag("snippets", desc="Tag for enabling code snippet-related commands") +mod.list("snippets", desc="List of code snippets") + + +@imgui.open() +def gui(gui: imgui.GUI): + gui.text("snippets") + gui.line() + + if "user.snippets" in registry.lists: + function_list = sorted(registry.lists["user.snippets"][0].keys()) + # print(str(registry.lists["user.snippets"])) + + # print(str(registry.lists["user.code_functions"])) + if function_list: + for i, entry in enumerate(function_list): + gui.text("{}".format(entry, function_list)) + + +@mod.action_class +class Actions: + def snippet_search(text: str): + """Triggers the program's snippet search""" + + def snippet_insert(text: str): + """Inserts a snippet""" + + def snippet_create(): + """Triggers snippet creation""" + + def snippet_toggle(): + """Toggles UI for available snippets""" + if gui.showing: + gui.hide() + else: + gui.show() + diff --git a/talon/user/community/tags/splits/splits.py b/talon/community/community-cursorless-0.4.0/code/splits.py similarity index 86% rename from talon/user/community/tags/splits/splits.py rename to talon/community/community-cursorless-0.4.0/code/splits.py index 12fbd63..ab2b6c6 100644 --- a/talon/user/community/tags/splits/splits.py +++ b/talon/community/community-cursorless-0.4.0/code/splits.py @@ -1,4 +1,4 @@ -from talon import Module +from talon import Module, actions, app mod = Module() mod.tag("splits", desc="Tag for enabling generic window split commands") @@ -27,12 +27,6 @@ class Actions: def split_flip(): """Flips the orietation of the active split""" - def split_maximize(): - """Maximizes the active split""" - - def split_reset(): - """Resets the split sizes""" - def split_window(): """Splits the window""" diff --git a/talon/community/community-cursorless-0.4.0/code/sql.py b/talon/community/community-cursorless-0.4.0/code/sql.py new file mode 100644 index 0000000..e69de29 diff --git a/talon/community/community-cursorless-0.4.0/code/switcher.py b/talon/community/community-cursorless-0.4.0/code/switcher.py new file mode 100644 index 0000000..b137649 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/switcher.py @@ -0,0 +1,346 @@ +import os +import re +import time + +import talon +from .create_spoken_forms import create_spoken_forms +from talon import Context, Module, app, imgui, ui, fs, actions +from glob import glob +from itertools import islice +from pathlib import Path + +# Construct at startup a list of overides for application names (similar to how homophone list is managed) +# ie for a given talon recognition word set `one note`, recognized this in these switcher functions as `ONENOTE` +# the list is a comma seperated `, ` +# TODO: Consider put list csv's (homophones.csv, app_name_overrides.csv) files together in a seperate directory,`knausj_talon/lists` +cwd = os.path.dirname(os.path.realpath(__file__)) +overrides_directory = os.path.join(cwd, "app_names") +override_file_name = f"app_name_overrides.{talon.app.platform}.csv" +override_file_path = os.path.join(overrides_directory, override_file_name) + +mod = Module() +mod.list("running", desc="all running applications") +mod.list("launch", desc="all launchable applications") +ctx = Context() + +# a list of the current overrides +overrides = {} + +# a list of the currently running application names +running_application_dict = {} + + +mac_application_directories = [ + "/Applications", + "/Applications/Utilities", + "/System/Applications", + "/System/Applications/Utilities", +] + +# windows_application_directories = [ +# "%AppData%/Microsoft/Windows/Start Menu/Programs", +# "%ProgramData%/Microsoft/Windows/Start Menu/Programs", +# "%AppData%/Microsoft/Internet Explorer/Quick Launch/User Pinned/TaskBar", +# ] + +words_to_exclude = [ + "and", + "zero", + "one", + "two", + "three", + "for", + "four", + "five", + "six", + "seven", + "eight", + "nine", + "microsoft", + "windows", + "Windows", +] + +# windows-specific logic +if app.platform == "windows": + import os + import ctypes + import pywintypes + import pythoncom + import winerror + + try: + import winreg + except ImportError: + # Python 2 + import _winreg as winreg + + bytes = lambda x: str(buffer(x)) + + from ctypes import wintypes + from win32com.shell import shell, shellcon + from win32com.propsys import propsys, pscon + + # KNOWNFOLDERID + # https://msdn.microsoft.com/en-us/library/dd378457 + # win32com defines most of these, except the ones added in Windows 8. + FOLDERID_AppsFolder = pywintypes.IID("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}") + + # win32com is missing SHGetKnownFolderIDList, so use ctypes. + + _ole32 = ctypes.OleDLL("ole32") + _shell32 = ctypes.OleDLL("shell32") + + _REFKNOWNFOLDERID = ctypes.c_char_p + _PPITEMIDLIST = ctypes.POINTER(ctypes.c_void_p) + + _ole32.CoTaskMemFree.restype = None + _ole32.CoTaskMemFree.argtypes = (wintypes.LPVOID,) + + _shell32.SHGetKnownFolderIDList.argtypes = ( + _REFKNOWNFOLDERID, # rfid + wintypes.DWORD, # dwFlags + wintypes.HANDLE, # hToken + _PPITEMIDLIST, + ) # ppidl + + def get_known_folder_id_list(folder_id, htoken=None): + if isinstance(folder_id, pywintypes.IIDType): + folder_id = bytes(folder_id) + pidl = ctypes.c_void_p() + try: + _shell32.SHGetKnownFolderIDList(folder_id, 0, htoken, ctypes.byref(pidl)) + return shell.AddressAsPIDL(pidl.value) + except WindowsError as e: + if e.winerror & 0x80070000 == 0x80070000: + # It's a WinAPI error, so re-raise it, letting Python + # raise a specific exception such as FileNotFoundError. + raise ctypes.WinError(e.winerror & 0x0000FFFF) + raise + finally: + if pidl: + _ole32.CoTaskMemFree(pidl) + + def enum_known_folder(folder_id, htoken=None): + id_list = get_known_folder_id_list(folder_id, htoken) + folder_shell_item = shell.SHCreateShellItem(None, None, id_list) + items_enum = folder_shell_item.BindToHandler( + None, shell.BHID_EnumItems, shell.IID_IEnumShellItems + ) + result = [] + for item in items_enum: + # print(item.GetDisplayName(shellcon.SIGDN_NORMALDISPLAY)) + result.append(item.GetDisplayName(shellcon.SIGDN_NORMALDISPLAY)) + + return result + + def list_known_folder(folder_id, htoken=None): + result = [] + for item in enum_known_folder(folder_id, htoken): + result.append(item.GetDisplayName(shellcon.SIGDN_NORMALDISPLAY)) + result.sort(key=lambda x: x.upper()) + return result + + +@mod.capture(rule="{self.running}") # | )") +def running_applications(m) -> str: + "Returns a single application name" + try: + return m.running + except AttributeError: + return m.text + + +@mod.capture(rule="{self.launch}") +def launch_applications(m) -> str: + "Returns a single application name" + return m.launch + + +def update_lists(): + global running_application_dict + running_application_dict = {} + running = {} + for cur_app in ui.apps(background=False): + name = cur_app.name + + spoken_forms = create_spoken_forms(name, words_to_exclude=words_to_exclude) + for spoken_form in spoken_forms: + if spoken_form not in running: + running[spoken_form] = cur_app.name + + running_application_dict[cur_app.name] = True + print(running) + + for override in overrides: + running[override] = overrides[override] + + lists = { + "self.running": running, + # "self.launch": launch, + } + + # batch update lists + ctx.lists.update(lists) + + +def update_overrides(name, flags): + """Updates the overrides list""" + global overrides + overrides = {} + + if name is None or name == override_file_path: + # print("update_overrides") + with open(override_file_path, "r") as f: + for line in f: + line = line.rstrip() + line = line.split(",") + if len(line) == 2: + overrides[line[0].lower()] = line[1].strip() + + update_lists() + + +@mod.action_class +class Actions: + def get_running_app(name: str) -> ui.App: + """Get the first available running app with `name`.""" + # We should use the capture result directly if it's already in the list + # of running applications. Otherwise, name is from and we + # can be a bit fuzzier + if name not in running_application_dict: + if len(name) < 3: + raise RuntimeError( + f'Skipped getting app: "{name}" has less than 3 chars.' + ) + for running_name, full_application_name in ctx.lists[ + "self.running" + ].items(): + if running_name == name or running_name.lower().startswith( + name.lower() + ): + name = full_application_name + break + for app in ui.apps(): + if app.name == name and not app.background: + return app + raise RuntimeError(f'App not running: "{name}"') + + def switcher_focus(name: str): + """Focus a new application by name""" + app = actions.user.get_running_app(name) + app.focus() + + # Hacky solution to do this reliably on Mac. + timeout = 5 + t1 = time.monotonic() + if talon.app.platform == "mac": + while ui.active_app() != app and time.monotonic() - t1 < timeout: + time.sleep(0.1) + + def switcher_launch(path: str): + """Launch a new application by path""" + if app.platform == "windows": + is_valid_path = False + try: + current_path = Path(path) + is_valid_path = current_path.is_file() + # print("valid path: {}".format(is_valid_path)) + + except: + # print("invalid path") + is_valid_path = False + + if is_valid_path: + # print("path: " + path) + ui.launch(path=path) + + else: + # print("envelop") + actions.key("super-s") + actions.sleep("300ms") + actions.insert("apps: {}".format(path)) + actions.sleep("150ms") + actions.key("enter") + + else: + ui.launch(path=path) + + def switcher_toggle_running(): + """Shows/hides all running applications""" + if gui.showing: + gui.hide() + else: + gui.show() + + def switcher_hide_running(): + """Hides list of running applications""" + gui.hide() + + +@imgui.open() +def gui(gui: imgui.GUI): + gui.text("Names of running applications") + gui.line() + for line in ctx.lists["self.running"]: + gui.text(line) + + +def update_launch_list(): + launch = {} + if app.platform == "mac": + for base in mac_application_directories: + if os.path.isdir(base): + for name in os.listdir(base): + path = os.path.join(base, name) + name = name.rsplit(".", 1)[0].lower() + launch[name] = path + words = name.split(" ") + for word in words: + if word and word not in launch: + if len(name) > 6 and len(word) < 3: + continue + launch[word] = path + + elif app.platform == "windows": + shortcuts = enum_known_folder(FOLDERID_AppsFolder) + # str(shortcuts) + for name in shortcuts: + # print("hit: " + name) + # print(name) + # name = path.rsplit("\\")[-1].split(".")[0].lower() + if "install" not in name: + spoken_form = create_spoken_forms(name) + # print(spoken_form) + launch[spoken_form] = name + words = spoken_form.split(" ") + for word in words: + if word not in words_to_exclude and word not in launch: + if len(name) > 6 and len(word) < 3: + continue + launch[word] = name + + ctx.lists["self.launch"] = launch + + +def ui_event(event, arg): + if event in ("app_launch", "app_close"): + update_lists() + + +# Currently update_launch_list only does anything on mac, so we should make sure +# to initialize user launch to avoid getting "List not found: user.launch" +# errors on other platforms. +ctx.lists["user.launch"] = {} +ctx.lists["user.running"] = {} + +# Talon starts faster if you don't use the `talon.ui` module during launch +def on_ready(): + update_overrides(None, None) + fs.watch(overrides_directory, update_overrides) + update_launch_list() + ui.register("", ui_event) + + +# NOTE: please update this from "launch" to "ready" in Talon v0.1.5 +app.register("ready", on_ready) diff --git a/talon/community/community-cursorless-0.4.0/code/tabs.py b/talon/community/community-cursorless-0.4.0/code/tabs.py new file mode 100644 index 0000000..546829f --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/tabs.py @@ -0,0 +1,11 @@ +from talon import Context, actions, ui, Module, app + +mod = Module() + +@mod.action_class +class tab_actions: + def tab_jump(number: int): + """Jumps to the specified tab""" + + def tab_final(): + """Jumps to the final tab""" diff --git a/talon/user/community/core/tags.py b/talon/community/community-cursorless-0.4.0/code/tags.py similarity index 74% rename from talon/user/community/core/tags.py rename to talon/community/community-cursorless-0.4.0/code/tags.py index a9d2f76..f9e44a7 100644 --- a/talon/user/community/core/tags.py +++ b/talon/community/community-cursorless-0.4.0/code/tags.py @@ -1,17 +1,17 @@ -from talon import Module +from talon import Context, Module mod = Module() tagList = [ + "debugger", "disassembler", + "gdb", "git", # commandline tag for git commands "ida", "tabs", - "generic_windows_shell", - "generic_unix_shell", - "readline", "taskwarrior", # commandline tag for taskwarrior commands "tmux", + "windbg", ] for entry in tagList: mod.tag(entry, f"tag to load {entry} and/or related plugins ") diff --git a/talon/community/community-cursorless-0.4.0/code/talon_helpers.py b/talon/community/community-cursorless-0.4.0/code/talon_helpers.py new file mode 100644 index 0000000..e8d3e30 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/talon_helpers.py @@ -0,0 +1,55 @@ +from talon import Context, actions, ui, Module, app, clip +import os +import re +from itertools import islice + + +mod = Module() +pattern = re.compile(r"[A-Z][a-z]*|[a-z]+|\d") + +# todo: should this be an action that lives elsewhere?? +def create_name(text, max_len=20): + return "_".join(list(islice(pattern.findall(text), max_len))).lower() + + +@mod.action_class +class Actions: + def talon_add_context_clipboard_python(): + """Adds os-specific context info to the clipboard for the focused app for .py files. Assumes you've a Module named mod declared.""" + friendly_name = actions.app.name() + # print(actions.app.executable()) + executable = actions.app.executable().split(os.path.sep)[-1] + app_name = create_name(friendly_name.replace(".exe", "")) + if app.platform == "mac": + result = 'mod.apps.{} = """\nos: {}\nand app.bundle: {}\n"""'.format( + app_name, app.platform, actions.app.bundle() + ) + elif app.platform == "windows": + result = 'mod.apps.{} = """\nos: windows\nand app.name: {}\nos: windows\nand app.exe: {}\n"""'.format( + app_name, friendly_name, executable + ) + else: + result = 'mod.apps.{} = """\nos: {}\nand app.name: {}\n"""'.format( + app_name, app.platform, friendly_name + ) + + clip.set_text(result) + + def talon_add_context_clipboard(): + """Adds os-specific context info to the clipboard for the focused app for .talon files""" + friendly_name = actions.app.name() + # print(actions.app.executable()) + executable = actions.app.executable().split(os.path.sep)[-1] + if app.platform == "mac": + result = "os: {}\nand app.bundle: {}\n".format( + app.platform, actions.app.bundle() + ) + elif app.platform == "windows": + result = "os: windows\nand app.name: {}\nos: windows\nand app.exe: {}\n".format( + friendly_name, executable + ) + else: + result = "os: {}\nand app.name: {}\n".format(app.platform, friendly_name) + + clip.set_text(result) + diff --git a/talon/community/community-cursorless-0.4.0/code/terms.py b/talon/community/community-cursorless-0.4.0/code/terms.py new file mode 100644 index 0000000..e5e5d7c --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/terms.py @@ -0,0 +1,42 @@ +""" +Stores terms that are used in many different places +""" +from talon import Module + +mod = Module() + +SELECT = "take" +TELEPORT = "pop" +OPERATOR = "do" +DELETE = "chuck" +FIND = "scout" + + +@mod.capture(rule=SELECT) +def select(m) -> str: + """Term for select""" + return str(m) + + +@mod.capture(rule=TELEPORT) +def teleport(m) -> str: + """Verb to use for commands that teleport the cursor to another place""" + return str(m) + + +@mod.capture(rule=OPERATOR) +def operator(m) -> str: + """Prefix for operators""" + return str(m) + + +@mod.capture(rule=DELETE) +def delete(m) -> str: + """Verb to use for commands that delete things""" + return str(m) + + +@mod.capture(rule=FIND) +def find(m) -> str: + """Verb to use for commands that find things""" + return str(m) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/code/todo_list.py b/talon/community/community-cursorless-0.4.0/code/todo_list.py new file mode 100644 index 0000000..5e8e221 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/todo_list.py @@ -0,0 +1,39 @@ +from talon import Context, actions, ui, Module, app + +mod = Module() +mod.tag("todo_list", desc="Tag for enabling generic todo list commands") + + +@mod.action_class +class Actions: + def mark_complete(): + """Mark a todo as completed""" + pass + + def mark_cancelled(): + """Mark a todo as cancelled""" + pass + + def show_today(): + """Show today""" + pass + + def show_inbox(): + """Show inbox""" + pass + + def show_upcoming(): + """Show upcoming""" + pass + + def show_anytime(): + """Show anytime""" + pass + + def show_someday(): + """Show someday""" + pass + + def show_logbook(): + """Show logbook""" + pass diff --git a/talon/community/community-cursorless-0.4.0/code/user_settings.py b/talon/community/community-cursorless-0.4.0/code/user_settings.py new file mode 100644 index 0000000..55b34a3 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/user_settings.py @@ -0,0 +1,60 @@ +import csv +import os +from pathlib import Path +from typing import Dict, List, Tuple +from talon import resource + +# NOTE: This method requires this module to be one folder below the top-level +# knausj folder. +SETTINGS_DIR = Path(__file__).parents[1] / "settings" + +if not SETTINGS_DIR.is_dir(): + os.mkdir(SETTINGS_DIR) + + +def get_list_from_csv( + filename: str, headers: Tuple[str, str], default: Dict[str, str] = {} +): + """Retrieves list from CSV""" + path = SETTINGS_DIR / filename + assert filename.endswith(".csv") + + if not path.is_file(): + with open(path, "w", encoding="utf-8") as file: + writer = csv.writer(file) + writer.writerow(headers) + for key, value in default.items(): + writer.writerow([key] if key == value else [value, key]) + + # Now read via resource to take advantage of talon's + # ability to reload this script for us when the resource changes + with resource.open(str(path), "r") as f: + rows = list(csv.reader(f)) + + # print(str(rows)) + mapping = {} + if len(rows) >= 2: + actual_headers = rows[0] + if not actual_headers == list(headers): + print( + f'"{filename}": Malformed headers - {actual_headers}.' + + f" Should be {list(headers)}. Ignoring row." + ) + for row in rows[1:]: + if len(row) == 0: + # Windows newlines are sometimes read as empty rows. :champagne: + continue + if len(row) == 1: + output = spoken_form = row[0] + else: + output, spoken_form = row[:2] + if len(row) > 2: + print( + f'"{filename}": More than two values in row: {row}.' + + " Ignoring the extras." + ) + # Leading/trailing whitespace in spoken form can prevent recognition. + spoken_form = spoken_form.strip() + mapping[spoken_form] = output + + return mapping diff --git a/talon/community/community-cursorless-0.4.0/code/vocabulary.py b/talon/community/community-cursorless-0.4.0/code/vocabulary.py new file mode 100644 index 0000000..cd94e5a --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/vocabulary.py @@ -0,0 +1,90 @@ +from talon import Context, Module +from .user_settings import get_list_from_csv + +mod = Module() +ctx = Context() + +mod.list("vocabulary", desc="additional vocabulary words") + + +# Default words that will need to be capitalized (particularly under w2l). +# NB. These defaults and those later in this file are ONLY used when +# auto-creating the corresponding settings/*.csv files. Those csv files +# determine the contents of user.vocabulary and dictate.word_map. Once they +# exist, the contents of the lists/dictionaries below are irrelevant. +_capitalize_defaults = [ + "I", + "I'm", + "I've", + "I'll", + "I'd", + "Monday", + "Mondays", + "Tuesday", + "Tuesdays", + "Wednesday", + "Wednesdays", + "Thursday", + "Thursdays", + "Friday", + "Fridays", + "Saturday", + "Saturdays", + "Sunday", + "Sundays", + "January", + "February", + # March omitted because it's a regular word too + "April", + # May omitted because it's a regular word too + "June", + "July", + "August", + "September", + "October", + "November", + "December", +] + +# Default words that need to be remapped. +_word_map_defaults = { + # E.g: + # "cash": "cache", +} +_word_map_defaults.update({word.lower(): word for word in _capitalize_defaults}) + + +# "dictate.word_map" is used by `actions.dictate.replace_words` to rewrite words +# Talon recognized. Entries in word_map don't change the priority with which +# Talon recognizes some words over others. + +ctx.settings["dictate.word_map"] = get_list_from_csv( + "words_to_replace.csv", + headers=("Replacement", "Original"), + default=_word_map_defaults, +) + + +# Default words that should be added to Talon's vocabulary. +_simple_vocab_default = ["nmap", "admin", "Cisco", "Citrix", "VPN", "DNS", "Minecraft"] + +# Defaults for different pronounciations of words that need to be added to +# Talon's vocabulary. +_default_vocabulary = { + "N map": "nmap", + "under documented": "under-documented", +} +_default_vocabulary.update({word: word for word in _simple_vocab_default}) + +# "user.vocabulary" is used to explicitly add words/phrases that Talon doesn't +# recognize. Words in user.vocabulary (or other lists and captures) are +# "command-like" and their recognition is prioritized over ordinary words. +ctx.lists["user.vocabulary"] = get_list_from_csv( + "additional_words.csv", + headers=("Word(s)", "Spoken Form (If Different)"), + default=_default_vocabulary, +) + +# for quick verification of the reload +# print(str(ctx.settings["dictate.word_map"])) +# print(str(ctx.lists["user.vocabulary"])) diff --git a/talon/community/community-cursorless-0.4.0/code/window_snap.py b/talon/community/community-cursorless-0.4.0/code/window_snap.py new file mode 100644 index 0000000..b7ade5a --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/code/window_snap.py @@ -0,0 +1,221 @@ +"""Tools for voice-driven window management. + +Originally from dweil/talon_community - modified for newapi by jcaw. + +""" + +# TODO: Map keyboard shortcuts to this manager once Talon has key hooks on all +# platforms + +import time +from operator import xor +from typing import Optional + +from talon import ui, Module, Context, actions + + +def sorted_screens(): + """Return screens sorted by their topmost, then leftmost, edge. + + Screens will be sorted left-to-right, then top-to-bottom as a tiebreak. + + """ + + return sorted( + sorted(ui.screens(), key=lambda screen: screen.visible_rect.top), + key=lambda screen: screen.visible_rect.left, + ) + + +def _set_window_pos(window, x, y, width, height): + """Helper to set the window position.""" + # TODO: Special case for full screen move - use os-native maximize, rather + # than setting the position? + + # 2020/10/01: While the upstream Talon implementation for MS Windows is + # settling, this may be buggy on full screen windows. Aegis doesn't want a + # hacky solution merged, so for now just repeat the command. + # + # TODO: Audit once upstream Talon is bug-free on MS Windows + window.rect = ui.Rect(round(x), round(y), round(width), round(height)) + + +def _bring_forward(window): + current_window = ui.active_window() + try: + window.focus() + current_window.focus() + except Exception as e: + # We don't want to block if this fails. + print(f"Couldn't bring window to front: {e}") + + +def _get_app_window(app_name: str) -> ui.Window: + return actions.self.get_running_app(app_name).active_window + + +def _move_to_screen( + window, offset: Optional[int] = None, screen_number: Optional[int] = None +): + """Move a window to a different screen. + + Provide one of `offset` or `screen_number` to specify a target screen. + + Provide `window` to move a specific window, otherwise the current window is + moved. + + """ + assert ( + screen_number or offset and not (screen_number and offset) + ), "Provide exactly one of `screen_number` or `offset`." + + src_screen = window.screen + screens = sorted_screens() + if offset: + screen_number = (screens.index(src_screen) + offset) % len(screens) + else: + # Human to array index + screen_number -= 1 + + dest_screen = screens[screen_number] + if src_screen == dest_screen: + return + + # Retain the same proportional position on the new screen. + dest = dest_screen.visible_rect + src = src_screen.visible_rect + # TODO: Test this on different-sized screens + # + # TODO: Is this the best behaviour for moving to a vertical screen? Probably + # not. + proportional_width = dest.width / src.width + proportional_height = dest.height / src.height + _set_window_pos( + window, + x=dest.left + (window.rect.left - src.left) * proportional_width, + y=dest.top + (window.rect.top - src.top) * proportional_height, + width=window.rect.width * proportional_width, + height=window.rect.height * proportional_height, + ) + + +def _snap_window_helper(window, pos): + screen = window.screen.visible_rect + + _set_window_pos( + window, + x=screen.x + (screen.width * pos.left), + y=screen.y + (screen.height * pos.top), + width=screen.width * (pos.right - pos.left), + height=screen.height * (pos.bottom - pos.top), + ) + + +class RelativeScreenPos(object): + """Represents a window position as a fraction of the screen.""" + + def __init__(self, left, top, right, bottom): + self.left = left + self.top = top + self.bottom = bottom + self.right = right + + +mod = Module() +mod.list( + "window_snap_positions", + "Predefined window positions for the current window. See `RelativeScreenPos`.", +) + + +_snap_positions = { + # Halves + # .---.---. .-------. + # | | | & |-------| + # '---'---' '-------' + "left": RelativeScreenPos(0, 0, 0.5, 1), + "right": RelativeScreenPos(0.5, 0, 1, 1), + "top": RelativeScreenPos(0, 0, 1, 0.5), + "bottom": RelativeScreenPos(0, 0.5, 1, 1), + # Thirds + # .--.--.--. + # | | | | + # '--'--'--' + "center third": RelativeScreenPos(1 / 3, 0, 2 / 3, 1), + "left third": RelativeScreenPos(0, 0, 1 / 3, 1), + "right third": RelativeScreenPos(2 / 3, 0, 1, 1), + "left two thirds": RelativeScreenPos(0, 0, 2 / 3, 1), + "right two thirds": RelativeScreenPos(1 / 3, 0, 1, 1,), + # Quarters + # .---.---. + # |---|---| + # '---'---' + "top left": RelativeScreenPos(0, 0, 0.5, 0.5), + "top right": RelativeScreenPos(0.5, 0, 1, 0.5), + "bottom left": RelativeScreenPos(0, 0.5, 0.5, 1), + "bottom right": RelativeScreenPos(0.5, 0.5, 1, 1), + # Sixths + # .--.--.--. + # |--|--|--| + # '--'--'--' + "top right third": RelativeScreenPos(2 / 3, 0, 1, 0.5), + "top left two thirds": RelativeScreenPos(0, 0, 2 / 3, 0.5), + "top right two thirds": RelativeScreenPos(1 / 3, 0, 1, 0.5), + "top center third": RelativeScreenPos(1 / 3, 0, 2 / 3, 0.5), + "bottom left third": RelativeScreenPos(0, 0.5, 1 / 3, 1), + "bottom right third": RelativeScreenPos(2 / 3, 0.5, 1, 1), + "bottom left two thirds": RelativeScreenPos(0, 0.5, 2 / 3, 1), + "bottom right two thirds": RelativeScreenPos(1 / 3, 0.5, 1, 1), + "bottom center third": RelativeScreenPos(1 / 3, 0.5, 2 / 3, 1), + # Special + "center": RelativeScreenPos(1 / 8, 1 / 6, 7 / 8, 5 / 6), + "full": RelativeScreenPos(0, 0, 1, 1), + "fullscreen": RelativeScreenPos(0, 0, 1, 1), +} + + +@mod.capture(rule="{user.window_snap_positions}") +def window_snap_position(m) -> RelativeScreenPos: + return _snap_positions[m.window_snap_positions] + + +ctx = Context() +ctx.lists["user.window_snap_positions"] = _snap_positions.keys() + + +@mod.action_class +class Actions: + def snap_window(pos: RelativeScreenPos) -> None: + """Move the active window to a specific position on-screen. + + See `RelativeScreenPos` for the structure of this position. + + """ + _snap_window_helper(ui.active_window(), pos) + + def move_window_next_screen() -> None: + """Move the active window to a specific screen.""" + _move_to_screen(ui.active_window(), offset=1) + + def move_window_previous_screen() -> None: + """Move the active window to the previous screen.""" + _move_to_screen(ui.active_window(), offset=-1) + + def move_window_to_screen(screen_number: int) -> None: + """Move the active window leftward by one.""" + _move_to_screen(ui.active_window(), screen_number=screen_number) + + def snap_app(app_name: str, pos: RelativeScreenPos): + """Snap a specific application to another screen.""" + window = _get_app_window(app_name) + _bring_forward(window) + _snap_window_helper(window, pos) + + def move_app_to_screen(app_name: str, screen_number: int): + """Move a specific application to another screen.""" + window = _get_app_window(app_name) + print(window) + _bring_forward(window) + _move_to_screen( + window, screen_number=screen_number, + ) diff --git a/talon/user/community/plugin/eye_tracking_settings.py b/talon/community/community-cursorless-0.4.0/eye_tracking_settings.py similarity index 74% rename from talon/user/community/plugin/eye_tracking_settings.py rename to talon/community/community-cursorless-0.4.0/eye_tracking_settings.py index c945a9f..d24369a 100644 --- a/talon/user/community/plugin/eye_tracking_settings.py +++ b/talon/community/community-cursorless-0.4.0/eye_tracking_settings.py @@ -1,6 +1,6 @@ # from talon import app -# from talon.types import Point2d -# from talon_plugins import eye_mouse, eye_zoom_mouse +# from talon.track.geom import Point2d +# from talon_plugins import speech, eye_mouse, eye_zoom_mouse # if app.platform == "mac": # eye_zoom_mouse.config.screen_area = Point2d(100, 75) @@ -8,3 +8,4 @@ # elif app.platform == "windows": # eye_zoom_mouse.config.screen_area = Point2d(200, 150) # eye_zoom_mouse.config.img_scale = 4.5 + diff --git a/talon/user/community/lang/batch/batch.talon b/talon/community/community-cursorless-0.4.0/lang/batch.talon similarity index 65% rename from talon/user/community/lang/batch/batch.talon rename to talon/community/community-cursorless-0.4.0/lang/batch.talon index fe66aec..84550bc 100644 --- a/talon/user/community/lang/batch/batch.talon +++ b/talon/community/community-cursorless-0.4.0/lang/batch.talon @@ -1,6 +1,13 @@ -code.language: batch +mode: user.batch +mode: command +and code.language: batch - -tag(): user.code_comment_line +#tag(): user.code_operators +tag(): user.code_comment +#tag(): user.code_generic + + +action(user.code_comment): "REM " # exit without killing cmd shell soft exit: "exit /B 1\n" diff --git a/talon/user/community/lang/tags/comment_block.talon b/talon/community/community-cursorless-0.4.0/lang/block_comment.talon similarity index 66% rename from talon/user/community/lang/tags/comment_block.talon rename to talon/community/community-cursorless-0.4.0/lang/block_comment.talon index 9a28a1f..128f2bd 100644 --- a/talon/user/community/lang/tags/comment_block.talon +++ b/talon/community/community-cursorless-0.4.0/lang/block_comment.talon @@ -1,49 +1,51 @@ -tag: user.code_comment_block +tag: user.code_block_comment - -block comment: user.code_comment_block() +block comment: user.code_block_comment() block comment line: #todo: this should probably be a single function once #.talon supports implementing actions with parameters? - edit.line_start() - user.code_comment_block_prefix() + edit.line_start() + user.code_block_comment_prefix() key(space) - edit.line_end() + edit.line_end() key(space) - user.code_comment_block_suffix() + user.code_block_comment_suffix() #adds comment to the start of the line block comment line over: #todo: this should probably be a single function once #.talon supports implementing actions with parameters? edit.line_start() - user.code_comment_block() - insert(user.text) + user.code_block_comment() + insert(user.text) block comment over: #todo: this should probably be a single function once #.talon supports implementing actions with parameters? - user.code_comment_block() + user.code_block_comment() insert(user.text) block comment $: #todo: this should probably be a single function once #.talon supports implementing actions with parameters? - user.code_comment_block() + user.code_block_comment() insert(user.text) (line | inline) block comment over: #todo: this should probably be a single function once #.talon supports implementing actions with parameters? - edit.line_end() - user.code_comment_block_prefix() + edit.line_end() + user.code_block_comment_prefix() key(space) insert(user.text) key(space) - user.code_comment_block_suffix() + user.code_block_comment_suffix() (line | inline) block comment $: #todo: this should probably be a single function once #.talon supports implementing actions with parameters? - edit.line_end() - user.code_comment_block_prefix() + edit.line_end() + user.code_block_comment_prefix() key(space) insert(user.text) key(space) - user.code_comment_block_suffix() -open block comment: user.code_comment_block_prefix() -close block comment: user.code_comment_block_suffix() + user.code_block_comment_suffix() +open block comment: + user.code_block_comment_prefix() +close block comment: + user.code_block_comment_suffix() diff --git a/talon/user/community/lang/c/c.py b/talon/community/community-cursorless-0.4.0/lang/c/c.py similarity index 51% rename from talon/user/community/lang/c/c.py rename to talon/community/community-cursorless-0.4.0/lang/c/c.py index b485d0c..5a86301 100644 --- a/talon/user/community/lang/c/c.py +++ b/talon/community/community-cursorless-0.4.0/lang/c/c.py @@ -1,10 +1,18 @@ from talon import Context, Module, actions, settings mod = Module() +mod.setting( + "use_stdint_datatypes ", + type=int, + default=1, + desc="Use the stdint datatype naming in commands by default", +) ctx = Context() ctx.matches = r""" -code.language: c +mode: user.c +mode: command +and code.language: c """ ctx.lists["self.c_pointers"] = { @@ -22,7 +30,7 @@ ctx.lists["self.c_signed"] = { "unsigned": "unsigned ", } -ctx.lists["self.c_keywords"] = { +common_types = { "static": "static", "volatile": "volatile", "register": "register", @@ -81,7 +89,7 @@ ctx.lists["user.code_libraries"] = { "standard int": "stdint.h", } -ctx.lists["user.code_common_function"] = { +ctx.lists["user.code_functions"] = { "mem copy": "memcpy", "mem set": "memset", "string cat": "strcat", @@ -127,7 +135,6 @@ ctx.lists["user.code_common_function"] = { mod.list("c_pointers", desc="Common C pointers") mod.list("c_signed", desc="Common C datatype signed modifiers") -mod.list("c_keywords", desc="C keywords") mod.list("c_types", desc="Common C types") mod.list("stdint_types", desc="Common stdint C types") mod.list("stdint_signed", desc="Common stdint C datatype signed modifiers") @@ -145,12 +152,6 @@ def c_signed(m) -> str: return m.c_signed -@mod.capture(rule="{self.c_keywords}") -def c_keywords(m) -> str: - "Returns a string" - return m.c_keywords - - @mod.capture(rule="{self.c_types}") def c_types(m) -> str: "Returns a string" @@ -175,193 +176,33 @@ def stdint_signed(m) -> str: return m.stdint_signed -@mod.capture(rule="[] [+]") +# NOTE: we purposely we don't have a space after signed, to faciltate stdint +# style uint8_t constructions +@mod.capture(rule="[] [+]") def c_cast(m) -> str: "Returns a string" return "(" + " ".join(list(m)) + ")" -@mod.capture(rule="[] [+]") -def stdint_cast(m) -> str: +# NOTE: we purposely we don't have a space after signed, to faciltate stdint +# style uint8_t constructions +@mod.capture(rule="[] [+]") +def c_stdint_cast(m) -> str: "Returns a string" return "(" + "".join(list(m)) + ")" -@mod.capture(rule="[] []") +@mod.capture(rule="[][]") def c_variable(m) -> str: "Returns a string" return " ".join(list(m)) @ctx.action_class("user") -class UserActions: - def code_operator_indirection(): - actions.auto_insert("*") - - def code_operator_address_of(): - actions.auto_insert("&") - - def code_operator_structure_dereference(): - actions.auto_insert("->") - - def code_operator_subscript(): - actions.insert("[]") - actions.key("left") - - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_subtraction_assignment(): - actions.auto_insert(" -= ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_addition_assignment(): - actions.auto_insert(" += ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_multiplication_assignment(): - actions.auto_insert(" *= ") - - # action(user.code_operator_exponent): " ** " - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_division_assignment(): - actions.auto_insert(" /= ") - - def code_operator_modulo(): - actions.auto_insert(" % ") - - def code_operator_modulo_assignment(): - actions.auto_insert(" %= ") - - def code_operator_equal(): - actions.auto_insert(" == ") - - def code_operator_not_equal(): - actions.auto_insert(" != ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" && ") - - def code_operator_or(): - actions.auto_insert(" || ") - - def code_operator_not(): - actions.auto_insert("!") - - def code_operator_bitwise_and(): - actions.auto_insert(" & ") - - def code_operator_bitwise_and_assignment(): - actions.auto_insert(" &= ") - - def code_operator_bitwise_or(): - actions.auto_insert(" | ") - - def code_operator_bitwise_or_assignment(): - actions.auto_insert(" |= ") - - def code_operator_bitwise_exclusive_or(): - actions.auto_insert(" ^ ") - - def code_operator_bitwise_exclusive_or_assignment(): - actions.auto_insert(" ^= ") - - def code_operator_bitwise_not(): - actions.auto_insert("~") - - def code_operator_bitwise_left_shift(): - actions.auto_insert(" << ") - - def code_operator_bitwise_left_shift_assignment(): - actions.auto_insert(" <<= ") - - def code_operator_bitwise_right_shift(): - actions.auto_insert(" >> ") - - def code_operator_bitwise_right_shift_assignment(): - actions.auto_insert(" >>= ") - - def code_insert_null(): - actions.auto_insert("NULL") - - def code_insert_is_null(): - actions.auto_insert(" == NULL ") - - def code_insert_is_not_null(): - actions.auto_insert(" != NULL") - - def code_state_if(): - actions.insert("if () {\n}\n") - actions.key("up:2 left:3") - - def code_state_else_if(): - actions.insert("else if () {\n}\n") - actions.key("up:2 left:3") - - def code_state_else(): - actions.insert("else\n{\n}\n") - actions.key("up:2") - - def code_state_switch(): - actions.insert("switch ()") - actions.edit.left() - - def code_state_case(): - actions.insert("case \nbreak;") - actions.edit.up() - - def code_state_for(): - actions.auto_insert("for ") - - def code_state_go_to(): - actions.auto_insert("goto ") - - def code_state_while(): - actions.insert("while ()") - actions.edit.left() - - def code_state_return(): - actions.auto_insert("return ") - - def code_break(): - actions.auto_insert("break;") - - def code_next(): - actions.auto_insert("continue;") - - def code_insert_true(): - actions.auto_insert("true") - - def code_insert_false(): - actions.auto_insert("false") - - def code_comment_line_prefix(): - actions.auto_insert("//") - +class user_actions: def code_insert_function(text: str, selection: str): if selection: - text = text + f"({selection})" + text = text + "({})".format(selection) else: text = text + "()" @@ -391,4 +232,5 @@ class UserActions: actions.user.code_insert_function(result, None) def code_insert_library(text: str, selection: str): - actions.user.paste(f"include <{text}>") + actions.user.paste("include <{}>".format(selection)) + diff --git a/talon/community/community-cursorless-0.4.0/lang/c/c.talon b/talon/community/community-cursorless-0.4.0/lang/c/c.talon new file mode 100644 index 0000000..c5ca9fb --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/c/c.talon @@ -0,0 +1,162 @@ +mode: user.c +mode: command +and code.language: c +- +tag(): user.code_operators +tag(): user.code_comment +tag(): user.code_block_comment +tag(): user.code_generic +settings(): + user.code_private_function_formatter = "SNAKE_CASE" + user.code_protected_function_formatter = "SNAKE_CASE" + user.code_public_function_formatter = "SNAKE_CASE" + user.code_private_variable_formatter = "SNAKE_CASE" + user.code_protected_variable_formatter = "SNAKE_CASE" + user.code_public_variable_formatter = "SNAKE_CASE" + # whether or not to use uint_8 style datatypes + # user.use_stdint_datatypes = 1 + + +action(user.code_operator_indirection): "*" +action(user.code_operator_address_of): "&" +action(user.code_operator_structure_dereference): "->" +action(user.code_operator_subscript): + insert("[]") + key(left) +action(user.code_operator_assignment): " = " +action(user.code_operator_subtraction): " - " +action(user.code_operator_subtraction_assignment): " -= " +action(user.code_operator_addition): " + " +action(user.code_operator_addition_assignment): " += " +action(user.code_operator_multiplication): " * " +action(user.code_operator_multiplication_assignment): " *= " +#action(user.code_operator_exponent): " ** " +action(user.code_operator_division): " / " +action(user.code_operator_division_assignment): " /= " +action(user.code_operator_modulo): " % " +action(user.code_operator_modulo_assignment): " %= " +action(user.code_operator_equal): " == " +action(user.code_operator_not_equal): " != " +action(user.code_operator_greater_than): " > " +action(user.code_operator_greater_than_or_equal_to): " >= " +action(user.code_operator_less_than): " < " +action(user.code_operator_less_than_or_equal_to): " <= " +action(user.code_operator_and): " && " +action(user.code_operator_or): " || " +action(user.code_operator_bitwise_and): " & " +action(user.code_operator_bitwise_and_assignment): " &= " +action(user.code_operator_bitwise_or): " | " +action(user.code_operator_bitwise_or_assignment): " |= " +action(user.code_operator_bitwise_exclusive_or): " ^ " +action(user.code_operator_bitwise_exclusive_or_assignment): " ^= " +action(user.code_operator_bitwise_left_shift): " << " +action(user.code_operator_bitwise_left_shift_assignment): " <<= " +action(user.code_operator_bitwise_right_shift): " >> " +action(user.code_operator_bitwise_right_shift_assignment): " >>= " +action(user.code_null): "NULL" +action(user.code_is_null): " == NULL " +action(user.code_is_not_null): " != NULL" +action(user.code_state_if): + insert("if () {\n}\n") + key(up:2 left:3) +action(user.code_state_else_if): + insert("else if () {\n}\n") + key(up:2 left:3) +action(user.code_state_else): + insert("else\n{\n}\n") + key(up:2) +action(user.code_state_switch): + insert("switch ()") + edit.left() +action(user.code_state_case): + insert("case \nbreak;") + edit.up() +action(user.code_state_for): "for " +action(user.code_state_go_to): "goto " +action(user.code_state_while): + insert("while ()") + edit.left() +action(user.code_state_return): "return " +action(user.code_break): "break;" +action(user.code_next): "continue;" +action(user.code_true): "true" +action(user.code_false): "false" +action(user.code_type_definition): "typedef " +action(user.code_typedef_struct): + insert("typedef struct") + insert("{\n\n}") + edit.up() + key(tab) +action(user.code_from_import): "using " +action(user.code_include): insert("#include ") +action(user.code_include_system): + insert("#include <>") + edit.left() +action(user.code_include_local): + insert('#include ""') + edit.left() +action(user.code_comment): "//" +action(user.code_block_comment): + insert("/*") + key(enter) + key(enter) + insert("*/") + edit.up() +action(user.code_block_comment_prefix): "/*" +action(user.code_block_comment_suffix): "*/" + +^funky $: user.code_default_function(text) +^static funky $: user.code_private_static_function(text) + + +# XXX - make these generic in programming, as they will match cpp, etc +state define: "#define " +state undefine: "#undef " +state if define: "#ifdef " + +# XXX - preprocessor instead of pre? +state pre if: "#if " +state error: "#error " +state pre else if: "#elif " +state pre end: "#endif " +state pragma: "#pragma " +state default: "default:\nbreak;" + +#control flow +#best used with a push like command +#the below example may not work in editors that automatically add the closing bracket +#if so uncomment the two lines and comment out the rest accordingly +push brackets: + edit.line_end() + #insert("{") + #key(enter) + insert("{}") + edit.left() + key(enter) + key(enter) + edit.up() + +# Declare variables or structs etc. +# Ex. * int myList + : + insert("{c_variable} ") + insert(user.formatted_text(phrase, "PRIVATE_CAMEL_CASE,NO_SPACES")) + + : + insert("{c_variable} {letter} ") + +# Ex. (int *) +cast to : "{c_cast}" +standard cast to : "{stdint_cast}" +: "{c_types}" +: "{c_pointers}" +: "{c_signed}" +standard : "{stdint_types}" +int main: + insert("int main()") + edit.left() + +toggle includes: user.code_toggle_libraries() +include : + user.code_insert_library(code_libraries, "") + key(end enter) diff --git a/talon/community/community-cursorless-0.4.0/lang/comment.talon b/talon/community/community-cursorless-0.4.0/lang/comment.talon new file mode 100644 index 0000000..8a0e2e6 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/comment.talon @@ -0,0 +1,38 @@ +tag: user.code_comment +- +# comment: user.code_comment() +# comment line: +# #todo: this should probably be a single function once +# #.talon supports implementing actions with parameters? +# edit.line_start() +# user.code_comment() +#adds comment to the start of the line +# comment line over: +# #todo: this should probably be a single function once +# #.talon supports implementing actions with parameters? +# edit.line_start() +# user.code_comment() +# insert(user.text) +# insert(" ") +# comment over: +# #todo: this should probably be a single function once +# #.talon supports implementing actions with parameters? +# user.code_comment() +# insert(user.text) +# comment $: +# #todo: this should probably be a single function once +# #.talon supports implementing actions with parameters? +# user.code_comment() +# insert(user.text) +(line | inline) comment over: + #todo: this should probably be a single function once + #.talon supports implementing actions with parameters? + edit.line_end() + user.code_comment() + insert(user.text) +(line | inline) comment $: + #todo: this should probably be a single function once + #.talon supports implementing actions with parameters? + edit.line_end() + user.code_comment() + insert(user.text) diff --git a/talon/community/community-cursorless-0.4.0/lang/csharp/csharp.py b/talon/community/community-cursorless-0.4.0/lang/csharp/csharp.py new file mode 100644 index 0000000..b3a991f --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/csharp/csharp.py @@ -0,0 +1,81 @@ +from talon import Context, Module, actions, imgui, settings, ui + +ctx = Context() +ctx.matches = r""" +mode: user.csharp +mode: command +and code.language: csharp +""" +ctx.lists["user.code_functions"] = { + "integer": "int.TryParse", + "print": "Console.WriteLine", + "string": ".ToString", +} + + +@ctx.action_class("user") +class user_actions: + def code_insert_function(text: str, selection: str): + if selection: + text = text + "({})".format(selection) + else: + text = text + "()" + + actions.user.paste(text) + actions.edit.left() + + def code_private_function(text: str): + """Inserts private function declaration""" + result = "private void {}".format( + actions.user.formatted_text( + text, settings.get("user.code_private_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + def code_private_static_function(text: str): + """Inserts private static function""" + result = "private static void {}".format( + actions.user.formatted_text( + text, settings.get("user.code_private_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + def code_protected_function(text: str): + result = "private void {}".format( + actions.user.formatted_text( + text, settings.get("user.code_protected_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + def code_protected_static_function(text: str): + result = "protected static void {}".format( + actions.user.formatted_text( + text, settings.get("user.code_protected_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + def code_public_function(text: str): + result = "public void {}".format( + actions.user.formatted_text( + text, settings.get("user.code_public_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + def code_public_static_function(text: str): + result = "public static void {}".format( + actions.user.formatted_text( + text, settings.get("user.code_public_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) diff --git a/talon/community/community-cursorless-0.4.0/lang/csharp/csharp.talon b/talon/community/community-cursorless-0.4.0/lang/csharp/csharp.talon new file mode 100644 index 0000000..f1659c1 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/csharp/csharp.talon @@ -0,0 +1,111 @@ +mode: user.csharp +mode: command +and code.language: csharp +- +tag(): user.code_operators +tag(): user.code_comment +tag(): user.code_generic +settings(): + user.code_private_function_formatter = "PRIVATE_CAMEL_CASE" + user.code_protected_function_formatter = "PUBLIC_CAMEL_CASE" + user.code_public_function_formatter = "PUBLIC_CAMEL_CASE" + user.code_private_variable_formatter = "PRIVATE_CAMEL_CASE" + user.code_protected_variable_formatter = "PUBLIC_CAMEL_CASE" + user.code_public_variable_formatter = "PUBLIC_CAMEL_CASE" + +action(user.code_operator_indirection): "*" +action(user.code_operator_address_of): "&" +action(user.code_operator_structure_dereference): "->" +action(user.code_operator_lambda): "=>" +action(user.code_operator_subscript): + insert("[]") + key(left) +action(user.code_operator_assignment): " = " +action(user.code_operator_subtraction): " - " +action(user.code_operator_subtraction_assignment): " -= " +action(user.code_operator_addition): " + " +action(user.code_operator_addition_assignment): " += " +action(user.code_operator_multiplication): " * " +action(user.code_operator_multiplication_assignment): " *= " +#action(user.code_operator_exponent): " ** " +action(user.code_operator_division): " / " +action(user.code_operator_division_assignment): " /= " +action(user.code_operator_modulo): " % " +action(user.code_operator_modulo_assignment): " %= " +action(user.code_operator_equal): " == " +action(user.code_operator_not_equal): " != " +action(user.code_operator_greater_than): " > " +action(user.code_operator_greater_than_or_equal_to): " >= " +action(user.code_operator_less_than): " < " +action(user.code_operator_less_than_or_equal_to): " <= " +action(user.code_operator_and): " && " +action(user.code_operator_or): " || " +action(user.code_operator_bitwise_and): " & " +action(user.code_operator_bitwise_and_assignment): " &= " +action(user.code_operator_bitwise_or): " | " +action(user.code_operator_bitwise_or_assignment): " |= " +action(user.code_operator_bitwise_exclusive_or): " ^ " +action(user.code_operator_bitwise_exclusive_or_assignment): " ^= " +action(user.code_operator_bitwise_left_shift): " << " +action(user.code_operator_bitwise_left_shift_assignment): " <<= " +action(user.code_operator_bitwise_right_shift): " >> " +action(user.code_operator_bitwise_right_shift_assignment): " >>= " +action(user.code_block): + insert("{}") + key(left enter enter up tab) +action(user.code_self): "this" +action(user.code_null): "null" +action(user.code_is_null): " == null " +action(user.code_is_not_null): " != null" +action(user.code_state_if): + insert("if()") + key(left) +action(user.code_state_else_if): + insert("else if()") + key(left) +action(user.code_state_else): + insert("else\n{{\n}}\n") + key(up ) +action(user.code_state_switch): + insert("switch()") + edit.left() +action(user.code_state_case): + insert("case \nbreak;") + edit.up() +action(user.code_state_for): "for " +action(user.code_state_for_each): + insert("foreach() ") + key(left) + edit.word_left() + key(space) + edit.left() +action(user.code_state_go_to): "go to " +action(user.code_state_while): + insert("while()") + edit.left() +action(user.code_state_return): "return " +action(user.code_break): "break;" +action(user.code_next): "continue;" +action(user.code_true): "true" +action(user.code_false): "false" + +#action(user.code_type_definition): "typedef " +#action(user.code_typedef_struct): +# insert("typedef struct") +# insert("{{\n\n}}") +# edit.up() +# key(tab) +action(user.code_type_class): "class " +action(user.code_import): "using " +action(user.code_from_import): "using " +action(user.code_include): insert("using ") +action(user.code_include_system): insert("using ") +action(user.code_include_local): insert('using ') +action(user.code_comment): "//" + +^funky $: user.code_default_function(text) +^pro funky $: user.code_protected_function(text) +^pub funky $: user.code_public_function(text) +^static funky $: user.code_private_static_function(text) +^pro static funky $: user.code_protected_static_function(text) +^pub static funky $: user.code_public_static_function(text) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/lang/go.talon b/talon/community/community-cursorless-0.4.0/lang/go.talon new file mode 100644 index 0000000..b403a61 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/go.talon @@ -0,0 +1,200 @@ +mode: user.go +mode: command +and code.language: go +- +variadic: "..." +logical and: " && " +logical or: " || " +# Many of these add extra terrible spacing under the assumption that +# gofmt/goimports will erase it. +state comment: "// " +[line] comment : + key("cmd-right") + insert(" // ") + insert(user.formatted_text(text, "sentence")) + +# "add comment [over]: +# key("cmd-right") +# text_with_leading(" // ") +# ] +# "[state] context: insert("ctx") +state (funk | func | fun): "func " +function (Annette | init) [over]: "func init() {\n" +function [over]: + insert("func ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + insert("(") + sleep(100ms) + +method [over]: + insert("meth ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + sleep(100ms) + +state var: "var " +variable [] [over]: + insert("var ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + # insert(" ") + sleep(100ms) + +of type [] [over]: + insert(" ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +# "set [over]: +# insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) +# insert(" := ") +# sleep(100ms) +# ] +state break: "break" +state (chan | channel): " chan " +state go: "go " +state if: "if " +if [over]: + insert("if ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) +spawn [over]: + insert("go ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) +state else if: " else if " +else if [over]: + insert(" else if ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +state else: " else " +else [over]: + insert(" else {") + key("enter") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +state while: "while " +while [over]: + insert("while ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +state for: "for " +for [over]: + insert("for ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +state for range: "forr " +range [over]: + insert("forr ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +state format: "fmt" +format [over]: + insert("fmt.") + insert(user.formatted_text(text, "PUBLIC_CAMEL_CASE")) + +state switch: "switch " +switch [over]: + insert("switch ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +state select: "select " +# "select :insert("select "), insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")] +state (const | constant): " const " +constant [over]: + insert("const ") + insert(user.formatted_text(text, "PUBLIC_CAMEL_CASE")) + +state case: " case " +state default: " default:" +case [over]: + insert("case ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +state type: " type " +type [over]: + insert("type ") + insert(user.formatted_text(text, "PUBLIC_CAMEL_CASE")) +state true: " true " +state false: " false " +state (start | struct | struck): + insert(" struct {") + key("enter") +(struct | struck) [over]: + insert(" struct {") + key("enter") + insert(user.formatted_text(text, "PUBLIC_CAMEL_CASE")) + +[state] empty interface: " interface{} " +state interface: + insert(" interface {") + key("enter") +interface [over]: + insert(" interface {") + key("enter") + insert(user.formatted_text(text, "PUBLIC_CAMEL_CASE")) + +state string: " string " +[state] (int | integer | ant): "int" +state slice: " []" +slice of: "[]" +[state] (no | nil): "nil" +state (int | integer | ant) 64: " int64 " +state tag: + insert(" ``") + key("left") +field tag [over]: + insert(" ``") + key("left") + sleep(100ms) + insert(user.formatted_text(text, "snake")) + insert(" ") + sleep(100ms) + +state return: " return " +return [over]: + insert("return ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +map of string to string: " map[string]string " +map of [over]: + insert("map[") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + key("right") + sleep(100ms) + +receive: " <- " +make: "make(" +loggers [] [over]: + insert("logrus.") + insert(user.formatted_text(text, "PUBLIC_CAMEL_CASE")) + +length [over]: + insert("len(") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +append [over]: + insert("append(") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +state (air | err): "err" +error: " err " +loop over [] [over]: + insert("forr ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +item [over]: + insert(", ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +value [over]: + insert(": ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +address of [] [over]: + insert("&") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +pointer to [] [over]: + insert("*") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) + +swipe [] [over]: + key("right") + insert(", ") + insert(user.formatted_text(text, "PRIVATE_CAMEL_CASE")) diff --git a/talon/community/community-cursorless-0.4.0/lang/java/java.talon b/talon/community/community-cursorless-0.4.0/lang/java/java.talon new file mode 100644 index 0000000..82d6678 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/java/java.talon @@ -0,0 +1,81 @@ +mode: user.java +mode: command +and code.language: java + +- +tag(): user.code_operators +tag(): user.code_generic + +action(user.code_operator_indirection): skip() +action(user.code_operator_address_of): skip() +action(user.code_operator_lambda): " -> " +action(user.code_operator_subscript): + insert("[]") + key(left) +action(user.code_operator_assignment): " = " +action(user.code_operator_subtraction): " - " +action(user.code_operator_subtraction_assignment): " -= " +action(user.code_operator_addition): " + " +action(user.code_operator_addition_assignment): " += " +action(user.code_operator_multiplication): " * " +action(user.code_operator_multiplication_assignment): " *= " +action(user.code_operator_exponent): " ^ " +action(user.code_operator_division): " / " +action(user.code_operator_division_assignment): " /= " +action(user.code_operator_modulo): " % " +action(user.code_operator_modulo_assignment): " %= " +action(user.code_operator_equal): " == " +action(user.code_operator_not_equal): " != " +action(user.code_operator_greater_than): " > " +action(user.code_operator_greater_than_or_equal_to): " >= " +action(user.code_operator_less_than): " < " +action(user.code_operator_less_than_or_equal_to): " <= " +action(user.code_operator_and): " && " +action(user.code_operator_or): " || " +action(user.code_operator_bitwise_and): " & " +action(user.code_operator_bitwise_or): " | " +action(user.code_operator_bitwise_exclusive_or): " ^ " +action(user.code_operator_bitwise_left_shift): " << " +action(user.code_operator_bitwise_left_shift_assignment): " <<= " +action(user.code_operator_bitwise_right_shift): " >> " +action(user.code_operator_bitwise_right_shift_assignment): " >>= " +action(user.code_self): "this" +action(user.code_null): "null" +action(user.code_is_null): " == null" +action(user.code_is_not_null): " != null" +action(user.code_state_if): + insert("if () ") + key(left) + key(left) +action(user.code_state_else_if): + insert("else if () ") + key(left) + key(left) +action(user.code_state_else): + insert("else ") + key(enter) +action(user.code_state_switch): + insert("switch () ") + key(left) + edit.left() +action(user.code_state_case): + insert("case \nbreak;") + edit.up() +action(user.code_state_for): + insert('for () ') + key(left) + key(left) +action(user.code_state_while): + insert("while () ") + edit.left() + edit.left() +action(user.code_type_class): "class " +action(user.code_private_function): + insert("private") +action(user.code_protected_function): + user.code_private_function() +action(user.code_public_function): + insert("public ") +action(user.code_state_return): + insert("return ") + \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/lang/javascript/javascript.py b/talon/community/community-cursorless-0.4.0/lang/javascript/javascript.py new file mode 100644 index 0000000..704829d --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/javascript/javascript.py @@ -0,0 +1,83 @@ +from talon import Module, Context, actions, ui, imgui, settings + +ctx = Context() +ctx.matches = r""" +mode: user.javascript +mode: command +and code.language: javascript +""" +# tbd +# ctx.lists["user.code_functions"] = { +# "integer": "int.TryParse", +# "print": "Console.WriteLine", +# "string": ".ToString", +# } + + +@ctx.action_class("user") +class user_actions: + def code_insert_function(text: str, selection: str): + if selection: + text = text + "({})".format(selection) + else: + text = text + "()" + + actions.user.paste(text) + actions.edit.left() + + def code_private_function(text: str): + """Inserts private function declaration""" + result = "function {}".format( + actions.user.formatted_text( + text, settings.get("user.code_private_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + # def code_private_static_function(text: str): + # """Inserts private static function""" + # result = "private static void {}".format( + # actions.user.formatted_text( + # text, settings.get("user.code_private_function_formatter") + # ) + # ) + + # actions.user.code_insert_function(result, None) + + def code_protected_function(text: str): + result = "function {}".format( + actions.user.formatted_text( + text, settings.get("user.code_protected_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + # def code_protected_static_function(text: str): + # result = "protected static void {}".format( + # actions.user.formatted_text( + # text, settings.get("user.code_protected_function_formatter") + # ) + # ) + + # actions.user.code_insert_function(result, None) + + def code_public_function(text: str): + result = "function {}".format( + actions.user.formatted_text( + text, settings.get("user.code_public_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + # def code_public_static_function(text: str): + # result = "public static void {}".format( + # actions.user.formatted_text( + # text, settings.get("user.code_public_function_formatter") + # ) + # ) + + # actions.user.code_insert_function(result, None) + diff --git a/talon/community/community-cursorless-0.4.0/lang/javascript/javascript.talon b/talon/community/community-cursorless-0.4.0/lang/javascript/javascript.talon new file mode 100644 index 0000000..7bd4680 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/javascript/javascript.talon @@ -0,0 +1,166 @@ +mode: user.javascript +mode: command +and code.language: javascript +- +tag(): user.code_operators +tag(): user.code_comment +tag(): user.code_generic + +settings(): + user.code_private_function_formatter = "PRIVATE_CAMEL_CASE" + user.code_protected_function_formatter = "PRIVATE_CAMEL_CASE" + user.code_public_function_formatter = "PRIVATE_CAMEL_CASE" + user.code_private_variable_formatter = "PRIVATE_CAMEL_CASE" + user.code_protected_variable_formatter = "PRIVATE_CAMEL_CASE" + user.code_public_variable_formatter = "PRIVATE_CAMEL_CASE" + +action(user.code_is_not_null): " !== null" + +action(user.code_is_null): " === null" + +action(user.code_type_dictionary): + insert("{}") + key(left) + +action(user.code_state_if): + insert("if ()") + key(left) + +action(user.code_state_else_if): + insert(" else if ()") + key(left) + +action(user.code_state_else): + insert(" else {}") + key(left enter) + +action(user.code_block): + insert("{}") + key(left enter) + +action(user.code_self): "this" + +action(user.code_state_while): + insert("while ()") + key(left) + +action(user.code_state_return): + insert("return ") + +action(user.code_state_for): + insert("for ()") + key(left) + +action(user.code_state_switch): + insert("switch ()") + key(left) + +action(user.code_state_case): "case :" + +action(user.code_state_go_to): "" + +action(user.code_import): "import " + +action(user.code_from_import): + insert(" from \"\"") + key(left) + +action(user.code_type_class): "class " + +action(user.code_include): "" + +action(user.code_include_system): "" + +action(user.code_include_local): "" + +action(user.code_type_definition): "" + +action(user.code_typedef_struct): "" + +action(user.code_state_for_each): + insert(".forEach()") + key(left) + +action(user.code_break): "break;" +action(user.code_next): "continue;" +action(user.code_true): "true" +action(user.code_false): "false" + +action(user.code_null): "null" + +action(user.code_operator_indirection): "" +action(user.code_operator_address_of): "" +action(user.code_operator_structure_dereference): "" +action(user.code_operator_lambda): " => " +action(user.code_operator_subscript): + insert("[]") + key(left) +action(user.code_operator_assignment): " = " +action(user.code_operator_subtraction): " - " +action(user.code_operator_subtraction_assignment): " -= " +action(user.code_operator_addition): " + " +action(user.code_operator_addition_assignment): " += " +action(user.code_operator_multiplication): " * " +action(user.code_operator_multiplication_assignment): " *= " +action(user.code_operator_exponent): " ** " +action(user.code_operator_division): " / " +action(user.code_operator_division_assignment): " /= " +action(user.code_operator_modulo): " % " +action(user.code_operator_modulo_assignment): " %= " +action(user.code_operator_equal): " == " +action(user.code_operator_not_equal): " != " +op treek: " === " +op nottreek: " !== " +action(user.code_operator_greater_than): " > " +action(user.code_operator_greater_than_or_equal_to): " >= " +action(user.code_operator_less_than): " < " +action(user.code_operator_less_than_or_equal_to): " <= " +action(user.code_operator_and): " && " +action(user.code_operator_or): " || " +action(user.code_operator_bitwise_and): " & " +action(user.code_operator_bitwise_and_assignment): " &= " +action(user.code_operator_bitwise_or): " | " +action(user.code_operator_bitwise_or_assignment): " |= " +action(user.code_operator_bitwise_exclusive_or): " ^ " +action(user.code_operator_bitwise_exclusive_or_assignment): " ^= " +action(user.code_operator_bitwise_left_shift): " << " +action(user.code_operator_bitwise_left_shift_assignment): " <<= " +action(user.code_operator_bitwise_right_shift): " >> " +action(user.code_operator_bitwise_right_shift_assignment): " >>= " + +state const: "const " + +state let: "let " + +state var: "var " + +state async: "async " + +state await: "await " + +state map: + insert(".map()") + key(left) + +state filter: + insert(".filter()") + key(left) + +state reduce: + insert(".reduce()") + key(left) + +state length: insert(".length") + +state quote var: + insert("${}") + key(left) + +state log: + insert("console.log()") + key(left) +state spread: "..." + +^funky $: user.code_default_function(text) +^pro funky $: user.code_protected_function(text) +^pub funky $: user.code_public_function(text) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/lang/operators.talon b/talon/community/community-cursorless-0.4.0/lang/operators.talon new file mode 100644 index 0000000..570077e --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/operators.talon @@ -0,0 +1,55 @@ +tag: user.code_operators +- +#pointer operators + dereference: user.code_operator_indirection() + address of: user.code_operator_address_of() + arrow: user.code_operator_structure_dereference() + +#lambda + lambda: user.code_operator_lambda() + +#subscript + subscript: user.code_operator_subscript() + +#assignment + (equals | assign): user.code_operator_assignment() + +#math operators + (minus | subtract): user.code_operator_subtraction() + (minus | subtract) equals: user.code_operator_subtraction_assignment() + (plus | add): user.code_operator_addition() + (plus | add) equals: user.code_operator_addition_assignment() + (times | multiply): user.code_operator_multiplication() + (times | multiply) equals: user.code_operator_multiplication_assignment() + divide: user.code_operator_division() + divide equals: user.code_operator_division_assignment() + mod: user.code_operator_modulo() + mod equals: user.code_operator_modulo_assignment() +( (power | exponent) | to the power [of]): user.code_operator_exponent() + +#comparison operators +( | is) equal: user.code_operator_equal() +( | is) not equal: user.code_operator_not_equal() +( | is) (greater | more): user.code_operator_greater_than() +( | is) (less | below) [than]: user.code_operator_less_than() +( | is) greater [than] or equal: user.code_operator_greater_than_or_equal_to() +( | is) less [than] or equal: user.code_operator_less_than_or_equal_to() +( | is) in: user.code_operator_in() + +#logical operators +( | logical) and: user.code_operator_and() +( | logical) or: user.code_operator_or() + +#bitwise operators +[] bitwise and: user.code_operator_bitwise_and() +( | logical | bitwise) and equals: user.code_operator_bitwise_and_equals() +[] bitwise or: user.code_operator_bitwise_or() +( | logical | bitwise) or equals: user.code_operator_bitwise_or_equals() +( | logical | bitwise) (ex | exclusive) or: user.code_operator_bitwise_exclusive_or() +( | logical | bitwise) (left shift | shift left): user.code_operator_bitwise_left_shift() +( | logical | bitwise) (right shift | shift right): user.code_operator_bitwise_right_shift() +( | logical | bitwise) (ex | exclusive) or equals: user.code_operator_bitwise_exclusive_or_equals() +[( | logical | bitwise)] (left shift | shift left) equals: user.code_operator_bitwise_left_shift_equals() +[( | logical | bitwise)] (left right | shift right) equals: user.code_operator_bitwise_right_shift_equals() + +#tbd \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/lang/programming.talon b/talon/community/community-cursorless-0.4.0/lang/programming.talon new file mode 100644 index 0000000..3bff1cd --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/programming.talon @@ -0,0 +1,67 @@ +tag: user.code_generic +- +block: user.code_block() + +#todo should we have a keyword list? type list capture? stick with "word"? +state in: insert(" in ") +is not (none|null): user.code_is_not_null() +is (none|null): user.code_is_null() +#todo: types? +#word (dickt | dictionary): user.code_type_dictionary() +state if: user.code_state_if() +state else if: user.code_state_else_if() +state else: user.code_state_else() +state self: user.code_self() +#todo: this is valid for many languages, +# but probably not all +self dot: + user.code_self() + insert(".") +state while: user.code_state_while() +state for: user.code_state_for() +state for in: user.code_state_for_each() +state switch: user.code_state_switch() +state case: user.code_state_case() +state do: user.code_state_do() +state goto: user.code_state_go_to() +state return: user.code_state_return() +state import: user.code_import() +from import: user.code_from_import() +state class: user.code_type_class() +state include: user.code_include() +state include system: user.code_include_system() +state include local: user.code_include_local() +state type deaf: user.code_type_definition() +state type deaf struct: user.code_typedef_struct() +state (no | nil | null): user.code_null() +state break: user.code_break() +state next: user.code_next() +state true: user.code_true() +state false: user.code_false() + +# show and print functions and libraries +toggle funk: user.code_toggle_functions() +funk : + user.code_insert_function(code_functions, "") +funk cell : + user.code_select_function(number - 1, "") +funk wrap : + user.code_insert_function(code_functions, edit.selected_text()) +funk wrap : + user.code_select_function(number - 1, edit.selected_text()) +dock string: user.code_document_string() + +slider: + edit.line_end() + insert(",") + key(enter) + +breaker: + edit.line_end() + insert(" {") + key(enter) + +chronic: + edit.line_end() + insert(":") + key(enter) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/lang/python/python.py b/talon/community/community-cursorless-0.4.0/lang/python/python.py new file mode 100644 index 0000000..d8703cb --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/python/python.py @@ -0,0 +1,187 @@ +import re + +from talon import Context, Module, actions, settings + +mod = Module() +ctx = Context() +ctx.matches = r""" +mode: user.python +mode: command +and code.language: python +""" +ctx.lists["user.code_functions"] = { + "enumerate": "enumerate", + "integer": "int", + "length": "len", + "list": "list", + "print": "print", + "range": "range", + "set": "set", + "split": "split", + "string": "str", + "update": "update", +} + +"""a set of fields used in python docstrings that will follow the +reStructuredText format""" +docstring_fields = { + "class": ":class:", + "function": ":func:", + "parameter": ":param:", + "raise": ":raise:", + "returns": ":return:", + "type": ":type:", + "return type": ":rtype:", + # these are sphinx-specific + "see also": ".. seealso:: ", + "notes": ".. notes:: ", + "warning": ".. warning:: ", + "todo": ".. todo:: ", +} + +mod.list("python_docstring_fields", desc="python docstring fields") +ctx.lists["user.python_docstring_fields"] = docstring_fields + +type_list = { + "boolean": "bool", + "integer": "int", + "string": "str", + "none": "None", + "dick": "Dict", + "float": "float", + "any": "Any", + "tuple": "Tuple", + "union": "UnionAny", + "iterable": "Iterable", + "vector": "Vector", + "bytes": "bytes", + "sequence": "Sequence", + "callable": "Callable", + "list": "List", + "no return": "NoReturn", +} + +mod.list("python_type_list", desc="python types") +ctx.lists["user.python_type_list"] = type_list + +exception_list = [ + "BaseException", + "SystemExit", + "KeyboardInterrupt", + "GeneratorExit", + "Exception", + "StopIteration", + "StopAsyncIteration", + "ArithmeticError", + "FloatingPointError", + "OverflowError", + "ZeroDivisionError", + "AssertionError", + "AttributeError", + "BufferError", + "EOFError", + "ImportError", + "ModuleNotFoundError", + "LookupError", + "IndexError", + "KeyError", + "MemoryError", + "NameError", + "UnboundLocalError", + "OSError", + "BlockingIOError", + "ChildProcessError", + "ConnectionError", + "BrokenPipeError", + "ConnectionAbortedError", + "ConnectionRefusedError", + "ConnectionResetError", + "FileExistsError", + "FileNotFoundError", + "InterruptedError", + "IsADirectoryError", + "NotADirectoryError", + "PermissionError", + "ProcessLookupError", + "TimeoutError", + "ReferenceError", + "RuntimeError", + "NotImplementedError", + "RecursionError", + "SyntaxError", + "IndentationError", + "TabError", + "SystemError", + "TypeError", + "ValueError", + "UnicodeError", + "UnicodeDecodeError", + "UnicodeEncodeError", + "UnicodeTranslateError", + "Warning", + "DeprecationWarning", + "PendingDeprecationWarning", + "RuntimeWarning", + "SyntaxWarning", + "UserWarning", + "FutureWarning", + "ImportWarning", + "UnicodeWarning", + "BytesWarning", + "ResourceWarning", +] +mod.list("python_exception", desc="python exceptions") +ctx.lists["user.python_exception"] = { + " ".join(re.findall("[A-Z][^A-Z]*", exception)).lower(): exception + for exception in exception_list +} + + +@ctx.action_class("user") +class user_actions: + def code_insert_function(text: str, selection: str): + if selection: + text = text + "({})".format(selection) + else: + text = text + "()" + actions.user.paste(text) + actions.edit.left() + + def code_default_function(text: str): + actions.user.code_public_function(text) + + def code_private_function(text: str): + """Inserts private function declaration""" + result = "def _{}():".format( + actions.user.formatted_text( + text, settings.get("user.code_private_function_formatter") + ) + ) + + actions.user.paste(result) + actions.edit.left() + actions.edit.left() + + def code_public_function(text: str): + result = "def {}():".format( + actions.user.formatted_text( + text, settings.get("user.code_public_function_formatter") + ) + ) + actions.user.paste(result) + actions.edit.left() + actions.edit.left() + + +@mod.action_class +class module_actions: + # TODO this could go somewhere else + def insert_cursor(text: str): + """Insert a string. Leave the cursor wherever [|] is in the text""" + if "[|]" in text: + end_pos = text.find("[|]") + s = text.replace("[|]", "") + actions.insert(s) + actions.key(f"left:{len(s) - end_pos}") + else: + actions.insert(text) diff --git a/talon/community/community-cursorless-0.4.0/lang/python/python.talon b/talon/community/community-cursorless-0.4.0/lang/python/python.talon new file mode 100644 index 0000000..a4ae82d --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/python/python.talon @@ -0,0 +1,129 @@ +mode: user.python +mode: command +and code.language: python +- +tag(): user.code_operators +tag(): user.code_comment +tag(): user.code_generic +settings(): + user.code_private_function_formatter = "SNAKE_CASE" + user.code_protected_function_formatter = "SNAKE_CASE" + user.code_public_function_formatter = "SNAKE_CASE" + user.code_private_variable_formatter = "SNAKE_CASE" + user.code_protected_variable_formatter = "SNAKE_CASE" + user.code_public_variable_formatter = "SNAKE_CASE" +action(user.code_operator_indirection): "" +action(user.code_operator_address_of): "" +action(user.code_operator_structure_dereference): "" +action(user.code_operator_lambda): "" +action(user.code_operator_subscript): + insert("[]") + key(left) +action(user.code_operator_assignment): " = " +action(user.code_operator_subtraction): " - " +action(user.code_operator_subtraction_assignment): " -= " +action(user.code_operator_addition): " + " +action(user.code_operator_addition_assignment): " += " +action(user.code_operator_multiplication): " * " +action(user.code_operator_multiplication_assignment): " *= " +action(user.code_operator_exponent): " ** " +action(user.code_operator_division): " / " +action(user.code_operator_division_assignment): " /= " +action(user.code_operator_modulo): " % " +action(user.code_operator_modulo_assignment): " %= " +action(user.code_operator_equal): " == " +action(user.code_operator_not_equal): " != " +action(user.code_operator_greater_than): " > " +action(user.code_operator_greater_than_or_equal_to): " >= " +action(user.code_operator_less_than): " < " +action(user.code_operator_less_than_or_equal_to): " <= " +action(user.code_operator_and): " and " +action(user.code_operator_or): " or " +action(user.code_operator_bitwise_and): " & " +action(user.code_operator_bitwise_and_assignment): " &= " +action(user.code_operator_bitwise_or): " | " +action(user.code_operator_bitwise_or_assignment): " |= " +action(user.code_operator_bitwise_exclusive_or): " ^ " +action(user.code_operator_bitwise_exclusive_or_assignment): " ^= " +action(user.code_operator_bitwise_left_shift): " << " +action(user.code_operator_bitwise_left_shift_assignment): " <<= " +action(user.code_operator_bitwise_right_shift): " >> " +action(user.code_operator_bitwise_right_shift_assignment): " >>= " +action(user.code_self): "self" +action(user.code_null): "None" +action(user.code_is_null): " is None" +action(user.code_is_not_null): " is not None" +action(user.code_state_if): + insert("if :") + key(left) +action(user.code_state_else_if): + insert("elif :") + key(left) +action(user.code_state_else): + insert("else:") + key(enter) +action(user.code_state_switch): + insert("switch ()") + edit.left() +action(user.code_state_case): + insert("case \nbreak;") + edit.up() +action(user.code_state_for): "for " +action(user.code_state_for_each): + insert("for in ") + key(left) + edit.word_left() + key(space) + edit.left() +action(user.code_state_while): + insert("while :") + edit.left() +action(user.code_type_class): "class " +action(user.code_import): "import " +action(user.code_from_import): + insert("from import ") + key(left) + edit.word_left() + key(space) + edit.left() +action(user.code_comment): "# " +action(user.code_state_return): + insert("return ") +action(user.code_true): "True" +action(user.code_false): "False" +action(user.code_document_string): user.insert_cursor("\"\"\"[|]\"\"\"") + +#python-specific grammars +dunder in it: "__init__" +state (def | deaf | deft): "def " +self taught: "self." +pie test: "pytest" +state past: "pass" + +^funky $: user.code_default_function(text) +#^pro funky $: user.code_protected_function(text) +^pub funky $: user.code_public_function(text) +#^static funky $: user.code_private_static_function(text) +#^pro static funky $: user.code_protected_static_function(text) +#^pub static funky $: user.code_public_static_function(text) +raise {user.python_exception}: user.insert_cursor("raise {python_exception}([|])") + +# for annotating function parameters +is type {user.python_type_list}: + insert(": {python_type_list}") +returns [type] {user.python_type_list}: + insert(" -> {python_type_list}") +# for generic reference of types +type {user.python_type_list}: + insert("{python_type_list}") +dock {user.python_docstring_fields}: + insert("{python_docstring_fields}") + edit.left() +dock type {user.python_type_list}: + user.insert_cursor(":type [|]: {python_type_list}") +dock returns type {user.python_type_list}: + user.insert_cursor(":rtype [|]: {python_type_list}") +toggle imports: user.code_toggle_libraries() +import : + user.code_insert_library(code_libraries, "") + key(end enter) diff --git a/talon/user/community/lang/r/r.py b/talon/community/community-cursorless-0.4.0/lang/r/r.py similarity index 64% rename from talon/user/community/lang/r/r.py rename to talon/community/community-cursorless-0.4.0/lang/r/r.py index 1b2e3ea..7d0b2de 100644 --- a/talon/user/community/lang/r/r.py +++ b/talon/community/community-cursorless-0.4.0/lang/r/r.py @@ -1,12 +1,14 @@ -from talon import Context, actions, settings +from talon import Context, Module, actions, clip, imgui, settings, ui ctx = Context() ctx.matches = r""" -code.language: r +mode: user.r +mode: command +and code.language: r """ -ctx.lists["user.code_common_function"] = { +ctx.lists["user.code_functions"] = { # base R "as character": "as.character", "as data frame": "as.data.frame", @@ -205,137 +207,14 @@ ctx.lists["user.code_libraries"] = { "shiny alert": "shinyalert", } -ctx.lists["user.code_parameter_name"] = { - "alpha": "alpha", - "breaks": "breaks", - "colour": "colour", - "data": "data", - "fill": "fill", - "H just": "hjust", - "keep": ".keep", - "label": "label", - "labels": "labels", - "log": "log", - "main": "main", - "mapping": "mapping", - "method": "method", - "NA remove": "na.rm", - "path": "path", - "position": "position", - "plex label": "xlab", - "plex limit": "xlim", - "scales": "scales", - "size": "size", - "show legend": "show.legend", - "sort": "sort", - "title": "title", - "type": "type", - "vee just": "vjust", - "width": "width", - "with ties": "with_ties", - "why label": "ylab", - "why limit": "ylim", - "why max": "ymax", - "why min": "ymin", -} - @ctx.action_class("user") -class UserActions: - def code_operator_assignment(): - actions.auto_insert(" <- ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_exponent(): - actions.auto_insert(" ** ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_modulo(): - actions.auto_insert(" %% ") - - def code_operator_equal(): - actions.auto_insert(" == ") - - def code_operator_not_equal(): - actions.auto_insert(" != ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" & ") - - def code_operator_or(): - actions.auto_insert(" | ") - - def code_operator_bitwise_and(): - actions.auto_insert(" & ") - - def code_insert_null(): - actions.auto_insert("NULL") - - def code_state_if(): - actions.insert("if () {}") - actions.key("left enter up end left:3") - - def code_state_else_if(): - actions.insert(" else if () {}") - actions.key("left enter up end left:3") - - def code_state_else(): - actions.insert(" else {}") - actions.key("left enter") - - def code_state_for(): - actions.insert("for ( in ) {}") - actions.key("left enter up end left:7") - - def code_state_while(): - actions.insert("while () {}") - actions.key("left enter up end left:3") - - def code_import(): - actions.user.insert_between("library(", ")") - - def code_comment_line_prefix(): - actions.auto_insert("#") - - def code_state_return(): - actions.user.insert_between("return(", ")") - - def code_break(): - actions.auto_insert("break") - - def code_next(): - actions.auto_insert("next") - - def code_insert_true(): - actions.auto_insert("TRUE") - - def code_insert_false(): - actions.auto_insert("FALSE") - +class user_actions: def code_insert_function(text: str, selection: str): - text += f"({selection or ''})" + if selection: + text = text + "({})".format(selection) + else: + text = text + "()" actions.user.paste(text) actions.edit.left() @@ -355,8 +234,7 @@ class UserActions: actions.edit.left() def code_insert_library(text: str, selection: str): - actions.user.insert_between("library(", ")") - actions.user.paste(text + selection) - - def code_insert_named_argument(parameter_name: str): - actions.insert(f"{parameter_name} = ") + actions.insert("library()") + actions.edit.left() + actions.clip.set_text(text + "{}".format(selection)) + actions.edit.paste() diff --git a/talon/community/community-cursorless-0.4.0/lang/r/r.talon b/talon/community/community-cursorless-0.4.0/lang/r/r.talon new file mode 100644 index 0000000..200e6ac --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/r/r.talon @@ -0,0 +1,74 @@ +mode: user.r +mode: command +and code.language: r +- +# TODO: functions + +tag(): user.code_operators +tag(): user.code_comment +tag(): user.code_generic +settings(): + user.code_private_function_formatter = "SNAKE_CASE" + user.code_protected_function_formatter = "SNAKE_CASE" + user.code_public_function_formatter = "SNAKE_CASE" + user.code_private_variable_formatter = "SNAKE_CASE" + user.code_protected_variable_formatter = "SNAKE_CASE" + user.code_public_variable_formatter = "SNAKE_CASE" +action(user.code_operator_assignment): " <- " +action(user.code_operator_subtraction): " - " +action(user.code_operator_addition): " + " +action(user.code_operator_multiplication): " * " +action(user.code_operator_exponent): " ** " +action(user.code_operator_division): " / " +action(user.code_operator_modulo): " %% " +action(user.code_operator_equal): " == " +action(user.code_operator_not_equal): " != " +action(user.code_operator_greater_than): " > " +action(user.code_operator_greater_than_or_equal_to): " >= " +action(user.code_operator_less_than): " < " +action(user.code_operator_less_than_or_equal_to): " <= " +action(user.code_operator_in): " %in% " +action(user.code_operator_and): " & " +action(user.code_operator_or): " | " +action(user.code_operator_bitwise_and): " & " +action(user.code_null): "NULL" +action(user.code_state_if): + insert("if () {}") + key(left enter up end left:3) +action(user.code_state_else_if): + insert(" else if () {}") + key(left enter up end left:3) +action(user.code_state_else): + insert(" else {}") + key(left enter) +action(user.code_state_for): + insert("for ( in ) {}") + key(left enter up end left:7) +action(user.code_state_while): + insert("while () {}") + key(left enter up end left:3) +toggle library: user.code_toggle_libraries() +library : + user.code_insert_library(code_libraries, "") + key(end enter) +action(user.code_import): + insert("library()") + key(left) +action(user.code_comment): "#" +action(user.code_state_return): + insert("return()") + key(left) +action(user.code_break): "break" +action(user.code_next): "next" +action(user.code_true): "TRUE" +action(user.code_false): "FALSE" + +# R specific commands +(chain|pipe that): + key(end) + " %>%" + key(enter) +state na: + insert("NA") + +^function define $: user.code_private_function(text) diff --git a/talon/community/community-cursorless-0.4.0/lang/snippets.talon b/talon/community/community-cursorless-0.4.0/lang/snippets.talon new file mode 100644 index 0000000..d31551f --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/snippets.talon @@ -0,0 +1,7 @@ +tag: user.snippets +- +snip {user.snippets}: user.snippet_insert(user.snippets) +snip hunt : user.snippet_search(user.text) +snip hunt: user.snippet_search("") +snip create: user.snippet_create() +snip show: user.snippet_toggle() diff --git a/talon/community/community-cursorless-0.4.0/lang/sql.talon b/talon/community/community-cursorless-0.4.0/lang/sql.talon new file mode 100644 index 0000000..7ef80bd --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/sql.talon @@ -0,0 +1,20 @@ +app: datagrip + +- +select: "SELECT " +star: "*" +from: "FROM " +select star from: "SELECT * FROM " +where: "WHERE " +order by: "ORDER BY " +descending: " DESC" +ascending: " ASC" +dot i d: ".id" +is not null: " IS NOT NULL" +is null: " IS NULL" +inner join: + insert("INNER JOIN ON ") + key(left) + key(left) + key(left) + key(left) diff --git a/talon/community/community-cursorless-0.4.0/lang/talon/talon.py b/talon/community/community-cursorless-0.4.0/lang/talon/talon.py new file mode 100644 index 0000000..b8ea504 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/talon/talon.py @@ -0,0 +1,26 @@ +from talon import Module, Context, actions, ui, imgui, clip, settings + +ctx = Context() +ctx.matches = r""" +mode: user.talon +mode: command +and code.language: talon +""" +ctx.lists["user.code_functions"] = { + "insert": "insert", + "key": "key", + "print": "print", + "repeat": "repeat", +} + + +@ctx.action_class("user") +class user_actions: + def code_insert_function(text: str, selection: str): + if selection: + text = text + "({})".format(selection) + else: + text = text + "()" + + actions.user.paste(text) + actions.edit.left() diff --git a/talon/community/community-cursorless-0.4.0/lang/talon/talon.talon b/talon/community/community-cursorless-0.4.0/lang/talon/talon.talon new file mode 100644 index 0000000..f7c2a31 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/talon/talon.talon @@ -0,0 +1,51 @@ +mode: user.talon +mode: command +and code.language: talon +- +tag(): user.code_operators +tag(): user.code_comment +action(user.code_operator_and): " and " +action(user.code_operator_or): " or " +action(user.code_operator_subtraction): " - " +action(user.code_operator_addition): " + " +action(user.code_operator_multiplication): " * " +action(user.code_operator_division): " / " +action(user.code_operator_assignment): " = " +action(user.code_comment): "#" + +dot talon: insert(".talon") +#defintion blocks for the context +action block: + insert("action():") + edit.left() + edit.left() +setting block: + insert("settings():\n\t") +#context requirements +win require: + insert("os: windows\n") +mac require: + insert("os: mac\n") +linux require: + insert("os: linux\n") +title require: + insert("win.title: ") +app require: + insert("app: ") +tag require: + insert("tag: ") +tag set: + insert("tag(): ") +#commands for dictating key combos +key over: "{keys}" +key over: "{modifiers}" +#funk commands, consistent with other languages +toggle funk: user.code_toggle_functions() +funk : + user.code_insert_function(code_functions, "") +funk cell : + user.code_select_function(number - 1, "") +funk wrap : + user.code_insert_function(code_functions, edit.selected_text()) +funk wrap : + user.code_select_function(number - 1, edit.selected_text()) diff --git a/talon/community/community-cursorless-0.4.0/lang/typescript/typescript.py b/talon/community/community-cursorless-0.4.0/lang/typescript/typescript.py new file mode 100644 index 0000000..ea1598d --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/typescript/typescript.py @@ -0,0 +1,83 @@ +from talon import Module, Context, actions, ui, imgui, settings + +ctx = Context() +ctx.matches = r""" +mode: user.typescript +mode: command +and code.language: typescript +""" +# tbd +ctx.lists["user.code_functions"] = { +# "integer": "int.TryParse", + "print": "console.log", +# "string": ".ToString", +} + + +@ctx.action_class("user") +class user_actions: + def code_insert_function(text: str, selection: str): + if selection: + text = text + "({})".format(selection) + else: + text = text + "()" + + actions.user.paste(text) + actions.edit.left() + + def code_private_function(text: str): + """Inserts private function declaration""" + result = "private function {}".format( + actions.user.formatted_text( + text, settings.get("user.code_private_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + # def code_private_static_function(text: str): + # """Inserts private static function""" + # result = "private static void {}".format( + # actions.user.formatted_text( + # text, settings.get("user.code_private_function_formatter") + # ) + # ) + + # actions.user.code_insert_function(result, None) + + def code_protected_function(text: str): + result = "protected function {}".format( + actions.user.formatted_text( + text, settings.get("user.code_protected_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + # def code_protected_static_function(text: str): + # result = "protected static void {}".format( + # actions.user.formatted_text( + # text, settings.get("user.code_protected_function_formatter") + # ) + # ) + + # actions.user.code_insert_function(result, None) + + def code_public_function(text: str): + result = "public function {}".format( + actions.user.formatted_text( + text, settings.get("user.code_public_function_formatter") + ) + ) + + actions.user.code_insert_function(result, None) + + # def code_public_static_function(text: str): + # result = "public static void {}".format( + # actions.user.formatted_text( + # text, settings.get("user.code_public_function_formatter") + # ) + # ) + + # actions.user.code_insert_function(result, None) + diff --git a/talon/community/community-cursorless-0.4.0/lang/typescript/typescript.talon b/talon/community/community-cursorless-0.4.0/lang/typescript/typescript.talon new file mode 100644 index 0000000..fe042fb --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/typescript/typescript.talon @@ -0,0 +1,183 @@ +mode: user.typescript +mode: command +and code.language: typescript +- +tag(): user.code_operators +tag(): user.code_comment +tag(): user.code_generic + +settings(): + user.code_private_function_formatter = "PRIVATE_CAMEL_CASE" + user.code_protected_function_formatter = "PRIVATE_CAMEL_CASE" + user.code_public_function_formatter = "PRIVATE_CAMEL_CASE" + user.code_private_variable_formatter = "PRIVATE_CAMEL_CASE" + user.code_protected_variable_formatter = "PRIVATE_CAMEL_CASE" + user.code_public_variable_formatter = "PRIVATE_CAMEL_CASE" + +action(user.code_is_not_null): " !== null" + +action(user.code_is_null): " === null" + +action(user.code_type_dictionary): + insert("{}") + key(left) + +action(user.code_state_if): + insert("if ()") + key(left) + +action(user.code_state_else_if): + insert(" else if ()") + key(left) + +action(user.code_state_else): + insert(" else {}") + key(left enter) + +action(user.code_block): + insert("{}") + key(left enter) + +action(user.code_self): "this" + +action(user.code_state_while): + insert("while ()") + key(left) + +action(user.code_state_do): + insert("do ") + +action(user.code_state_return): + insert("return ") + +action(user.code_state_for): + insert("for ()") + key(left) + +action(user.code_state_switch): + insert("switch ()") + key(left) + +action(user.code_state_case): + insert("case :") + key(left) + +action(user.code_state_go_to): "" + +action(user.code_import): "import " +state export: "export " + +action(user.code_from_import): + insert(" from \"\"") + key(left) + +action(user.code_type_class): "class " +state interface: "interface " +state type: "type " + +action(user.code_include): "" + +action(user.code_include_system): "" + +action(user.code_include_local): "" + +action(user.code_type_definition): "" + +action(user.code_typedef_struct): "" + +action(user.code_state_for_each): + insert(".forEach()") + key(left) + +action(user.code_break): "break;" +action(user.code_next): "continue;" +action(user.code_true): "true" +action(user.code_false): "false" + +action(user.code_null): "null" + +action(user.code_operator_indirection): "" +action(user.code_operator_address_of): "" +action(user.code_operator_structure_dereference): "" +action(user.code_operator_lambda): " => " +action(user.code_operator_subscript): + insert("[]") + key(left) +action(user.code_operator_assignment): " = " +action(user.code_operator_subtraction): " - " +action(user.code_operator_subtraction_assignment): " -= " +action(user.code_operator_addition): " + " +action(user.code_operator_addition_assignment): " += " +action(user.code_operator_multiplication): " * " +action(user.code_operator_multiplication_assignment): " *= " +action(user.code_operator_exponent): " ** " +action(user.code_operator_division): " / " +action(user.code_operator_division_assignment): " /= " +action(user.code_operator_modulo): " % " +action(user.code_operator_modulo_assignment): " %= " +action(user.code_operator_equal): " == " +action(user.code_operator_not_equal): " != " + treek: " === " + nottreek: " !== " +action(user.code_operator_greater_than): " > " +action(user.code_operator_greater_than_or_equal_to): " >= " +action(user.code_operator_less_than): " < " +action(user.code_operator_less_than_or_equal_to): " <= " +action(user.code_operator_and): " && " +action(user.code_operator_or): " || " + null coal: " ?? " +action(user.code_operator_bitwise_and): " & " +action(user.code_operator_bitwise_and_assignment): " &= " +action(user.code_operator_bitwise_or): " | " +action(user.code_operator_bitwise_or_assignment): " |= " +action(user.code_operator_bitwise_exclusive_or): " ^ " +action(user.code_operator_bitwise_exclusive_or_assignment): " ^= " +action(user.code_operator_bitwise_left_shift): " << " +action(user.code_operator_bitwise_left_shift_assignment): " <<= " +action(user.code_operator_bitwise_right_shift): " >> " +action(user.code_operator_bitwise_right_shift_assignment): " >>= " + cast: " as " + +state const: "const " + +state let: "let " + +state var: "var " + +state async: "async " + + await: "await " + +state map: + insert(".map()") + key(left) + +state filter: + insert(".filter()") + key(left) + +state reduce: + insert(".reduce()") + key(left) + +state concat: + insert(".concat()") + key(left) + +state length: insert(".length") + +state log: + insert("console.log()") + key(left) + +state quote var: + insert("${}") + key(left) + +state spread: "..." + +funky $: user.code_default_function(text) +pro funky $: user.code_protected_function(text) +pub funky $: user.code_public_function(text) + + new: insert("new ") \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/lang/vimscript/vimscript.py b/talon/community/community-cursorless-0.4.0/lang/vimscript/vimscript.py new file mode 100644 index 0000000..1b78e95 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/vimscript/vimscript.py @@ -0,0 +1,39 @@ +from talon import Context, Module, actions, settings + +mod = Module() +ctx = Context() +ctx.lists["self.vimscript_functions"] = { + "string len": "strlen", + "get line": "getline", + "set line": "setline", + "length": "len", +} + +ctx.lists["self.vimscript_scope"] = { + "argument": "a:", + "arg": "a:", + "buffer": "b:", + "buf": "b:", + "window": "w:", + "win": "w:", + "tab": "t:", + "special": "v:", + "global": "g:", + "local": "l:", + "script local": "s:", +} + +mod.list("vimscript_functions", desc="Standard built-in vimscript functions") +mod.list("vimscript_scope", desc="vimscript scoping types for functions and variables") + + +@mod.capture(rule="{self.vimscript_functions}") +def vimscript_functions(m) -> str: + "Returns a string" + return m.vimscript_functions + + +@mod.capture(rule="{self.vimscript_scope}") +def vimscript_scope(m) -> str: + "Returns a string" + return m.vimscript_scope diff --git a/talon/community/community-cursorless-0.4.0/lang/vimscript/vimscript.talon b/talon/community/community-cursorless-0.4.0/lang/vimscript/vimscript.talon new file mode 100644 index 0000000..5af723c --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/lang/vimscript/vimscript.talon @@ -0,0 +1,72 @@ + +mode: user.vimscript +mode: command +and code.language: vimscript +- +tag(): user.code_operators +tag(): user.code_comment +tag(): user.code_generic +# XXX - revisit these +settings(): + user.code_private_function_formatter = "SNAKE_CASE" + user.code_protected_function_formatter = "SNAKE_CASE" + user.code_public_function_formatter = "SNAKE_CASE" + user.code_private_variable_formatter = "SNAKE_CASE" + user.code_protected_variable_formatter = "SNAKE_CASE" + user.code_public_variable_formatter = "SNAKE_CASE" + + +### +# Generic Actions - see appropriate generic talon file for spoken command +### + +# operators - see lang/operators.talon +action(user.code_operator_assignment): " = " +action(user.code_operator_subtraction): " - " +action(user.code_operator_subtraction_assignment): " -= " +action(user.code_operator_addition): " + " +action(user.code_operator_addition_assignment): " += " +action(user.code_operator_multiplication): " * " +action(user.code_operator_multiplication_assignment): " *= " +action(user.code_operator_division): " / " +action(user.code_operator_division_assignment): " /= " + +# comments - see lang/code_comment.talon +action(user.code_comment): "\"" + +# conditionals - see lang/programming.talon +action(user.code_state_if): + insert("if ") +action(user.code_state_else_if): + insert("elseif ") +action(user.code_state_else): + insert("else") + +action(user.code_private_function): "function " +action(user.code_protected_function): "function " +action(user.code_public_function): "function " + + +### +# VIM Script Specific +### +assign [] (variable|var) [] [over]: + insert("let ") + insert(vimscript_scope or '') + user.code_private_variable_formatter(text) + +[] (variable|var) [] [over]: + insert(vimscript_scope or '') + user.code_private_variable_formatter(text) + +# see lang/vimscript/vimscript.py for list +: + insert("{vimscript_functions} ") + +# XXX - possibly overlap with some programming.talon +state command: "command! " +state end if: "endif" +state end for: "endfor" +state end while: "endwhile" +state end function: "endfunction" +state continue: "continue" diff --git a/talon/community/community-cursorless-0.4.0/misc/abbreviate.talon b/talon/community/community-cursorless-0.4.0/misc/abbreviate.talon new file mode 100644 index 0000000..47004b9 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/abbreviate.talon @@ -0,0 +1,2 @@ +- +(abbreviate|abreviate|brief) {user.abbreviation}: "{abbreviation}" diff --git a/talon/community/community-cursorless-0.4.0/misc/extensions.talon b/talon/community/community-cursorless-0.4.0/misc/extensions.talon new file mode 100644 index 0000000..99bab02 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/extensions.talon @@ -0,0 +1 @@ +{user.file_extension}: "{file_extension}" \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/misc/formatters.talon b/talon/community/community-cursorless-0.4.0/misc/formatters.talon new file mode 100644 index 0000000..b7b646c --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/formatters.talon @@ -0,0 +1,17 @@ +#provide both anchored and unachored commands via 'over' +phrase $: user.insert_formatted(text, "NOOP") +phrase halt: user.insert_formatted(text, "NOOP") +{user.prose_formatter} $: user.insert_formatted(prose, prose_formatter) +{user.prose_formatter} halt: user.insert_formatted(prose, prose_formatter) ++$: user.insert_many(format_text_list) ++ halt: user.insert_many(format_text_list) + that: user.formatters_reformat_selection(user.formatters) +word : user.insert_formatted(user.word, "NOOP") +format help: user.formatters_help_toggle() +recent list: user.toggle_phrase_history() +recent repeat : insert(user.get_recent_phrase(number_small)) +recent copy : clip.set_text(user.get_recent_phrase(number_small)) +select that: user.select_last_phrase() +nope that | scratch that: user.clear_last_phrase() +nope that was : user.formatters_reformat_last(formatters) +trot : "{word} " \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/misc/git.talon b/talon/community/community-cursorless-0.4.0/misc/git.talon new file mode 100644 index 0000000..4c26050 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/git.talon @@ -0,0 +1,101 @@ +tag: terminal +and tag: user.git +- +# Standard commands +git add patch: "git add . -p\n" +git add: "git add " +git add everything: "git add -u\n" +git bisect: "git bisect " +git blame: "git blame " +git branch: "git branch " +git remote branches: "git branch --remote\n" +git branch : "git branch {text}" +git checkout: "git checkout " +git checkout master: "git checkout master\n" +git checkout main: "git checkout main\n" +git checkout : "git checkout {text}" +git cherry pick: "git cherry-pick " +git cherry pick continue: "git cherry-pick --continue " +git cherry pick abort: "git cherry-pick --abort " +git cherry pick skip: "git cherry-pick --skip " +git clone: "git clone " +git commit message : "git commit -m '{text}'" +git commit: "git commit\n" +git diff (colour|color) words: "git diff --color-words " +git diff: "git diff " +git diff cached: "git diff --cached\n" +git fetch: "git fetch\n" +git fetch : "git fetch {text}" +git fetch prune: "git fetch --prune\n" +git in it: "git init\n" +git log all: "git log\n" +git log all changes: "git log -c\n" +git log: "git log " +git log changes: "git log -c " +git merge: "git merge " +git merge :"git merge {text}" +git move: "git mv " +git new branch: "git checkout -b " +git pull: "git pull\n" +git pull origin: "git pull origin " +git pull rebase: "git pull --rebase\n" +git pull fast forward: "git pull --ff-only\n" +git pull : "git pull {text} " +git push: "git push\n" +git push origin: "git push origin " +git push up stream origin: "git push -u origin" +git push : "git push {text} " +git push tags: "git push --tags\n" +git rebase: "git rebase\n" +git rebase continue: "git rebase --continue" +git rebase skip: "git rebase --skip" +git remove: "git rm " +git (remove|delete) branch: "git branch -d " +git (remove|delete) remote branch: "git push --delete origin " +git reset: "git reset " +git reset soft: "git reset --soft " +git reset hard: "git reset --hard " +git restore: "git restore " +git restore staged: "git restore --staged " +git remote show origin: "git remote show origin\n" +git remote add upstream: "git remote add upstream " +git show: "git show " +git stash pop: "git stash pop\n" +git stash: "git stash\n" +git stash apply: "git stash apply\n" +git stash list: "git stash list\n" +git stash show: "git stash show" +git status: "git status\n" +git submodule add: "git submodule add " +git tag: "git tag " + +# Convenience +git edit config: "git config --local -e\n" + +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") diff --git a/talon/community/community-cursorless-0.4.0/misc/git_add_patch.talon b/talon/community/community-cursorless-0.4.0/misc/git_add_patch.talon new file mode 100644 index 0000000..78acb22 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/git_add_patch.talon @@ -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) diff --git a/talon/community/community-cursorless-0.4.0/misc/help.talon b/talon/community/community-cursorless-0.4.0/misc/help.talon new file mode 100644 index 0000000..290ae06 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/help.talon @@ -0,0 +1,7 @@ +help alphabet: user.help_alphabet(user.get_alphabet()) +help context$: user.help_context() +help active$: user.help_context_enabled() +help search $: user.help_search(text) +help context {user.help_contexts}$: user.help_selected_context(help_contexts) + + diff --git a/talon/community/community-cursorless-0.4.0/misc/help_open.talon b/talon/community/community-cursorless-0.4.0/misc/help_open.talon new file mode 100644 index 0000000..94bd674 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/help_open.talon @@ -0,0 +1,7 @@ +mode: user.help +- +help next: user.help_next() +help previous: user.help_previous() +help : user.help_select_index(number - 1) +help return: user.help_return() +help close: user.help_hide() \ No newline at end of file diff --git a/talon/user/community/plugin/command_history/command_history.talon b/talon/community/community-cursorless-0.4.0/misc/history.talon similarity index 78% rename from talon/user/community/plugin/command_history/command_history.talon rename to talon/community/community-cursorless-0.4.0/misc/history.talon index 7289894..a1aac23 100644 --- a/talon/user/community/plugin/command_history/command_history.talon +++ b/talon/community/community-cursorless-0.4.0/misc/history.talon @@ -1,5 +1,4 @@ command history: user.history_toggle() -command history close: user.history_disable() command history clear: user.history_clear() command history less: user.history_less() command history more: user.history_more() diff --git a/talon/community/community-cursorless-0.4.0/misc/keeper.talon b/talon/community/community-cursorless-0.4.0/misc/keeper.talon new file mode 100644 index 0000000..4b3ba9a --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/keeper.talon @@ -0,0 +1 @@ +^keeper: skip() \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/misc/keys.talon b/talon/community/community-cursorless-0.4.0/misc/keys.talon new file mode 100644 index 0000000..552b771 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/keys.talon @@ -0,0 +1,8 @@ +: key(letter) +uppercase [(lowercase | sunk)]: + user.insert_formatted(letters, "ALL_CAPS") +: key(symbol_key) +: key(function_key) +: key(special_key) + : key("{modifiers}-{unmodified_key}") +flay: key(escape:5) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/misc/macro.talon b/talon/community/community-cursorless-0.4.0/misc/macro.talon new file mode 100644 index 0000000..08a44c3 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/macro.talon @@ -0,0 +1,4 @@ +macro record: user.macro_record() +macro stop: user.macro_stop() +macro play: user.macro_play() +[macro] sleep [] [seconds]: user.macro_insert_wait(number_small or 0) \ No newline at end of file diff --git a/talon/user/community/plugin/media/media.talon b/talon/community/community-cursorless-0.4.0/misc/media.talon similarity index 68% rename from talon/user/community/plugin/media/media.talon rename to talon/community/community-cursorless-0.4.0/misc/media.talon index 30bcda4..9aed77f 100644 --- a/talon/user/community/plugin/media/media.talon +++ b/talon/community/community-cursorless-0.4.0/misc/media.talon @@ -1,7 +1,7 @@ volume up: key(volup) volume down: key(voldown) set volume : user.media_set_volume(number) -(volume | media) mute: key(mute) +(volume|media) mute: key(mute) [media] play next: key(next) [media] play previous: key(prev) -media (play | pause): user.play_pause() +media (play | pause): key(play) diff --git a/talon/user/community/tags/messaging/messaging.talon b/talon/community/community-cursorless-0.4.0/misc/messaging.talon similarity index 80% rename from talon/user/community/tags/messaging/messaging.talon rename to talon/community/community-cursorless-0.4.0/misc/messaging.talon index 6bd580c..dc60ec2 100644 --- a/talon/user/community/tags/messaging/messaging.talon +++ b/talon/community/community-cursorless-0.4.0/misc/messaging.talon @@ -7,6 +7,12 @@ channel: user.messaging_open_channel_picker() channel : user.messaging_open_channel_picker() insert(user.formatted_text(user.text, "ALL_LOWERCASE")) + sleep(100ms) + chan : + user.messaging_open_channel_picker() + insert(user.formatted_text(user.text, "ALL_LOWERCASE")) + sleep(100ms) + key(enter) channel up: user.messaging_channel_previous() channel down: user.messaging_channel_next() ([channel] unread last | gopreev): user.messaging_unread_previous() diff --git a/talon/user/community/plugin/microphone_selection/microphone_selection.talon b/talon/community/community-cursorless-0.4.0/misc/microphone_selection.talon similarity index 66% rename from talon/user/community/plugin/microphone_selection/microphone_selection.talon rename to talon/community/community-cursorless-0.4.0/misc/microphone_selection.talon index 478bda6..b12ef8d 100644 --- a/talon/user/community/plugin/microphone_selection/microphone_selection.talon +++ b/talon/community/community-cursorless-0.4.0/misc/microphone_selection.talon @@ -1,3 +1,2 @@ ^microphone show$: user.microphone_selection_toggle() -^microphone close$: user.microphone_selection_hide() -^microphone pick $: user.microphone_select(number_small) +^microphone pick $: user.microphone_select(number_small) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/misc/momentary.talon b/talon/community/community-cursorless-0.4.0/misc/momentary.talon new file mode 100644 index 0000000..e12522f --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/momentary.talon @@ -0,0 +1,4 @@ +# mode: sleep +mode: dictation +- +^listen $: user.momentary() diff --git a/talon/community/community-cursorless-0.4.0/misc/momentary_command.talon b/talon/community/community-cursorless-0.4.0/misc/momentary_command.talon new file mode 100644 index 0000000..075df26 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/momentary_command.talon @@ -0,0 +1 @@ +^listen: skip() diff --git a/talon/community/community-cursorless-0.4.0/misc/mouse.talon b/talon/community/community-cursorless-0.4.0/misc/mouse.talon new file mode 100644 index 0000000..ac3755b --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/mouse.talon @@ -0,0 +1,103 @@ +control mouse: user.mouse_toggle_control_mouse() +zoom mouse: user.mouse_toggle_zoom_mouse() +camera overlay: user.mouse_toggle_camera_overlay() +run calibration: user.mouse_calibrate() +touch: + mouse_click(0) + # close the mouse grid if open + user.grid_close() + +right click: + mouse_click(1) + # close the mouse grid if open + user.grid_close() + +midclick: + mouse_click(2) + # close the mouse grid + user.grid_close() + +#see keys.py for modifiers. +#defaults +#command +#control +#option = alt +#shift +#super = windows key + touch: + key("{modifiers}:down") + mouse_click(0) + key("{modifiers}:up") + # close the mouse grid + user.grid_close() + righty: + key("{modifiers}:down") + mouse_click(1) + key("{modifiers}:up") + # close the mouse grid + user.grid_close() +dubclick: + mouse_click() + mouse_click() + # close the mouse grid + user.grid_close() +(tripclick | triplick): + mouse_click() + mouse_click() + mouse_click() + # close the mouse grid + user.grid_close() +drag: + user.mouse_drag() + # close the mouse grid + user.grid_close() +(scroll | school) down: user.mouse_scroll_down() +(scroll | school) down here: + user.mouse_move_center_active_window() + user.mouse_scroll_down() +(scroll | school) tiny [down]: mouse_scroll(20) +(scroll | school) tiny [down] here: + user.mouse_move_center_active_window() + mouse_scroll(20) +(scroll | school) downer: user.mouse_scroll_down_continuous() +(scroll | school) downer here: + user.mouse_move_center_active_window() + user.mouse_scroll_down_continuous() +(scroll | school) up: user.mouse_scroll_up() +(scroll | school) up here: + user.mouse_scroll_up() +(scroll | school) tiny up: mouse_scroll(-20) +(scroll | school) tiny up here: + user.mouse_move_center_active_window() + mouse_scroll(-20) +(scroll | school) upper: user.mouse_scroll_up_continuous() +(scroll | school) upper here: + user.mouse_move_center_active_window() + user.mouse_scroll_up_continuous() +(scroll | school) gaze: user.mouse_gaze_scroll() +(scroll | school) gaze here: + user.mouse_move_center_active_window() + user.mouse_gaze_scroll() +(scroll | school) stop: user.mouse_scroll_stop() +(scroll | school) stop here: + user.mouse_move_center_active_window() + user.mouse_scroll_stop() +(scroll | school) left: mouse_scroll(0, -40) +(scroll | school) left here: + user.mouse_move_center_active_window() + mouse_scroll(0, -40) +(scroll | school) tiny left: mouse_scroll(0, -20) +(scroll | school) tiny left here: + user.mouse_move_center_active_window() + mouse_scroll(0, -20) +(scroll | school) right: mouse_scroll(0, 40) +(scroll | school) right here: + user.mouse_move_center_active_window() + mouse_scroll(0, 40) +(scroll | school) tiny right: mouse_scroll(0, 20) +(scroll | school) tiny right here: + user.mouse_move_center_active_window() + mouse_scroll(0, 20) +curse yes: user.mouse_show_cursor() +curse no: user.mouse_hide_cursor() +copy mouse position: user.copy_mouse_position() \ No newline at end of file diff --git a/talon/user/community/tags/multiple_cursors/multiple_cursors.talon b/talon/community/community-cursorless-0.4.0/misc/multiple_cursors.talon similarity index 89% rename from talon/user/community/tags/multiple_cursors/multiple_cursors.talon rename to talon/community/community-cursorless-0.4.0/misc/multiple_cursors.talon index 9a70b43..32d9efe 100644 --- a/talon/user/community/tags/multiple_cursors/multiple_cursors.talon +++ b/talon/community/community-cursorless-0.4.0/misc/multiple_cursors.talon @@ -6,6 +6,5 @@ cursor up: user.multi_cursor_add_above() cursor down: user.multi_cursor_add_below() cursor less: user.multi_cursor_select_fewer_occurrences() cursor more: user.multi_cursor_select_more_occurrences() -cursor skip: user.multi_cursor_skip_occurrence() cursor all: user.multi_cursor_select_all_occurrences() cursor lines: user.multi_cursor_add_to_line_ends() diff --git a/talon/community/community-cursorless-0.4.0/misc/repeater.talon b/talon/community/community-cursorless-0.4.0/misc/repeater.talon new file mode 100644 index 0000000..e1e4605 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/repeater.talon @@ -0,0 +1,5 @@ +# -1 because we are repeating, so the initial command counts as one + now: core.repeat_command(number-1) +twice: core.repeat_command(1) +(repeat that|again): core.repeat_phrase(1) +repeat that [times]: core.repeat_phrase(number_small) diff --git a/talon/community/community-cursorless-0.4.0/misc/screenshot.talon b/talon/community/community-cursorless-0.4.0/misc/screenshot.talon new file mode 100644 index 0000000..0a484d3 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/screenshot.talon @@ -0,0 +1,5 @@ +^grab window$: user.screenshot_window() +^grab screen$: user.screenshot() +^grab selection$: user.screenshot_selection() +^grab window clip$: user.screenshot_window_clipboard() +^grab screen clip$: user.screenshot_clipboard() \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/misc/search_engines.talon b/talon/community/community-cursorless-0.4.0/misc/search_engines.talon new file mode 100644 index 0000000..fba69ec --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/search_engines.talon @@ -0,0 +1,4 @@ +{user.search_engine} hunt : user.search_with_search_engine(search_engine, user.text) +{user.search_engine} (that|this): + text = edit.selected_text() + user.search_with_search_engine(search_engine, text) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/misc/slow_mode.talon b/talon/community/community-cursorless-0.4.0/misc/slow_mode.talon new file mode 100644 index 0000000..b8e86ad --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/slow_mode.talon @@ -0,0 +1,6 @@ +mode: user.slow +- +settings(): + speech.timeout = 0.3 + +fast mode: mode.disable("user.slow") \ No newline at end of file diff --git a/talon/user/community/tags/splits/splits.talon b/talon/community/community-cursorless-0.4.0/misc/splits.talon similarity index 89% rename from talon/user/community/tags/splits/splits.talon rename to talon/community/community-cursorless-0.4.0/misc/splits.talon index 51e5277..8326235 100644 --- a/talon/user/community/tags/splits/splits.talon +++ b/talon/community/community-cursorless-0.4.0/misc/splits.talon @@ -7,8 +7,6 @@ split up: user.split_window_up() split (vertically | vertical): user.split_window_vertically() split (horizontally | horizontal): user.split_window_horizontally() split flip: user.split_flip() -split max: user.split_maximize() -split reset: user.split_reset() split window: user.split_window() split clear: user.split_clear() split clear all: user.split_clear_all() diff --git a/talon/community/community-cursorless-0.4.0/misc/standard.talon b/talon/community/community-cursorless-0.4.0/misc/standard.talon new file mode 100644 index 0000000..6c75186 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/standard.talon @@ -0,0 +1,44 @@ +#(jay son | jason ): "json" +#(http | htp): "http" +#tls: "tls" +#M D five: "md5" +#word (regex | rejex): "regex" +#word queue: "queue" +#word eye: "eye" +#word iter: "iter" +#word no: "NULL" +#word cmd: "cmd" +#word dup: "dup" +#word shell: "shell". +zoom in: edit.zoom_in() +zoom out: edit.zoom_out() +scroll up: edit.page_up() +scroll down: edit.page_down() +copy that: edit.copy() +cut that: edit.cut() +pace that: edit.paste() +show clip: key(cmd-shift-v) +(undo that | nope): edit.undo() +(redo that | yes indeed): edit.redo() +paste match: edit.paste_match_style() +disc: edit.save() +padding: + insert(" ") + key(left) +pour this: user.new_line_below() +drink this: user.new_line_above() + +slow mode: mode.enable("user.slow") + +emoji hunt []: + key(cmd-ctrl-space) + sleep(100ms) + insert(user.text or "") + +^dictate $: + auto_insert(text) + mode.disable("sleep") + mode.disable("command") + mode.enable("dictation") + user.code_clear_language_mode() + mode.disable("user.gdb") \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/misc/tabs.talon b/talon/community/community-cursorless-0.4.0/misc/tabs.talon new file mode 100644 index 0000000..6e4ea5f --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/tabs.talon @@ -0,0 +1,9 @@ +tag: user.tabs +- +tab make: app.tab_open() +tab last: app.tab_previous() +tab next: app.tab_next() +tab close: app.tab_close() +tab reopen: app.tab_reopen() +tab : user.tab_jump(ordinals) +tab final: user.tab_final() diff --git a/talon/community/community-cursorless-0.4.0/misc/talon_helpers.talon b/talon/community/community-cursorless-0.4.0/misc/talon_helpers.talon new file mode 100644 index 0000000..a40fb27 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/talon_helpers.talon @@ -0,0 +1,14 @@ +talon copy context pie: user.talon_add_context_clipboard_python() +talon copy context: user.talon_add_context_clipboard() +talon copy title: + title = win.title() + clip.set_text(title) +talon dump context: + name = app.name() + executable = app.executable() + bundle = app.bundle() + title = win.title() + print("Name: {name}") + print("Executable: {executable}") + print("Bundle: {bundle}") + print("Title: {title}") diff --git a/talon/community/community-cursorless-0.4.0/misc/todo_list.talon b/talon/community/community-cursorless-0.4.0/misc/todo_list.talon new file mode 100644 index 0000000..1e161d1 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/todo_list.talon @@ -0,0 +1,11 @@ +tag: user.todo_list +- +done this: user.mark_complete() +cancel this: user.mark_cancelled() + +show inbox: user.show_inbox() +show today: user.show_today() +show upcoming: user.show_upcoming() +show anytime: user.show_anytime() +show someday: user.show_someday() +show logbook: user.show_logbook() \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/misc/toggles.talon b/talon/community/community-cursorless-0.4.0/misc/toggles.talon new file mode 100644 index 0000000..139597f --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/toggles.talon @@ -0,0 +1,2 @@ + + diff --git a/talon/community/community-cursorless-0.4.0/misc/window_management.talon b/talon/community/community-cursorless-0.4.0/misc/window_management.talon new file mode 100644 index 0000000..56bfdc2 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/misc/window_management.talon @@ -0,0 +1,42 @@ +window (new|open): app.window_open() +window next: app.window_next() +window last: app.window_previous() +window close: app.window_close() +focus : user.switcher_focus(running_applications) +running list: user.switcher_toggle_running() +launch : user.switcher_launch(launch_applications) + +snap : user.snap_window(window_snap_position) +snap next [screen]: user.move_window_next_screen() +snap last [screen]: user.move_window_previous_screen() +snap screen : user.move_window_to_screen(number) +snap : + user.snap_app(running_applications, window_snap_position) +snap [screen] : + user.move_app_to_screen(running_applications, number) +browser: user.switcher_focus("Google Chrome") +coder: user.switcher_focus("Code") +voice code: + user.switcher_focus("Code") + user.vscode("workbench.action.openRecent") + sleep(50ms) + insert("pokey_talon") + key(enter) + sleep(250ms) + +slacker: user.switcher_focus("Slack") +folk things: user.switcher_focus("Things") +folk sim: user.switcher_focus("Simulator") + site [] [{user.file_extension}]: + user.switcher_focus("Google Chrome") + key(cmd-t l p space) + insert(text or "") + insert(file_extension or "") + sleep(500ms) + key(down enter) + site [] [{user.file_extension}]: + user.switcher_focus("Google Chrome") + key(cmd-t l p space) + insert(text or "") + insert(file_extension or "") + sleep(500ms) \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/modes/dictation_mode.talon b/talon/community/community-cursorless-0.4.0/modes/dictation_mode.talon new file mode 100644 index 0000000..e5607ad --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/modes/dictation_mode.talon @@ -0,0 +1,86 @@ +mode: dictation +- +^press $: key("{keys}") + +# Everything here should call auto_insert to preserve the state to correctly auto-capitalize/auto-space. +: auto_insert(prose) +new line: "\n" +new paragraph: "\n\n" +cap : + result = user.formatted_text(word, "CAPITALIZE_FIRST_WORD") + auto_insert(result) + +# Navigation +go up (line|lines): + edit.up() + repeat(number_small - 1) +go down (line|lines): + edit.down() + repeat(number_small - 1) +go left (word|words): + edit.word_left() + repeat(number_small - 1) +go right (word|words): + edit.word_right() + repeat(number_small - 1) +go line start: edit.line_start() +go line end: edit.line_end() + +# Selection +select left (word|words): + edit.extend_word_left() + repeat(number_small - 1) +select right (word|words): + edit.extend_word_right() + repeat(number_small - 1) +select left (character|characters): + edit.extend_left() + repeat(number_small - 1) +select right (character|characters): + edit.extend_right() + repeat(number_small - 1) +clear left (word|words): + edit.extend_word_left() + repeat(number_small - 1) + edit.delete() +clear right (word|words): + edit.extend_word_right() + repeat(number_small - 1) + edit.delete() +clear left (character|characters): + edit.extend_left() + repeat(number_small - 1) + edit.delete() +clear right (character|characters): + edit.extend_right() + repeat(number_small - 1) + edit.delete() + +# Formatting +formatted : + user.dictation_insert_raw(format_text) +^format selection $: + user.formatters_reformat_selection(formatters) + +# Corrections +scratch that: user.clear_last_phrase() +scratch selection: edit.delete() +select that: user.select_last_phrase() +spell that : auto_insert(letters) +spell that : + result = user.formatted_text(letters, formatters) + user.auto_format_pause() + auto_insert(result) + user.auto_format_resume() + +# Escape, type things that would otherwise be commands +^escape $: + auto_insert(user.text) + +numb : "{number_string}" +numb (dot | point) : "{number_string}.{digit_string}" + +^commander$: + mode.disable("sleep") + mode.disable("dictation") + mode.enable("command") \ No newline at end of file diff --git a/talon/user/community/core/modes/dragon_mode.talon b/talon/community/community-cursorless-0.4.0/modes/dragon_modes.talon similarity index 87% rename from talon/user/community/core/modes/dragon_mode.talon rename to talon/community/community-cursorless-0.4.0/modes/dragon_modes.talon index 179e182..295b3a4 100644 --- a/talon/user/community/core/modes/dragon_mode.talon +++ b/talon/community/community-cursorless-0.4.0/modes/dragon_modes.talon @@ -5,4 +5,4 @@ mode: all # wakes Dragon on Mac, deactivates talon speech commands dragon mode: user.dragon_mode() #sleep dragon on Mac, activates talon speech commands -talon mode: user.talon_mode() +talon mode: user.talon_mode() \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/modes/language_modes.talon b/talon/community/community-cursorless-0.4.0/modes/language_modes.talon new file mode 100644 index 0000000..413df57 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/modes/language_modes.talon @@ -0,0 +1,16 @@ +^force see sharp$: user.code_set_language_mode("csharp") +^force see plus plus$: user.code_set_language_mode("cplusplus") +^force go (lang|language)$: user.code_set_language_mode("go") +^force java$: user.code_set_language_mode("java") +^force java script$: user.code_set_language_mode("javascript") +^force type script$: user.code_set_language_mode("typescript") +^force markdown$: user.code_set_language_mode("markdown") +^force python$: user.code_set_language_mode("python") +^force are language$: user.code_set_language_mode("r") +^force talon [language]$: user.code_set_language_mode("talon") +^clear language modes$: user.code_clear_language_mode() +[enable] debug mode: + mode.enable("user.gdb") +disable debug mode: + mode.disable("user.gdb") + \ No newline at end of file diff --git a/talon/user/community/core/modes/modes.py b/talon/community/community-cursorless-0.4.0/modes/modes.py similarity index 63% rename from talon/user/community/core/modes/modes.py rename to talon/community/community-cursorless-0.4.0/modes/modes.py index 60771af..dbfe27e 100644 --- a/talon/user/community/core/modes/modes.py +++ b/talon/community/community-cursorless-0.4.0/modes/modes.py @@ -1,39 +1,19 @@ -from talon import Context, Module, actions, app, speech_system +from talon import Context, Module, app, actions, speech_system mod = Module() -ctx_sleep = Context() -ctx_awake = Context() modes = { "admin": "enable extra administration commands terminal (docker, etc)", "debug": "a way to force debugger commands to be loaded", + "gdb": "a way to force gdb commands to be loaded", "ida": "a way to force ida commands to be loaded", "presentation": "a more strict form of sleep where only a more strict wake up command works", + "windbg": "a way to force windbg commands to be loaded", } for key, value in modes.items(): mod.mode(key, value) -ctx_sleep.matches = r""" -mode: sleep -""" - -ctx_awake.matches = r""" -not mode: sleep -""" - - -@ctx_sleep.action_class("speech") -class ActionsSleepMode: - def disable(): - actions.app.notify("Talon is already asleep") - - -@ctx_awake.action_class("speech") -class ActionsAwakeMode: - def enable(): - actions.app.notify("Talon is already awake") - @mod.action_class class Actions: @@ -45,11 +25,11 @@ class Actions: # app.notify(engine) if "dragon" in engine: if app.platform == "mac": - actions.user.dragon_engine_sleep() + actions.user.engine_sleep() elif app.platform == "windows": - actions.user.dragon_engine_wake() + actions.user.engine_wake() # note: this may not do anything for all versions of Dragon. Requires Pro. - actions.user.dragon_engine_command_mode() + actions.user.engine_mimic("switch to command mode") def dragon_mode(): """For windows and Mac with Dragon, disables Talon commands and exits Dragon's command mode""" @@ -60,8 +40,8 @@ class Actions: # app.notify("dragon mode") actions.speech.disable() if app.platform == "mac": - actions.user.dragon_engine_wake() + actions.user.engine_wake() elif app.platform == "windows": - actions.user.dragon_engine_wake() + actions.user.engine_wake() # note: this may not do anything for all versions of Dragon. Requires Pro. - actions.user.dragon_engine_normal_mode() + actions.user.engine_mimic("start normal mode") diff --git a/talon/community/community-cursorless-0.4.0/modes/sleep_mode.talon b/talon/community/community-cursorless-0.4.0/modes/sleep_mode.talon new file mode 100644 index 0000000..6f478f8 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/modes/sleep_mode.talon @@ -0,0 +1,9 @@ +mode: sleep +- +settings(): + #stop continuous scroll/gaze scroll with a pop + user.mouse_enable_pop_stops_scroll = 0 + #enable pop click with 'control mouse' mode + user.mouse_enable_pop_click = 0 +#this exists solely to prevent talon from walking up super easily in sleep mode at the moment with wav2letter +: skip() diff --git a/talon/user/community/core/modes/sleep_mode_wav2letter.talon b/talon/community/community-cursorless-0.4.0/modes/sleep_mode_wav2letter.talon similarity index 92% rename from talon/user/community/core/modes/sleep_mode_wav2letter.talon rename to talon/community/community-cursorless-0.4.0/modes/sleep_mode_wav2letter.talon index e854a98..ea61486 100644 --- a/talon/user/community/core/modes/sleep_mode_wav2letter.talon +++ b/talon/community/community-cursorless-0.4.0/modes/sleep_mode_wav2letter.talon @@ -3,4 +3,4 @@ speech.engine: wav2letter - #this exists solely to prevent talon from waking up super easily in sleep mode at the moment with wav2letter #you probably shouldn't have any other commands here -: skip() +: skip() \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/modes/wake_up.talon b/talon/community/community-cursorless-0.4.0/modes/wake_up.talon new file mode 100644 index 0000000..5fdba8b --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/modes/wake_up.talon @@ -0,0 +1,18 @@ +#defines the commands that sleep/wake Talon +mode: all +- +^welcome back$: + user.mouse_wake() + user.history_enable() + user.talon_mode() +^sleep all$: + user.switcher_hide_running() + user.history_disable() + user.homophones_hide() + user.help_hide() + user.mouse_sleep() + speech.disable() + user.engine_sleep() +^talon sleep$: speech.disable() +^talon wake$: speech.enable() + diff --git a/talon/user/community/core/mouse_grid/mouse_grid.py b/talon/community/community-cursorless-0.4.0/mouse_grid/mouse_grid.py similarity index 83% rename from talon/user/community/core/mouse_grid/mouse_grid.py rename to talon/community/community-cursorless-0.4.0/mouse_grid/mouse_grid.py index a811808..7087014 100644 --- a/talon/user/community/core/mouse_grid/mouse_grid.py +++ b/talon/community/community-cursorless-0.4.0/mouse_grid/mouse_grid.py @@ -1,31 +1,25 @@ # courtesy of https://github.com/timo/ # see https://github.com/timo/talon_scripts -import math +from talon import Module, Context, app, canvas, screen, settings, ui, ctrl, cron +from talon.skia import Shader, Color, Paint, Rect +from talon.types.point import Point2d +from talon_plugins import eye_mouse, eye_zoom_mouse from typing import Union -from talon import Context, Module, actions, canvas, cron, ctrl, screen, settings, ui -from talon.skia import Paint, Rect -from talon.types.point import Point2d +import math, time + +import typing mod = Module() -mod.setting( +narrow_expansion = mod.setting( "grid_narrow_expansion", type=int, default=0, desc="""After narrowing, grow the new region by this many pixels in every direction, to make things immediately on edges easier to hit, and when the grid is at its smallest, it allows you to still nudge it around""", ) -mod.setting( - "grids_put_one_bottom_left", - type=bool, - default=False, - desc="""Allows you to switch mouse grid and friends between a computer numpad and a phone numpad (the number one goes on the bottom left or the top left)""", -) mod.tag("mouse_grid_showing", desc="Tag indicates whether the mouse grid is showing") -mod.tag( - "mouse_grid_enabled", - desc="Deprecated: do not use. Activates legacy m grid command", -) +mod.tag("mouse_grid_enabled", desc="Tag enables the mouse grid commands.") ctx = Context() @@ -38,9 +32,8 @@ class MouseSnapNine: self.mcanvas = None self.active = False self.count = 0 - self.was_zoom_mouse_active = False self.was_control_mouse_active = False - self.was_control1_mouse_active = False + self.was_zoom_mouse_active = False def setup(self, *, rect: Rect = None, screen_num: int = None): screens = ui.screens() @@ -71,15 +64,12 @@ class MouseSnapNine: if self.active: return # noinspection PyUnresolvedReferences - if actions.tracking.control_zoom_enabled(): + if eye_zoom_mouse.zoom_mouse.enabled: self.was_zoom_mouse_active = True - actions.tracking.control_zoom_toggle(False) - if actions.tracking.control_enabled(): + eye_zoom_mouse.toggle_zoom_mouse(False) + if eye_mouse.control_mouse.enabled: self.was_control_mouse_active = True - actions.tracking.control_toggle(False) - if actions.tracking.control1_enabled(): - self.was_control1_mouse_active = True - actions.tracking.control1_toggle(False) + eye_mouse.control_mouse.toggle() self.mcanvas.register("draw", self.draw) self.mcanvas.freeze() self.active = True @@ -94,17 +84,13 @@ class MouseSnapNine: self.img = None self.active = False - - if self.was_control_mouse_active and not actions.tracking.control_enabled(): - actions.tracking.control_toggle(True) - if self.was_control1_mouse_active and not actions.tracking.control1_enabled(): - actions.tracking.control1_toggle(True) - if self.was_zoom_mouse_active and not actions.tracking.control_zoom_enabled(): - actions.tracking.control_zoom_toggle(True) + if self.was_control_mouse_active and not eye_mouse.control_mouse.enabled: + eye_mouse.control_mouse.toggle() + if self.was_zoom_mouse_active and not eye_zoom_mouse.zoom_mouse.enabled: + eye_zoom_mouse.toggle_zoom_mouse(True) self.was_zoom_mouse_active = False self.was_control_mouse_active = False - self.was_control1_mouse_active = False def draw(self, canvas): paint = canvas.paint @@ -152,7 +138,7 @@ class MouseSnapNine: for row in range(3): for col in range(3): text_string = "" - if settings.get("user.grids_put_one_bottom_left"): + if settings["user.grids_put_one_bottom_left"]: text_string = f"{(2 - row)*3+col+1}" else: text_string = f"{row*3+col+1}" @@ -207,10 +193,10 @@ class MouseSnapNine: def calc_narrow(self, which, rect): rect = rect.copy() - bdr = settings.get("user.grid_narrow_expansion") + bdr = narrow_expansion.get() row = int(which - 1) // 3 col = int(which - 1) % 3 - if settings.get("user.grids_put_one_bottom_left"): + if settings["user.grids_put_one_bottom_left"]: row = 2 - row rect.x += int(col * rect.width // 3) - bdr rect.y += int(row * rect.height // 3) - bdr @@ -290,10 +276,10 @@ class GridActions: mg.setup(screen_num=screen - 1) mg.show() - def grid_narrow_list(digit_list: list[str]): + def grid_narrow_list(digit_list: typing.List[str]): """Choose fields multiple times in a row""" for d in digit_list: - actions.self.grid_narrow(int(d)) + GridActions.grid_narrow(int(d)) def grid_narrow(digit: Union[int, str]): """Choose a field of the grid and narrow the selection down""" @@ -307,7 +293,3 @@ class GridActions: """Close the active grid""" ctx.tags = [] mg.close() - - def grid_is_active(): - """check if grid is already active""" - return mg.active diff --git a/talon/user/community/core/mouse_grid/mouse_grid_always.talon b/talon/community/community-cursorless-0.4.0/mouse_grid/mouse_grid.talon similarity index 88% rename from talon/user/community/core/mouse_grid/mouse_grid_always.talon rename to talon/community/community-cursorless-0.4.0/mouse_grid/mouse_grid.talon index 46dc9b1..3191a18 100644 --- a/talon/user/community/core/mouse_grid/mouse_grid_always.talon +++ b/talon/community/community-cursorless-0.4.0/mouse_grid/mouse_grid.talon @@ -1,4 +1,6 @@ -mouse grid: +tag: user.mouse_grid_enabled +- +M grid: user.grid_select_screen(1) user.grid_activate() diff --git a/talon/community/community-cursorless-0.4.0/mouse_grid/mouse_grid_open.talon b/talon/community/community-cursorless-0.4.0/mouse_grid/mouse_grid_open.talon new file mode 100644 index 0000000..d730011 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/mouse_grid/mouse_grid_open.talon @@ -0,0 +1,12 @@ +tag: user.mouse_grid_showing +- +: + user.grid_narrow(number_key) +grid off: + user.grid_close() + +grid reset: + user.grid_reset() + +grid back: + user.grid_go_back() diff --git a/talon/community/community-cursorless-0.4.0/settings.talon b/talon/community/community-cursorless-0.4.0/settings.talon new file mode 100644 index 0000000..551da0a --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/settings.talon @@ -0,0 +1,42 @@ +- +settings(): + speech.record_all = 1 + speech.debug = 1 + speech.timeout = 0.5 + #adjust the scale of the imgui to my liking + imgui.scale = 1.3 + # enable if you'd like the picker gui to automatically appear when explorer has focus + user.file_manager_auto_show_pickers = 0 + #set the max number of command lines per page in help + user.help_max_command_lines_per_page = 50 + # set the max number of contexts display per page in help + user.help_max_contexts_per_page = 20 + # The default amount used when scrolling continuously + user.mouse_continuous_scroll_amount = 80 + #stop continuous scroll/gaze scroll with a pop + user.mouse_enable_pop_stops_scroll = 1 + #enable pop click with 'control mouse' mode + user.mouse_enable_pop_click = 1 + #When enabled, the 'Scroll Mouse' GUI will not be shown. + user.mouse_hide_mouse_gui = 0 + #hide cursor when mouse_wake is called to enable zoom mouse + user.mouse_wake_hides_cursor = 0 + #the amount to scroll up/down (equivalent to mouse wheel on Windows by default) + user.mouse_wheel_down_amount = 120 + #mouse grid and friends put the number one on the bottom left (vs on the top left) + user.grids_put_one_bottom_left = 1 + # the number of lines of command history to display by default + user.command_history_display = 2 + # the number of lines of command history to keep in total; + # "command history more" to display all of them, "command history less" to restore + user.command_history_size = 50 + + # Uncomment the below to enable context-sensitive dictation. This determines + # how to format (capitalize, space) dictation-mode speech by selecting & + # copying surrounding text before inserting. This can be slow and may not + # work in some applications. You may wish to enable this on a + # per-application basis. + user.context_sensitive_dictation = 1 + +# uncomment tag to enable mouse grid +tag(): user.mouse_grid_enabled diff --git a/talon/community/community-cursorless-0.4.0/settings/additional_words.csv b/talon/community/community-cursorless-0.4.0/settings/additional_words.csv new file mode 100644 index 0000000..e9d116f --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/settings/additional_words.csv @@ -0,0 +1,47 @@ +Word(s),Spoken Form (If Different) +nmap,N map +under-documented,under documented +nmap +admin +Cisco +Citrix +VPN +DNS +Minecraft +fontello +git +Github +app +vscode,van cake +Pokey +json,jason +config +meta +Schwab +lodash +debounced +debounce +timeout +fifo +mkfifo,make fifo +args +arg +node.js,node jay yes +enum,eenum +todo,to do +flashcard,flash card +flashcards,flash cards +pytorch,pie torch +HSBC +subword +subwords +nth,enth +karabiner +URL, U R L +Gmail +min +cursorless +synology +API,A P I +infer +null \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/settings/file_extensions.csv b/talon/community/community-cursorless-0.4.0/settings/file_extensions.csv new file mode 100644 index 0000000..7d081f1 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/settings/file_extensions.csv @@ -0,0 +1,27 @@ +File extension,Name +.py,dot pie +.talon,dot talon +.md,dot mark down +.sh,dot shell +.vim,dot vim +.c,dot see +.cs,dot see sharp +.com,dot com +.net,dot net +.org,dot org +.us,dot us +.us,dot U S +.exe,dot exe +.bin,dot bin +.bin,dot bend +.json,dot jason +.json,dot jay son +.js,dot J S +.js,dot java script +.ts,dot TS +.ts,dot type script +.csv,dot csv +.csv,totssv +.csv,tot csv +.csv,dot cassie +.txt,dot text diff --git a/talon/community/community-cursorless-0.4.0/settings/search_engines.csv b/talon/community/community-cursorless-0.4.0/settings/search_engines.csv new file mode 100644 index 0000000..038433e --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/settings/search_engines.csv @@ -0,0 +1,6 @@ +URL Template,Name +https://smile.amazon.co.uk/s/?field-keywords=%s,amazon +https://www.google.com/search?q=%s,google +https://maps.google.com/maps?q=%s,map +https://scholar.google.com/scholar?q=%s,scholar +https://en.wikipedia.org/w/index.php?search=%s,wiki diff --git a/talon/community/community-cursorless-0.4.0/settings/words_to_replace.csv b/talon/community/community-cursorless-0.4.0/settings/words_to_replace.csv new file mode 100644 index 0000000..ec470f1 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/settings/words_to_replace.csv @@ -0,0 +1,37 @@ +Replacement,Original +I,i +I'm,i'm +I've,i've +I'll,i'll +I'd,i'd +Monday,monday +Mondays,mondays +Tuesday,tuesday +Tuesdays,tuesdays +Wednesday,wednesday +Wednesdays,wednesdays +Thursday,thursday +Thursdays,thursdays +Friday,friday +Fridays,fridays +Saturday,saturday +Saturdays,saturdays +Sunday,sunday +Sundays,sundays +January,january +February,february +April,april +June,june +July,july +August,august +September,september +October,october +November,november +December,december +talon,talent +jupyter,jupiter +firebase,fire base +karabiner,carabiner +queue,cue +queue,cu +Debbie,debby \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/shared_settings_module.py b/talon/community/community-cursorless-0.4.0/shared_settings_module.py new file mode 100644 index 0000000..5754297 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/shared_settings_module.py @@ -0,0 +1,9 @@ +from talon import Module + +mod = Module() +grids_put_one_bottom_left = mod.setting( + "grids_put_one_bottom_left", + type=bool, + default=False, + desc="""Allows you to switch mouse grid and friends between a computer numpad and a phone numpad (the number one goes on the bottom left or the top left)""", +) diff --git a/talon/user/community/plugin/talon_draft_window/LICENSE b/talon/community/community-cursorless-0.4.0/talon_draft_window/LICENSE similarity index 100% rename from talon/user/community/plugin/talon_draft_window/LICENSE rename to talon/community/community-cursorless-0.4.0/talon_draft_window/LICENSE diff --git a/talon/user/community/plugin/talon_draft_window/README.md b/talon/community/community-cursorless-0.4.0/talon_draft_window/README.md similarity index 65% rename from talon/user/community/plugin/talon_draft_window/README.md rename to talon/community/community-cursorless-0.4.0/talon_draft_window/README.md index e50bc61..5af0e93 100644 --- a/talon/user/community/plugin/talon_draft_window/README.md +++ b/talon/community/community-cursorless-0.4.0/talon_draft_window/README.md @@ -1,4 +1,4 @@ -The draft window allows you to more easily edit prose style text via a task-specific UI. +This reposiory is a [Talon](https://talonvoice.com/) script that allows you to more easily edit prose style text via a task-specific UI. # Usage @@ -8,10 +8,10 @@ An session might go like this for example: # Start with the text "this is a sentence with an elephant." in your editor or other textbox draft edit all # Select all the text in your editor and moves it to the draft window - replace gust with error # Replaces the word corresponding with the red anchor 'g' (gust in community) with the word 'error' + replace gust with error # Replaces the word corresponding with the red anchor 'g' (gust in knausj_talon) with the word 'error' period # Add a full stop select each through fine # Select the words starting at the 'e' anchor and ending at 'f' - say without # Insert the word 'without' (community) + say without # Insert the word 'without' (knausj_talon) title word air # Make the word corresponding to the 'a' anchor capitalised draft submit # Type the text in your draft window back into your editor # End with the text "This is a sentence without error." in your editor or other textbox @@ -20,13 +20,17 @@ Here's a video of me going through the above commands: ![Video of talon draft window in action](doc/talon-draft-demo.gif) +# Installation + +The `draft_window.talon` file depends on functions and lists defined by the [knausj\_talon](https://github.com/knausj85/knausj_talon) repo. Once you have knausj set up you can just drop this folder in next to it in the Talon user scripts directory (~/.talon/user/ under Linux). So you might end up with ~/.talon/user/ containing a `knausj_talon` folder, a `talon_draft_window` folder, and an engines.py. + # Customising If you want to change the display of the window you can do by adding some settings to one of your .talon files. See `settings.talon.example` for more details. # Running tests -There are unit tests that you can run from the repository root like this (assuming your directory is called talon_draft_window): +There are unit tests that you can run from the repository root like this (assuming your directory is called talon\_draft\_window): (cd ../ && python -m unittest talon_draft_window.test_draft_ui) diff --git a/talon/user/community/plugin/talon_draft_window/doc/talon-draft-demo.gif b/talon/community/community-cursorless-0.4.0/talon_draft_window/doc/talon-draft-demo.gif similarity index 100% rename from talon/user/community/plugin/talon_draft_window/doc/talon-draft-demo.gif rename to talon/community/community-cursorless-0.4.0/talon_draft_window/doc/talon-draft-demo.gif diff --git a/talon/user/community/plugin/talon_draft_window/draft_global.talon b/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_global.talon similarity index 100% rename from talon/user/community/plugin/talon_draft_window/draft_global.talon rename to talon/community/community-cursorless-0.4.0/talon_draft_window/draft_global.talon diff --git a/talon/user/community/plugin/talon_draft_window/draft_talon_helpers.py b/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_talon_helpers.py similarity index 92% rename from talon/user/community/plugin/talon_draft_window/draft_talon_helpers.py rename to talon/community/community-cursorless-0.4.0/talon_draft_window/draft_talon_helpers.py index b62e2a0..549626b 100644 --- a/talon/user/community/plugin/talon_draft_window/draft_talon_helpers.py +++ b/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_talon_helpers.py @@ -1,7 +1,5 @@ from typing import Optional - -from talon import Context, Module, actions, settings, ui - +from talon import ui, settings, Module, Context, actions from .draft_ui import DraftManager mod = Module() @@ -18,19 +16,19 @@ title: Talon Draft """ mod.tag("draft_window_showing", desc="Tag set when draft window showing") -mod.setting( +setting_theme = mod.setting( "draft_window_theme", type=str, default="dark", desc="Sets the main colors of the window, one of 'dark' or 'light'", ) -mod.setting( +setting_label_size = mod.setting( "draft_window_label_size", type=int, default=20, desc="Sets the size of the word labels used in the draft window", ) -mod.setting( +setting_label_color = mod.setting( "draft_window_label_color", type=str, default=None, @@ -39,7 +37,7 @@ mod.setting( "E.g. 00ff00 would be green" ), ) -mod.setting( +setting_text_size = mod.setting( "draft_window_text_size", type=int, default=20, @@ -49,17 +47,16 @@ mod.setting( draft_manager = DraftManager() - # Update the styling of the draft window dynamically as user settings change def _update_draft_style(*args): draft_manager.set_styling( **{ - arg: settings.get(setting) + arg: setting.get() for setting, arg in ( - ("user.draft_window_theme", "theme"), - ("user.draft_window_label_size", "label_size"), - ("user.draft_window_label_color", "label_color"), - ("user.draft_window_text_size", "text_size"), + (setting_theme, "theme"), + (setting_label_size, "label_size"), + (setting_label_color, "label_color"), + (setting_text_size, "text_size"), ) } ) @@ -72,15 +69,16 @@ settings.register("", _update_draft_style) class ContextSensitiveDictationActions: """ Override these actions to assist 'Smart dictation mode'. - see https://github.com/talonhub/community/pull/356 + see https://github.com/knausj85/knausj_talon/pull/356 """ - def dictation_peek(left, right): + def dictation_peek_left(clobber=False): area = draft_manager.area - return ( - area[max(0, area.sel.left - 50) : area.sel.left], - area[area.sel.right : area.sel.right + 50], - ) + return area[max(0, area.sel.left - 50) : area.sel.left] + + def dictation_peek_right(): + area = draft_manager.area + return area[area.sel.right : area.sel.right + 50] def paste(text: str): # todo: remove once user.paste works reliably with the draft window diff --git a/talon/user/community/plugin/talon_draft_window/draft_ui.py b/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_ui.py similarity index 91% rename from talon/user/community/plugin/talon_draft_window/draft_ui.py rename to talon/community/community-cursorless-0.4.0/talon_draft_window/draft_ui.py index 557d8a4..2d316be 100644 --- a/talon/user/community/plugin/talon_draft_window/draft_ui.py +++ b/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_ui.py @@ -1,20 +1,19 @@ -import re from typing import Optional +import re from talon.experimental.textarea import ( - DarkThemeLabels, - LightThemeLabels, - Span, TextArea, + Span, + DarkThemeLabels, + LightThemeLabels ) + word_matcher = re.compile(r"([^\s]+)(\s*)") - - def calculate_text_anchors(text, cursor_position, anchor_labels=None): """ Produces an iterator of (anchor, start_word_index, end_word_index, last_space_index) - tuples from the given text. Each tuple indicates a particular point you may want to + tuples from the given text. Each tuple indicates a particular point you may want to reference when editing along with some useful ranges you may want to operate on. - text is the text you want to process. @@ -35,7 +34,11 @@ def calculate_text_anchors(text, cursor_position, anchor_labels=None): matches = [] cursor_idx = None for match in word_matcher.finditer(text): - matches.append((match.start(), match.end() - len(match.group(2)), match.end())) + matches.append(( + match.start(), + match.end() - len(match.group(2)), + match.end() + )) if matches[-1][0] <= cursor_position and matches[-1][2] >= cursor_position: cursor_idx = len(matches) - 1 @@ -50,7 +53,12 @@ def calculate_text_anchors(text, cursor_position, anchor_labels=None): # Now add anchors to the selected matches for i, anchor in zip(range(anchor_start_idx, anchor_end_idx), anchor_labels): word_start, word_end, whitespace_end = matches[i] - yield (anchor, word_start, word_end, whitespace_end) + yield ( + anchor, + word_start, + word_end, + whitespace_end + ) class DraftManager: @@ -65,7 +73,13 @@ class DraftManager: self.area.register("label", self._update_labels) self.set_styling() - def set_styling(self, theme="dark", text_size=20, label_size=20, label_color=None): + def set_styling( + self, + theme="dark", + text_size=20, + label_size=20, + label_color=None + ): """ Allow settings the style of the draft window. Will dynamically update the style based on the passed in parameters. @@ -166,9 +180,7 @@ class DraftManager: self.area.sel = index def anchor_to_range(self, anchor): - anchors_data = calculate_text_anchors( - self._get_visible_text(), self.area.sel.left - ) + anchors_data = calculate_text_anchors(self._get_visible_text(), self.area.sel.left) for loop_anchor, start_index, end_index, last_space_index in anchors_data: if anchor == loop_anchor: return (start_index, end_index, last_space_index) @@ -180,9 +192,7 @@ class DraftManager: Updates the position of the labels displayed on top of each word """ - anchors_data = calculate_text_anchors( - self._get_visible_text(), self.area.sel.left - ) + anchors_data = calculate_text_anchors(self._get_visible_text(), self.area.sel.left) return [ (Span(start_index, end_index), anchor) for anchor, start_index, end_index, _ in anchors_data diff --git a/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_window.talon b/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_window.talon new file mode 100644 index 0000000..52bbf9a --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_window.talon @@ -0,0 +1,51 @@ +# These are active when we have focus on the draft window +title:Talon Draft +- +settings(): + # Enable 'Smart dictation mode', see https://github.com/knausj85/knausj_talon/pull/356 + user.context_sensitive_dictation = 1 + +# Replace a single word with a phrase +replace with : + user.draft_select("{draft_anchor}") + result = user.formatted_text(text, "NOOP") + insert(result) + +# Position cursor before word +cursor : + user.draft_position_caret("{draft_anchor}") + +cursor before : + user.draft_position_caret("{draft_anchor}") + +# Position cursor after word +cursor after : + user.draft_position_caret("{draft_anchor}", 1) + +# Select a whole word +select : + user.draft_select("{draft_anchor}") + +# Select a range of words +select through : + user.draft_select("{draft_anchor_1}", "{draft_anchor_2}") + +# Delete a word +clear : + user.draft_select("{draft_anchor}", "", 1) + key(backspace) + +# Delete a range of words +clear through : + user.draft_select(draft_anchor_1, draft_anchor_2, 1) + key(backspace) + +# reformat word + word : + user.draft_select("{draft_anchor}", "", 1) + user.formatters_reformat_selection(user.formatters) + +# reformat range + through : + user.draft_select(draft_anchor_1, draft_anchor_2, 1) + user.formatters_reformat_selection(user.formatters) diff --git a/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_window_open.talon b/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_window_open.talon new file mode 100644 index 0000000..83c4f1c --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/talon_draft_window/draft_window_open.talon @@ -0,0 +1,12 @@ +# These are available when the draft window is open, but not necessarily focussed +tag: user.draft_window_showing +- +draft hide: user.draft_hide() + +draft submit: + content = user.draft_get_text() + user.draft_hide() + insert(content) + # user.paste may be somewhat faster, but seems to be unreliable on MacOSX, see + # https://github.com/talonvoice/talon/issues/254#issuecomment-789355238 + # user.paste(content) diff --git a/talon/user/community/plugin/talon_draft_window/settings.talon.example b/talon/community/community-cursorless-0.4.0/talon_draft_window/settings.talon.example similarity index 100% rename from talon/user/community/plugin/talon_draft_window/settings.talon.example rename to talon/community/community-cursorless-0.4.0/talon_draft_window/settings.talon.example diff --git a/talon/user/community/plugin/talon_draft_window/test_draft_ui.py b/talon/community/community-cursorless-0.4.0/talon_draft_window/test_draft_ui.py similarity index 52% rename from talon/user/community/plugin/talon_draft_window/test_draft_ui.py rename to talon/community/community-cursorless-0.4.0/talon_draft_window/test_draft_ui.py index 3a0615c..573b75e 100644 --- a/talon/user/community/plugin/talon_draft_window/test_draft_ui.py +++ b/talon/community/community-cursorless-0.4.0/talon_draft_window/test_draft_ui.py @@ -1,13 +1,35 @@ -import talon +try: + import talon.experimental.textarea + running_in_talon = True +except ModuleNotFoundError: + # Some shenanigans to stub out the Talon imports + import imp, sys + name = "talon.experimental.textarea" + module = imp.new_module(name) + sys.modules[name] = module + exec( + "\n".join([ + "TextArea = 1", + "Span = 1", + "DarkThemeLabels = 1", + "LightThemeLabels = 1" + ]), + module.__dict__ + ) + running_in_talon = False -if hasattr(talon, "test_mode"): - # Only include this when we're running tests +from unittest import TestCase +from functools import wraps - pass +from .draft_ui import calculate_text_anchors - from .draft_ui import calculate_text_anchors - def test_finds_anchors(): +class CalculateAnchorsTest(TestCase): + """ + Tests calculate_text_anchors + """ + + def test_finds_anchors(self): examples = [ ("one-word", [("a", 0, 8, 8)]), ("two words", [("a", 0, 3, 4), ("b", 4, 9, 9)]), @@ -21,23 +43,29 @@ if hasattr(talon, "test_mode"): result = list(calculate_text_anchors(text, 0, anchor_labels=anchor_labels)) # Then it matches what we expect - assert result == expected, text + self.assertEqual(result, expected, text) - def test_positions_anchors_around_cursor(): + def test_positions_anchors_around_cursor(self): # In these examples the cursor is at the asterisk which is stripped by the test # code. Indicies after the asterisk have to take this into account. examples = [ ("one*-word", [("a", 0, 8, 8)]), ("one-word*", [("a", 0, 8, 8)]), - ("the three words*", [("a", 0, 3, 4), ("b", 4, 9, 10), ("c", 10, 15, 15)]), - ("*the three words", [("a", 0, 3, 4), ("b", 4, 9, 10), ("c", 10, 15, 15)]), + ( + "the three words*", + [("a", 0, 3, 4), ("b", 4, 9, 10), ("c", 10, 15, 15)] + ), + ( + "*the three words", + [("a", 0, 3, 4), ("b", 4, 9, 10), ("c", 10, 15, 15)] + ), ( "too many* words for the number of anchors", - [("a", 0, 3, 4), ("b", 4, 8, 9), ("c", 9, 14, 15)], + [("a", 0, 3, 4), ("b", 4, 8, 9), ("c", 9, 14, 15)] ), ( "too many words fo*r the number of anchors", - [("a", 9, 14, 15), ("b", 15, 18, 19), ("c", 19, 22, 23)], + [("a", 9, 14, 15), ("b", 15, 18, 19), ("c", 19, 22, 23)] ), ] anchor_labels = ["a", "b", "c"] @@ -48,9 +76,11 @@ if hasattr(talon, "test_mode"): text = text_with_cursor.replace("*", "") # When we calculate the result and turn it into a list - result = list( - calculate_text_anchors(text, cursor_pos, anchor_labels=anchor_labels) - ) + result = list(calculate_text_anchors( + text, + cursor_pos, + anchor_labels=anchor_labels + )) # Then it matches what we expect - assert result == expected, text + self.assertEqual(result, expected, text) diff --git a/talon/user/community/tags/find_and_replace/find_and_replace.talon b/talon/community/community-cursorless-0.4.0/text/find_and_replace.talon similarity index 68% rename from talon/user/community/tags/find_and_replace/find_and_replace.talon rename to talon/community/community-cursorless-0.4.0/text/find_and_replace.talon index 2e3eb83..47ad5d3 100644 --- a/talon/user/community/tags/find_and_replace/find_and_replace.talon +++ b/talon/community/community-cursorless-0.4.0/text/find_and_replace.talon @@ -1,16 +1,14 @@ tag: user.find_and_replace - -tag(): user.find - -hunt all: user.find_everywhere("") -hunt all (pace | paste): - user.find_everywhere("") - sleep(25ms) - edit.paste() -hunt all : user.find_everywhere(text) -hunt case: user.find_toggle_match_by_case() -hunt word: user.find_toggle_match_by_word() -hunt expression: user.find_toggle_match_by_regex() + this: user.find("") + this : user.find(text) + all: user.find_everywhere("") + all : user.find_everywhere(text) + case : user.find_toggle_match_by_case() + word : user.find_toggle_match_by_word() + expression : user.find_toggle_match_by_regex() + next: user.find_next() + previous: user.find_previous() replace this []: user.replace(text or "") replace all: user.replace_everywhere("") replace all: user.replace_everywhere(text) @@ -18,62 +16,64 @@ replace confirm that: user.replace_confirm() replace confirm all: user.replace_confirm_all() #quick replace commands, modeled after jetbrains -clear last [over]: +clear last [over]: user.select_previous_occurrence(text) sleep(100ms) edit.delete() -clear next [over]: +clear next [over]: user.select_next_occurrence(text) sleep(100ms) edit.delete() -clear last clip: +clear last clip: user.select_previous_occurrence(clip.text()) edit.delete() -clear next clip: +clear next clip: user.select_next_occurrence(clip.text()) sleep(100ms) edit.delete() -comment last [over]: +comment last [over]: user.select_previous_occurrence(text) sleep(100ms) code.toggle_comment() -comment last clip: +comment last clip: user.select_previous_occurrence(clip.text()) sleep(100ms) code.toggle_comment() -comment next [over]: +comment next [over]: user.select_next_occurrence(text) sleep(100ms) code.toggle_comment() -comment next clip: +comment next clip: user.select_next_occurrence(clip.text()) sleep(100ms) code.toggle_comment() -go last [over]: +go last [over]: user.select_previous_occurrence(text) sleep(100ms) edit.right() -go last clip: +go last clip: user.select_previous_occurrence(clip.text()) sleep(100ms) edit.right() -go next [over]: +go next [over]: user.select_next_occurrence(text) edit.right() +go next token: + user.select_next_token() go next clip: user.select_next_occurrence(clip.text()) edit.right() -paste last [over]: +paste last [over]: user.select_previous_occurrence(text) sleep(100ms) edit.right() edit.paste() -paste next [over]: +paste next [over]: user.select_next_occurrence(text) sleep(100ms) edit.right() edit.paste() -replace last [over]: +replace last [over]: user.select_previous_occurrence(text) sleep(100ms) edit.paste() @@ -85,3 +85,6 @@ select last [over]: user.select_previous_occurrence(text) select next [over]: user.select_next_occurrence(text) select last clip: user.select_previous_occurrence(clip.text()) select next clip: user.select_next_occurrence(clip.text()) + + + diff --git a/talon/community/community-cursorless-0.4.0/text/generic_editor.talon b/talon/community/community-cursorless-0.4.0/text/generic_editor.talon new file mode 100644 index 0000000..7c40372 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/text/generic_editor.talon @@ -0,0 +1,229 @@ +find it: + edit.find() + +next one: + edit.find_next() + +tug: + edit.left() + +tug now: + user.left_n(number_small) + +draw: + edit.word_left() + +draw now: + user.word_left_n(number_small) + +push: + edit.right() + +push now: + user.right_n(number_small) + +step: + edit.word_right() + +step now: + user.word_right_n(number_small) + +north: + user.up_n(1) + +north now: + user.up_n(number_small) + +south: + user.down_n(1) + +south now: + user.down_n(number_small) + +head: + edit.line_start() + +tail: + edit.line_end() + +go way down: + edit.file_end() + +go way up: + edit.file_start() + +go page down: + edit.page_down() + +go page up: + edit.page_up() + +# selecting +select line: + edit.select_line() + +select all: + edit.select_all() + +select left: + edit.extend_left() + +select right: + edit.extend_right() + +select up: + edit.extend_line_up() + +select down: + edit.extend_line_down() + +select word: + edit.select_word() + + lefter: + edit.extend_word_left() + + writer: + edit.extend_word_right() + +take start: + edit.extend_line_start() + +take close: + edit.extend_line_end() + +select way up: + edit.extend_file_start() + +select way down: + edit.extend_file_end() + +# editing +indent [more]: + edit.indent_more() + +(indent less | out dent): + edit.indent_less() + +# deleting +clear line: + edit.delete_line() + +wipe now: + user.delete_left_n(number_small) + +drill now: + user.delete_right_n(number_small) + + up: + edit.extend_line_up() + edit.delete() + + down: + edit.extend_line_down() + edit.delete() + + word: + edit.delete_word() + +scratch: + user.delete_word_left_n(1) + +scratch now: + user.delete_word_left_n(number_small) + +swallow: + user.delete_word_right_n(1) + +swallow now: + user.delete_word_right_n(number_small) + + start: + edit.extend_line_start() + edit.delete() + + close: + edit.extend_line_end() + edit.delete() + + way up: + edit.extend_file_start() + edit.delete() + + way down: + edit.extend_file_end() + edit.delete() + + all: + edit.select_all() + edit.delete() + +#copy commands +copy all: + edit.select_all() + edit.copy() +#to do: do we want these variants, seem to conflict +# copy left: +# edit.extend_left() +# edit.copy() +# copy right: +# edit.extend_right() +# edit.copy() +# copy up: +# edit.extend_up() +# edit.copy() +# copy down: +# edit.extend_down() +# edit.copy() + +copy word: + edit.select_word() + edit.copy() + +copy lefter: + edit.extend_word_left() + edit.copy() + +copy righter: + edit.extend_word_right() + edit.copy() + +copy line: + edit.select_line() + edit.copy() + +#cut commands +cut all: + edit.select_all() + edit.cut() +#to do: do we want these variants +# cut left: +# edit.select_all() +# edit.cut() +# cut right: +# edit.select_all() +# edit.cut() +# cut up: +# edit.select_all() +# edit.cut() +# cut down: +# edit.select_all() +# edit.cut() + +cut word: + edit.select_word() + edit.cut() + +cut lefter: + edit.extend_word_left() + edit.cut() + +cut righter: + edit.extend_word_right() + edit.cut() + +cut line: + edit.select_line() + edit.cut() + +Pokey mail: "pokey.rule@gmail.com" \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/text/homophones.talon b/talon/community/community-cursorless-0.4.0/text/homophones.talon new file mode 100644 index 0000000..a8fda4d --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/text/homophones.talon @@ -0,0 +1,5 @@ +phones : user.homophones_show(homophones_canonical) +phones that: user.homophones_show_selection() +phones force : user.homophones_force_show(homophones_canonical) +phones force: user.homophones_force_show_selection() +phones hide: user.homophones_hide() diff --git a/talon/user/community/core/homophones/homophones_open.talon b/talon/community/community-cursorless-0.4.0/text/homophones_open.talon similarity index 55% rename from talon/user/community/core/homophones/homophones_open.talon rename to talon/community/community-cursorless-0.4.0/text/homophones_open.talon index 2b7417e..ea79590 100644 --- a/talon/user/community/core/homophones/homophones_open.talon +++ b/talon/community/community-cursorless-0.4.0/text/homophones_open.talon @@ -1,7 +1,10 @@ -tag: user.homophones_open +mode: user.homophones - - +choose : + result = user.homophones_select(number_small) + insert(result) + user.homophones_hide() choose : result = user.homophones_select(number_small) insert(user.formatted_text(result, formatters)) - user.homophones_hide() + user.homophones_hide() \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/text/line_commands.talon b/talon/community/community-cursorless-0.4.0/text/line_commands.talon new file mode 100644 index 0000000..dc248b5 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/text/line_commands.talon @@ -0,0 +1,79 @@ +tag: user.line_commands +- +#this defines some common line commands. More may be defined that are ide-specific. +paste line: + edit.line_end() + key(enter) + edit.paste() +paste line up: + edit.line_start() + key(left) + key(enter) + edit.paste() +# go start: edit.jump_line(number) +# go end: +# edit.jump_line(number) +# edit.line_end() +comment (line | this | that): + code.toggle_comment() +add comment $: + #todo: this should probably be a single function once + #.talon supports implementing actions with parameters? + code.toggle_comment() + insert(user.text) +# comment until : +# user.select_range(number_1, number_2) +# code.toggle_comment() +# line : +# edit.jump_line(number) +# user.select_range(number, number) +# edit.delete() +# until : +# user.select_range(number_1, number_2) +# edit.delete() +# copy [line] : +# user.select_range(number, number) +# edit.copy() +# copy until : +# user.select_range(number_1, number_2) +# edit.copy() +# cut line : +# user.select_range(number, number) +# edit.cut() +# cut line until : +# user.select_range(number_1, number_2) +# edit.cut() +# (paste | replace) until : +# user.select_range(number_1, number_2) +# edit.paste() +# (select | cell | sell) [line] : user.select_range(number, number) +# (select | cell | sell) until : user.select_range(number_1, number_2) +tab that: edit.indent_more() +# tab line : +# edit.jump_line(number) +# edit.indent_more() +# tab until : +# user.select_range(number_1, number_2) +# edit.indent_more() +# retab that: edit.indent_less() +# retab [line] : +# user.select_range(number, number) +# edit.indent_less() +# retab until : +# user.select_range(number_1, number_2) +# edit.indent_less() +drag line down: edit.line_swap_down() +drag line up: edit.line_swap_up() +# drag up [line] : +# user.select_range(number, number) +# edit.line_swap_up() +# drag up until : +# user.select_range(number_1, number_2) +# edit.line_swap_up() +# drag down [line] : +# user.select_range(number, number) +# edit.line_swap_down() +# drag down until : +# user.select_range(number_1, number_2) +# edit.line_swap_down() +smear (line|that): edit.line_clone() diff --git a/talon/community/community-cursorless-0.4.0/text/numbers.talon b/talon/community/community-cursorless-0.4.0/text/numbers.talon new file mode 100644 index 0000000..04748ce --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/text/numbers.talon @@ -0,0 +1,4 @@ +not tag: user.mouse_grid_showing +- +numb : "{number_string}" +numb (dot | point) : "{number_string}.{digit_string}" \ No newline at end of file diff --git a/talon/community/community-cursorless-0.4.0/text/symbols.talon b/talon/community/community-cursorless-0.4.0/text/symbols.talon new file mode 100644 index 0000000..2d2bd92 --- /dev/null +++ b/talon/community/community-cursorless-0.4.0/text/symbols.talon @@ -0,0 +1,78 @@ +double dash: "--" +triple quote: "'''" +(dot dot | dotdot): ".." +#ellipses: "…" +ellipses: "..." +spam: ", " +coal gap: ": " +pipe gap: " | " +boom: ". " +arrow: "->" +dub arrow: "=>" +new line: "\\n" +carriage return: "\\r" +line feed: "\\r\\n" +empty round: "()" +empty square: "[]" +empty curly: "{}" +empty diamond: "<>" +empty quad: '""' +empty twin: "''" +empty escaped quad: '\\"\\"' +empty escaped twin: "\\'\\' +tween : + '{symbol_key}{symbol_key}' + key(left) +quad: + '""' + key(left) +twin: + "''" + key(left) +eyes: + '``' + key(left) +escaped quad: + '\\"\\"' + key(left) + key(left) +escaped twin: + "\\'\\'" + key(left) + key(left) +round: + insert("()") + key(left) +square: + insert("[]") + key(left) +curly: + insert("{}") + key(left) +diamond: + insert("<>") + key(left) +(diamond | angle) that: + text = edit.selected_text() + user.paste("<{text}>") +(curly | lace) that: + text = edit.selected_text() + user.paste("{{{text}}}") +(round | leper) that: + text = edit.selected_text() + user.paste("({text})") +(double | quad) that: + text = edit.selected_text() + user.paste('"{text}"') +(single | twin) that: + text = edit.selected_text() + user.paste("'{text}'") + +slicer: + edit.line_end() + key(enter) + insert("- ") + +end gap: + edit.line_end() + key(space) \ No newline at end of file diff --git a/talon/user/community/plugin/text_navigation/text_navigation.py b/talon/community/community-cursorless-0.4.0/text/text_navigation.py similarity index 66% rename from talon/user/community/plugin/text_navigation/text_navigation.py rename to talon/community/community-cursorless-0.4.0/text/text_navigation.py index 050345c..587943d 100644 --- a/talon/user/community/plugin/text_navigation/text_navigation.py +++ b/talon/community/community-cursorless-0.4.0/text/text_navigation.py @@ -1,30 +1,30 @@ -import itertools import re - -from talon import Context, Module, actions, settings +from talon import ctrl, ui, Module, Context, actions, clip +import itertools +from typing import Union ctx = Context() mod = Module() -mod.setting( +text_navigation_max_line_search = mod.setting( "text_navigation_max_line_search", type=int, default=10, - desc="The maximum number of rows that will be included in the search for the keywords above and below in ", + desc="the maximum number of rows that will be included in the search for the keywords above and below in ", ) mod.list( "navigation_action", - desc="Actions to perform, for instance move, select, cut, etc", + desc="actions to perform, for instance move, select, cut, etc", ) mod.list( "before_or_after", - desc="Words to indicate if the cursor should be moved before or after a given reference point", + desc="words to indicate if the cursor should be moved before or after a given reference point", ) mod.list( "navigation_target_name", - desc="Names for regular expressions for common things to navigate to, for instance a word with or without underscores", + desc="names for regular expressions for common things to navigate to, for instance a word with or without underscores", ) ctx.lists["self.navigation_action"] = { @@ -44,76 +44,53 @@ navigation_target_names = { "word": r"\w+", "small": r"[A-Z]?[a-z0-9]+", "big": r"[\S]+", - "parens": r"\((.*?)\)", - "squares": r"\[(.*?)\]", - "braces": r"\{(.*?)\}", - "quotes": r"\"(.*?)\"", - "angles": r"\<(.*?)\>", - # "single quotes": r'\'(.*?)\'', - "all": r"(.+)", - "method": r"\w+\((.*?)\)", - "constant": r"[A-Z_][A-Z_]+", + "parens": r'\((.*?)\)', + "squares": r'\[(.*?)\]', + "braces": r'\{(.*?)\}', + "quotes": r'\"(.*?)\"', + "angles": r'\<(.*?)\>', + #"single quotes": r'\'(.*?)\'', + "all": r'(.+)', + "method": r'\w+\((.*?)\)', + "constant": r'[A-Z_][A-Z_]+' } ctx.lists["self.navigation_target_name"] = navigation_target_names - -@mod.capture( - rule=" | {user.navigation_target_name} | phrase " -) +@mod.capture(rule=" | {user.navigation_target_name} | phrase ") def navigation_target(m) -> re.Pattern: """A target to navigate to. Returns a regular expression.""" - if hasattr(m, "any_alphanumeric_key"): + if hasattr(m, 'any_alphanumeric_key'): return re.compile(re.escape(m.any_alphanumeric_key), re.IGNORECASE) - if hasattr(m, "navigation_target_name"): + if hasattr(m, 'navigation_target_name'): return re.compile(m.navigation_target_name) return re.compile(re.escape(m.text), re.IGNORECASE) - @mod.action_class class Actions: def navigation( - navigation_action: str, # GO, EXTEND, SELECT, DELETE, CUT, COPY - direction: str, # up, down, left, right - navigation_target_name: str, - before_or_after: str, # BEFORE, AFTER, DEFAULT - regex: re.Pattern, - occurrence_number: int, + navigation_action: str, # GO, EXTEND, SELECT, DELETE, CUT, COPY + direction: str, # up, down, left, right + navigation_target_name: str, + before_or_after: str, # BEFORE, AFTER, DEFAULT + regex: re.Pattern, + occurrence_number: int, ): """Navigate in `direction` to the occurrence_number-th time that `regex` occurs, then execute `navigation_action` at the given `before_or_after` position.""" direction = direction.upper() - navigation_target_name = re.compile( - navigation_target_names["word"] - if (navigation_target_name == "DEFAULT") - else navigation_target_name - ) + navigation_target_name = re.compile((navigation_target_names["word"] if (navigation_target_name == "DEFAULT") else navigation_target_name)) function = navigate_left if direction in ("UP", "LEFT") else navigate_right - function( - navigation_action, - navigation_target_name, - before_or_after, - regex, - occurrence_number, - direction, - ) + function(navigation_action, navigation_target_name, before_or_after, regex, occurrence_number, direction) def navigation_by_name( - navigation_action: str, # GO, EXTEND, SELECT, DELETE, CUT, COPY - direction: str, # up, down, left, right - before_or_after: str, # BEFORE, AFTER, DEFAULT - navigation_target_name: str, # word, big, small - occurrence_number: int, + navigation_action: str, # GO, EXTEND, SELECT, DELETE, CUT, COPY + direction: str, # up, down, left, right + before_or_after: str, # BEFORE, AFTER, DEFAULT + navigation_target_name: str, # word, big, small + occurrence_number: int, ): """Like user.navigation, but to a named target.""" r = re.compile(navigation_target_names[navigation_target_name]) - actions.user.navigation( - navigation_action, - direction, - "DEFAULT", - before_or_after, - r, - occurrence_number, - ) - + actions.user.navigation(navigation_action, direction, "DEFAULT", before_or_after, r, occurrence_number) def get_text_left(): actions.edit.extend_line_start() @@ -132,7 +109,7 @@ def get_text_right(): def get_text_up(): actions.edit.up() actions.edit.line_end() - for j in range(0, settings.get("user.text_navigation_max_line_search")): + for j in range(0, text_navigation_max_line_search.get()): actions.edit.extend_up() actions.edit.extend_line_start() text = actions.edit.selected_text() @@ -143,7 +120,7 @@ def get_text_up(): def get_text_down(): actions.edit.down() actions.edit.line_start() - for j in range(0, settings.get("user.text_navigation_max_line_search")): + for j in range(0, text_navigation_max_line_search.get()): actions.edit.extend_down() actions.edit.extend_line_end() text = actions.edit.selected_text() @@ -185,12 +162,7 @@ def select(direction, start, end, length): def navigate_left( - navigation_action, - navigation_target_name, - before_or_after, - regex, - occurrence_number, - direction, + navigation_action, navigation_target_name, before_or_after, regex, occurrence_number, direction ): current_selection_length = get_current_selection_size() if current_selection_length > 0: @@ -201,30 +173,19 @@ def navigate_left( text if current_selection_length <= 0 else text[:-current_selection_length] ) match = match_backwards(regex, occurrence_number, subtext) - if match is None: + if match == None: # put back the old selection, if the search failed extend_left(current_selection_length) return start = match.start() end = match.end() handle_navigation_action( - navigation_action, - navigation_target_name, - before_or_after, - direction, - text, - start, - end, + navigation_action, navigation_target_name, before_or_after, direction, text, start, end ) def navigate_right( - navigation_action, - navigation_target_name, - before_or_after, - regex, - occurrence_number, - direction, + navigation_action, navigation_target_name, before_or_after, regex, occurrence_number, direction ): current_selection_length = get_current_selection_size() if current_selection_length > 0: @@ -234,66 +195,44 @@ def navigate_right( sub_text = text[current_selection_length:] # pick the next interrater, Skip n number of occurrences, get an iterator given the Regex match = match_forward(regex, occurrence_number, sub_text) - if match is None: + if match == None: # put back the old selection, if the search failed extend_right(current_selection_length) return start = current_selection_length + match.start() end = current_selection_length + match.end() handle_navigation_action( - navigation_action, - navigation_target_name, - before_or_after, - direction, - text, - start, - end, + navigation_action, navigation_target_name, before_or_after, direction, text, start, end ) def handle_navigation_action( - navigation_action, - navigation_target_name, - before_or_after, - direction, - text, - start, - end, + navigation_action, navigation_target_name, before_or_after, direction, text, start, end ): length = len(text) if navigation_action == "GO": handle_move(direction, before_or_after, start, end, length) elif navigation_action == "SELECT": - handle_select( - navigation_target_name, before_or_after, direction, text, start, end, length - ) + handle_select(navigation_target_name, before_or_after, direction, text, start, end, length) elif navigation_action == "DELETE": - handle_select( - navigation_target_name, before_or_after, direction, text, start, end, length - ) + handle_select(navigation_target_name, before_or_after, direction, text, start, end, length) actions.edit.delete() elif navigation_action == "CUT": - handle_select( - navigation_target_name, before_or_after, direction, text, start, end, length - ) + handle_select(navigation_target_name, before_or_after, direction, text, start, end, length) actions.edit.cut() elif navigation_action == "COPY": - handle_select( - navigation_target_name, before_or_after, direction, text, start, end, length - ) + handle_select(navigation_target_name, before_or_after, direction, text, start, end, length) actions.edit.copy() elif navigation_action == "EXTEND": handle_extend(before_or_after, direction, start, end, length) -def handle_select( - navigation_target_name, before_or_after, direction, text, start, end, length -): +def handle_select(navigation_target_name, before_or_after, direction, text, start, end, length): if before_or_after == "BEFORE": select_left = length - start text_left = text[:-select_left] match2 = match_backwards(navigation_target_name, 1, text_left) - if match2 is None: + if match2 == None: end = start start = 0 else: @@ -302,7 +241,7 @@ def handle_select( elif before_or_after == "AFTER": text_right = text[end:] match2 = match_forward(navigation_target_name, 1, text_right) - if match2 is None: + if match2 == None: start = end end = length else: diff --git a/talon/user/community/plugin/text_navigation/text_navigation.talon b/talon/community/community-cursorless-0.4.0/text/text_navigation.talon similarity index 62% rename from talon/user/community/plugin/text_navigation/text_navigation.talon rename to talon/community/community-cursorless-0.4.0/text/text_navigation.talon index 812569f..c3d6160 100644 --- a/talon/user/community/plugin/text_navigation/text_navigation.talon +++ b/talon/community/community-cursorless-0.4.0/text/text_navigation.talon @@ -1,9 +1,8 @@ ## (2021-03-09) This syntax is experimental and may change. See below for an explanation. -## If you are having issues with this module not working in vscode try adding the vscode setting "editor.emptySelectionClipboard": false navigate [{user.arrow_key}] [{user.navigation_action}] [{user.navigation_target_name}] [{user.before_or_after}] [] : - ## If you use this command a lot, you may wish to have a shorter syntax that omits the navigate keyword. Note that you then at least have to say either a navigation_action or before_or_after: - #({user.navigation_action} [{user.arrow_key}] [{user.navigation_target_name}] [{user.before_or_after}] | [{user.arrow_key}] {user.before_or_after}) [] : - user.navigation(navigation_action or "GO", arrow_key or "RIGHT", navigation_target_name or "DEFAULT", before_or_after or "DEFAULT", navigation_target, ordinals or 1) +## If you use this command a lot, you may wish to have a shorter syntax that omits the navigate keyword. Note that you then at least have to say either a navigation_action or before_or_after: +#({user.navigation_action} [{user.arrow_key}] [{user.navigation_target_name}] [{user.before_or_after}] | [{user.arrow_key}] {user.before_or_after}) [] : + user.navigation(navigation_action or "GO", arrow_key or "RIGHT", navigation_target_name or "DEFAULT", before_or_after or "DEFAULT", navigation_target, ordinals or 1) # ===== Examples of use ===== # @@ -34,9 +33,9 @@ navigate [{user.arrow_key}] [{user.navigation_action}] [{user.navigation_target_ # # We can specify what gets selected before or after the given input: # -# navigate select parens after equals: Select the first "(" and everything until the first ")" after the "=" -# navigate left copy all before equals: Copy everything from the start of the line until the first "=" you encounter while moving left -# navigate clear constant before semicolon: Delete the last word consisting of only uppercase characters or underscores before a ";" +# navigate select parens after equals: Select the first "(" and everything until the first ")" after the "=" +# navigate left copy all before equals: Copy everything from the start of the line until the first "=" you encounter while moving left +# navigate clear constant before semicolon: Delete the last word consisting of only uppercase characters or underscores before a ";" # # ===== Explanation of the grammar ===== # @@ -49,8 +48,8 @@ navigate [{user.arrow_key}] [{user.navigation_action}] [{user.navigation_target_ # What action to perform. # # [{user.navigation_target_name}]: word, small, big, parens, squares, braces, quotes, angles, all, method, constant (default: word) -# The predetermined unit to select if before_or_after was specified. -# Defaults to "word" +# The predetermined unit to select if before_or_after was specified. +# Defaults to "word" # # [{user.before_or_after}]: before, after (default: special behavior) # For move/extend: where to leave the cursor, before or after the target. @@ -68,16 +67,10 @@ navigate [{user.arrow_key}] [{user.navigation_action}] [{user.navigation_target_ # - "phrase " # Specifies the target to search for/navigate to. -# The functionality for all these commands is covered in the lines above, but these commands are kept here for convenience. Originally from word_selection.talon. -word neck []: - user.navigation_by_name("SELECT", "RIGHT", "DEFAULT", "word", number_small or 1) -word pre []: - user.navigation_by_name("SELECT", "LEFT", "DEFAULT", "word", number_small or 1) -small word neck []: - user.navigation_by_name("SELECT", "RIGHT", "DEFAULT", "small", number_small or 1) -small word pre []: - user.navigation_by_name("SELECT", "LEFT", "DEFAULT", "small", number_small or 1) -big word neck []: - user.navigation_by_name("SELECT", "RIGHT", "DEFAULT", "big", number_small or 1) -big word pre []: - user.navigation_by_name("SELECT", "LEFT", "DEFAULT", "big", number_small or 1) +# The functionality for all these commands is covered in the lines above, but these commands are kept here for convenience. Originally from word_selection.talon. +word neck []: user.navigation_by_name("SELECT", "RIGHT", "DEFAULT", "word", number_small or 1) +word pre []: user.navigation_by_name("SELECT", "LEFT", "DEFAULT", "word", number_small or 1) +small word neck []: user.navigation_by_name("SELECT", "RIGHT", "DEFAULT", "small", number_small or 1) +small word pre []: user.navigation_by_name("SELECT", "LEFT", "DEFAULT", "small", number_small or 1) +big word neck []: user.navigation_by_name("SELECT", "RIGHT", "DEFAULT", "big", number_small or 1) +big word pre []: user.navigation_by_name("SELECT", "LEFT", "DEFAULT", "big", number_small or 1) diff --git a/talon/user/community/.editorconfig b/talon/user/community/.editorconfig deleted file mode 100644 index 0d6fd5f..0000000 --- a/talon/user/community/.editorconfig +++ /dev/null @@ -1,17 +0,0 @@ -# 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 diff --git a/talon/user/community/.git-blame-ignore-revs b/talon/user/community/.git-blame-ignore-revs deleted file mode 100644 index 92da84f..0000000 --- a/talon/user/community/.git-blame-ignore-revs +++ /dev/null @@ -1,3 +0,0 @@ -2877a6849d75e5fa78c9453991a9235b4f6d9dcf -3bf4882fa0a05b22171e59118bd7c9640aae753a -446ec764c9caa98973eacd7f792b6a087a1b635f diff --git a/talon/user/community/.github/dependabot.yml b/talon/user/community/.github/dependabot.yml deleted file mode 100644 index 2c7d170..0000000 --- a/talon/user/community/.github/dependabot.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: 2 -updates: - # Maintain dependencies for GitHub Actions - - package-ecosystem: "github-actions" - directory: "/" - schedule: - interval: "daily" diff --git a/talon/user/community/.github/workflows/ci.yml b/talon/user/community/.github/workflows/ci.yml deleted file mode 100644 index 43bc282..0000000 --- a/talon/user/community/.github/workflows/ci.yml +++ /dev/null @@ -1,25 +0,0 @@ -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 diff --git a/talon/user/community/.gitignore b/talon/user/community/.gitignore deleted file mode 100644 index 224ff3d..0000000 --- a/talon/user/community/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -# Dev stuff -__pycache__ -*.sw? -.idea/ -# Locally generated -/settings -.vscode/settings.json -.DS_Store -.bak diff --git a/talon/user/community/.pre-commit-config.yaml b/talon/user/community/.pre-commit-config.yaml deleted file mode 100644 index 2a8c651..0000000 --- a/talon/user/community/.pre-commit-config.yaml +++ /dev/null @@ -1,52 +0,0 @@ -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"] diff --git a/talon/user/community/BREAKING_CHANGES.txt b/talon/user/community/BREAKING_CHANGES.txt deleted file mode 100644 index ba6219f..0000000 --- a/talon/user/community/BREAKING_CHANGES.txt +++ /dev/null @@ -1,44 +0,0 @@ -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-.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 ' in favor of - overridable 'choose' helper -* 2024-01-27 Deprecate '' 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. diff --git a/talon/user/community/CONTRIBUTING.md b/talon/user/community/CONTRIBUTING.md deleted file mode 100644 index c5ddf51..0000000 --- a/talon/user/community/CONTRIBUTING.md +++ /dev/null @@ -1,13 +0,0 @@ -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. diff --git a/talon/user/community/README.md b/talon/user/community/README.md deleted file mode 100644 index f8bd6bb..0000000 --- a/talon/user/community/README.md +++ /dev/null @@ -1,382 +0,0 @@ -# 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 `. 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 A–Z. - -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_.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 `, `file ` and `open ` 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 ` with `pre-commit run --files `. - - 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 don’t 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`. diff --git a/talon/user/community/apps/1password/1password_mac.py b/talon/user/community/apps/1password/1password_mac.py deleted file mode 100644 index 4465a3e..0000000 --- a/talon/user/community/apps/1password/1password_mac.py +++ /dev/null @@ -1,31 +0,0 @@ -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") diff --git a/talon/user/community/apps/1password/1password_win.py b/talon/user/community/apps/1password/1password_win.py deleted file mode 100644 index 89a3ff8..0000000 --- a/talon/user/community/apps/1password/1password_win.py +++ /dev/null @@ -1,31 +0,0 @@ -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") diff --git a/talon/user/community/apps/README.md b/talon/user/community/apps/README.md deleted file mode 100644 index b0bf4e7..0000000 --- a/talon/user/community/apps/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# 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 diff --git a/talon/user/community/apps/adobe/adobe_acrobat_reader_dc.py b/talon/user/community/apps/adobe/adobe_acrobat_reader_dc.py deleted file mode 100644 index 858d21c..0000000 --- a/talon/user/community/apps/adobe/adobe_acrobat_reader_dc.py +++ /dev/null @@ -1,15 +0,0 @@ -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 diff --git a/talon/user/community/apps/adobe/adobe_acrobat_reader_dc.talon b/talon/user/community/apps/adobe/adobe_acrobat_reader_dc.talon deleted file mode 100644 index 0375101..0000000 --- a/talon/user/community/apps/adobe/adobe_acrobat_reader_dc.talon +++ /dev/null @@ -1,5 +0,0 @@ -app: adobe_acrobat_reader_dc -- -# Set tags -tag(): user.tabs -tag(): user.pages diff --git a/talon/user/community/apps/adobe/adobe_acrobat_reader_dc_win.py b/talon/user/community/apps/adobe/adobe_acrobat_reader_dc_win.py deleted file mode 100644 index 846ec5c..0000000 --- a/talon/user/community/apps/adobe/adobe_acrobat_reader_dc_win.py +++ /dev/null @@ -1,61 +0,0 @@ -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") diff --git a/talon/user/community/apps/anaconda/anaconda.py b/talon/user/community/apps/anaconda/anaconda.py deleted file mode 100644 index 791f91e..0000000 --- a/talon/user/community/apps/anaconda/anaconda.py +++ /dev/null @@ -1,9 +0,0 @@ -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 -""" diff --git a/talon/user/community/apps/anaconda/anaconda.talon b/talon/user/community/apps/anaconda/anaconda.talon deleted file mode 100644 index c2354bb..0000000 --- a/talon/user/community/apps/anaconda/anaconda.talon +++ /dev/null @@ -1,40 +0,0 @@ -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 " diff --git a/talon/user/community/apps/apple_notes/apple_notes.py b/talon/user/community/apps/apple_notes/apple_notes.py deleted file mode 100644 index 5f2920a..0000000 --- a/talon/user/community/apps/apple_notes/apple_notes.py +++ /dev/null @@ -1,21 +0,0 @@ -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-[") diff --git a/talon/user/community/apps/apple_notes/apple_notes.talon b/talon/user/community/apps/apple_notes/apple_notes.talon deleted file mode 100644 index ba4e37f..0000000 --- a/talon/user/community/apps/apple_notes/apple_notes.talon +++ /dev/null @@ -1,32 +0,0 @@ -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') diff --git a/talon/user/community/apps/apple_terminal/apple_terminal.talon b/talon/user/community/apps/apple_terminal/apple_terminal.talon deleted file mode 100644 index 2d0f6d1..0000000 --- a/talon/user/community/apps/apple_terminal/apple_terminal.talon +++ /dev/null @@ -1,25 +0,0 @@ -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) diff --git a/talon/user/community/apps/arc/arc.py b/talon/user/community/apps/arc/arc.py deleted file mode 100644 index c89731e..0000000 --- a/talon/user/community/apps/arc/arc.py +++ /dev/null @@ -1,34 +0,0 @@ -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") diff --git a/talon/user/community/apps/arc/arc_mac.talon b/talon/user/community/apps/arc/arc_mac.talon deleted file mode 100644 index 0fa8d5d..0000000 --- a/talon/user/community/apps/arc/arc_mac.talon +++ /dev/null @@ -1,6 +0,0 @@ -app: arc -os: mac -- -tag(): browser -tag(): user.tabs -tag(): user.command_search diff --git a/talon/user/community/apps/arc/little-arc_mac.talon b/talon/user/community/apps/arc/little-arc_mac.talon deleted file mode 100644 index bec24f9..0000000 --- a/talon/user/community/apps/arc/little-arc_mac.talon +++ /dev/null @@ -1,8 +0,0 @@ -user.running: arc -os: mac -- -# This assumes that you have not disabled Little Arc -little arc []: - key("cmd-alt-n") - sleep(200ms) - insert(user.text or "") diff --git a/talon/user/community/apps/atril/atril.py b/talon/user/community/apps/atril/atril.py deleted file mode 100644 index daaa70b..0000000 --- a/talon/user/community/apps/atril/atril.py +++ /dev/null @@ -1,39 +0,0 @@ -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") diff --git a/talon/user/community/apps/atril/atril.talon b/talon/user/community/apps/atril/atril.talon deleted file mode 100644 index 5b87d7e..0000000 --- a/talon/user/community/apps/atril/atril.talon +++ /dev/null @@ -1,4 +0,0 @@ -app: atril -- -# Set tags -tag(): user.pages diff --git a/talon/user/community/apps/brave/brave.py b/talon/user/community/apps/brave/brave.py deleted file mode 100644 index b89a57d..0000000 --- a/talon/user/community/apps/brave/brave.py +++ /dev/null @@ -1,32 +0,0 @@ -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") diff --git a/talon/user/community/apps/brave/brave.talon b/talon/user/community/apps/brave/brave.talon deleted file mode 100644 index 11cc1af..0000000 --- a/talon/user/community/apps/brave/brave.talon +++ /dev/null @@ -1,4 +0,0 @@ -app: brave -- -tag(): browser -tag(): user.tabs diff --git a/talon/user/community/apps/calibre/calibre.py b/talon/user/community/apps/calibre/calibre.py deleted file mode 100644 index effa604..0000000 --- a/talon/user/community/apps/calibre/calibre.py +++ /dev/null @@ -1,19 +0,0 @@ -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 diff --git a/talon/user/community/apps/calibre/calibre_viewer.py b/talon/user/community/apps/calibre/calibre_viewer.py deleted file mode 100644 index 52bda95..0000000 --- a/talon/user/community/apps/calibre/calibre_viewer.py +++ /dev/null @@ -1,39 +0,0 @@ -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") diff --git a/talon/user/community/apps/calibre/calibre_viewer.talon b/talon/user/community/apps/calibre/calibre_viewer.talon deleted file mode 100644 index bffed5d..0000000 --- a/talon/user/community/apps/calibre/calibre_viewer.talon +++ /dev/null @@ -1,5 +0,0 @@ -app: calibre_viewer -- -# Set tags -tag(): user.pages -tag(): user.chapters diff --git a/talon/user/community/apps/chrome/chrome.py b/talon/user/community/apps/chrome/chrome.py deleted file mode 100644 index 5a429a4..0000000 --- a/talon/user/community/apps/chrome/chrome.py +++ /dev/null @@ -1,54 +0,0 @@ -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") diff --git a/talon/user/community/apps/chrome/chrome.talon b/talon/user/community/apps/chrome/chrome.talon deleted file mode 100644 index 5626f96..0000000 --- a/talon/user/community/apps/chrome/chrome.talon +++ /dev/null @@ -1,14 +0,0 @@ -app: chrome -- -tag(): browser -tag(): user.tabs - -profile switch: user.chrome_mod("shift-m") - -tab search: user.chrome_mod("shift-a") - -tab search $: - user.chrome_mod("shift-a") - sleep(200ms) - insert("{text}") - key(down) diff --git a/talon/user/community/apps/discord/discord_mac.py b/talon/user/community/apps/discord/discord_mac.py deleted file mode 100644 index 82a58fd..0000000 --- a/talon/user/community/apps/discord/discord_mac.py +++ /dev/null @@ -1,96 +0,0 @@ -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") diff --git a/talon/user/community/apps/discord/discord_win.py b/talon/user/community/apps/discord/discord_win.py deleted file mode 100644 index 2f7246f..0000000 --- a/talon/user/community/apps/discord/discord_win.py +++ /dev/null @@ -1,97 +0,0 @@ -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") diff --git a/talon/user/community/apps/dock/dock.py b/talon/user/community/apps/dock/dock.py deleted file mode 100644 index 7174ac5..0000000 --- a/talon/user/community/apps/dock/dock.py +++ /dev/null @@ -1,22 +0,0 @@ -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) diff --git a/talon/user/community/apps/dock/dock.talon b/talon/user/community/apps/dock/dock.talon deleted file mode 100644 index 23879a9..0000000 --- a/talon/user/community/apps/dock/dock.talon +++ /dev/null @@ -1,5 +0,0 @@ -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") diff --git a/talon/user/community/apps/eclipse/eclipse.talon b/talon/user/community/apps/eclipse/eclipse.talon deleted file mode 100644 index f399510..0000000 --- a/talon/user/community/apps/eclipse/eclipse.talon +++ /dev/null @@ -1,144 +0,0 @@ -#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 []: - 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) diff --git a/talon/user/community/apps/edge/edge.py b/talon/user/community/apps/edge/edge.py deleted file mode 100644 index a7f72f1..0000000 --- a/talon/user/community/apps/edge/edge.py +++ /dev/null @@ -1,28 +0,0 @@ -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") diff --git a/talon/user/community/apps/edge/edge.talon b/talon/user/community/apps/edge/edge.talon deleted file mode 100644 index 7bfee4e..0000000 --- a/talon/user/community/apps/edge/edge.talon +++ /dev/null @@ -1,4 +0,0 @@ -app: microsoft_edge -- -tag(): browser -tag(): user.tabs diff --git a/talon/user/community/apps/emacs/emacs.py b/talon/user/community/apps/emacs/emacs.py deleted file mode 100644 index 9488db5..0000000 --- a/talon/user/community/apps/emacs/emacs.py +++ /dev/null @@ -1,363 +0,0 @@ -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() diff --git a/talon/user/community/apps/emacs/emacs.talon b/talon/user/community/apps/emacs/emacs.talon deleted file mode 100644 index a144915..0000000 --- a/talon/user/community/apps/emacs/emacs.talon +++ /dev/null @@ -1,367 +0,0 @@ -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.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.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.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.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 : user.emacs("enlarge-window", number_small) -split shrink : - amount = number_small or 1 - user.emacs("enlarge-window", 0 - amount) -split widen []: - user.emacs("enlarge-window-horizontally", number_small or 1) -split narrow []: - 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.emacs_help("a") - user.insert_formatted(text, "DASH_SEPARATED") - key(enter) -describe (fun | function) $: - user.emacs_help("f") - user.insert_formatted(text, "DASH_SEPARATED") - key(enter) -describe symbol $: - user.emacs_help("o") - user.insert_formatted(text, "DASH_SEPARATED") - key(enter) -describe variable $: - 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.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] from) top: - user.emacs("recenter-top-bottom", number_small or 0) -(center | [center] from) bottom: - number = number_small or 0 - user.emacs("recenter-top-bottom", -1 - number) -go top: - edit.jump_line(number) - user.emacs("recenter-top-bottom", 0) -go 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 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") diff --git a/talon/user/community/apps/emacs/emacs_commands.csv b/talon/user/community/apps/emacs/emacs_commands.csv deleted file mode 100644 index 7c09c33..0000000 --- a/talon/user/community/apps/emacs/emacs_commands.csv +++ /dev/null @@ -1,231 +0,0 @@ -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 diff --git a/talon/user/community/apps/emacs/emacs_commands.py b/talon/user/community/apps/emacs/emacs_commands.py deleted file mode 100644 index 12fd817..0000000 --- a/talon/user/community/apps/emacs/emacs_commands.py +++ /dev/null @@ -1,71 +0,0 @@ -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 diff --git a/talon/user/community/apps/evernote/mac.talon b/talon/user/community/apps/evernote/mac.talon deleted file mode 100644 index fb478fc..0000000 --- a/talon/user/community/apps/evernote/mac.talon +++ /dev/null @@ -1,6 +0,0 @@ -app: evernote -os: mac -- -settings(): - # Necessary to stop commands like 'slap' getting jumbled - key_wait = 9.0 diff --git a/talon/user/community/apps/evince/evince.talon b/talon/user/community/apps/evince/evince.talon deleted file mode 100644 index 68b9f87..0000000 --- a/talon/user/community/apps/evince/evince.talon +++ /dev/null @@ -1,4 +0,0 @@ -app: evince -- -# Set tags -tag(): user.pages diff --git a/talon/user/community/apps/evince/evince_linux.py b/talon/user/community/apps/evince/evince_linux.py deleted file mode 100644 index 0820799..0000000 --- a/talon/user/community/apps/evince/evince_linux.py +++ /dev/null @@ -1,39 +0,0 @@ -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") diff --git a/talon/user/community/apps/firefox/firefox.py b/talon/user/community/apps/firefox/firefox.py deleted file mode 100644 index 92dac91..0000000 --- a/talon/user/community/apps/firefox/firefox.py +++ /dev/null @@ -1,60 +0,0 @@ -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") diff --git a/talon/user/community/apps/firefox/firefox.talon b/talon/user/community/apps/firefox/firefox.talon deleted file mode 100644 index db3224d..0000000 --- a/talon/user/community/apps/firefox/firefox.talon +++ /dev/null @@ -1,15 +0,0 @@ -app: firefox -- -tag(): browser -tag(): user.tabs - -tab search: - browser.focus_address() - insert("% ") -tab search $: - browser.focus_address() - insert("% {text}") - key(down) - -(sidebar | panel) bookmarks: user.firefox_bookmarks_sidebar() -(sidebar | panel) history: user.firefox_history_sidebar() diff --git a/talon/user/community/apps/firefox/firefox_mac.py b/talon/user/community/apps/firefox/firefox_mac.py deleted file mode 100644 index d8418c8..0000000 --- a/talon/user/community/apps/firefox/firefox_mac.py +++ /dev/null @@ -1,33 +0,0 @@ -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") diff --git a/talon/user/community/apps/firefox/firefox_win_linux.py b/talon/user/community/apps/firefox/firefox_win_linux.py deleted file mode 100644 index f93eebf..0000000 --- a/talon/user/community/apps/firefox/firefox_win_linux.py +++ /dev/null @@ -1,44 +0,0 @@ -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") diff --git a/talon/user/community/apps/foxit_reader/foxit_reader.py b/talon/user/community/apps/foxit_reader/foxit_reader.py deleted file mode 100644 index ca0c441..0000000 --- a/talon/user/community/apps/foxit_reader/foxit_reader.py +++ /dev/null @@ -1,70 +0,0 @@ -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") diff --git a/talon/user/community/apps/foxit_reader/foxit_reader.talon b/talon/user/community/apps/foxit_reader/foxit_reader.talon deleted file mode 100644 index 8390889..0000000 --- a/talon/user/community/apps/foxit_reader/foxit_reader.talon +++ /dev/null @@ -1,6 +0,0 @@ -app: foxit_reader -- -tag(): user.tabs -tag(): user.pages - -tab close all: key(ctrl-shift-w) diff --git a/talon/user/community/apps/gdb/gdb.py b/talon/user/community/apps/gdb/gdb.py deleted file mode 100644 index 33db895..0000000 --- a/talon/user/community/apps/gdb/gdb.py +++ /dev/null @@ -1,117 +0,0 @@ -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") diff --git a/talon/user/community/apps/gdb/gdb_active.talon b/talon/user/community/apps/gdb/gdb_active.talon deleted file mode 100644 index ba73105..0000000 --- a/talon/user/community/apps/gdb/gdb_active.talon +++ /dev/null @@ -1,102 +0,0 @@ -os: linux -# XXX - this matches .gdb files atm -#win.title: /gdb/ -tag: terminal -and tag: user.gdb -- -tag(): user.debugger -until : "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] : "p {text}" -print hex: "p/x " -print hex [variable] : "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 bytes: "x/{number}bx " -hex dump (half | short) words: "x/{number}hx " -hex dump (d | long) words: "x/{number}dx " -hex dump 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 : "enable display {number_small}\n" -disable display : "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 $: "inferior {number_small}\n" -inferior: "inferior " -resume main (inf | inferior): - insert("inferior 1\n") - insert("c\n") -resume [from] (inf | inferior) $: - 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 : "set listsize {number_small}\n" - -# misc -clear screen: "shell clear\n" diff --git a/talon/user/community/apps/gdb/gdb_global.talon b/talon/user/community/apps/gdb/gdb_global.talon deleted file mode 100644 index 75dddbd..0000000 --- a/talon/user/community/apps/gdb/gdb_global.talon +++ /dev/null @@ -1,2 +0,0 @@ -[enable] debug mode: user.gdb_enable() -disable debug mode: user.gdb_disable() diff --git a/talon/user/community/apps/git/git.py b/talon/user/community/apps/git/git.py deleted file mode 100644 index 01acd5d..0000000 --- a/talon/user/community/apps/git/git.py +++ /dev/null @@ -1,17 +0,0 @@ -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) diff --git a/talon/user/community/apps/git/git.talon b/talon/user/community/apps/git/git.talon deleted file mode 100644 index 9ba5687..0000000 --- a/talon/user/community/apps/git/git.talon +++ /dev/null @@ -1,51 +0,0 @@ -tag: terminal -and tag: user.git -- -git {user.git_command} []: - args = git_arguments or "" - "git {git_command}{args} " -git commit [] message []: - args = git_arguments or "" - message = prose or "" - user.insert_between('git commit{args} --message "{message}', '"') -git stash [push] [] message []: - 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") diff --git a/talon/user/community/apps/git/git_add_patch.talon b/talon/user/community/apps/git/git_add_patch.talon deleted file mode 100644 index d495002..0000000 --- a/talon/user/community/apps/git/git_add_patch.talon +++ /dev/null @@ -1,19 +0,0 @@ -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) diff --git a/talon/user/community/apps/git/git_argument.talon-list b/talon/user/community/apps/git/git_argument.talon-list deleted file mode 100644 index ac991af..0000000 --- a/talon/user/community/apps/git/git_argument.talon-list +++ /dev/null @@ -1,69 +0,0 @@ -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 diff --git a/talon/user/community/apps/git/git_command.talon-list b/talon/user/community/apps/git/git_command.talon-list deleted file mode 100644 index 04655a3..0000000 --- a/talon/user/community/apps/git/git_command.talon-list +++ /dev/null @@ -1,71 +0,0 @@ -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 diff --git a/talon/user/community/apps/github/github_web.talon b/talon/user/community/apps/github/github_web.talon deleted file mode 100644 index 3c709c8..0000000 --- a/talon/user/community/apps/github/github_web.talon +++ /dev/null @@ -1,70 +0,0 @@ -# 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) diff --git a/talon/user/community/apps/gnome_terminal/gnome_terminal.py b/talon/user/community/apps/gnome_terminal/gnome_terminal.py deleted file mode 100644 index 5946e4d..0000000 --- a/talon/user/community/apps/gnome_terminal/gnome_terminal.py +++ /dev/null @@ -1,93 +0,0 @@ -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 diff --git a/talon/user/community/apps/gnome_terminal/gnome_terminal.talon b/talon/user/community/apps/gnome_terminal/gnome_terminal.talon deleted file mode 100644 index e86530c..0000000 --- a/talon/user/community/apps/gnome_terminal/gnome_terminal.talon +++ /dev/null @@ -1,8 +0,0 @@ -app: gnome_terminal -- -# Set tags -tag(): terminal -tag(): user.tabs -tag(): user.generic_unix_shell -tag(): user.git -tag(): user.kubectl diff --git a/talon/user/community/apps/guake/guake_linux.py b/talon/user/community/apps/guake/guake_linux.py deleted file mode 100644 index 34c9909..0000000 --- a/talon/user/community/apps/guake/guake_linux.py +++ /dev/null @@ -1,23 +0,0 @@ -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") diff --git a/talon/user/community/apps/i3wm/i3wm.py b/talon/user/community/apps/i3wm/i3wm.py deleted file mode 100644 index 3e5840a..0000000 --- a/talon/user/community/apps/i3wm/i3wm.py +++ /dev/null @@ -1,118 +0,0 @@ -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") diff --git a/talon/user/community/apps/i3wm/i3wm.talon b/talon/user/community/apps/i3wm/i3wm.talon deleted file mode 100644 index fafe4c8..0000000 --- a/talon/user/community/apps/i3wm/i3wm.talon +++ /dev/null @@ -1,99 +0,0 @@ -# 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 : 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) : - 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.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() diff --git a/talon/user/community/apps/jetbrains/jetbrains.py b/talon/user/community/apps/jetbrains/jetbrains.py deleted file mode 100644 index ea08bba..0000000 --- a/talon/user/community/apps/jetbrains/jetbrains.py +++ /dev/null @@ -1,376 +0,0 @@ -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): diff --git a/talon/user/community/apps/kde_konsole/kde_konsole.py b/talon/user/community/apps/kde_konsole/kde_konsole.py deleted file mode 100644 index 953c730..0000000 --- a/talon/user/community/apps/kde_konsole/kde_konsole.py +++ /dev/null @@ -1,65 +0,0 @@ -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 diff --git a/talon/user/community/apps/kde_konsole/kde_konsole.talon b/talon/user/community/apps/kde_konsole/kde_konsole.talon deleted file mode 100644 index e75f63e..0000000 --- a/talon/user/community/apps/kde_konsole/kde_konsole.talon +++ /dev/null @@ -1,17 +0,0 @@ -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 diff --git a/talon/user/community/apps/kindle/kindle.py b/talon/user/community/apps/kindle/kindle.py deleted file mode 100644 index a43db7d..0000000 --- a/talon/user/community/apps/kindle/kindle.py +++ /dev/null @@ -1,11 +0,0 @@ -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 diff --git a/talon/user/community/apps/kindle/kindle.talon b/talon/user/community/apps/kindle/kindle.talon deleted file mode 100644 index 3c376f9..0000000 --- a/talon/user/community/apps/kindle/kindle.talon +++ /dev/null @@ -1,4 +0,0 @@ -app: kindle -- -# Set tags -tag(): user.pages diff --git a/talon/user/community/apps/kindle/kindle_win.py b/talon/user/community/apps/kindle/kindle_win.py deleted file mode 100644 index e37e118..0000000 --- a/talon/user/community/apps/kindle/kindle_win.py +++ /dev/null @@ -1,24 +0,0 @@ -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") diff --git a/talon/user/community/apps/kubectl/kubectl.talon b/talon/user/community/apps/kubectl/kubectl.talon deleted file mode 100644 index 3f6bc88..0000000 --- a/talon/user/community/apps/kubectl/kubectl.talon +++ /dev/null @@ -1,66 +0,0 @@ -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") diff --git a/talon/user/community/apps/meld/meld.py b/talon/user/community/apps/meld/meld.py deleted file mode 100644 index 564913e..0000000 --- a/talon/user/community/apps/meld/meld.py +++ /dev/null @@ -1,44 +0,0 @@ -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.") diff --git a/talon/user/community/apps/meld/meld.talon b/talon/user/community/apps/meld/meld.talon deleted file mode 100644 index f237c17..0000000 --- a/talon/user/community/apps/meld/meld.talon +++ /dev/null @@ -1,6 +0,0 @@ -app: meld -- -tag(): user.tabs - -change next: key(alt-down) -change (previous | last): key(alt-up) diff --git a/talon/user/community/apps/nautilus/nautilus.py b/talon/user/community/apps/nautilus/nautilus.py deleted file mode 100644 index 9a75c84..0000000 --- a/talon/user/community/apps/nautilus/nautilus.py +++ /dev/null @@ -1,66 +0,0 @@ -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()}"]) diff --git a/talon/user/community/apps/nautilus/nautilus.talon b/talon/user/community/apps/nautilus/nautilus.talon deleted file mode 100644 index 4d0c06c..0000000 --- a/talon/user/community/apps/nautilus/nautilus.talon +++ /dev/null @@ -1,5 +0,0 @@ -app: nautilus -- -# Set tags -tag(): user.tabs -tag(): user.file_manager diff --git a/talon/user/community/apps/nitro_reader/nitro_reader_5.py b/talon/user/community/apps/nitro_reader/nitro_reader_5.py deleted file mode 100644 index 2828c56..0000000 --- a/talon/user/community/apps/nitro_reader/nitro_reader_5.py +++ /dev/null @@ -1,43 +0,0 @@ -from talon import Context, Module, actions - -# --- App definition --- -mod = Module() -mod.apps.nitro_reader_five = """ -os: windows -and app.name: Nitro Reader 5 -os: windows -and app.exe: /^nitropdfreader\.exe$/i -""" - -# Context matching -ctx = Context() -ctx.matches = """ -app: nitro_reader_five -""" - - -# --- Implement actions --- -@ctx.action_class("app") -class app_actions: - # app.tabs - def tab_open(): - actions.key("ctrl-shift-o") - - -@ctx.action_class("user") -class UserActions: - # user.pages - def page_next(): - actions.key("right") - - def page_previous(): - actions.key("left") - - def page_jump(number: int): - actions.key("ctrl-g") - actions.edit.select_line() - actions.insert(str(number)) - actions.key("enter alt:2") - - def page_final(): - actions.key("end") diff --git a/talon/user/community/apps/nitro_reader/nitro_reader_5.talon b/talon/user/community/apps/nitro_reader/nitro_reader_5.talon deleted file mode 100644 index a68d75b..0000000 --- a/talon/user/community/apps/nitro_reader/nitro_reader_5.talon +++ /dev/null @@ -1,5 +0,0 @@ -app: nitro_reader_five -- -# Set tags -tag(): user.pages -tag(): user.tabs diff --git a/talon/user/community/apps/notepad/notepad.py b/talon/user/community/apps/notepad/notepad.py deleted file mode 100644 index 9770892..0000000 --- a/talon/user/community/apps/notepad/notepad.py +++ /dev/null @@ -1,22 +0,0 @@ -from talon import Context, Module, actions - -ctx = Context() -mod = Module() - -mod.apps.notepad = r""" -os: windows -and app.exe: notepad.exe -""" - -ctx.matches = r""" -app: notepad -""" - - -@ctx.action_class("win") -class win_actions: - def filename(): - filename = actions.win.title().split(" - ")[0] - if "." in filename: - return filename - return "" diff --git a/talon/user/community/apps/notepad/notepad.talon b/talon/user/community/apps/notepad/notepad.talon deleted file mode 100644 index 6593544..0000000 --- a/talon/user/community/apps/notepad/notepad.talon +++ /dev/null @@ -1,5 +0,0 @@ -app: notepad -- - -tag(): user.tabs -tag(): user.find_and_replace diff --git a/talon/user/community/apps/obsidian/obsidian.py b/talon/user/community/apps/obsidian/obsidian.py deleted file mode 100644 index bff0d02..0000000 --- a/talon/user/community/apps/obsidian/obsidian.py +++ /dev/null @@ -1,16 +0,0 @@ -from talon import Context, Module - -mod = Module() -mod.apps.obsidian = "app.name: Obsidian" - -lang_ctx = Context() -lang_ctx.matches = r""" -app: obsidian -not tag: user.code_language_forced -""" - - -@lang_ctx.action_class("code") -class CodeActions: - def language(): - return "markdown" diff --git a/talon/user/community/apps/obsidian/obsidian.talon b/talon/user/community/apps/obsidian/obsidian.talon deleted file mode 100644 index bb33103..0000000 --- a/talon/user/community/apps/obsidian/obsidian.talon +++ /dev/null @@ -1,3 +0,0 @@ -app: obsidian -- -tag(): user.tabs diff --git a/talon/user/community/apps/okular/okular.py b/talon/user/community/apps/okular/okular.py deleted file mode 100644 index d4d21c8..0000000 --- a/talon/user/community/apps/okular/okular.py +++ /dev/null @@ -1,49 +0,0 @@ -from talon import Context, Module, actions - -# --- App definition --- -mod = Module() -mod.apps.okular = r""" -os: windows -and app.name: okular.exe -os: windows -and app.exe: /^okular\.exe$/i -""" -mod.apps.okular = """ -os: linux -and app.name: okular -""" -# TODO: mac context and implementation - -# Context matching -ctx = Context() -ctx.matches = """ -os: windows -os: linux -app: okular -""" - - -# --- Implement actions --- -@ctx.action_class("user") -class UserActions: - # user.pages - def page_current(): - actions.key("ctrl-g") - page = actions.edit.selected_text() - actions.key("escape") - return int(page) - - def page_next(): - actions.key("l") - - def page_previous(): - actions.key("h") - - def page_jump(number: int): - actions.key("ctrl-g") - actions.sleep("100ms") - actions.insert(str(number)) - actions.key("enter") - - def page_final(): - actions.key("ctrl-end") diff --git a/talon/user/community/apps/okular/okular.talon b/talon/user/community/apps/okular/okular.talon deleted file mode 100644 index 8cfc62a..0000000 --- a/talon/user/community/apps/okular/okular.talon +++ /dev/null @@ -1,4 +0,0 @@ -app: okular -- -# Set tags -tag(): user.pages diff --git a/talon/user/community/apps/opera/opera.py b/talon/user/community/apps/opera/opera.py deleted file mode 100644 index 860d421..0000000 --- a/talon/user/community/apps/opera/opera.py +++ /dev/null @@ -1,18 +0,0 @@ -from talon import Module - -mod = Module() -apps = mod.apps -apps.opera = "app.name: Opera" -apps.opera = "app.name: Opera Internet Browser" -apps.opera = """ -os: mac -and app.bundle: com.operasoftware.Opera -""" -apps.opera = r""" -os: windows -and app.exe: /^opera\.exe$/i -""" -apps.opera = """ -os: linux -and app.exe: opera -""" diff --git a/talon/user/community/apps/opera/opera.talon b/talon/user/community/apps/opera/opera.talon deleted file mode 100644 index 86e6438..0000000 --- a/talon/user/community/apps/opera/opera.talon +++ /dev/null @@ -1,4 +0,0 @@ -app: opera -- -tag(): browser -tag(): user.tabs diff --git a/talon/user/community/apps/opera/opera_mac.py b/talon/user/community/apps/opera/opera_mac.py deleted file mode 100644 index 8f74e23..0000000 --- a/talon/user/community/apps/opera/opera_mac.py +++ /dev/null @@ -1,73 +0,0 @@ -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -os: mac -app: opera -""" - - -@ctx.action_class("user") -class UserActions: - def tab_duplicate(): - actions.browser.focus_address() - actions.sleep("180ms") - possibly_edited_url = actions.edit.selected_text() - actions.key("esc:2") - actions.browser.focus_address() - actions.sleep("180ms") - url_address = actions.edit.selected_text() - actions.user.paste(possibly_edited_url) - actions.app.tab_open() - actions.user.paste(url_address) - actions.key("enter") - - def tab_final(): - raise NotImplementedError( - "Opera doesn't have a default shortcut for this functionality but it can be configured" - ) - - def tab_close_wrapper(): - actions.sleep("180ms") - actions.app.tab_close() - - -@ctx.action_class("app") -class AppActions: - def tab_next(): - actions.key("cmd-alt-right") - - def tab_previous(): - actions.key("cmd-alt-left") - - -@ctx.action_class("browser") -class BrowserActions: - def bookmark_tabs(): - raise NotImplementedError("Opera doesn't support this functionality") - - def go_home(): - raise NotImplementedError("Opera doesn't support this functionality") - - def go_back(): - actions.browser.focus_page() - actions.next() - - def go_forward(): - actions.browser.focus_page() - actions.next() - - def show_downloads(): - actions.key("cmd-j") - - def show_extensions(): - actions.key("cmd-shift-e") - - def show_history(): - actions.key("cmd-shift-h") - - def focus_page(): - actions.key("cmd-alt-l") - - def reload_hard(): - actions.key("shift-5") diff --git a/talon/user/community/apps/opera/opera_win_linux.py b/talon/user/community/apps/opera/opera_win_linux.py deleted file mode 100644 index 6fca6d3..0000000 --- a/talon/user/community/apps/opera/opera_win_linux.py +++ /dev/null @@ -1,83 +0,0 @@ -from talon import Context, actions, app - -ctx = Context() -ctx.matches = r""" -os: windows -os: linux -app: opera -""" - - -@ctx.action_class("user") -class UserActions: - def tab_duplicate(): - actions.browser.focus_address() - actions.sleep("180ms") - possibly_edited_url = actions.edit.selected_text() - actions.key("esc:2") - actions.browser.focus_address() - actions.sleep("180ms") - url_address = actions.edit.selected_text() - actions.user.paste(possibly_edited_url) - actions.app.tab_open() - actions.user.paste(url_address) - actions.key("enter") - - def tab_jump(number: int): - if number < 9: - actions.key(f"ctrl-{number}") - - def tab_final(): - raise NotImplementedError( - "Opera doesn't have a default shortcut for this functionality but it can be configured" - ) - - def tab_close_wrapper(): - actions.sleep("180ms") - actions.app.tab_close() - - -@ctx.action_class("app") -class AppActions: - def tab_next(): - actions.key("ctrl-pagedown") - - def tab_previous(): - actions.key("ctrl-pageup") - - -@ctx.action_class("browser") -class BrowserActions: - def bookmarks_bar(): - raise NotImplementedError( - "Opera doesn't have a default shortcut for this functionality but it can be configured" - ) - - def bookmark_tabs(): - raise NotImplementedError("Opera doesn't support this functionality") - - def go_home(): - raise NotImplementedError("Opera doesn't support this functionality") - - def go_back(): - actions.browser.focus_page() - actions.next() - - def go_forward(): - actions.browser.focus_page() - actions.next() - - def bookmarks(): - actions.key("ctrl-shift-b") - - def show_downloads(): - actions.key("ctrl-j") - - def show_extensions(): - actions.key("ctrl-shift-e") - - def focus_page(): - actions.key("f9") - - def reload_hard(): - actions.key("shift-5") diff --git a/talon/user/community/apps/orion/orion.py b/talon/user/community/apps/orion/orion.py deleted file mode 100644 index c7db542..0000000 --- a/talon/user/community/apps/orion/orion.py +++ /dev/null @@ -1,45 +0,0 @@ -from talon import Context, Module, actions, ui -from talon.mac import applescript - -ctx = Context() -mod = Module() -apps = mod.apps -mod.apps.orion = """ -os: mac -app.bundle: com.kagi.kagimacOS -""" - -ctx.matches = r""" -app: orion -""" - - -@ctx.action_class("user") -class UserActions: - def browser_open_address_in_new_tab(): - actions.key("cmd-enter") - - -@ctx.action_class("browser") -class BrowserActions: - def bookmark_tabs(): - raise NotImplementedError("Orion doesn't have a default shortcut for this") - - def show_clear_cache(): - actions.key("cmd-alt-e") - - def reload_hard(): - actions.key("cmd-alt-r") - - def show_downloads(): - actions.key("cmd-alt-l") - - def show_extensions(): - actions.key("cmd-shift-x") - - -@mod.action_class -class Actions: - def overview_tabs(): - "Toggle tab overview in Orion" - actions.key("cmd-shift-\\") diff --git a/talon/user/community/apps/orion/orion.talon b/talon/user/community/apps/orion/orion.talon deleted file mode 100644 index e845d3e..0000000 --- a/talon/user/community/apps/orion/orion.talon +++ /dev/null @@ -1,6 +0,0 @@ -app: orion -- -tag(): browser -tag(): user.tabs - -tab overview [open | close]: user.overview_tabs() diff --git a/talon/user/community/apps/powershell/powershell_win.talon b/talon/user/community/apps/powershell/powershell_win.talon deleted file mode 100644 index d3e92f9..0000000 --- a/talon/user/community/apps/powershell/powershell_win.talon +++ /dev/null @@ -1,16 +0,0 @@ -os: windows -and app.name: Windows PowerShell -os: windows -and app.exe: powershell.exe -- -# makes the commands in terminal.talon available -tag(): terminal - -# activates the implementation of the commands/functions in terminal.talon -tag(): user.generic_windows_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 diff --git a/talon/user/community/apps/rstudio/rstudio_mac.talon b/talon/user/community/apps/rstudio/rstudio_mac.talon deleted file mode 100644 index 50d5803..0000000 --- a/talon/user/community/apps/rstudio/rstudio_mac.talon +++ /dev/null @@ -1,127 +0,0 @@ -os: mac -app: RStudio -- - -run that: key("cmd-enter") -run document: key("cmd-alt-r") -run from top: key("cmd-alt-b") -run to end: key("cmd-alt-e") -run (function | funk): key("cmd-alt-f") -run section: key("cmd-alt-t") -run previous chunks: key("cmd-alt-p") -run chunk: key("cmd-alt-c") -run next chunk: key("cmd-alt-n") -run all: key("cmd-shift-s") -run knitter: key("cmd-shift-k") -run profiler: key("cmd-shift-alt-p") - -# Moving around and formatting -jump back: key("cmd-f9") -jump forward: key("cmd-f10") -close all tabs: key("cmd-shift-w") -indent lines: key("cmd-i") -toggle comment: key("cmd-shift-c") -reformat comment: key("cmd-shift-/") -reformat R code: key("cmd-shift-a") -line up: key("alt-up") -line down: key("alt-down") -duplicate line up: key("cmd-alt-up") -duplicate line [down]: key("cmd-alt-down") -select to paren: key("ctrl-shift-e") -select to matching paren: key("ctrl-shift-alt-e") -jump to matching: key("ctrl-p") -expand selection: key("shift-alt-cmd-up") -reduce selection: key("shift-alt-cmd-down") -add cursor up: key("ctrl-alt-up") -add cursor down: key("ctrl-alt-down") -move active cursor up: key("ctrl-alt-shift-up") -move active cursor down: key("ctrl-alt-shift-down") -delete line: key("cmd-d") -delete word left: key("alt-backspace") -delete word right: key("alt-delete") -assign that: key("alt--") -pipe that: key("cmd-shift-m") -insert knitter chunk: key("cmd-alt-i") - -# Folding -fold that: key("cmd-alt-l") -unfold that: key("cmd-shift-alt-l") -fold all: key("cmd-alt-o") -unfold all: key("cmd-shift-alt-o") - -# Find and replace -find and replace: key("cmd-f") -find next: key("cmd-g") -find previous: key("cmd-shift-g") -find with selection: key("cmd-e") -find in files: key("cmd-shift-f") -run replace: key("cmd-shift-j") -run spell check: key("f7") - -# Navigation and panels -go to source: key("ctrl-1") -go to console: key("ctrl-2") -go to help: key("ctrl-3") -go to history: key("ctrl-4") -go to files: key("ctrl-5") -go to (plots | plot): key("ctrl-6") -go to packages: key("ctrl-7") -go to environment: key("ctrl-8") -go to git: key("ctrl-9") -go to build: key("ctrl-0") -go to terminal: key("alt-shift-t") -go to omni: key("ctrl-.") -go to line: key("cmd-shift-alt-g") -go to section: key("cmd-shift-alt-j") -go to tab: key("ctrl-shift-.") -go to previous tab: key("ctrl-f11") -go to next tab: key("ctrl-f12") -go to first tab: key("ctrl-shift-f11") -go to last tab: key("ctrl-shift-f12") - -zoom source: key("ctrl-shift-1") -(zoom | show) all: key("ctrl-shift-0") - -help that: key("f1") -define that: key("f2") -previous plot: key("cmd-alt-f11") -next plot: key("cmd-alt-f12") - -# devtools, package development, and session management -restart R session: key("cmd-shift-f10") -dev tools build: key("cmd-shift-b") -dev tools load all: key("cmd-shift-l") -dev tools test: key("cmd-shift-t") -dev tools check: key("cmd-shift-e") -dev tools document: key("cmd-shift-d") - -# Debugging -toggle breakpoint: key("shift-f9") -debug next: key("f10") -debug step into (function | funk): key("shift-f4") -debug finish (function | funk): key("shift-f6") -debug continue: key("shift-f5") -debug stop: key("shift-f8") - -# Git/SVN -run git diff: key("ctrl-alt-d") -run git commit: key("ctrl-alt-m") - -# Other shortcuts that could be enabled -# run line and stay: key("alt-enter") -# run and echo all: key("cmd-shift-enter") -# extract (function|funk): key("cmd-alt-x") -# extract variable: key("cmd-alt-v") -# new terminal: key("shift-alt-t") -# rename current terminal: key("shift-alt-r") -# clear current terminal: key("ctrl-shift-l") -# previous terminal: key("ctrl-alt-f11") -# next terminal: key("ctrl-alt-f12") -# clear console: key("ctrl-l") -# popup history: key("cmd-up") -# change working directory: key("ctrl-shift-h") -# new document: key("cmd-shift-n") -# new document (chrome only): key("cmd-shift-alt-n") -# insert code section: key("cmd-shift-r") -# scroll diff view: key("ctrl-up/down") -# sync editor & pdf preview: key("cmd-f8") diff --git a/talon/user/community/apps/safari/safari.py b/talon/user/community/apps/safari/safari.py deleted file mode 100644 index ea60f81..0000000 --- a/talon/user/community/apps/safari/safari.py +++ /dev/null @@ -1,66 +0,0 @@ -from talon import Context, Module, actions, ui -from talon.mac import applescript - -ctx = Context() -mod = Module() -apps = mod.apps -mod.apps.safari = """ -os: mac -app.bundle: com.apple.Safari -app.bundle: com.apple.SafariTechnologyPreview -""" - -ctx.matches = r""" -app: safari -""" - - -@ctx.action_class("user") -class UserActions: - def browser_open_address_in_new_tab(): - actions.key("cmd-enter") - - -@ctx.action_class("browser") -class BrowserActions: - def address() -> str: - try: - window = ui.active_app().windows()[0] - except IndexError: - return "" - try: - toolbar = window.children.find_one(AXRole="AXToolbar", max_depth=0) - address_field = toolbar.children.find_one( - AXRole="AXTextField", - AXIdentifier="WEB_BROWSER_ADDRESS_AND_SEARCH_FIELD", - ) - address = address_field.AXValue - except (ui.UIErr, AttributeError): - address = applescript.run( - f""" - tell application id "{actions.app.bundle()}" - with timeout of 0.1 seconds - if not (exists (window 1)) then return "" - return window 1's current tab's URL - end timeout - end tell - """ - ) - return address - - def bookmark_tabs(): - raise NotImplementedError( - "Safari doesn't have a default shortcut for this functionality but it can be configured" - ) - - def show_clear_cache(): - raise NotImplementedError("Safari doesn't support this functionality") - - def reload_hard(): - actions.key("cmd-alt-r") - - def show_downloads(): - actions.key("cmd-alt-l") - - def show_extensions(): - actions.key("cmd-, tab:8 space") diff --git a/talon/user/community/apps/safari/safari.talon b/talon/user/community/apps/safari/safari.talon deleted file mode 100644 index 303d31d..0000000 --- a/talon/user/community/apps/safari/safari.talon +++ /dev/null @@ -1,4 +0,0 @@ -app: safari -- -tag(): browser -tag(): user.tabs diff --git a/talon/user/community/apps/slack/slack.py b/talon/user/community/apps/slack/slack.py deleted file mode 100644 index 7fd57ca..0000000 --- a/talon/user/community/apps/slack/slack.py +++ /dev/null @@ -1,30 +0,0 @@ -from talon import Context, Module, actions - -ctx = Context() -mod = Module() -apps = mod.apps -apps.slack = "app.name: Slack" -mod.apps.slack = r""" -os: windows -and app.name: Slack -os: windows -and app.exe: /^slack\.exe$/i -""" -apps.slack = """ -os: mac -and app.bundle: com.tinyspeck.slackmacgap -""" -apps.slack = """ -tag: browser -browser.host: app.slack.com -""" -ctx.matches = r""" -app: slack -""" - - -@ctx.action_class("edit") -class EditActions: - def line_insert_down(): - actions.edit.line_end() - actions.key("shift-enter") diff --git a/talon/user/community/apps/slack/slack_mac.py b/talon/user/community/apps/slack/slack_mac.py deleted file mode 100644 index d82626b..0000000 --- a/talon/user/community/apps/slack/slack_mac.py +++ /dev/null @@ -1,44 +0,0 @@ -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -os: mac -app: slack -""" - - -@ctx.action_class("user") -class UserActions: - def messaging_workspace_previous(): - actions.key("cmd-shift-[") - - def messaging_workspace_next(): - actions.key("cmd-shift-]") - - def messaging_open_channel_picker(): - actions.key("cmd-k") - - 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 messaging_open_search(): - actions.key("cmd-f") - - def messaging_mark_workspace_read(): - actions.key("shift-esc") - - def messaging_mark_channel_read(): - actions.key("esc") - - # Files and Snippets - def messaging_upload_file(): - actions.key("cmd-u") diff --git a/talon/user/community/apps/slack/slack_win.py b/talon/user/community/apps/slack/slack_win.py deleted file mode 100644 index e6b7bc8..0000000 --- a/talon/user/community/apps/slack/slack_win.py +++ /dev/null @@ -1,46 +0,0 @@ -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -os: windows -os: linux -app: slack -""" - - -@ctx.action_class("user") -class UserActions: - def messaging_workspace_previous(): - actions.key("ctrl-shift-tab") - - def messaging_workspace_next(): - actions.key("ctrl-tab") - - def messaging_open_channel_picker(): - actions.key("ctrl-k") - - 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") - - # (go | undo | toggle) full: key(ctrl-cmd-f) - def messaging_open_search(): - actions.key("ctrl-f") - - def messaging_mark_workspace_read(): - actions.key("shift-esc") - - def messaging_mark_channel_read(): - actions.key("esc") - - # Files and Snippets - def messaging_upload_file(): - actions.key("ctrl-u") diff --git a/talon/user/community/apps/stata/stata.py b/talon/user/community/apps/stata/stata.py deleted file mode 100644 index 9223987..0000000 --- a/talon/user/community/apps/stata/stata.py +++ /dev/null @@ -1,21 +0,0 @@ -from talon import Context, Module - -mod = Module() -ctx = Context() - -mod.apps.stata = r""" -os: windows -and app.name: Stata -os: windows -and app.exe: /^statase\-64\.exe$/i -""" - -ctx.matches = r""" -app: stata -""" - - -@ctx.action_class("code") -class CodeActions: - def language(): - return "stata" diff --git a/talon/user/community/apps/stata/stata_do_file_editor.talon b/talon/user/community/apps/stata/stata_do_file_editor.talon deleted file mode 100644 index 0362b7b..0000000 --- a/talon/user/community/apps/stata/stata_do_file_editor.talon +++ /dev/null @@ -1,25 +0,0 @@ -# Commands for the Stata Do-File Editor -os: windows -app: stata -win.title: /^Do-file Editor/ -- -do this: key(ctrl-d) - -do line: - edit.select_line() - key(ctrl-d) - -do (all | file): - edit.select_all() - edit.copy() - key(ctrl-d) - -do way up: - edit.extend_file_start() - edit.copy() - key(ctrl-d) - -do way down: - edit.extend_file_end() - edit.copy() - key(ctrl-d) diff --git a/talon/user/community/apps/sumatrapdf/sumatrapdf.py b/talon/user/community/apps/sumatrapdf/sumatrapdf.py deleted file mode 100644 index 8dfd023..0000000 --- a/talon/user/community/apps/sumatrapdf/sumatrapdf.py +++ /dev/null @@ -1,71 +0,0 @@ -from talon import Context, Module, actions - -# --- App definition --- -mod = Module() -mod.apps.sumatrapdf = """ -os: windows -and app.name: SumatraPDF -os: windows -and app.exe: /^sumatrapdf\.exe$/i -""" - -# Context matching -ctx = Context() -ctx.matches = """ -app: sumatrapdf -""" - - -# --- Implement actions --- -@ctx.action_class("app") -class app_actions: - # app.tabs - def tab_open(): - actions.key("ctrl-o") - - -@ctx.action_class("edit") -class EditActions: - def zoom_in(): - actions.key("+") - - def zoom_out(): - actions.key("-") - - -@ctx.action_class("user") -class UserActions: - # user.pages - def page_current(): - actions.key("ctrl-g") - 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-g") - actions.insert(str(number)) - actions.key("enter") - - def page_final(): - actions.key("end") - - def page_rotate_right(): - actions.key("shift-ctrl-keypad_plus") - - def page_rotate_left(): - actions.key("shift-ctrl-keypad_minus") - - # user.tabs - def tab_jump(number: int): - if number < 9: - actions.key(f"alt-{number}") - - def tab_final(): - actions.key("alt-9") diff --git a/talon/user/community/apps/sumatrapdf/sumatrapdf.talon b/talon/user/community/apps/sumatrapdf/sumatrapdf.talon deleted file mode 100644 index 127e57c..0000000 --- a/talon/user/community/apps/sumatrapdf/sumatrapdf.talon +++ /dev/null @@ -1,5 +0,0 @@ -app: sumatrapdf -- -# Set tags -tag(): user.pages -tag(): user.tabs diff --git a/talon/user/community/apps/talon/talon_debug_window/talon_debug_window.py b/talon/user/community/apps/talon/talon_debug_window/talon_debug_window.py deleted file mode 100644 index 664bdf0..0000000 --- a/talon/user/community/apps/talon/talon_debug_window/talon_debug_window.py +++ /dev/null @@ -1,16 +0,0 @@ -# this functionality is only available in the talon beta -from talon import Module - -mod = Module() -mod.apps.talon_debug_window = """ -os: mac -and app.bundle: com.talonvoice.Talon -win.title: Talon Debug -""" -mod.apps.talon_debug_window = """ -os: windows -and app.name: Talon -os: windows -and app.exe: talon.exe -win.title: Talon Debug -""" diff --git a/talon/user/community/apps/talon/talon_debug_window/talon_debug_window.talon b/talon/user/community/apps/talon/talon_debug_window/talon_debug_window.talon deleted file mode 100644 index 0c44a6e..0000000 --- a/talon/user/community/apps/talon/talon_debug_window/talon_debug_window.talon +++ /dev/null @@ -1,24 +0,0 @@ -# this functionality is only available in the talon beta -# note: these commands are only useful when the search box is focused -app: talon_debug_window -- -# uncomment user.talon_populate_lists tag to activate talon-specific lists of actions, scopes, modes etcetera. -# Do not enable this tag with dragon, as it will be unusable. -# with conformer, the latency increase may also be unacceptable depending on your cpu -# see https://github.com/talonhub/community/issues/600 -# tag(): user.talon_populate_lists - -tag {user.talon_tags}: "{talon_tags}" - -#commands for dictating key combos -key over: "{keys}" -key over: "{modifiers}" - -action {user.talon_actions}: "{talon_actions}" -# requires user.talon_populate_lists tag. do not use with dragon -list {user.talon_lists}: "{talon_lists}" - -# requires user.talon_populate_lists tag. do not use with dragon -capture {user.talon_captures}: "{talon_captures}" -set {user.talon_settings}: "{talon_settings}" -application {user.talon_apps}: "{talon_apps}" diff --git a/talon/user/community/apps/talon/talon_repl/talon_repl.py b/talon/user/community/apps/talon/talon_repl/talon_repl.py deleted file mode 100644 index ca7a968..0000000 --- a/talon/user/community/apps/talon/talon_repl/talon_repl.py +++ /dev/null @@ -1,19 +0,0 @@ -from talon import Context, Module - -mod = Module() -mod.apps.talon_repl = r""" -win.title: /Talon - REPL/ -win.title: /.talon\/bin\/repl/ -""" - -ctx = Context() -ctx.matches = r""" -app: talon_repl -not tag: user.code_language_forced -""" - - -@ctx.action_class("code") -class CodeActions: - def language(): - return "python" diff --git a/talon/user/community/apps/talon/talon_repl/talon_repl.talon b/talon/user/community/apps/talon/talon_repl/talon_repl.talon deleted file mode 100644 index 4f0fd51..0000000 --- a/talon/user/community/apps/talon/talon_repl/talon_repl.talon +++ /dev/null @@ -1,56 +0,0 @@ -app: talon_repl -- -tag(): user.talon_python -tag(): user.readline - -# uncomment user.talon_populate_lists tag to activate talon-specific lists of actions, scopes, modes etcetera. -# Do not enable this tag with dragon, as it will be unusable. -# with conformer, the latency increase may also be unacceptable depending on your cpu -# see https://github.com/talonhub/community/issues/600 -# tag(): user.talon_populate_lists - -^test last$: - phrase = user.history_get(1) - command = "sim('{phrase}')" - insert(command) - key(enter) -^test $: - insert("sim('{phrase}')") - key(enter) -^test numb $: - phrase = user.history_get(number_small) - command = "sim('{phrase}')" - #to do: shouldn't this work? - #user.paste("sim({phrase})") - insert(command) - key(enter) -# requires user.talon_populate_lists tag. do not use with dragon -^debug action {user.talon_actions}$: - insert("actions.find('{user.talon_actions}')") - key(enter) -# requires user.talon_populate_lists tag. do not use with dragon -^debug list {user.talon_lists}$: - insert("actions.user.talon_pretty_print(registry.lists['{talon_lists}'])") - key(enter) -^debug tags$: - insert("actions.user.talon_pretty_print(registry.tags)") - key(enter) -^debug settings$: - insert("actions.user.talon_pretty_print(registry.settings)") - key(enter) -^debug modes$: - insert("actions.user.talon_pretty_print(scope.get('mode'))") - key(enter) -# requires user.talon_populate_lists tag. do not use with dragon -^debug scope {user.talon_scopes}$: - insert("actions.user.talon_pretty_print(scope.get('{talon_scopes}'))") - key(enter) -^debug running apps$: - insert("actions.user.talon_pretty_print(ui.apps(background=False))") - key(enter) -^debug all windows$: - insert("actions.user.talon_pretty_print(ui.windows())") - key(enter) -^debug {user.running} windows$: - insert("actions.user.talon_debug_app_windows('{running}')") - key(enter) diff --git a/talon/user/community/apps/teams/teams_linux.py b/talon/user/community/apps/teams/teams_linux.py deleted file mode 100644 index a2647a5..0000000 --- a/talon/user/community/apps/teams/teams_linux.py +++ /dev/null @@ -1,22 +0,0 @@ -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -os: linux -app: microsoft_teams -""" - - -@ctx.action_class("edit") -class EditActions: - # zoom in: key(ctrl-=) - # zoom out: key(ctrl--) - # reset zoom: key(ctrl-0) - def zoom_in(): - actions.key("ctrl-=") - - def zoom_out(): - actions.key("ctrl--") - - def zoom_reset(): - actions.key("ctrl-0") diff --git a/talon/user/community/apps/teams/teams_mac.talon b/talon/user/community/apps/teams/teams_mac.talon deleted file mode 100644 index 6b3f4b6..0000000 --- a/talon/user/community/apps/teams/teams_mac.talon +++ /dev/null @@ -1,74 +0,0 @@ -os: mac -app: com.microsoft.teams -- - -open history: key(super-shift-h) -view shortcuts: key(super-.) -show shortcuts: key(super-.) -[go] [to] search: key(super-e) -show commands: key(super-/) -open filter: key(super-shift-f) -go to: key(super-g) -open (apps | applications): key(ctrl-`) -[start] new chat: key(super-n) -open settings: key(super-,) -open help: key(f1) -close: key(escape) -reset: - key(escape) - key(escape) - key(escape) - key(escape) -zoom reset: key(super-0) - -# navigations -open (act | activity): key(super-1) -open chat: key(super-2) -open teams: key(super-3) -open calendar: key(super-4) -open calls: key(super-5) -open files: key(super-6) -[go] [to] (prev | previous) [list] item: key(alt-up) -[go] [to] next [list] item: key(alt-down) -move [selected] team up: key(super-shift-up) -move [selected] team down: key(super-shift-down) -[go] [to] (prev | previous) section: key(super-shift-f6) -[go] [to] next section: key(super-f6) - -# messaging -[go] [to] compose [box]: key(shift-alt-c) -[expand] compose [box]: key(super-shift-x) -send message: key(super-enter) -attach file: key(shift-alt-o) -attach local file: - key(shift-alt-o) - sleep(100ms) - key(down) - key(space) -[start] new line: key(shift-enter) -reply [to] [thread]: key(shift-alt-r) - -# Meetings, Calls and Calendar -accept video call: key(super-shift-a) -accept audio call: key(super-shift-s) -decline call: key(super-shift-d) -start audio call: key(super-shift-c) -start video call: key(super-shift-u) -toggle mute: key(super-shift-m) -starch screen share session: key(super-shift-e) -toggle video: key(super-shift-o) -[go] [to] sharing toolbar: key(super-shift-space) -decline screen share: key(super-shift-d) -accept screen share: key(super-shift-a) -schedule [a] meeting: key(alt-shift-n) -go to current time: key(alt-.) -go to (prev | previous) (day | week): key(super-alt-left) -go to next (day | week): key(super-alt-right) -view day: key(super-alt-1) -view work week: key(super-alt-2) -view week: key(super-alt-3) -(save | send) meeting request: key(super-s) -join [from] meeting [details]: key(alt-shift-j) -go to suggested time: key(alt-shift-s) -(raise | lower) hand: key(super-shift-k) -leave team meeting: key(super-shift-h) diff --git a/talon/user/community/apps/terminator/terminator_linux.py b/talon/user/community/apps/terminator/terminator_linux.py deleted file mode 100644 index 1b258db..0000000 --- a/talon/user/community/apps/terminator/terminator_linux.py +++ /dev/null @@ -1,119 +0,0 @@ -from talon import Context, Module, actions - -# App definition -mod = Module() -mod.apps.terminator = """ -os: linux -and app.exe: terminator -os: linux -and app.name: Terminator -""" - -# Context matching -ctx = Context() -ctx.matches = r""" -app: terminator -""" -ctx.tags = [ - "terminal", - "user.tabs", - "user.splits", - "user.generic_unix_shell", - "user.git", - "user.kubectl", -] - - -# --- Implement actions --- -@ctx.action_class("user") -class user_actions: - # user.splits - def split_window_right(): - actions.key("alt-right") - - def split_window_left(): - actions.key("alt-left") - - def split_window_down(): - actions.key("alt-down") - - def split_window_up(): - actions.key("alt-up") - - def split_window_vertically(): - actions.key("shift-ctrl-e") - - def split_window_horizontally(): - actions.key("shift-ctrl-o") - - def split_flip(): - actions.key("super-r") - - def split_maximize(): - actions.key("shift-ctrl-x") - - def split_reset(): - actions.key("shift-ctrl-x") - - def split_window(): - actions.key("shift-ctrl-o") - - def split_clear(): - actions.key("shift-ctrl-r") - - def split_clear_all(): - actions.key("shift-ctrl-g") - - def split_next(): - actions.key("shift-ctrl-n") - - def split_last(): - actions.key("shift-ctrl-p") - - -@ctx.action_class("app") -class AppActions: - # 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-i") - - 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") diff --git a/talon/user/community/apps/terraform/terraform.py b/talon/user/community/apps/terraform/terraform.py deleted file mode 100644 index 30b956b..0000000 --- a/talon/user/community/apps/terraform/terraform.py +++ /dev/null @@ -1,4 +0,0 @@ -from talon import Module - -mod = Module() -mod.tag("terraform_client", desc="tag for enabling terraform commands in your terminal") diff --git a/talon/user/community/apps/terraform/terraform.talon b/talon/user/community/apps/terraform/terraform.talon deleted file mode 100644 index 1b63123..0000000 --- a/talon/user/community/apps/terraform/terraform.talon +++ /dev/null @@ -1,15 +0,0 @@ -tag: terminal -and tag: user.terraform_client -- -terraform: "terraform " - -terraform apply: "terraform apply " -terraform destroy: "terraform destroy " -terraform format recursive: "terraform fmt -recursive\n" -terraform format: "terraform fmt\n" -terraform help: "terraform -help" -terraform init upgrade: "terraform init -upgrade\n" -terraform init: "terraform init\n" -terraform plan: "terraform plan\n" -terraform state move: "terraform state mv " -terraform validate: "terraform validate\n" diff --git a/talon/user/community/apps/thunderbird/thunderbird.py b/talon/user/community/apps/thunderbird/thunderbird.py deleted file mode 100644 index 289b728..0000000 --- a/talon/user/community/apps/thunderbird/thunderbird.py +++ /dev/null @@ -1,97 +0,0 @@ -from talon import Context, Module - -# --- App definitions --- -# Main app TODO: mac context -mod = Module() -mod.apps.thunderbird = r""" -os: windows -and app.name: Thunderbird -os: windows -and app.exe: /^thunderbird\.exe$/i -""" -mod.apps.thunderbird = """ -os: linux -and app.name: Thunderbird -""" - -# Inbox tab TODO: also matches emails opened in new tab -mod.apps.thunderbird_inbox = """ -app: thunderbird -title: /@/ -""" - -# Calendar tab (lightning) -months = [ - "January", # English - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - "Januar", # German - "Februar", - "März", - "Mai", - "Juni", - "Juli", - "Oktober", - "Dezember", -] -mod.apps.thunderbird_calendar = f""" -app: thunderbird -title: Calendar - Mozilla Thunderbird -title: Kalender - Mozilla Thunderbird -title: /({"|".join(map(lambda m: m + " ", months))})/ -""" - -# Tasks tab -mod.apps.thunderbird_tasks = """ -app: thunderbird -title: Tasks - Mozilla Thunderbird -title: Aufgaben - Mozilla Thunderbird -""" - -# Mail composer window -mod.apps.thunderbird_composer = """ -app: thunderbird -title: /Write: / -title: /Verfassen: / -""" - -# Address book popup window -mod.apps.thunderbird_contacts = """ -app: thunderbird -title: Address Book -title: Adressbuch -""" - -# Context matching -ctx = Context() -ctx.matches = r""" -app: thunderbird -""" - - -# --- Define actions --- -@mod.action_class -class UserActions: - def thunderbird_mod(keys: str): - """Press keys with modifier ctrl or cmd""" - - def thunderbird_calendar_view(number: int): - """Select between calendar view tabs""" - - -# --- Implement actions --- -@ctx.action_class("app") -class AppActions: - # app.tabs - # not possible in thunderbird - def tab_open(): - pass diff --git a/talon/user/community/apps/thunderbird/thunderbird.talon b/talon/user/community/apps/thunderbird/thunderbird.talon deleted file mode 100644 index 4586eff..0000000 --- a/talon/user/community/apps/thunderbird/thunderbird.talon +++ /dev/null @@ -1,14 +0,0 @@ -app: thunderbird -and not app: thunderbird_contacts -and not app: thunderbird_composer -- -# Set tags -tag(): user.tabs - -# navigate tabs -go (mails | messages | inbox): user.tab_jump(1) -go (calendar | lightning): user.thunderbird_mod("shift-c") -go tasks: user.thunderbird_mod("shift-d") -# open windows -(open address [book] | address book | open contacts): user.thunderbird_mod("shift-b") -dev tools: user.thunderbird_mod("shift-i") diff --git a/talon/user/community/apps/thunderbird/thunderbird_calendar.talon b/talon/user/community/apps/thunderbird/thunderbird_calendar.talon deleted file mode 100644 index 0f66aef..0000000 --- a/talon/user/community/apps/thunderbird/thunderbird_calendar.talon +++ /dev/null @@ -1,13 +0,0 @@ -app: thunderbird_calendar -- -# event/task -event new: user.thunderbird_mod("i") -task new: user.thunderbird_mod("d") -(task | event) delete: key(delete) -# layout -toggle today: key(f11) -view : user.thunderbird_calendar_view(number_small) -view day: user.thunderbird_calendar_view(1) -view week: user.thunderbird_calendar_view(2) -view multi [week]: user.thunderbird_calendar_view(3) -view month: user.thunderbird_calendar_view(4) diff --git a/talon/user/community/apps/thunderbird/thunderbird_composer.talon b/talon/user/community/apps/thunderbird/thunderbird_composer.talon deleted file mode 100644 index 3863373..0000000 --- a/talon/user/community/apps/thunderbird/thunderbird_composer.talon +++ /dev/null @@ -1,15 +0,0 @@ -app: thunderbird_composer -- -# mail -(draft | mail | message) save: user.thunderbird_mod("s") -(draft | mail | message) print: user.thunderbird_mod("p") -(draft | mail | message) send: user.thunderbird_mod("enter") -# layout -toggle contacts: key(f9) -# navigation -go (inbox | thunderbird | main): user.thunderbird_mod("1") -# edit -cite paste: user.thunderbird_mod("shift-o") -(unformatted | raw) paste: user.thunderbird_mod("shift-v") -link new: user.thunderbird_mod("k") -link delete: user.thunderbird_mod("shift-k") diff --git a/talon/user/community/apps/thunderbird/thunderbird_contacts.talon b/talon/user/community/apps/thunderbird/thunderbird_contacts.talon deleted file mode 100644 index 74e2600..0000000 --- a/talon/user/community/apps/thunderbird/thunderbird_contacts.talon +++ /dev/null @@ -1,9 +0,0 @@ -app: thunderbird_contacts -- -contact new: user.thunderbird_mod("n") -contact edit: user.thunderbird_mod("i") -contact delete: key(delete) -contact print: user.thunderbird_mod("p") -contact message: user.thunderbird_mod("m") -contact up: key(up) -contact down: key(down) diff --git a/talon/user/community/apps/thunderbird/thunderbird_inbox.talon b/talon/user/community/apps/thunderbird/thunderbird_inbox.talon deleted file mode 100644 index 3f5c932..0000000 --- a/talon/user/community/apps/thunderbird/thunderbird_inbox.talon +++ /dev/null @@ -1,29 +0,0 @@ -app: thunderbird_inbox -- -# navigate -(mail | message) open: key(enter) -(mail | message) (up | last): key(b) -(mail | message) (down | next): key(f) -unread [mail | message] (up | last): key(p) -unread [mail | message] (down | next): key(n) -go home: key(alt-home) -toggle (mail | message) [pane]: key(f8) -# mark -(mail | message) (favorite | unfavorite): key(s) -(mail | message) (read | unread): key(m) -(mail | message) (watch | unwatch): key(w) -(mail | message) (ignore | unignore): key(k) -(mail | message) suspend: key(c) -(mail | message) spam: key(j) -# send -(mail | message) new: user.thunderbird_mod("n") -(mail | message) edit: user.thunderbird_mod("e") -(mail | message) reply sender: user.thunderbird_mod("r") -(mail | message) reply all: user.thunderbird_mod("shift-r") -(mail | message) reply list: user.thunderbird_mod("shift-l") -(mail | message) forward: user.thunderbird_mod("l") -# organize -(mail | message) delete: key(delete) -(mail | message) archive: key(a) -(mail | message) save: user.thunderbird_mod("s") -(mail | message) print: user.thunderbird_mod("p") diff --git a/talon/user/community/apps/thunderbird/thunderbird_linux.py b/talon/user/community/apps/thunderbird/thunderbird_linux.py deleted file mode 100644 index a892081..0000000 --- a/talon/user/community/apps/thunderbird/thunderbird_linux.py +++ /dev/null @@ -1,34 +0,0 @@ -from talon import Context, actions - -# Context matching -ctx = Context() -ctx.matches = r""" -os: linux -app: thunderbird -""" - - -# --- Implement actions --- -@ctx.action_class("app") -class AppActions: - # app.tabs - def tab_reopen(): - actions.key("ctrl-shift-t") # only works from inbox tab - - -@ctx.action_class("user") -class UserActions: - # user.tabs - def tab_jump(number: int): - if number <= 9: - actions.key(f"alt-{number}") - - def tab_final(): - actions.key("alt-9") - - # custom actions - def thunderbird_mod(keys: str): - actions.key(f"ctrl-{keys}") - - def thunderbird_calendar_view(number: int): - actions.key(f"ctrl-{number}") diff --git a/talon/user/community/apps/thunderbird/thunderbird_mac.py b/talon/user/community/apps/thunderbird/thunderbird_mac.py deleted file mode 100644 index 095d28f..0000000 --- a/talon/user/community/apps/thunderbird/thunderbird_mac.py +++ /dev/null @@ -1,34 +0,0 @@ -from talon import Context, actions - -# Context matching -ctx = Context() -ctx.matches = r""" -os: mac -app: thunderbird -""" - - -# --- Implement actions --- -@ctx.action_class("app") -class AppActions: - # app.tabs - def tab_reopen(): - actions.key("cmd-shift-t") # only works from inbox tab - - -@ctx.action_class("user") -class UserActions: - # user.tabs - def tab_jump(number: int): - if number <= 9: - actions.key(f"cmd-{number}") - - def tab_final(): - actions.key("cmd-9") - - # custom actions - def thunderbird_mod(keys: str): - actions.key(f"cmd-{keys}") - - def thunderbird_calendar_view(number: int): - actions.key(f"alt-{number}") diff --git a/talon/user/community/apps/thunderbird/thunderbird_tasks.talon b/talon/user/community/apps/thunderbird/thunderbird_tasks.talon deleted file mode 100644 index ff442ab..0000000 --- a/talon/user/community/apps/thunderbird/thunderbird_tasks.talon +++ /dev/null @@ -1,8 +0,0 @@ -app: thunderbird_tasks -- -# event/task -event new: user.thunderbird_mod("i") -task new: user.thunderbird_mod("d") -(task | event) delete: key(delete) -# layout -toggle today: key(f11) diff --git a/talon/user/community/apps/thunderbird/thunderbird_win.py b/talon/user/community/apps/thunderbird/thunderbird_win.py deleted file mode 100644 index 5f4880d..0000000 --- a/talon/user/community/apps/thunderbird/thunderbird_win.py +++ /dev/null @@ -1,34 +0,0 @@ -from talon import Context, actions - -# Context matching -ctx = Context() -ctx.matches = r""" -os: windows -app: thunderbird -""" - - -# --- Implement actions --- -@ctx.action_class("app") -class AppActions: - # app.tabs - def tab_reopen(): - actions.key("ctrl-shift-t") # only works from inbox tab - - -@ctx.action_class("user") -class UserActions: - # user.tabs - def tab_jump(number: int): - if number <= 9: - actions.key(f"ctrl-{number}") - - def tab_final(): - actions.key("ctrl-9") - - # custom actions - def thunderbird_mod(keys: str): - actions.key(f"ctrl-{keys}") - - def thunderbird_calendar_view(number: int): - actions.key(f"alt-{number}") diff --git a/talon/user/community/apps/tmux/tmux.py b/talon/user/community/apps/tmux/tmux.py deleted file mode 100644 index 1f2114a..0000000 --- a/talon/user/community/apps/tmux/tmux.py +++ /dev/null @@ -1,123 +0,0 @@ -from talon import Context, Module, actions, settings - -mod = Module() - -mod.apps.tmux = """ -tag: terminal -and tag: user.tmux -""" - -mod.setting( - "tmux_prefix_key", - type=str, - default="ctrl-b", - desc="The key used to prefix all tmux commands", -) - - -@mod.action_class -class TmuxActions: - def tmux_prefix(): - """press control and the configured tmux prefix key""" - actions.key(settings.get("user.tmux_prefix_key")) - - def tmux_keybind(key: str): - """press tmux prefix followed by a key bind""" - actions.user.tmux_prefix() - actions.key(key) - - def tmux_enter_command(command: str = ""): - """Enter tmux command mode and optionally insert a command without executing it.""" - actions.user.tmux_keybind(":") - actions.insert(command) - - def tmux_execute_command(command: str): - """execute tmux command""" - actions.user.tmux_enter_command(command) - actions.key("enter") - actions.sleep("100ms") - - def tmux_execute_command_with_confirmation(command: str, confirmation_prompt: str): - """execute tmux command with confirm-before""" - actions.user.tmux_execute_command( - f'confirm-before -p "{confirmation_prompt} (y/n)" {command}' - ) - actions.key("\n") - - -ctx = Context() -ctx.matches = "app: tmux" - - -@ctx.action_class("app") -class AppActions: - def tab_open(): - actions.user.tmux_execute_command("new-window") - - def tab_next(): - actions.user.tmux_execute_command("select-window -n") - - def tab_previous(): - actions.user.tmux_execute_command("select-window -p") - - -@ctx.action_class("user") -class UserActions: - def tab_jump(number: int): - if number < 10: - actions.user.tmux_keybind(f"{number}") - else: - actions.user.tmux_execute_command(f"select-window -t {number}") - - def tab_close_wrapper(): - actions.user.tmux_execute_command_with_confirmation( - "kill-window", "kill-window #W?" - ) - - def split_window_right(): - actions.user.split_window_horizontally() - actions.user.tmux_execute_command("swap-pane -U -s #P") - - def split_window_left(): - actions.user.split_window_horizontally() - - def split_window_down(): - actions.user.split_window_vertically() - actions.user.tmux_execute_command("swap-pane -U -s #P") - - def split_window_up(): - actions.user.split_window_vertically() - - def split_flip(): - actions.user.tmux_execute_command("next-layout") - - def split_window_vertically(): - actions.user.tmux_execute_command("split-pane") - - def split_window_horizontally(): - actions.user.tmux_execute_command("split-pane -h") - - def split_maximize(): - # toggle the maximization because zooming when already zoomed is pointless - actions.user.tmux_execute_command("resize-pane -Z") - - def split_reset(): - actions.user.tmux_execute_command("resize-pane -Z") - - def split_window(): - actions.user.split_window_horizontally() - - def split_clear(): - actions.user.tmux_execute_command_with_confirmation( - "kill-pane", "kill-pane #P?" - ) - - def split_next(): - # select-pane doesn't seem to support the prefix-o behavior - actions.user.tmux_keybind("o") - - def split_last(): - actions.user.tmux_execute_command("select-pane -l") - - def split_number(index: int): - actions.user.tmux_execute_command(f"select-pane -t {index}") diff --git a/talon/user/community/apps/tmux/tmux.talon b/talon/user/community/apps/tmux/tmux.talon deleted file mode 100644 index 5424f87..0000000 --- a/talon/user/community/apps/tmux/tmux.talon +++ /dev/null @@ -1,20 +0,0 @@ -app: tmux -- -tag(): user.splits -tag(): user.tabs - -# Note that you will need to add something to match the tmux app in your configuration -# This is not active by default -# Adding a file with a matcher for detecting tmux active in your terminal and activating -# the tmux tag is required -# Something like: -# -# title: /^tmux/ -# - -# tag(): user.tmux - -# pane management - these commands use the word split to match with the splits -# tag defined in tags/splits/splits.talon -go split : user.tmux_keybind(arrow_key) -#Say a number after this command to switch to pane -go split: user.tmux_execute_command("display-panes -d 0") diff --git a/talon/user/community/apps/visualstudio/visual_studio_win.py b/talon/user/community/apps/visualstudio/visual_studio_win.py deleted file mode 100644 index f297428..0000000 --- a/talon/user/community/apps/visualstudio/visual_studio_win.py +++ /dev/null @@ -1,71 +0,0 @@ -from talon import Context, actions - -ctx = Context() - -ctx.matches = r""" -os: windows -app: visual_studio -""" - - -@ctx.action_class("app") -class AppActions: - # talon app actions - def tab_close(): - actions.key("ctrl-f4") - - def tab_next(): - actions.key("ctrl-tab") - - def tab_previous(): - actions.key("ctrl-shift-tab") - - def tab_reopen(): - actions.key("ctrl-1 ctrl-r enter") - - -@ctx.action_class("code") -class CodeActions: - # talon code actions - def toggle_comment(): - actions.key("ctrl-k ctrl-/") - - -@ctx.action_class("edit") -class EditActions: - # talon edit actions - 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: - # multiple_cursor.py support begin - # note: visual studio has no explicit mode for multiple cursors; requires https://marketplace.visualstudio.com/items?itemName=VaclavNadrasky.MultiCaretBooster - def multi_cursor_add_above(): - actions.key("shift-alt-up") - - def multi_cursor_add_below(): - actions.key("shift-alt-down") - - # action(user.multi_cursor_add_to_line_ends): does not exist :( - def multi_cursor_disable(): - actions.key("escape") - - def multi_cursor_enable(): - actions.skip() - - def multi_cursor_select_all_occurrences(): - actions.key("shift-alt-;") - - def multi_cursor_select_fewer_occurrences(): - actions.key("shift-alt-k") - - def multi_cursor_select_more_occurrences(): - actions.key("shift-alt->") diff --git a/talon/user/community/apps/vivaldi/vivaldi.py b/talon/user/community/apps/vivaldi/vivaldi.py deleted file mode 100644 index 61b9400..0000000 --- a/talon/user/community/apps/vivaldi/vivaldi.py +++ /dev/null @@ -1,85 +0,0 @@ -from talon import Context, Module, actions, app - -ctx = Context() -mod = Module() - -mod.apps.vivaldi = "app.name: Vivaldi" -mod.apps.vivaldi = "app.name: Vivaldi-stable" -mod.apps.vivaldi = r""" -os: windows -and app.exe: /^vivaldi\.exe$/i -os: linux -and app.exe: vivaldi-bin -os: mac -and app.bundle: com.vivaldi.Vivaldi -""" -ctx.matches = r""" -app: vivaldi -""" - - -@mod.action_class -class Actions: - def vivaldi_history_panel(): - """Toggles the Vivaldi history panel""" - actions.key("ctrl-shift-h") - - def vivaldi_bookmarks_panel(): - """Toggles the Vivaldi bookmarks panel""" - actions.user.command_search("Bookmarks Panel") - actions.key("enter") - - def vivaldi_downloads_panel(): - """Toggles the Vivaldi downloads panel""" - actions.key("ctrl-shift-d") - - def vivaldi_notes_panel(): - """Toggles the Vivaldi notes panel""" - actions.key("ctrl-shift-o") - - -@ctx.action_class("user") -class UserActions: - def tab_close_wrapper(): - actions.sleep("180ms") - actions.app.tab_close() - - def tab_jump(number: int): - actions.key(f"ctrl-{number}") - - def command_search(command: str = ""): - actions.key("ctrl-e") - if command != "": - actions.sleep("180ms") - actions.insert(command) - - -@ctx.action_class("browser") -class BrowserActions: - def show_extensions(): - actions.key("ctrl-shift-e") - - def focus_address(): - actions.key("ctrl-l") - - def focus_page(): - actions.key("f9") - - def bookmarks(): - actions.key("ctrl-b") - - def bookmark_tabs(): - raise NotImplementedError("Vivaldi doesn't support this functionality") - - def show_downloads(): - # There is no default shortcut for showing the downloads page. You can - # configure one. - actions.app.tab_open() - actions.sleep("180ms") - actions.browser.go("vivaldi://downloads") - - def go(url: str): - actions.browser.focus_address() - actions.sleep("150ms") - actions.insert(url) - actions.key("enter") diff --git a/talon/user/community/apps/vivaldi/vivaldi.talon b/talon/user/community/apps/vivaldi/vivaldi.talon deleted file mode 100644 index aad2916..0000000 --- a/talon/user/community/apps/vivaldi/vivaldi.talon +++ /dev/null @@ -1,10 +0,0 @@ -app: vivaldi -- -tag(): browser -tag(): user.tabs -tag(): user.command_search - -(sidebar | panel) history: user.vivaldi_history_panel() -(sidebar | panel) downloads: user.vivaldi_downloads_panel() -(sidebar | panel) bookmarks: user.vivaldi_bookmarks_panel() -(sidebar | panel) notes: user.vivaldi_notes_panel() diff --git a/talon/user/community/apps/vivaldi/vivaldi_mac.py b/talon/user/community/apps/vivaldi/vivaldi_mac.py deleted file mode 100644 index 0f0791a..0000000 --- a/talon/user/community/apps/vivaldi/vivaldi_mac.py +++ /dev/null @@ -1,49 +0,0 @@ -from talon import Context, actions, app - -ctx = Context() - -ctx.matches = r""" -os: mac -app: vivaldi -""" - - -@ctx.action_class("user") -class UserActions: - def vivaldi_history_panel(): - actions.key("cmd-alt-y") - - def vivaldi_downloads_panel(): - actions.key("cmd-alt-l") - - def vivaldi_notes_panel(): - # This shortcut didn't work for me. You might need to change it to a - # different one. - actions.key("cmd-alt-n") - - def vivaldi_toggle_quick_commands(): - actions.key("cmd-e") - - def tab_jump(number: int): - actions.key(f"cmd-{number}") - - -@ctx.action_class("app") -class AppActions: - def tab_next(): - actions.key("cmd-shift-]") - - def tab_previous(): - actions.key("cmd-shift-[") - - -@ctx.action_class("browser") -class BrowserActions: - def show_extensions(): - actions.key("ctrl-cmd-e") - - def bookmarks(): - actions.key("cmd-ctrl-b") - - def focus_address(): - actions.key("cmd-l") diff --git a/talon/user/community/apps/vscode/README.md b/talon/user/community/apps/vscode/README.md deleted file mode 100644 index fb339e6..0000000 --- a/talon/user/community/apps/vscode/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# VSCode support - -It is recommended to install the [VSCode talon extension pack](https://marketplace.visualstudio.com/items?itemName=pokey.talon), which will enable a couple advanced commands and improve the speed / robustness of VSCode commands. - -## Cursorless - -If you'd like to use Cursorless, follow the instructions in the [cursorless-talon repo](https://github.com/pokey/cursorless-talon). - -## Terminal - -In order to use terminal commands when the VSCode terminal is focused, you must add the following line to your [VSCode `settings.json`](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson): - -``` -"window.title": "${activeEditorShort}${separator}${rootName}${separator}${profileName}${separator}focus:[${focusedView}]", -``` - -This setting will cause VSCode to include a special string in the window title whenever the terminal is focused. Talon will look for this string in the window title and activate the terminal commands in response. - -Note that if you have customizations in your window title that you'd like to keep, the important part is just to ensure that `focus:[${focusedView}]` appears somewhere within your custom window title. - -In order to enable additional terminal commands you will need to set some tags when the terminal tag is active. You can do this by creating a file in your talon settings that looks something like this: - -``` -tag: terminal -- -tag(): user.generic_unix_shell -tag(): user.git -tag(): user.kubectl -tag(): user.readline -``` diff --git a/talon/user/community/apps/vscode/command_client/README.md b/talon/user/community/apps/vscode/command_client/README.md deleted file mode 100644 index 844d584..0000000 --- a/talon/user/community/apps/vscode/command_client/README.md +++ /dev/null @@ -1,22 +0,0 @@ -# Talon VSCode command client - -This directory contains the client code for communicating with the [VSCode command server](https://marketplace.visualstudio.com/items?itemName=pokey.command-server). - -## Contributing - -The source of truth is in https://github.com/talonhub/community/tree/main/apps/vscode/command_client, but the code is also maintained as a subtree at https://github.com/pokey/talon-vscode-command-client. - -To contribute, first open a PR on `community`. - -Once the PR is merged, you can push the changes to the subtree by running the following commands on an up-to-date `community` main: (need write access) - -```sh -git subtree split --prefix=apps/vscode/command_client --annotate="[split] " -b split -git push talon-vscode-command-client split:master -``` - -Note that you'll need to have set the upstream up the first time: - -```sh -git remote add talon-vscode-command-client git@github.com:pokey/talon-vscode-command-client.git -``` diff --git a/talon/user/community/apps/vscode/command_client/command_client.py b/talon/user/community/apps/vscode/command_client/command_client.py deleted file mode 100644 index 86d9681..0000000 --- a/talon/user/community/apps/vscode/command_client/command_client.py +++ /dev/null @@ -1,411 +0,0 @@ -import json -import os -import time -from dataclasses import dataclass -from pathlib import Path -from tempfile import gettempdir -from typing import Any -from uuid import uuid4 - -from talon import Context, Module, actions, speech_system - -# How old a request file needs to be before we declare it stale and are willing -# to remove it -STALE_TIMEOUT_MS = 60_000 - -# The amount of time to wait for application to perform a command, in seconds -RPC_COMMAND_TIMEOUT_SECONDS = 3.0 - -# When doing exponential back off waiting for application to perform a command, how -# long to sleep the first time -MINIMUM_SLEEP_TIME_SECONDS = 0.0005 - -# Indicates whether a pre-phrase signal was emitted during the course of the -# current phrase -did_emit_pre_phrase_signal = False - -mod = Module() -ctx = Context() -mac_ctx = Context() - -ctx.matches = r""" -tag: user.command_client -""" -mac_ctx.matches = r""" -os: mac -tag: user.command_client -""" - - -class NotSet: - def __repr__(self): - return "" - - -class NoFileServerException(Exception): - pass - - -def write_json_exclusive(path: Path, body: Any): - """Writes jsonified object to file, failing if the file already exists - - Args: - path (Path): The path of the file to write - body (Any): The object to convert to json and write - """ - with path.open("x") as out_file: - out_file.write(json.dumps(body)) - - -@dataclass -class Request: - command_id: str - args: list[Any] - wait_for_finish: bool - return_command_output: bool - uuid: str - - def to_dict(self): - return { - "commandId": self.command_id, - "args": self.args, - "waitForFinish": self.wait_for_finish, - "returnCommandOutput": self.return_command_output, - "uuid": self.uuid, - } - - -def write_request(request: Request, path: Path): - """Converts the given request to json and writes it to the file, failing if - the file already exists unless it is stale in which case it replaces it - - Args: - request (Request): The request to serialize - path (Path): The path to write to - - Raises: - Exception: If another process has an active request file - """ - try: - write_json_exclusive(path, request.to_dict()) - request_file_exists = False - except FileExistsError: - request_file_exists = True - - if request_file_exists: - handle_existing_request_file(path) - write_json_exclusive(path, request.to_dict()) - - -def handle_existing_request_file(path): - stats = path.stat() - - modified_time_ms = stats.st_mtime_ns / 1e6 - current_time_ms = time.time() * 1e3 - time_difference_ms = abs(modified_time_ms - current_time_ms) - - if time_difference_ms < STALE_TIMEOUT_MS: - raise Exception( - "Found recent request file; another Talon process is probably running" - ) - - print("Removing stale request file") - robust_unlink(path) - - -def run_command( - command_id: str, - *args, - wait_for_finish: bool = False, - return_command_output: bool = False, -): - """Runs a command, using command server if available - - Args: - command_id (str): The ID of the command to run. - args: The arguments to the command. - wait_for_finish (bool, optional): Whether to wait for the command to finish before returning. Defaults to False. - return_command_output (bool, optional): Whether to return the output of the command. Defaults to False. - - Raises: - Exception: If there is an issue with the file-based communication, or - application raises an exception - - Returns: - Object: The response from the command, if requested. - """ - # NB: This is a hack to work around the fact that talon doesn't support - # variable argument lists - args = [x for x in args if x is not NotSet] - - communication_dir_path = get_communication_dir_path() - - if not communication_dir_path.exists(): - if args or return_command_output: - raise Exception("Must use command-server extension for advanced commands") - raise NoFileServerException("Communication directory not found") - - request_path = communication_dir_path / "request.json" - response_path = communication_dir_path / "response.json" - - # Generate uuid that will be mirrored back to us by command server for - # sanity checking - uuid = str(uuid4()) - - request = Request( - command_id=command_id, - args=args, - wait_for_finish=wait_for_finish, - return_command_output=return_command_output, - uuid=uuid, - ) - - # First, write the request to the request file, which makes us the sole - # owner because all other processes will try to open it with 'x' - write_request(request, request_path) - - # We clear the response file if it does exist, though it shouldn't - if response_path.exists(): - print("WARNING: Found old response file") - robust_unlink(response_path) - - # Then, perform keystroke telling application to execute the command in the - # request file. Because only the active application instance will accept - # keypresses, we can be sure that the active application instance will be the - # one to execute the command. - actions.user.trigger_command_server_command_execution() - - try: - decoded_contents = read_json_with_timeout(response_path) - finally: - # NB: We remove response file first because we want to do this while we - # still own the request file - robust_unlink(response_path) - robust_unlink(request_path) - - if decoded_contents["uuid"] != uuid: - raise Exception("uuids did not match") - - for warning in decoded_contents["warnings"]: - print(f"WARNING: {warning}") - - if decoded_contents["error"] is not None: - raise Exception(decoded_contents["error"]) - - actions.sleep("25ms") - - return decoded_contents["returnValue"] - - -def get_communication_dir_path(): - """Returns directory that is used by command-server for communication - - Returns: - Path: The path to the communication dir - """ - suffix = "" - - # NB: We don't suffix on Windows, because the temp dir is user-specific - # anyways - if hasattr(os, "getuid"): - suffix = f"-{os.getuid()}" - - return Path(gettempdir()) / f"{actions.user.command_server_directory()}{suffix}" - - -def robust_unlink(path: Path): - """Unlink the given file if it exists, and if we're on windows and it is - currently in use, just rename it - - Args: - path (Path): The path to unlink - """ - try: - path.unlink(missing_ok=True) - except OSError as e: - if hasattr(e, "winerror") and e.winerror == 32: - graveyard_dir = get_communication_dir_path() / "graveyard" - graveyard_dir.mkdir(parents=True, exist_ok=True) - graveyard_path = graveyard_dir / str(uuid4()) - print( - f"WARNING: File {path} was in use when we tried to delete it; " - f"moving to graveyard at path {graveyard_path}" - ) - path.rename(graveyard_path) - else: - raise e - - -def read_json_with_timeout(path: Path) -> Any: - """Repeatedly tries to read a json object from the given path, waiting - until there is a trailing new line indicating that the write is complete - - Args: - path (str): The path to read from - - Raises: - Exception: If we timeout waiting for a response - - Returns: - Any: The json-decoded contents of the file - """ - timeout_time = time.perf_counter() + RPC_COMMAND_TIMEOUT_SECONDS - sleep_time = MINIMUM_SLEEP_TIME_SECONDS - while True: - try: - raw_text = path.read_text() - - if raw_text.endswith("\n"): - break - except FileNotFoundError: - # If not found, keep waiting - pass - - actions.sleep(sleep_time) - - time_left = timeout_time - time.perf_counter() - - if time_left < 0: - raise Exception("Timed out waiting for response") - - # NB: We use minimum sleep time here to ensure that we don't spin with - # small sleeps due to clock slip - sleep_time = max(min(sleep_time * 2, time_left), MINIMUM_SLEEP_TIME_SECONDS) - - return json.loads(raw_text) - - -@mod.action_class -class Actions: - def run_rpc_command( - command_id: str, - arg1: Any = NotSet, - arg2: Any = NotSet, - arg3: Any = NotSet, - arg4: Any = NotSet, - arg5: Any = NotSet, - ): - """Execute command via RPC.""" - run_command( - command_id, - arg1, - arg2, - arg3, - arg4, - arg5, - ) - - def run_rpc_command_and_wait( - command_id: str, - arg1: Any = NotSet, - arg2: Any = NotSet, - arg3: Any = NotSet, - arg4: Any = NotSet, - arg5: Any = NotSet, - ): - """Execute command via application command server and wait for command to finish.""" - run_command( - command_id, - arg1, - arg2, - arg3, - arg4, - arg5, - wait_for_finish=True, - ) - - def run_rpc_command_get( - command_id: str, - arg1: Any = NotSet, - arg2: Any = NotSet, - arg3: Any = NotSet, - arg4: Any = NotSet, - arg5: Any = NotSet, - ) -> Any: - """Execute command via application command server and return command output.""" - return run_command( - command_id, - arg1, - arg2, - arg3, - arg4, - arg5, - return_command_output=True, - ) - - def command_server_directory() -> str: - """Return the directory of the command server""" - - def trigger_command_server_command_execution(): - """Issue keystroke to trigger command server to execute command that - was written to the file. For internal use only""" - actions.key("ctrl-shift-f17") - - def emit_pre_phrase_signal() -> bool: - """ - If in an application supporting the command client, returns True - and touches a file to indicate that a phrase is beginning execution. - Otherwise does nothing and returns False. - """ - return False - - def did_emit_pre_phrase_signal() -> bool: - """Indicates whether the pre-phrase signal was emitted at the start of this phrase""" - # NB: This action is used by cursorless; please don't delete it :) - return did_emit_pre_phrase_signal - - -@mac_ctx.action_class("user") -class MacUserActions: - def trigger_command_server_command_execution(): - actions.key("cmd-shift-f17") - - -@ctx.action_class("user") -class UserActions: - def emit_pre_phrase_signal(): - get_signal_path("prePhrase").touch() - return True - - -class MissingCommunicationDir(Exception): - pass - - -def get_signal_path(name: str) -> Path: - """ - Get the path to a signal in the signal subdirectory. - - Args: - name (str): The name of the signal - - Returns: - Path: The signal path - """ - communication_dir_path = get_communication_dir_path() - - if not communication_dir_path.exists(): - raise MissingCommunicationDir() - - signal_dir = communication_dir_path / "signals" - signal_dir.mkdir(parents=True, exist_ok=True) - - return signal_dir / name - - -def pre_phrase(_: Any): - try: - global did_emit_pre_phrase_signal - - did_emit_pre_phrase_signal = actions.user.emit_pre_phrase_signal() - except MissingCommunicationDir: - pass - - -def post_phrase(_: Any): - global did_emit_pre_phrase_signal - did_emit_pre_phrase_signal = False - - -speech_system.register("pre:phrase", pre_phrase) -speech_system.register("post:phrase", post_phrase) diff --git a/talon/user/community/apps/vscode/command_client/command_client_tag.py b/talon/user/community/apps/vscode/command_client/command_client_tag.py deleted file mode 100644 index 28e6d53..0000000 --- a/talon/user/community/apps/vscode/command_client/command_client_tag.py +++ /dev/null @@ -1,20 +0,0 @@ -from talon import Module - -mod = Module() - -mod.tag( - "command_client", desc="For applications which implement file-based RPC with Talon" -) - - -@mod.action_class -class Actions: - def command_server_directory() -> str: - """ - The dirctory which contains the files required for communication between - the application and Talon. This is the only function which absolutely - must be implemented for any application using the command-client. Each - application that supports file-based RPC should use its own directory - name. Note that this action should only return a name; the parent - directory is determined by the core command client code. - """ diff --git a/talon/user/community/apps/vscode/command_client/visual_studio.py b/talon/user/community/apps/vscode/command_client/visual_studio.py deleted file mode 100644 index f614d3c..0000000 --- a/talon/user/community/apps/vscode/command_client/visual_studio.py +++ /dev/null @@ -1,15 +0,0 @@ -from talon import Context - -ctx = Context() - -ctx.matches = r""" -app: visual_studio -""" - -ctx.tags = ["user.command_client"] - - -@ctx.action_class("user") -class VisualStudioActions: - def command_server_directory() -> str: - return "visual-studio-command-server" diff --git a/talon/user/community/apps/vscode/command_client/vscode.py b/talon/user/community/apps/vscode/command_client/vscode.py deleted file mode 100644 index 29905db..0000000 --- a/talon/user/community/apps/vscode/command_client/vscode.py +++ /dev/null @@ -1,106 +0,0 @@ -from typing import Any - -from talon import Context, Module, actions - -from .command_client import NoFileServerException, NotSet, run_command - -mod = Module() - -ctx = Context() -linux_ctx = Context() - -ctx.matches = r""" -app: vscode -""" -linux_ctx.matches = r""" -os: linux -app: vscode -""" - -ctx.tags = ["user.command_client"] - - -def command_server_or_client_fallback(command_id: str, wait: bool): - """Execute command via command server, falling back to command palette if directory not present.""" - try: - run_command(command_id, wait_for_finish=wait) - except NoFileServerException: - actions.user.command_palette() - actions.user.paste(command_id) - actions.key("enter") - print( - "Command server directory not found; falling back to command palette. For better performance, install the VSCode extension for Talon: https://marketplace.visualstudio.com/items?itemName=pokey.talon" - ) - - -@ctx.action_class("user") -class VsCodeAction: - def command_server_directory() -> str: - return "vscode-command-server" - - -@mod.action_class -class Actions: - def vscode(command_id: str): - """Execute command via vscode command server, if available, or fallback - to command palette.""" - command_server_or_client_fallback(command_id, False) - - def vscode_and_wait(command_id: str): - """Execute command via vscode command server, if available, and wait - for command to finish. If command server not available, uses command - palette and doesn't guarantee that it will wait for command to - finish.""" - command_server_or_client_fallback(command_id, True) - - # These commands are shims, to provide backwards compatibility, they may be removed in the fuuture. - # Prefer the run_command... version in command_client. - def vscode_with_plugin( - command_id: str, - arg1: Any = NotSet, - arg2: Any = NotSet, - arg3: Any = NotSet, - arg4: Any = NotSet, - arg5: Any = NotSet, - ): - """Execute command via vscode command server.""" - actions.user.run_rpc_command( - command_id, - arg1, - arg2, - arg3, - arg4, - arg5, - ) - - def vscode_with_plugin_and_wait( - command_id: str, - arg1: Any = NotSet, - arg2: Any = NotSet, - arg3: Any = NotSet, - arg4: Any = NotSet, - arg5: Any = NotSet, - ): - """Execute command via vscode command server and wait for command to finish.""" - actions.user.run_rpc_command_and_wait(command_id, arg1, arg2, arg3, arg4, arg5) - - def vscode_get( - command_id: str, - arg1: Any = NotSet, - arg2: Any = NotSet, - arg3: Any = NotSet, - arg4: Any = NotSet, - arg5: Any = NotSet, - ) -> Any: - """Execute command via vscode command server and return command output.""" - return actions.user.run_rpc_command_get( - command_id, arg1, arg2, arg3, arg4, arg5 - ) - - -@linux_ctx.action_class("user") -class LinuxUserActions: - def trigger_command_server_command_execution(): - # Work around bug with upper f-keys in VSCode on Linux. See - # https://github.com/pokey/command-server/issues/9#issuecomment-963733930 - actions.key("ctrl-shift-alt-p") diff --git a/talon/user/community/apps/vscode/vscode.py b/talon/user/community/apps/vscode/vscode.py deleted file mode 100644 index 2454794..0000000 --- a/talon/user/community/apps/vscode/vscode.py +++ /dev/null @@ -1,386 +0,0 @@ -from talon import Context, Module, actions, app - -is_mac = app.platform == "mac" - -ctx = Context() -mac_ctx = Context() -mod = Module() -mod.apps.vscode = """ -os: mac -and app.bundle: com.microsoft.VSCode -os: mac -and app.bundle: com.microsoft.VSCodeInsiders -os: mac -and app.bundle: com.visualstudio.code.oss -os: mac -and app.bundle: com.todesktop.230313mzl4w4u92 -""" -mod.apps.vscode = """ -os: linux -and app.name: Code -os: linux -and app.name: code-oss -os: linux -and app.name: code-insiders -os: linux -and app.name: VSCodium -os: linux -and app.name: Codium -os: linux -and app.name: Cursor -""" -mod.apps.vscode = r""" -os: windows -and app.name: Visual Studio Code -os: windows -and app.name: Visual Studio Code Insiders -os: windows -and app.name: Visual Studio Code - Insiders -os: windows -and app.exe: /^code\.exe$/i -os: windows -and app.exe: /^code-insiders\.exe$/i -os: windows -and app.name: VSCodium -os: windows -and app.exe: /^vscodium\.exe$/i -os: windows -and app.name: Azure Data Studio -os: windows -and app.exe: /^azuredatastudio\.exe$/i -""" - -ctx.matches = r""" -app: vscode -""" -mac_ctx.matches = r""" -os: mac -app: vscode -""" - - -@ctx.action_class("app") -class AppActions: - # talon app actions - def tab_open(): - actions.user.vscode("workbench.action.files.newUntitledFile") - - def tab_close(): - actions.user.vscode("workbench.action.closeActiveEditor") - - def tab_next(): - actions.user.vscode("workbench.action.nextEditorInGroup") - - def tab_previous(): - actions.user.vscode("workbench.action.previousEditorInGroup") - - def tab_reopen(): - actions.user.vscode("workbench.action.reopenClosedEditor") - - def window_close(): - actions.user.vscode("workbench.action.closeWindow") - - def window_open(): - actions.user.vscode("workbench.action.newWindow") - - -@ctx.action_class("code") -class CodeActions: - # talon code actions - def toggle_comment(): - actions.user.vscode("editor.action.commentLine") - - -@ctx.action_class("edit") -class EditActions: - # talon edit actions - def indent_more(): - actions.user.vscode("editor.action.indentLines") - - def indent_less(): - actions.user.vscode("editor.action.outdentLines") - - def save_all(): - actions.user.vscode("workbench.action.files.saveAll") - - def find_next(): - actions.user.vscode("editor.action.nextMatchFindAction") - - def find_previous(): - actions.user.vscode("editor.action.previousMatchFindAction") - - def line_swap_up(): - actions.key("alt-up") - - def line_swap_down(): - actions.key("alt-down") - - def line_clone(): - actions.key("shift-alt-down") - - def line_insert_down(): - actions.user.vscode("editor.action.insertLineAfter") - - def line_insert_up(): - actions.user.vscode("editor.action.insertLineBefore") - - def jump_line(n: int): - actions.user.vscode("workbench.action.gotoLine") - actions.insert(str(n)) - actions.key("enter") - actions.edit.line_start() - - def zoom_reset(): - actions.user.vscode("workbench.action.zoomReset") - - -@ctx.action_class("win") -class WinActions: - def filename(): - title = actions.win.title() - # this doesn't seem to be necessary on VSCode for Mac - # if title == "": - # title = ui.active_window().doc - - if is_mac: - result = title.split(" — ")[0] - else: - result = title.split(" - ")[0] - - if "." in result: - return result - - return "" - - -@mod.action_class -class Actions: - def vscode_terminal(number: int): - """Activate a terminal by number""" - actions.user.vscode(f"workbench.action.terminal.focusAtIndex{number}") - - def command_palette(): - """Show command palette""" - actions.key("ctrl-shift-p") - - -@mac_ctx.action_class("edit") -class MacEditActions: - def find(text: str = None): - actions.key("cmd-f") - if text: - actions.insert(text) - - -@mac_ctx.action_class("user") -class MacUserActions: - def command_palette(): - actions.key("cmd-shift-p") - - -@ctx.action_class("user") -class UserActions: - # splits.py support begin - def split_clear_all(): - actions.user.vscode("workbench.action.editorLayoutSingle") - - def split_clear(): - actions.user.vscode("workbench.action.joinTwoGroups") - - def split_flip(): - actions.user.vscode("workbench.action.toggleEditorGroupLayout") - - def split_maximize(): - actions.user.vscode("workbench.action.toggleMaximizeEditorGroup") - - def split_reset(): - actions.user.vscode("workbench.action.evenEditorWidths") - - def split_last(): - actions.user.vscode("workbench.action.focusLeftGroup") - - def split_next(): - actions.user.vscode_and_wait("workbench.action.focusRightGroup") - - def split_window_down(): - actions.user.vscode("workbench.action.moveEditorToBelowGroup") - - def split_window_horizontally(): - actions.user.vscode("workbench.action.splitEditorOrthogonal") - - def split_window_left(): - actions.user.vscode("workbench.action.moveEditorToLeftGroup") - - def split_window_right(): - actions.user.vscode("workbench.action.moveEditorToRightGroup") - - def split_window_up(): - actions.user.vscode("workbench.action.moveEditorToAboveGroup") - - def split_window_vertically(): - actions.user.vscode("workbench.action.splitEditor") - - def split_window(): - actions.user.vscode("workbench.action.splitEditor") - - def split_number(index: int): - supported_ordinals = [ - "First", - "Second", - "Third", - "Fourth", - "Fifth", - "Sixth", - "Seventh", - "Eighth", - ] - - if 0 <= index - 1 < len(supported_ordinals): - actions.user.vscode( - f"workbench.action.focus{supported_ordinals[index - 1]}EditorGroup" - ) - - # splits.py support end - - # multiple_cursor.py support begin - # note: vscode has no explicit mode for multiple cursors - def multi_cursor_add_above(): - actions.user.vscode("editor.action.insertCursorAbove") - - def multi_cursor_add_below(): - actions.user.vscode("editor.action.insertCursorBelow") - - def multi_cursor_add_to_line_ends(): - actions.user.vscode("editor.action.insertCursorAtEndOfEachLineSelected") - - def multi_cursor_disable(): - actions.key("escape") - - def multi_cursor_enable(): - actions.skip() - - def multi_cursor_select_all_occurrences(): - actions.user.vscode("editor.action.selectHighlights") - - def multi_cursor_select_fewer_occurrences(): - actions.user.vscode("cursorUndo") - - def multi_cursor_select_more_occurrences(): - actions.user.vscode("editor.action.addSelectionToNextFindMatch") - - def multi_cursor_skip_occurrence(): - actions.user.vscode("editor.action.moveSelectionToNextFindMatch") - - # multiple_cursor.py support end - - def command_search(command: str = ""): - actions.user.vscode("workbench.action.showCommands") - if command != "": - actions.insert(command) - - # tabs.py support begin - def tab_jump(number: int): - if number < 10: - if is_mac: - actions.user.vscode_with_plugin( - f"workbench.action.openEditorAtIndex{number}" - ) - else: - actions.key(f"alt-{number}") - else: - actions.user.vscode_with_plugin( - "workbench.action.openEditorAtIndex", number - ) - - def tab_final(): - if is_mac: - actions.user.vscode("workbench.action.lastEditorInGroup") - else: - actions.key("alt-0") - - def tab_duplicate(): - # Duplicates the current tab into a new tab group - # vscode does not allow duplicate tabs in the same tab group, and so is implemented through splits - actions.user.split_window_vertically() - - # tabs.py support end - - # find_and_replace.py support begin - - def find_everywhere(text: str): - """Triggers find across project""" - if is_mac: - actions.key("cmd-shift-f") - else: - actions.key("ctrl-shift-f") - - if text: - actions.insert(text) - - def find_toggle_match_by_case(): - """Toggles find match by case sensitivity""" - if is_mac: - actions.key("alt-cmd-c") - else: - actions.key("alt-c") - - def find_toggle_match_by_word(): - """Toggles find match by whole words""" - if is_mac: - actions.key("cmd-alt-w") - else: - actions.key("alt-w") - - def find_toggle_match_by_regex(): - """Toggles find match by regex""" - if is_mac: - actions.key("cmd-alt-r") - else: - actions.key("alt-r") - - def replace(text: str): - """Search and replaces in the active editor""" - if is_mac: - actions.key("alt-cmd-f") - else: - actions.key("ctrl-h") - - if text: - actions.insert(text) - - def replace_everywhere(text: str): - """Search and replaces in the entire project""" - if is_mac: - actions.key("cmd-shift-h") - else: - actions.key("ctrl-shift-h") - - if text: - actions.insert(text) - - def replace_confirm(): - """Confirm replace at current position""" - if is_mac: - actions.key("shift-cmd-1") - else: - actions.key("ctrl-shift-1") - - def replace_confirm_all(): - """Confirm replace all""" - if is_mac: - actions.key("cmd-enter") - else: - actions.key("ctrl-alt-enter") - - def select_previous_occurrence(text: str): - actions.edit.find(text) - actions.sleep("100ms") - actions.key("shift-enter esc") - - def select_next_occurrence(text: str): - actions.edit.find(text) - actions.sleep("100ms") - actions.key("esc") - - def insert_snippet(body: str): - actions.user.run_rpc_command("editor.action.insertSnippet", {"snippet": body}) diff --git a/talon/user/community/apps/vscode/vscode.talon b/talon/user/community/apps/vscode/vscode.talon deleted file mode 100644 index 45aabae..0000000 --- a/talon/user/community/apps/vscode/vscode.talon +++ /dev/null @@ -1,290 +0,0 @@ -#custom vscode commands go here -app: vscode -- -tag(): user.find_and_replace -tag(): user.line_commands -tag(): user.multiple_cursors -tag(): user.splits -tag(): user.tabs -tag(): user.command_search - -window reload: user.vscode("workbench.action.reloadWindow") -window close: user.vscode("workbench.action.closeWindow") -#multiple_cursor.py support end - -go view []: - user.vscode("workbench.action.openView") - insert(user.text or "") - -# Sidebar -bar explore: user.vscode("workbench.view.explorer") -bar extensions: user.vscode("workbench.view.extensions") -bar outline: user.vscode("outline.focus") -bar run: user.vscode("workbench.view.debug") -bar search: user.vscode("workbench.view.search") -bar source: user.vscode("workbench.view.scm") -bar test: user.vscode("workbench.view.testing.focus") -bar switch: user.vscode("workbench.action.toggleSidebarVisibility") - -# Symbol search -symbol hunt []: - user.vscode("workbench.action.gotoSymbol") - sleep(50ms) - insert(text or "") - -symbol hunt all []: - user.vscode("workbench.action.showAllSymbols") - sleep(50ms) - insert(text or "") - -# Panels -panel control: user.vscode("workbench.panel.repl.view.focus") -panel output: user.vscode("workbench.panel.output.focus") -panel problems: user.vscode("workbench.panel.markers.view.focus") -panel switch: user.vscode("workbench.action.togglePanel") -panel terminal: user.vscode("workbench.action.terminal.focus") -focus editor: user.vscode("workbench.action.focusActiveEditorGroup") - -# Settings -show settings: user.vscode("workbench.action.openGlobalSettings") -show settings json: user.vscode("workbench.action.openSettingsJson") -show settings folder: user.vscode("workbench.action.openFolderSettings") -show settings folder json: user.vscode("workbench.action.openFolderSettingsFile") -show settings workspace: user.vscode("workbench.action.openWorkspaceSettings") -show settings workspace json: user.vscode("workbench.action.openWorkspaceSettingsFile") -show shortcuts: user.vscode("workbench.action.openGlobalKeybindings") -show shortcuts json: user.vscode("workbench.action.openGlobalKeybindingsFile") -show snippets: user.vscode("workbench.action.openSnippets") - -# VSCode Snippets -snip (last | previous): user.vscode("jumpToPrevSnippetPlaceholder") -snip next: user.vscode("jumpToNextSnippetPlaceholder") - -# Display -centered switch: user.vscode("workbench.action.toggleCenteredLayout") -fullscreen switch: user.vscode("workbench.action.toggleFullScreen") -theme switch: user.vscode("workbench.action.selectTheme") -wrap switch: user.vscode("editor.action.toggleWordWrap") -zen switch: user.vscode("workbench.action.toggleZenMode") - -# File Commands -file hunt []: - user.vscode("workbench.action.quickOpen") - sleep(50ms) - insert(text or "") -file hunt (pace | paste): - user.vscode("workbench.action.quickOpen") - sleep(50ms) - edit.paste() -file copy name: user.vscode("fileutils.copyFileName") -file copy path: user.vscode("copyFilePath") -file copy local [path]: user.vscode("copyRelativeFilePath") -file create sibling: user.vscode_and_wait("explorer.newFile") -file create: user.vscode("workbench.action.files.newUntitledFile") -file create relative: user.vscode("fileutils.newFile") -file create root: user.vscode("fileutils.newFileAtRoot") -file rename: - user.vscode("fileutils.renameFile") - sleep(150ms) -file move: - user.vscode("fileutils.moveFile") - sleep(150ms) -file clone: - user.vscode("fileutils.duplicateFile") - sleep(150ms) -file delete: - user.vscode("fileutils.removeFile") - sleep(150ms) -file open folder: user.vscode("revealFileInOS") -file reveal: user.vscode("workbench.files.action.showActiveFileInExplorer") -save ugly: user.vscode("workbench.action.files.saveWithoutFormatting") - -# Language Features -suggest show: user.vscode("editor.action.triggerSuggest") -hint show: user.vscode("editor.action.triggerParameterHints") -definition show: user.vscode("editor.action.revealDefinition") -definition peek: user.vscode("editor.action.peekDefinition") -definition side: user.vscode("editor.action.revealDefinitionAside") -references show: user.vscode("editor.action.goToReferences") -hierarchy peek: user.vscode("editor.showCallHierarchy") -references find: user.vscode("references-view.find") -format that: user.vscode("editor.action.formatDocument") -format selection: user.vscode("editor.action.formatSelection") -imports fix: user.vscode("editor.action.organizeImports") -problem next: user.vscode("editor.action.marker.nextInFiles") -problem last: user.vscode("editor.action.marker.prevInFiles") -problem fix: user.vscode("problems.action.showQuickFixes") -rename that: user.vscode("editor.action.rename") -refactor that: user.vscode("editor.action.refactor") -whitespace trim: user.vscode("editor.action.trimTrailingWhitespace") -language switch: user.vscode("workbench.action.editor.changeLanguageMode") -refactor rename: user.vscode("editor.action.rename") -refactor this: user.vscode("editor.action.refactor") - -#code navigation -(go declaration | follow): user.vscode("editor.action.revealDefinition") -go back: user.vscode("workbench.action.navigateBack") -go forward: user.vscode("workbench.action.navigateForward") -go implementation: user.vscode("editor.action.goToImplementation") -go type: user.vscode("editor.action.goToTypeDefinition") -go usage: user.vscode("references-view.find") -go recent []: - user.vscode("workbench.action.openRecent") - sleep(50ms) - insert(text or "") - sleep(250ms) -go edit: user.vscode("workbench.action.navigateToLastEditLocation") - -# Bookmarks. Requires Bookmarks plugin -bar marks: user.vscode("workbench.view.extension.bookmarks") -go marks: - user.deprecate_command("2023-06-06", "go marks", "bar marks") - user.vscode("workbench.view.extension.bookmarks") -toggle mark: user.vscode("bookmarks.toggle") -go next mark: user.vscode("bookmarks.jumpToNext") -go last mark: user.vscode("bookmarks.jumpToPrevious") - -close other tabs: user.vscode("workbench.action.closeOtherEditors") -close all tabs: user.vscode("workbench.action.closeAllEditors") -close tabs way right: user.vscode("workbench.action.closeEditorsToTheRight") -close tabs way left: user.vscode("workbench.action.closeEditorsToTheLeft") - -# Folding -fold that: user.vscode("editor.fold") -unfold that: user.vscode("editor.unfold") -fold those: user.vscode("editor.foldAllMarkerRegions") -unfold those: user.vscode("editor.unfoldRecursively") -fold all: user.vscode("editor.foldAll") -unfold all: user.vscode("editor.unfoldAll") -fold comments: user.vscode("editor.foldAllBlockComments") -fold one: user.vscode("editor.foldLevel1") -fold two: user.vscode("editor.foldLevel2") -fold three: user.vscode("editor.foldLevel3") -fold four: user.vscode("editor.foldLevel4") -fold five: user.vscode("editor.foldLevel5") -fold six: user.vscode("editor.foldLevel6") -fold seven: user.vscode("editor.foldLevel7") - -# Git / Github (not using verb-noun-adjective pattern, mirroring terminal commands.) -git branch: user.vscode("git.branchFrom") -git branch this: user.vscode("git.branch") -git checkout []: - user.vscode("git.checkout") - sleep(50ms) - insert(text or "") -git commit []: - user.vscode("git.commitStaged") - sleep(100ms) - user.insert_formatted(text or "", "CAPITALIZE_FIRST_WORD") -git commit undo: user.vscode("git.undoCommit") -git commit amend: user.vscode("git.commitStagedAmend") -git diff: user.vscode("git.openChange") -git fetch: user.vscode("git.fetch") -git fetch all: user.vscode("git.fetchAll") -git ignore: user.vscode("git.ignore") -git merge: user.vscode("git.merge") -git output: user.vscode("git.showOutput") -git pull: user.vscode("git.pullRebase") -git push: user.vscode("git.push") -git push force: user.vscode("git.pushForce") -git rebase abort: user.vscode("git.rebaseAbort") -git reveal: user.vscode("git.revealInExplorer") -git revert: user.vscode("git.revertChange") -git stash: user.vscode("git.stash") -git stash pop: user.vscode("git.stashPop") -git status: user.vscode("workbench.scm.focus") -git stage: user.vscode("git.stage") -git stage all: user.vscode("git.stageAll") -git sync: user.vscode("git.sync") -git unstage: user.vscode("git.unstage") -git unstage all: user.vscode("git.unstageAll") -pull request: user.vscode("pr.create") -# Use keyboard shortcuts because VSCode relies on when clause contexts to choose the appropriate -# action: https://code.visualstudio.com/api/references/when-clause-contexts -change next: key(alt-f5) -change last: key(shift-alt-f5) - -# Testing -test run: user.vscode("testing.runAtCursor") -test run file: user.vscode("testing.runCurrentFile") -test run all: user.vscode("testing.runAll") -test run failed: user.vscode("testing.reRunFailTests") -test run last: user.vscode("testing.reRunLastRun") - -test debug: user.vscode("testing.debugAtCursor") -test debug file: user.vscode("testing.debugCurrentFile") -test debug all: user.vscode("testing.debugAll") -test debug failed: user.vscode("testing.debugFailTests") -test debug last: user.vscode("testing.debugLastRun") - -test cancel: user.vscode("testing.cancelRun") - -# Debugging -break point: user.vscode("editor.debug.action.toggleBreakpoint") -step over: user.vscode("workbench.action.debug.stepOver") -debug step into: user.vscode("workbench.action.debug.stepInto") -debug step out [of]: user.vscode("workbench.action.debug.stepOut") -debug start: user.vscode("workbench.action.debug.start") -debug pause: user.vscode("workbench.action.debug.pause") -debug stopper: user.vscode("workbench.action.debug.stop") -debug continue: user.vscode("workbench.action.debug.continue") -debug restart: user.vscode("workbench.action.debug.restart") -debug console: user.vscode("workbench.debug.action.toggleRepl") -debug clean: user.vscode("workbench.debug.panel.action.clearReplAction") - -# Terminal -terminal external: user.vscode("workbench.action.terminal.openNativeConsole") -terminal new: user.vscode("workbench.action.terminal.new") -terminal next: user.vscode("workbench.action.terminal.focusNext") -terminal last: user.vscode("workbench.action.terminal.focusPrevious") -terminal split: user.vscode("workbench.action.terminal.split") -terminal zoom: user.vscode("workbench.action.toggleMaximizedPanel") -terminal trash: user.vscode("workbench.action.terminal.kill") -terminal toggle: user.vscode_and_wait("workbench.action.terminal.toggleTerminal") -terminal scroll up: user.vscode("workbench.action.terminal.scrollUp") -terminal scroll down: user.vscode("workbench.action.terminal.scrollDown") -terminal : user.vscode_terminal(number_small) - -task run []: - user.vscode("workbench.action.tasks.runTask") - insert(user.text or "") -#TODO: should this be added to linecommands? -copy line down: user.vscode("editor.action.copyLinesDownAction") -copy line up: user.vscode("editor.action.copyLinesUpAction") - -#Expand/Shrink AST Selection -select less: user.vscode("editor.action.smartSelect.shrink") -select (more | this): user.vscode("editor.action.smartSelect.expand") - -minimap: user.vscode("editor.action.toggleMinimap") -maximize: user.vscode("workbench.action.minimizeOtherEditors") -restore: user.vscode("workbench.action.evenEditorWidths") - -#breadcrumb -select breadcrumb: user.vscode("breadcrumbs.focusAndSelect") -# Use `alt-left` and `alt-right` to navigate the bread crumb - -replace here: - user.replace("") - key(cmd-alt-l) - -hover show: user.vscode("editor.action.showHover") - -join lines: user.vscode("editor.action.joinLines") - -full screen: user.vscode("workbench.action.toggleFullScreen") - -curse undo: user.vscode("cursorUndo") -curse redo: user.vscode("cursorRedo") - -select word: user.vscode("editor.action.addSelectionToNextFindMatch") -skip word: user.vscode("editor.action.moveSelectionToNextFindMatch") - -# jupyter -cell next: user.vscode("notebook.focusNextEditor") -cell last: user.vscode("notebook.focusPreviousEditor") -cell run above: user.vscode("notebook.cell.executeCellsAbove") -cell run: user.vscode("notebook.cell.execute") - -install local: user.vscode("workbench.extensions.action.installVSIX") -preview markdown: user.vscode("markdown.showPreview") diff --git a/talon/user/community/apps/vscode/vscode_terminal.talon b/talon/user/community/apps/vscode/vscode_terminal.talon deleted file mode 100644 index 63caed5..0000000 --- a/talon/user/community/apps/vscode/vscode_terminal.talon +++ /dev/null @@ -1,7 +0,0 @@ -app: vscode -# Looks for special string in window title. -# NOTE: This requires you to add a special setting to your VSCode settings.json -# See [our vscode docs](./README.md#terminal) -win.title: /focus:\[Terminal\]/ -- -tag(): terminal diff --git a/talon/user/community/apps/warp/warp.py b/talon/user/community/apps/warp/warp.py deleted file mode 100644 index 50c6e22..0000000 --- a/talon/user/community/apps/warp/warp.py +++ /dev/null @@ -1,8 +0,0 @@ -from talon import Module - -mod = Module() - -mod.apps.warp = """ -os: mac -and app.bundle: dev.warp.Warp-Stable -""" diff --git a/talon/user/community/apps/warp/warp.talon b/talon/user/community/apps/warp/warp.talon deleted file mode 100644 index fc5fa2a..0000000 --- a/talon/user/community/apps/warp/warp.talon +++ /dev/null @@ -1,9 +0,0 @@ -app: warp -- -tag(): terminal -tag(): user.generic_unix_shell -tag(): user.git -tag(): user.kubectl -tag(): user.tabs -tag(): user.file_manager -tag(): user.readline diff --git a/talon/user/community/apps/windbg/windbg.py b/talon/user/community/apps/windbg/windbg.py deleted file mode 100644 index a1ebcbe..0000000 --- a/talon/user/community/apps/windbg/windbg.py +++ /dev/null @@ -1,178 +0,0 @@ -from talon import Context, Module, actions - -mod = Module() -mod.tag("windbg", "tag to enabled windbg related functionality") - -# global context for enabling and disabling user.gdb tag -ctx_global = Context() - -# user.windbg-specific context -ctx_windbg_enabled = Context() -ctx_windbg_enabled.matches = r""" -tag: user.windbg -""" - -ctx_windbg_enabled.lists["self.windows_dlls"] = { - "core": "ntdll", - "en tea": "ntdll", - "user": "user32", -} - - -@mod.capture(rule="{self.windows_dlls}") -def windows_dlls(m) -> str: - "Return an register" - return m.windows_dlls - - -@mod.action_class -class Actions: - def windbg_enable(): - """Enables the windbg tag""" - ctx_global.tags = ["user.windbg"] - - def windbg_disable(): - """Disables the windbg tag""" - ctx_global.tags = [] - - -# XXX - trigger alt-1 to hit command window for necessary commands? -# ex: user.windbg_insert_in_cmd() -# edit.left() -@ctx_windbg_enabled.action_class("user") -class UserActions: - ## - # Generic debugger actions - ## - - # Code execution - def debugger_step_into(): - actions.key("f8") - - def debugger_step_over(): - actions.key("f10") - # XXX - - - def debugger_step_line(): - actions.auto_insert("") - - def debugger_step_over_line(): - actions.auto_insert("") - - def debugger_step_out(): - actions.key("shift-f11") - - def debugger_continue(): - actions.key("f5") - - def debugger_stop(): - actions.key("shift-f5") - - def debugger_restart(): - actions.key("ctrl-shift-f5") - - def debugger_detach(): - actions.insert(".detach") - # Registers - - def debugger_show_registers(): - actions.key("r enter") - - def debugger_get_register(): - actions.insert("r @") - - def debugger_set_register(): - actions.user.insert_between("set $@", "=") - # Breakpoints - - def debugger_show_breakpoints(): - actions.insert("bl\n") - - def debugger_add_sw_breakpoint(): - actions.insert("bp ") - - def debugger_add_hw_breakpoint(): - actions.insert("ba e 1 ") - - def debugger_break_now(): - actions.key("ctrl-break") - - def debugger_clear_all_breakpoints(): - actions.insert("bc *\n") - - def debugger_clear_breakpoint(): - actions.insert("bc ") - - def debugger_enable_all_breakpoints(): - actions.insert("be *\n") - - def debugger_enable_breakpoint(): - actions.insert("be ") - - def debugger_disable_all_breakpoints(): - actions.insert("bd *\n") - - def debugger_disable_breakpoint(): - actions.insert("bd ") - # Navigation - - def debugger_goto_address(): - actions.insert("ctrl-g") - - def debugger_goto_clipboard(): - actions.insert("ctrl-g") - actions.edit.paste() - actions.key("enter") - - def debugger_goto_highlighted(): - actions.insert("ctrl-g") - actions.edit.copy() - actions.edit.paste() - actions.key("enter") - # Memory inspection - - def debugger_backtrace(): - actions.key("k enter") - - def debugger_disassemble(): - actions.key("u space") - - def debugger_disassemble_here(): - actions.key("u enter") - - def debugger_disassemble_clipboard(): - actions.key("u space") - actions.edit.paste() - actions.key("enter") - - def debugger_dump_ascii_string(): - actions.insert("da ") - - def debugger_dump_unicode_string(): - actions.insert("du ") - - def debugger_dump_pointers(): - actions.insert("dps ") - - def debugger_list_modules(): - actions.insert("lm\n") - # Registers XXX - - def debugger_inspect_type(): - actions.insert("dt ") - # Convenience - - def debugger_clear_line(): - actions.key("ctrl-a backspace") - ## - # Windbg specific functionality - ## - - def debugger_clear_breakpoint_id(number_small: int): - actions.insert(f"bc {number_small}\n") - - def debugger_disable_breakpoint_id(number_small: int): - actions.insert(f"bd {number_small}\n") - - def debugger_enable_breakpoint_id(number_small: int): - actions.insert(f"be {number_small}\n") diff --git a/talon/user/community/apps/windbg/windbg.talon b/talon/user/community/apps/windbg/windbg.talon deleted file mode 100644 index 22b14e1..0000000 --- a/talon/user/community/apps/windbg/windbg.talon +++ /dev/null @@ -1,46 +0,0 @@ -# XXX - trigger alt-1 to hit command window for necessary commands? -# ex: user.windbg_insert_in_cmd() -# edit.left() -tag: user.windbg -- -tag(): user.debugger - -register : - key(@) - insert("{registers}") - -open help: insert(".hh\n") - -# xxx - add window switching - -add microsoft symbols: - insert("srv*C:\\symbols*http://msdl.microsoft.com/download/symbols;\n") -force reload symbols: insert(".reload /f\n") -reload symbols: insert(".reload\n") -loaded modules: insert("lm l\n") - -display pointers: insert("dps ") - -# XXX - should be generic -dereference pointer: user.insert_between("poi(", ")") - -show version: key(ctrl-alt-w) - -## -# Windows -## - -view command: key(alt-1) -view watch: key(alt-2) -view locals: key(alt-3) -view registers: key(alt-4) -view memory: key(alt-5) -view call stack: key(alt-6) -view disassembly: key(alt-7) -view scratch pad: key(alt-8) -view (processes | threads): key(alt-9) - -# XXX - temp -dump function params: "r @rcx,@rdx,@r8,@r9\n" - -(lib | library) : "{windows_dlls}" diff --git a/talon/user/community/apps/windows_command_processor/command_processor_actions_win.py b/talon/user/community/apps/windows_command_processor/command_processor_actions_win.py deleted file mode 100644 index 20ebaf1..0000000 --- a/talon/user/community/apps/windows_command_processor/command_processor_actions_win.py +++ /dev/null @@ -1,27 +0,0 @@ -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -app: windows_command_processor -app: windows_terminal -and win.title: /Command Prompt/ -""" -ctx.tags = ["user.file_manager", "user.git", "user.kubectl", "terminal"] - - -@ctx.action_class("user") -class UserActions: - def file_manager_refresh_title(): - actions.insert("title Command Prompt: %CD%") - actions.key("enter") - - def file_manager_open_parent(): - actions.insert("cd ..") - actions.key("enter") - actions.user.file_manager_refresh_title() - - -@ctx.action_class("edit") -class EditActions: - def delete_line(): - actions.key("esc") diff --git a/talon/user/community/apps/windows_terminal/windows_terminal.py b/talon/user/community/apps/windows_terminal/windows_terminal.py deleted file mode 100644 index 0c2cb1e..0000000 --- a/talon/user/community/apps/windows_terminal/windows_terminal.py +++ /dev/null @@ -1,177 +0,0 @@ -import os - -from talon import Context, Module, actions, ui - -ctx = Context() -mod = Module() -ctx.matches = r""" -app: windows_terminal -""" - -user_path = os.path.expanduser("~") -directories_to_remap = {} -directories_to_exclude = {} - - -@ctx.action_class("app") -class AppActions: - def tab_close(): - actions.key("ctrl-shift-w") - - def tab_open(): - actions.key("ctrl-shift-t") - - -@ctx.action_class("edit") -class EditActions: - 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) - - -@ctx.action_class("user") -class UserActions: - def file_manager_current_path(): - path = ui.active_window().title - path = ( - path.replace("Administrator: ", "") - .replace("Windows PowerShell: ", "") - .replace("Command Prompt: ", "") - ) - - if path in directories_to_remap: - path = directories_to_remap[path] - - if path in directories_to_exclude: - path = "" - return path - - # def file_manager_terminal_here(): - # actions.key("ctrl-l") - # actions.insert("cmd.exe") - # actions.key("enter") - - # def file_manager_show_properties(): - # """Shows the properties for the file""" - # actions.key("alt-enter") - - def file_manager_open_directory(path: str): - """opens the directory that's already visible in the view""" - actions.insert(f'cd "{path}"') - actions.key("enter") - actions.user.file_manager_refresh_title() - - def file_manager_select_directory(path: str): - """selects the directory""" - actions.insert(f'"{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.insert(f'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) - actions.user.file_manager_refresh_title() - - def tab_jump(number: int): - actions.key(f"ctrl-alt-{number}") - - # user.splits implementation: - - def split_window_right(): - """Move active tab to right split""" - # TODO: decide whether this notification is good style - actions.app.notify( - '"Split right" is not possible in windows terminal without special configuration. Use "split vertically" instead.' - ) - - def split_window_left(): - """Move active tab to left split""" - # TODO: decide whether this notification is good style - actions.app.notify( - '"Split left" is not possible in windows terminal without special configuration. Use "split vertically" instead.' - ) - - def split_window_down(): - """Move active tab to lower split""" - # TODO: decide whether this notification is good style - actions.app.notify( - '"Split down" is not possible in windows terminal without special configuration. Use "split horizontally" instead.' - ) - - def split_window_up(): - """Move active tab to upper split""" - # TODO: decide whether this notification is good style - actions.app.notify( - '"Split up" is not possible in windows terminal without special configuration. Use "split horizontally" instead.' - ) - - def split_window_vertically(): - """Splits window vertically""" - actions.key("shift-alt-plus") - - def split_window_horizontally(): - """Splits window horizontally""" - actions.key("shift-alt-minus") - - def split_flip(): - """Flips the orietation of the active split""" - # TODO: decide whether this notification is good style - actions.app.notify( - '"Split flip" is not possible in windows terminal in default configuration.' - ) - - def split_window(): - """Splits the window""" - # in this implementation an alias for split vertically - actions.key("shift-alt-plus") - - def split_clear(): - """Clears the current split""" - # also closes tab, because shortcut is the same - # and closing a split does mean something differnent that in a code editor like vs code - actions.key("ctrl-shift-w") - - def split_clear_all(): - """Clears all splits""" - # TODO: decide whether to implement it at all since it either doesn't makes sense or closes the window/whole tab - - def split_next(): - """Goes to next split""" - # TODO: decide whether this notification is good style - actions.app.notify( - '"Split next" is not possible in windows terminal without special configuration. Use "focus left/right/up/down" instead.' - ) - - def split_last(): - """Goes to last split""" - # TODO: decide whether this notification is good style - actions.app.notify( - '"Split last" is not possible in windows terminal without special configuration. Use "focus left/right/up/down" instead.' - ) - - def split_number(index: int): - """Navigates to a the specified split""" - actions.app.notify( - '"Split_number" is not possible in windows terminal in default configuration.' - ) - - def tab_final(): - actions.key("ctrl-alt-9") diff --git a/talon/user/community/apps/windows_terminal/windows_terminal.talon b/talon/user/community/apps/windows_terminal/windows_terminal.talon deleted file mode 100644 index 038c4ec..0000000 --- a/talon/user/community/apps/windows_terminal/windows_terminal.talon +++ /dev/null @@ -1,32 +0,0 @@ -app: windows_terminal -- -# makes the commands in terminal.talon available -tag(): terminal - -# activates the implementation of the commands/functions in terminal.talon -tag(): user.generic_windows_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: file_manager -tag(): user.splits - -settings open: key(ctrl-,) -focus left: key(ctrl-alt-shift-left) -focus right: key(ctrl-alt-shift-right) -focus up: key(ctrl-alt-shift-up) -focus down: key(ctrl-alt-shift-down) -term menu: key(ctrl-shift-f1) - -find it: edit.find() - -find it $: - # handle consecutive finds by first escaping out of any previous one - key(escape) - # make it so - edit.find("{phrase}\n") diff --git a/talon/user/community/apps/wsl/wsl.py b/talon/user/community/apps/wsl/wsl.py deleted file mode 100644 index 5cea951..0000000 --- a/talon/user/community/apps/wsl/wsl.py +++ /dev/null @@ -1,531 +0,0 @@ -import logging -import os -import re -import subprocess -import sys - -from talon import Context, Module, actions, app, ui -from talon.debug import log_exception - -mod = Module() - -ctx = Context() - -# note: this context match is intentionally made more complex so that it is more specific -# than the context defined in apps/win/windows_terminal/windows_terminal.py (and thereby -# takes precedence). -ctx.matches = """ -app: windows_terminal -and tag: user.wsl -tag: user.wsl -""" - -if app.platform == "windows": - import platform - - import win32api - import win32con - import win32event - - wsl_distros = [] - - key_event = None - registry_key_handle = None - - # we expect the window title to begin with 'WSL: ' and end with ': '. - # this can be achieved by setting the window title in your .bashrc (or equivalent) - # file and making use of the WSL_DISTRO_NAME environment variable. - # - # take, for example, the default .bashrc for Ubuntu-20.04 - the window title was set - # by changing the prompt definition from this: - # - # PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1" - # - # to this: - # - # PS1="\[\e]0;${debian_chroot:+($debian_chroot)}WSL:${WSL_DISTRO_NAME} \u@\h: \w\a\]$PS1" ^^^^^^^^^^^^^^^^^^^^^^ - # - # any other regex can be used below if your title is formatted differently. just be sure the - # resulting capture groups contain the distro and the path, in that order. - wsl_title_regex = re.compile(r"^WSL:([^\s]+)\s*.*@.*:\s*(.*)$") - - # prepare flags to use for registry calls - registry_access_flags = win32con.KEY_READ - # not sure if this check is important...I know the win32con.KEY_WOW64_64KEY value is needed - # on my 64-bit windows install, but I don't know what happens on 32-bit installs...so, - # playing it safe here. - # https://stackoverflow.com/questions/2208828/detect-64bit-os-windows-in-python/12578715 - if platform.machine().endswith("64"): - registry_access_flags = registry_access_flags | win32con.KEY_WOW64_64KEY - - # close registry key, if open - def _close_key(): - global registry_key_handle - # print(f"_close_key(): {registry_key_handle}") - if registry_key_handle: - win32api.RegCloseKey(registry_key_handle) - registry_key_handle = None - - # open the registry key containing the list of installed wsl distros - def _initialize_key(): - global key_event, registry_key_handle, registry_access_flags - - try: - # make sure the registry key is not currently open - if registry_key_handle: - _close_key() - - # get an event for monitoring registry updates - key_event = win32event.CreateEvent(None, True, True, None) - # print(f"KEY_EVENT: {key_event}") - - # open the registry key - registry_key_handle = win32api.RegOpenKeyEx( - win32con.HKEY_CURRENT_USER, - r"SOFTWARE\Microsoft\Windows\CurrentVersion\Lxss", - 0, - registry_access_flags, - ) - # print(f"registry_key_handle: {registry_key_handle}") - - # register for registry change events - win32api.RegNotifyChangeKeyValue( - registry_key_handle, - True, - win32api.REG_NOTIFY_CHANGE_LAST_SET, - key_event, - True, - ) - - # trigger reading the list for the first time - win32event.SetEvent(key_event) - except OSError: - log_exception(f"[_initialize_key()] {sys.exc_info()[1]}") - - # read the list of wsl distros from the registry - def _update_wsl_distros(): - global ctx, registry_key_handle, wsl_distros, registry_access_flags - - # make sure registry is open - if not registry_key_handle: - _initialize_key() - - distro_handle = None - try: - # check for registry changes - result = win32event.WaitForSingleObjectEx(key_event, 0, False) - # for testing - if False: - print(f"WAIT - {result=} (looking for 'win32con.WAIT_OBJECT_0')") - print(f"WAIT - {win32con.WAIT_OBJECT_0=})") - print(f"WAIT - {win32con.WAIT_ABANDONED=})") - print(f"WAIT - {win32con.WAIT_TIMEOUT=})") - if result == win32con.WAIT_OBJECT_0: - # registry has changed since we last read it, load the distros - subkeys = win32api.RegEnumKeyEx(registry_key_handle) - for subkey in subkeys: - # print(f'{subkey=}') - - distro_handle = win32api.RegOpenKeyEx( - registry_key_handle, subkey[0], 0, registry_access_flags - ) - # print(f"{distro_handle=}") - - distro_name = win32api.RegQueryValueEx( - distro_handle, "DistributionName" - )[0] - # print(f'{distro_name=}') - wsl_distros.append(distro_name) - - win32api.RegCloseKey(distro_handle) - - # reset the event, will be set by system if reg key changes - win32event.ResetEvent(key_event) - - elif result != win32con.WAIT_TIMEOUT: - # something unexpected happened - error = win32api.GetLastError() - _close_key() - raise Exception( - f"failed while checking for wsl registry updates: {result=}: {error=}" - ) - except OSError: - if distro_handle: - win32api.RegCloseKey(distro_handle) - log_exception(f"[_update_wsl_distros()] {sys.exc_info()[1]}") - - # print(f'{wsl_distros=}') - - def _parse_win_title(): - path = ui.active_window().title - - _update_wsl_distros() - distro = None - try: - (distro, path) = re.match(wsl_title_regex, path).groups() - if distro not in wsl_distros: - raise Exception(f"Unknown wsl distro: {distro}") - # log_exception(f'[_update_wsl_distros()] {sys.exc_info()[1]}') - except: - try: - # select line tail following the last colon in the window title - path = path.split(":")[-1].lstrip() - except: - path = "" - - # print(f'TITLE PARSE - distro is {distro}, path is {path}') - return (distro, path) - - -directories_to_remap = {} -directories_to_exclude = {} - -# some definitions used for error handling -termination_error = "The Windows Subsystem for Linux instance has terminated." -restart_message = 'wsl path detection is offline, you need to restart your wsl session, e.g. "wsl --terminate ; wsl"' -path_detection_disable_title = "Talon - WSL path detection disabled" -path_detection_disable_notice = "WSL path detection has been disabled because new WSL sessions cannot be started. See the log for more detail." -path_detection_disabled = False - -user_path = os.path.expanduser("~") -if app.platform == "windows": - is_windows = True - one_drive_path = os.path.expanduser(os.path.join("~", "OneDrive")) - - # this is probably not the correct way to check for onedrive, quick and dirty - if os.path.isdir(os.path.expanduser(os.path.join("~", r"OneDrive\Desktop"))): - directories_to_remap = { - "Desktop": os.path.join(one_drive_path, "Desktop"), - "Documents": os.path.join(one_drive_path, "Documents"), - "Downloads": os.path.join(user_path, "Downloads"), - "Music": os.path.join(user_path, "Music"), - "OneDrive": one_drive_path, - "Pictures": os.path.join(one_drive_path, "Pictures"), - "Videos": os.path.join(user_path, "Videos"), - } - else: - # todo use expanduser for cross platform support - directories_to_remap = { - "Desktop": os.path.join(user_path, "Desktop"), - "Documents": os.path.join(user_path, "Documents"), - "Downloads": os.path.join(user_path, "Downloads"), - "Music": os.path.join(user_path, "Music"), - "OneDrive": one_drive_path, - "Pictures": os.path.join(user_path, "Pictures"), - "Videos": os.path.join(user_path, "Videos"), - } - - -def get_win_path(wsl_path, distro=None): - # for testing - # wsl_path = 'Ubuntu-20.04' - # wsl_path = '/mnt/qube/woobee/woobee/woobit' - # print(f"WINPATH: {wsl_path}") - return run_wslpath(["-w"], wsl_path, distro) - - -def get_usr_path(distro=None): - # print(f'USRPATH: {"~"}') - return run_wslpath(["-a"], "~", distro) - - -def get_wsl_path(win_path, distro=None): - # print(f"WSLPATH: {win_path}") - return run_wslpath(["-u"], f"'{win_path}'", distro) - - -def _disable_path_detection(notify=True): - global path_detection_disabled - path_detection_disabled = True - if notify: - app.notify( - title=path_detection_disable_title, body=path_detection_disable_notice - ) - - -# this command fails every once in a while, with no indication why. -# so, when that happens we just retry. -MAX_ATTEMPTS = 2 - - -def run_wslpath(args, in_path, in_distro=None): - path = "" - - if not path_detection_disabled: - loop_num = 0 - - while loop_num < MAX_ATTEMPTS: - # print(f"_run_wslpath(): {path_detection_disabled=}.") - (distro, path, error) = run_wsl(["wslpath", *args, in_path], in_distro) - if error: - if in_path == distro and error.endswith("No such file or directory"): - # for testing - # print(f"run_wslpath(): - ignoring expected failure.") - - # this is expected. happens when running after the window is created - # but before the default title has been changed. no need to spam the - # console for this case, just let it pass. - pass - else: - logging.error( - f"run_wslpath(): failed to translate given path - attempt: {loop_num}, error: {error}" - ) - - path = "" - if error == termination_error: - # disable this code until the user resets it - _disable_path_detection() - break - elif path: - # got it, no need to loop and try again - break - - loop_num += 1 - - return path - - -# Note: seems WSL itself generates utf-16-le errors, whereas your guest os probably does not. -# - see https://github.com/microsoft/WSL/issues/4607 and related issures. Not sure how this -# behavior might differ when the system locale has been changed from the default. -# -# Anyways, these WSL errors require special handling so they are logged clearly. This is presumably -# worthwhile given the likely importance of any such messages. For example, which would you rather -# see in the log? -# -# 1. Nothing at all, even though there might be serious problems. -# -# 2. b'T\x00h\x00e\x00 \x00W\x00i\x00n\x00d\x00o\x00w\x00s\x00 \x00S\x00u\x00b\x00s\x00y\x00s\x00t\x00e\x00m\x00 \x00f\x00o\x00r\x00 \x00L\x00i\x00n\x00u\x00x\x00 \x00i\x00n\x00s\x00t\x00a\x00n\x00c\x00e\x00 \x00h\x00a\x00s\x00 \x00t\x00e\x00r\x00m\x00i\x00n\x00a\x00t\x00e\x00d\x00.\x00\r\x00\r\x00\n\x00' -# -# 3. The Windows Subsystem for Linux instance has terminated. -# -# The error above indicates the WSL distro is hung and this result detection mechanism is offline. When -# that happens, it takes a while for the command to return and the talon watchdog generates messages -# in the log that indicate a hang but we can provide more contextual detail. The prime thing to do here -# is to get word to the user that WSL is not responding normally. Note that, even after reaching this -# state, existing interactive wsl sessions continue to run and so the user may be unaware of the true -# source of their "talon problems". For more information, see https://github.com/microsoft/WSL/issues/5110 -# and https://github.com/microsoft/WSL/issues/5318. -# -# Once the WSL distro is hung, every attempt to use it results in many repeated log messages like these: -# -# 2021-10-15 11:15:49 WARNING [watchdog] "talon.windows.ui._on_event" @30.0s (stalled) -# 2021-10-15 11:15:49 WARNING [watchdog] "user.knausj_talon.code.file_manager.win_event_handler" -# -# These messages are from code used to detect the current path from the window title, and it every time the -# focus shifts to a wsl context or the current path changes. This gets tiresome if you don't want to restart -# wsl immediately (because your existing sessions are still running and you want to finish working before -# restarting wsl). -# -# So, wsl path detection is disabled when this condition is first detected. The user -# must then re-enable the feature once the underlying problem has been resolved. This can be done by -# using the 'weasel reset path detection' voice command or simply reloading this file. - - -def _decode(value: bytes) -> str: - # check to see if the given byte string looks like utf-16-le. results may not be correct for all - # possible cases, but if there's a problem this code can be replaced with chardet (once that module - # covers utf-16-le - see https://github.com/chardet/chardet/pull/109#discussion_r119149003). of - # course, by that time wsl might not have the same problem anyways. - if (len(value) % 2 == 0) and sum(value[1::2]) == 0: - # looks like utf-16-le, see https://github.com/microsoft/WSL/issues/4607 (and related issues). - decoded = value.decode("UTF-16-LE") - else: - decoded = value.decode() - # print(f"_decode(): value is {value}") - # print(f"_decode(): decoded is {decoded}.") - return decoded.strip() - - -def _run_cmd(command_line): - result = error = "" - # print(f"_run_cmd(): RUNNING - command line is {command_line}.") - try: - # for testing - # raise subprocess.CalledProcessError(-4294967295, command_line, termination_error.encode('UTF-16-LE')) - - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - startupinfo.wShowWindow = subprocess.SW_HIDE - - tmp = subprocess.check_output( - command_line, stderr=subprocess.STDOUT, startupinfo=startupinfo - ) - result = _decode(tmp) - # print(f"RESULT: command: {' '.join(command_line)}, result: {result}") - except subprocess.CalledProcessError as exc: - result = "" - - # decode the error - error = _decode(exc.output) - - # log additional info for this particular case - if error == termination_error: - logging.error(f"_run_cmd(): failed to run command - error: {error}") - logging.error(f"_run_cmd(): - {restart_message}") - except: - result = "" - log_exception(f"[_run_cmd()] {sys.exc_info()[1]}") - - # return results for the last attempt - # print(f'_run_cmd(): RETURNING - result: {result}, error: {error}') - return [result, error] - - -def run_wsl(args, distro=None): - # for testing - if False: - wsl_cmd_str = "nosuchcommand" - else: - wsl_cmd_str = "wsl" - - # for testing - # distro = "Debian" - # distro = 'Ubuntu-20.04-ms-0' - - if not distro: - # fetch the (default) distro first - result = _run_cmd([wsl_cmd_str, "echo", "$WSL_DISTRO_NAME"]) - distro = result[0] - if not distro: - # if we can't fetch the distro, then the user's command is not likely to work - # either. so, we just return any error information we have to the caller. - # print(f'run_wsl(): RETURNING EARLY (no distro) - distro: {distro}, result: {result}') - return [None] + result - - # now run the caller's command - command_line = [wsl_cmd_str, "--distribution", distro] + args - result = _run_cmd(command_line) - # print(f'run_wsl(): RETURNING - distro: {distro}, result: {result}') - return [distro] + result - - -def get_distro(): - return run_wsl(["\n"])[0] - - -@ctx.action_class("user") -class UserActions: - def file_manager_refresh_title(): - actions.skip() - - def file_manager_open_parent(): - actions.insert("cd ..") - actions.key("enter") - - def file_manager_current_path(): - if path_detection_disabled: - logging.warning( - 'Skipping WSL path detection - try "weasel reset path detection"' - ) - return "" - - (distro, path) = _parse_win_title() - - if "~" in path: - # the only way I could find to correctly support the user folder: - # get absolute path of ~, and strip /mnt/x from the string - abs_usr_path = get_usr_path(distro) - abs_usr_path = abs_usr_path[abs_usr_path.find("/home") : len(abs_usr_path)] - path = path.replace("~", abs_usr_path) - - path = get_win_path(path, distro) - - if path in directories_to_remap: - path = directories_to_remap[path] - - if path in directories_to_exclude: - path = "" - - return path - - # def file_manager_terminal_here(): - # actions.key("ctrl-l") - # actions.insert("cmd.exe") - # actions.key("enter") - - # def file_manager_show_properties(): - # """Shows the properties for the file""" - # actions.key("alt-enter") - - def file_manager_open_directory(path: str): - """opens the directory that's already visible in the view""" - if ":" in str(path): - path = get_wsl_path(path) - - actions.insert(f'cd "{path}"') - actions.key("enter") - actions.user.file_manager_refresh_title() - - def file_manager_select_directory(path: str): - """selects the directory""" - actions.insert(f'"{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.insert(f'mkdir "{name}"') - - def file_manager_open_file(path: str): - actions.insert(path) - # actions.key("enter") - - def file_manager_select_file(path: str): - actions.insert(path) - - def file_manager_open_volume(volume: str): - 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") - - -@mod.action_class -class Actions: - def wsl_reset_path_detection(): - """reset wsl path detection""" - global path_detection_disabled - path_detection_disabled = False - - def wsl_speak(): - """ask each distro to say hello (in the log)""" - results = [] - _update_wsl_distros() - for in_distro in wsl_distros: - (distro, result, error) = run_wsl( - ["echo", 'Hello, my name is "${WSL_DISTRO_NAME}".'], in_distro - ) - if error: - logging.error(f"wsl_speak(): {error=}") - else: - # print(f'{result=}') - if len(result) == 0: - result = f'Distro "{in_distro}" has nothing to say.' - results.append(result) - print("\n" + "\n".join(results)) diff --git a/talon/user/community/apps/wsl/wsl.talon b/talon/user/community/apps/wsl/wsl.talon deleted file mode 100644 index 94fd921..0000000 --- a/talon/user/community/apps/wsl/wsl.talon +++ /dev/null @@ -1,40 +0,0 @@ -# NOTE: to use these commands you will need to activate the tag below in whatever contexts you -# choose. -# -# do this in a separate .talon file or via python. for example, if you use windows terminal for -# wsl then you might do this: -# -# os: windows -# app: windows_terminal -# - -# tag(): user.wsl -# -# however, if you also use windows terminal for other things (powershell), you will want something -# more specific...like this: -# -# os: windows -# app: windows_terminal -# title: /^WSL:/ -# - -# tag(): user.wsl -# -# then, you will need to find a way to set the window title accordingly. for example, to match -# the title pattern above, you can set the prompt in your .bashrc file like this: -# -# PS1="\[\e]0;${debian_chroot:+($debian_chroot)}WSL:${WSL_DISTRO_NAME} \u@\h: \w\a\]$PS1" -# -# ALSO: if you do populate your window title with your distro name, make sure the 'wsl_title_regex' -# value in wsl.py is set accordingly. -tag: user.wsl -- - -tag(): terminal -tag(): user.file_manager -tag(): user.generic_unix_shell -tag(): user.git -tag(): user.kubectl - -^go $: user.file_manager_open_volume("/mnt/{letter}") - -(wsl | weasel) reset path detection: user.wsl_reset_path_detection() -(wsl | weasel) speak: user.wsl_speak() diff --git a/talon/user/community/core/README.md b/talon/user/community/core/README.md deleted file mode 100644 index 83edbce..0000000 --- a/talon/user/community/core/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# core - -This folder contains `edit_settings.talon`, which has a command to open various [settings](https://github.com/talonhub/community?tab=readme-ov-file#settings) files. As an overview of what commands the subfolders contain: - -- `abbreviate` has a command for the use of abbreviations -- `app_switcher` does not have commands but has the implementation of functions that allow for switching between applications -- `edit` has commands for navigating and editing text with copy, paste, etc., as well as commands for zooming in and out -- `file_extension` has a command for simpler spoken forms of file and website name extensions -- `help` has commands to open various help menus, as described in the top level [README](https://github.com/talonhub/community?tab=readme-ov-file#getting-started-with-talon) -- `homophones` has commands to replace words with their homophones -- `keys` has commands for [pressing keys](https://github.com/talonhub/community?tab=readme-ov-file#keys) -- `modes` has commands for switching between dictation, command, and sleep mode, as well as for forcing a certain [programming language](https://github.com/talonhub/community?tab=readme-ov-file#programming-languages) mode -- `mouse_grid` has commands to use a grid on the screen to click at a specific location -- `numbers` has the command for writing a number -- `screens` has a command for talon to show the index associated with each of your computer screens for the sake of moving windows to different screens -- `snippets` has commands for inserting snippets of code for various languages -- `text` has commands for inserting and reformatting text -- `vocabulary` has commands for adding new words to be recognized and for having certain words automatically by replaced by others -- `websites_and_search_engines` has commands for opening websites, following links, and making browser searches -- `windows_and_tabs` has commands for tab and [window management](https://github.com/talonhub/community?tab=readme-ov-file#window-management), launching and switching between different applications, and snapping application windows to different locations on the screen diff --git a/talon/user/community/core/abbreviate/abbreviate.py b/talon/user/community/core/abbreviate/abbreviate.py deleted file mode 100644 index cb43d5c..0000000 --- a/talon/user/community/core/abbreviate/abbreviate.py +++ /dev/null @@ -1,473 +0,0 @@ -import re - -from talon import Context, Module - -from ..user_settings import track_csv_list - -mod = Module() -ctx = Context() -mod.list("abbreviation", desc="Common abbreviation") - -abbreviations_list = {} -abbreviations = { - "J peg": "jpg", - "abbreviate": "abbr", - "abort": "abrt", - "acknowledge": "ack", - "address": "addr", - "addresses": "addrs", - "administrator": "admin", - "administrators": "admins", - "advance": "adv", - "advanced": "adv", - "alberta": "ab", - "allocate": "alloc", - "alternative": "alt", - "apple": "appl", - "application": "app", - "applications": "apps", - "argument": "arg", - "arguments": "args", - "as far as i can tell": "afaict", - "as far as i know": "afaik", - "assembly": "asm", - "asynchronous": "async", - "at the moment": "atm", - "attribute": "attr", - "attributes": "attrs", - "authenticate": "auth", - "authentication": "authn", - "authorization": "authz", - "auto group": "augroup", - "average": "avg", - "away from keyboard": "afk", - "backup": "bkp", - "be right back": "brb", - "binary": "bin", - "block": "blk", - "boolean": "bool", - "bottom": "bot", - "break point": "bp", - "break points": "bps", - "british columbia": "bc", - "buffer": "buf", - "button": "btn", - "by the way": "btw", - "calculate": "calc", - "calculator": "calc", - "camera": "cam", - "canada": "ca", - "centimeter": "cm", - "char": "chr", - "character": "char", - "check": "chk", - "child": "chld", - "china": "cn", - "class": "cls", - "client": "cli", - "column": "col", - "command": "cmd", - "commands": "cmds", - "comment": "cmt", - "communication": "comm", - "communications": "comms", - "compare": "cmp", - "condition": "cond", - "conference": "conf", - "config": "cfg", - "configuration": "config", - "configurations": "configs", - "connection": "conn", - "constant": "const", - "contribute": "contrib", - "constructor": "ctor", - "context": "ctx", - "control flow graph": "cfg", - "control": "ctrl", - "coordinate": "coord", - "coordinates": "coords", - "copy": "cpy", - "count": "cnt", - "counter": "ctr", - "credential": "cred", - "credentials": "creds", - "cross reference": "xref", - "cross references": "xrefs", - "cuddle": "ctl", - "current": "cur", - "cute": "qt", - "database": "db", - "date format": "yyyy-mm-dd", - "debian": "deb", - "debug": "dbg", - "decimal": "dec", - "declaration": "decl", - "declare": "decl", - "decode": "dec", - "decrement": "dec", - "define": "def", - "definition": "def", - "degree": "deg", - "delete": "del", - "depend": "dep", - "depends": "deps", - "description": "desc", - "dest": "dst", - "destination": "dest", - "develop": "dev", - "development": "dev", - "device": "dev", - "diagnostic": "diag", - "dictation": "dict", - "dictionary": "dict", - "direction": "dir", - "directories": "dirs", - "directory": "dir", - "display": "disp", - "distance": "dist", - "distribution": "dist", - "document": "doc", - "documents": "docs", - "doing": "ing", # some way to add 'ing' to verbs - "double ended queue": "deque", - "double": "dbl", - "dupe": "dup", - "duplicate": "dup", - "dynamic": "dyn", - "elastic": "elast", - "element": "elem", - "elements": "elems", - "encode": "enc", - "end of day": "eod", - "end of month": "eom", - "end of quarter": "eoq", - "end of week": "eow", - "end of year": "eoy", - "entry": "ent", - "enumerate": "enum", - "environment": "env", - "error": "err", - "escape": "esc", - "etcetera": "etc", - "ethernet": "eth", - "evaluate": "eval", - "example": "ex", - "exception": "exc", - "executable": "exe", - "executables": "exes", - "execute": "exec", - "experience": "exp", - "exponent": "exp", - "expression": "expr", - "expressions": "exprs", - "extend": "ext", - "extension": "ext", - "external": "extern", - "eye dent": "id", - "eye octal": "ioctl", - "eye three": "i3", - "feature": "feat", - "file system": "fs", - "fingerprint": "fp", - "for what": "fwiw", - "format": "fmt", - "fortigate": "fgt", - "framework": "fw", - "frequency": "freq", - "function": "func", - "functions": "funcs", - "funny": "lol", - "fuzzy": "fzy", - "generate": "gen", - "generic": "gen", - "hardware": "hw", - "header": "hdr", - "hello": "helo", - "history": "hist", - "hypertext": "http", - "identity": "id", - "ignore": "ign", - "image": "img", - "implement": "impl", - "import address table": "iat", - "import table": "iat", - "in real life": "irl", - "increment": "inc", - "index": "idx", - "information": "info", - "infrastructure": "infra", - "initialize": "init", - "initializer": "init", - "inode": "ino", - "insert": "ins", - "instance": "inst", - "instruction": "insn", - "integer": "int", - "interpreter": "interp", - "interrupt": "int", - "iterate": "iter", - "jason": "json", - "jason five": "json5", - "java archive": "jar", - "javascript": "js", - "jiff": "gif", - "journal cuttle": "journalctl", - "jump": "jmp", - "just in time": "jit", - "kay": "kk", - "kernel": "krnl", - "key cuttle": "keyctl", - "keyboard": "kbd", - "keyword arguments": "kwargs", - "keyword": "kw", - "kilogram": "kg", - "kilometer": "km", - "language": "lang", - "laugh out loud": "lol", - "length": "len", - "lib see": "libc", - "library": "lib", - "lisp": "lsp", - "looks good to me": "lgtm", - "mail": "smtp", - "make": "mk", - "management": "mgmt", - "manager": "mgr", - "manitoba": "mb", - "markdown": "md", - "maximum": "max", - "memory": "mem", - "message": "msg", - "meta sploit framework": "msf", - "meta sploit": "msf", - "microphone": "mic", - "middle": "mid", - "milligram": "mg", - "millisecond": "ms", - "minimum viable product": "mvp", - "minimum": "min", - "miscellaneous": "misc", - "modify": "mod", - "module": "mod", - "modules": "mods", - "monitor": "mon", - "mount": "mnt", - "multiple": "multi", - "muscle": "musl", - "mutate": "mut", - "nano second": "ns", - "neo vim": "nvim", - "new brunswick": "nb", - "nova scotia": "ns", - "number": "num", - "numbers": "nums", - "object": "obj", - "objects": "objs", - "offset": "off", - "offsets": "offs", - "okay": "ok", - "ontario": "on", - "operating system": "os", - "operation": "op", - "operations": "ops", - "option": "opt", - "options": "opts", - "original": "orig", - "out of bounds": "oob", - "package build": "pkgbuild", - "package": "pkg", - "packages": "pkgs", - "packet": "pkt", - "packets": "pkts", - "parameter": "param", - "parameters": "params", - "password": "passwd", - "performance": "perf", - "physical": "phys", - "physical address": "paddr", - "pick": "pic", - "pico second": "ps", - "pie": "py", - "ping": "png", - "pixel": "px", - "point": "pt", - "pointer": "ptr", - "pointers": "ptrs", - "pone": "pwn", - "position independent code": "pic", - "position independent executable": "pie", - "position": "pos", - "pound bag": "pwndbg", - "preference": "pref", - "preferences": "prefs", - "previous": "prev", - "private": "priv", - "process": "proc", - "processor": "cpu", - "production": "prod", - "program": "prog", - "programs": "progs", - "properties": "props", - "property": "prop", - "protocol": "proto", - "protocol buffers": "protobuf", - "public": "pub", - "python": "py", - "quebec": "qc", - "query string": "qs", - "radian": "rad", - "random": "rand", - "read right ex": "rwx", - "receipt": "rcpt", - "receive": "recv", - "record": "rec", - "recording": "rec", - "rectangle": "rect", - "ref count": "refcnt", - "reference": "ref", - "references": "refs", - "register": "reg", - "registers": "regs", - "registery": "reg", - "regular expression": "regex", - "regular expressions": "regex", - "remove": "rm", - "repel": "repl", - "repetitive strain injury": "rsi", - "repository": "repo", - "represent": "repr", - "representation": "repr", - "request": "req", - "requests": "reqs", - "resources": "rsrcs", - "response": "resp", - "result": "res", - "return": "ret", - "revision": "rev", - "round": "rnd", - "ruby": "rb", - "rust": "rs", - "samba D": "smbd", - "samba": "smb", - "saskatchewan": "sk", - "schedule": "sched", - "scheduler": "sched", - "screen": "scr", - "scuzzy": "scsi", - "see": "C", - "segment": "seg", - "select": "sel", - "semaphore": "sem", - "send": "snd", - "sequel": "sql", - "sequence": "seq", - "service pack": "sp", - "session id": "sid", - "shell": "sh", - "shellcode": "sc", - "signal": "sig", - "size": "sz", - "snipped": "[...]", - "some": "sum", - "source": "src", - "sources": "srcs", - "special": "spec", - "specific": "spec", - "specification": "spec", - "specify": "spec", - "standard error": "stderr", - "standard in": "stdin", - "standard out": "stdout", - "standard": "std", - "start of day": "sod", - "start of month": "som", - "start of quarter": "soq", - "start of week": "sow", - "start of year": "soy", - "statement": "stmt", - "statistic": "stat", - "statistics": "stats", - "string": "str", - "structure": "struct", - "structures": "structs", - "symbol": "sym", - "symbolic link": "symlink", - "symbols": "syms", - "synchronize": "sync", - "synchronous": "sync", - "sys cuttle": "sysctl", - "system call": "syscall", - "system cuddle": "systemctl", - "system": "sys", - "table of contents": "toc", - "table": "tbl", - "taiwan": "tw", - "talk": "toc", - "technology": "tech", - "temp": "tmp", - "temperature": "temp", - "temporary": "tmp", - "terminal": "term", - "text": "txt", - "time format": "hh:mm:ss", - "time of check time of use": "toctou", - "time to live": "ttl", - "token": "tok", - "transaction": "txn", - "typescript": "ts", - "ultimate": "ulti", - "unique id": "uuid", - "unknown": "unk", - "user id": "uid", - "user": "usr", - "utilities": "utils", - "utility": "util", - "value": "val", - "values": "vals", - "variable": "var", - "variables": "vars", - "vector": "vec", - "verify": "vrfy", - "version": "ver", - "versus": "vs", - "video": "vid", - "videos": "vids", - "virtual machine": "vm", - "virtual": "virt", - "virtual address": "vaddr", - "visual studio": "msvc", - "visual": "vis", - "volume": "vol", - "vulnerable": "vuln", - "wave": "wav", - "web": "www", - "what the fuck": "wtf", - "wind": "wnd", - "window": "win", - "windows kernel": "ntoskrnl", - "work in progress": "wip", -} - - -@track_csv_list( - "abbreviations.csv", headers=("Abbreviation", "Spoken Form"), default=abbreviations -) -def on_abbreviations(values): - global abbreviations_list - - # note: abbreviations_list is imported by the create_spoken_forms module - abbreviations_list = values - - # Matches letters and spaces, as currently, Talon doesn't accept other characters in spoken forms. - PATTERN = re.compile(r"^[a-zA-Z ]+$") - abbreviation_values = { - v: v for v in abbreviations_list.values() if PATTERN.match(v) is not None - } - - # Allows the abbreviated/short form to be used as spoken phrase. eg "brief app" -> app - abbreviations_list_with_values = { - **{v: v for v in abbreviation_values.values()}, - **abbreviations_list, - } - - ctx.lists["user.abbreviation"] = abbreviations_list_with_values diff --git a/talon/user/community/core/app_switcher/app_name_overrides.linux.csv b/talon/user/community/core/app_switcher/app_name_overrides.linux.csv deleted file mode 100644 index d73a8f3..0000000 --- a/talon/user/community/core/app_switcher/app_name_overrides.linux.csv +++ /dev/null @@ -1,4 +0,0 @@ -Spoken form, App name (or list an app name by itself on a line to exclude it) -grip, DataGrip -py, jetbrains-pycharm-ce -terminal, Gnome-terminal diff --git a/talon/user/community/core/app_switcher/app_name_overrides.mac.csv b/talon/user/community/core/app_switcher/app_name_overrides.mac.csv deleted file mode 100644 index 1e5907a..0000000 --- a/talon/user/community/core/app_switcher/app_name_overrides.mac.csv +++ /dev/null @@ -1,4 +0,0 @@ -Spoken form, App name (or list an app name by itself on a line to exclude it) -grip, DataGrip -term, iTerm2 -one note, ONENOTE diff --git a/talon/user/community/core/app_switcher/app_name_overrides.windows.csv b/talon/user/community/core/app_switcher/app_name_overrides.windows.csv deleted file mode 100644 index f33a582..0000000 --- a/talon/user/community/core/app_switcher/app_name_overrides.windows.csv +++ /dev/null @@ -1,9 +0,0 @@ -Spoken form, App name/.exe (or list an app name/.exe by itself on a line to exclude it) -grip, DataGrip -term, WindowsTerminal.exe -one note, ONENOTE -lock, slack.exe -app, slack.exe -lockapp, slack.exe -pycharm, pycharm64.exe -webstorm, webstorm64.exe diff --git a/talon/user/community/core/app_switcher/app_switcher.py b/talon/user/community/core/app_switcher/app_switcher.py deleted file mode 100644 index 7c81595..0000000 --- a/talon/user/community/core/app_switcher/app_switcher.py +++ /dev/null @@ -1,461 +0,0 @@ -import os -import shlex -import subprocess -import time -from pathlib import Path - -import talon -from talon import Context, Module, actions, app, fs, imgui, ui - -# Construct a list of spoken form overrides for application names (similar to how homophone list is managed) -# These overrides are used *instead* of the generated spoken forms for the given app name or .exe (on Windows) -# CSV files contain lines of the form: -# , - to add a spoken form override for the app, or -# - to exclude the app from appearing in "running list" or "focus " - -# TODO: Consider moving overrides to settings directory -overrides_directory = os.path.dirname(os.path.realpath(__file__)) -override_file_name = f"app_name_overrides.{talon.app.platform}.csv" -override_file_path = os.path.normcase( - os.path.join(overrides_directory, override_file_name) -) - -mod = Module() -mod.list("running", desc="all running applications") -mod.list("launch", desc="all launchable applications") -ctx = Context() - -# a list of the current overrides -overrides = {} - -# apps to exclude from running list -excludes = set() - -# a list of the currently running application names -running_application_dict = {} - - -words_to_exclude = [ - "zero", - "one", - "two", - "three", - "for", - "four", - "five", - "six", - "seven", - "eight", - "nine", - "and", - "dot", - "exe", - "help", - "install", - "installer", - "microsoft", - "nine", - "readme", - "studio", - "terminal", - "visual", - "windows", -] - -# on Windows, WindowsApps are not like normal applications, so -# we use the shell:AppsFolder to populate the list of applications -# rather than via e.g. the start menu. This way, all apps, including "modern" apps are -# launchable. To easily retrieve the apps this makes available, navigate to shell:AppsFolder in Explorer -if app.platform == "windows": - import ctypes - import os - from ctypes import wintypes - - import pywintypes - from win32com.propsys import propsys, pscon - from win32com.shell import shell, shellcon - - # KNOWNFOLDERID - # https://msdn.microsoft.com/en-us/library/dd378457 - # win32com defines most of these, except the ones added in Windows 8. - FOLDERID_AppsFolder = pywintypes.IID("{1e87508d-89c2-42f0-8a7e-645a0f50ca58}") - - # win32com is missing SHGetKnownFolderIDList, so use ctypes. - - _ole32 = ctypes.OleDLL("ole32") - _shell32 = ctypes.OleDLL("shell32") - - _REFKNOWNFOLDERID = ctypes.c_char_p - _PPITEMIDLIST = ctypes.POINTER(ctypes.c_void_p) - - _ole32.CoTaskMemFree.restype = None - _ole32.CoTaskMemFree.argtypes = (wintypes.LPVOID,) - - _shell32.SHGetKnownFolderIDList.argtypes = ( - _REFKNOWNFOLDERID, # rfid - wintypes.DWORD, # dwFlags - wintypes.HANDLE, # hToken - _PPITEMIDLIST, - ) # ppidl - - def get_known_folder_id_list(folder_id, htoken=None): - if isinstance(folder_id, pywintypes.IIDType): - folder_id = bytes(folder_id) - pidl = ctypes.c_void_p() - try: - _shell32.SHGetKnownFolderIDList(folder_id, 0, htoken, ctypes.byref(pidl)) - return shell.AddressAsPIDL(pidl.value) - except OSError as e: - if e.winerror & 0x80070000 == 0x80070000: - # It's a WinAPI error, so re-raise it, letting Python - # raise a specific exception such as FileNotFoundError. - raise ctypes.WinError(e.winerror & 0x0000FFFF) - raise - finally: - if pidl: - _ole32.CoTaskMemFree(pidl) - - def enum_known_folder(folder_id, htoken=None): - id_list = get_known_folder_id_list(folder_id, htoken) - folder_shell_item = shell.SHCreateShellItem(None, None, id_list) - items_enum = folder_shell_item.BindToHandler( - None, shell.BHID_EnumItems, shell.IID_IEnumShellItems - ) - yield from items_enum - - def list_known_folder(folder_id, htoken=None): - result = [] - for item in enum_known_folder(folder_id, htoken): - result.append(item.GetDisplayName(shellcon.SIGDN_NORMALDISPLAY)) - result.sort(key=lambda x: x.upper()) - return result - - def get_apps(): - items = {} - for item in enum_known_folder(FOLDERID_AppsFolder): - try: - property_store = item.BindToHandler( - None, shell.BHID_PropertyStore, propsys.IID_IPropertyStore - ) - app_user_model_id = property_store.GetValue( - pscon.PKEY_AppUserModel_ID - ).ToString() - - except pywintypes.error: - continue - - name = item.GetDisplayName(shellcon.SIGDN_NORMALDISPLAY) - - # exclude anything with install/uninstall... - # 'cause I don't think we don't want 'em - if "install" not in name.lower(): - items[name] = app_user_model_id - - return items - -elif app.platform == "linux": - import configparser - import re - - linux_application_directories = [ - "/usr/share/applications", - "/usr/local/share/applications", - f"{Path.home()}/.local/share/applications", - "/var/lib/flatpak/exports/share/applications", - "/var/lib/snapd/desktop/applications", - ] - xdg_data_dirs = os.environ.get("XDG_DATA_DIRS") - if xdg_data_dirs is not None: - for directory in xdg_data_dirs.split(":"): - linux_application_directories.append(f"{directory}/applications") - linux_application_directories = list(set(linux_application_directories)) - - def get_apps(): - # app shortcuts in program menu are contained in .desktop files. This function parses those files for the app name and command - items = {} - # find field codes in exec key with regex - # https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables - args_pattern = re.compile(r"\%[UufFcik]") - for base in linux_application_directories: - if os.path.isdir(base): - for entry in os.scandir(base): - if entry.name.endswith(".desktop"): - try: - config = configparser.ConfigParser(interpolation=None) - config.read(entry.path) - # only parse shortcuts that are not hidden - if not config.has_option("Desktop Entry", "NoDisplay"): - name_key = config["Desktop Entry"]["Name"] - exec_key = config["Desktop Entry"]["Exec"] - # remove extra quotes from exec - if exec_key[0] == '"' and exec_key[-1] == '"': - exec_key = re.sub('"', "", exec_key) - # remove field codes and add full path if necessary - if exec_key[0] == "/": - items[name_key] = re.sub(args_pattern, "", exec_key) - else: - exec_path = ( - subprocess.check_output( - ["which", exec_key.split()[0]], - stderr=subprocess.DEVNULL, - ) - .decode("utf-8") - .strip() - ) - items[name_key] = ( - exec_path - + " " - + re.sub( - args_pattern, - "", - " ".join(exec_key.split()[1:]), - ) - ) - except Exception: - print( - "linux get_apps(): skipped parsing application file ", - entry.name, - ) - return items - -elif app.platform == "mac": - mac_application_directories = [ - "/Applications", - "/Applications/Utilities", - "/System/Applications", - "/System/Applications/Utilities", - f"{Path.home()}/Applications", - f"{Path.home()}/.nix-profile/Applications", - ] - - def get_apps(): - items = {} - for base in mac_application_directories: - base = os.path.expanduser(base) - if os.path.isdir(base): - for name in os.listdir(base): - path = os.path.join(base, name) - name = name.rsplit(".", 1)[0].lower() - items[name] = path - return items - - -@mod.capture(rule="{self.running}") # | )") -def running_applications(m) -> str: - "Returns a single application name" - try: - return m.running - except AttributeError: - return m.text - - -@mod.capture(rule="{self.launch}") -def launch_applications(m) -> str: - "Returns a single application name" - return m.launch - - -def update_running_list(): - global running_application_dict - running_application_dict = {} - running = {} - foreground_apps = ui.apps(background=False) - - for cur_app in foreground_apps: - running_application_dict[cur_app.name.lower()] = cur_app.name - - if app.platform == "windows": - exe = os.path.basename(cur_app.exe) - running_application_dict[exe.lower()] = exe - - override_apps = excludes.union(overrides.values()) - - running = actions.user.create_spoken_forms_from_list( - [ - curr_app.name - for curr_app in ui.apps(background=False) - if curr_app.name.lower() not in override_apps - and curr_app.exe.lower() not in override_apps - and os.path.basename(curr_app.exe).lower() not in override_apps - ], - words_to_exclude=words_to_exclude, - generate_subsequences=True, - ) - - for running_name, full_application_name in overrides.items(): - if running_app_name := running_application_dict.get(full_application_name): - running[running_name] = running_app_name - - ctx.lists["self.running"] = running - - -def update_overrides(name, flags): - """Updates the overrides and excludes lists""" - global overrides, excludes - - if name is None or os.path.normcase(name) == override_file_path: - overrides = {} - excludes = set() - - # print("update_overrides") - with open(override_file_path) as f: - for line in f: - line = line.rstrip().lower() - line = line.split(",") - if len(line) == 2 and line[0] != "Spoken form": - overrides[line[0]] = line[1].strip() - if len(line) == 1: - excludes.add(line[0].strip()) - - update_running_list() - - -@mod.action_class -class Actions: - def get_running_app(name: str) -> ui.App: - """Get the first available running app with `name`.""" - # We should use the capture result directly if it's already in the list - # of running applications. Otherwise, name is from and we - # can be a bit fuzzier - if name.lower() not in running_application_dict: - if len(name) < 3: - raise RuntimeError( - f'Skipped getting app: "{name}" has less than 3 chars.' - ) - for running_name, full_application_name in ctx.lists[ - "self.running" - ].items(): - if running_name == name or running_name.lower().startswith( - name.lower() - ): - name = full_application_name - break - for application in ui.apps(background=False): - if application.name == name or ( - app.platform == "windows" - and os.path.basename(application.exe).lower() == name - ): - return application - raise RuntimeError(f'App not running: "{name}"') - - def switcher_focus(name: str): - """Focus a new application by name""" - app = actions.user.get_running_app(name) - - # Focus next window on same app - if app == ui.active_app(): - actions.app.window_next() - # Focus new app - else: - actions.user.switcher_focus_app(app) - - def switcher_focus_app(app: ui.App): - """Focus application and wait until switch is made""" - app.focus() - t1 = time.perf_counter() - while ui.active_app() != app: - if time.perf_counter() - t1 > 1: - raise RuntimeError(f"Can't focus app: {app.name}") - actions.sleep(0.1) - - def switcher_focus_last(): - """Focus last window/application""" - - def switcher_focus_window(window: ui.Window): - """Focus window and wait until switch is made""" - window.focus() - t1 = time.perf_counter() - while ui.active_window() != window: - if time.perf_counter() - t1 > 1: - raise RuntimeError(f"Can't focus window: {window.title}") - actions.sleep(0.1) - - def switcher_launch(path: str): - """Launch a new application by path (all OSes), or AppUserModel_ID path on Windows""" - if app.platform == "mac": - ui.launch(path=path) - elif app.platform == "linux": - # Could potentially be merged with OSX code. Done in this explicit - # way for expediency around the 0.4 release. - cmd = shlex.split(path)[0] - args = shlex.split(path)[1:] - ui.launch(path=cmd, args=args) - elif app.platform == "windows": - is_valid_path = False - try: - current_path = Path(path) - is_valid_path = current_path.is_file() - except: - is_valid_path = False - if is_valid_path: - ui.launch(path=path) - else: - cmd = f"explorer.exe shell:AppsFolder\\{path}" - subprocess.Popen(cmd, shell=False) - else: - print("Unhandled platform in switcher_launch: " + app.platform) - - def switcher_menu(): - """Open a menu of running apps to switch to""" - if app.platform == "windows": - actions.key("alt-ctrl-tab") - elif app.platform == "mac": - # MacOS equivalent is "Mission Control" - actions.user.dock_send_notification("com.apple.expose.awake") - else: - print("Persistent Switcher Menu not supported on " + app.platform) - - def switcher_toggle_running(): - """Shows/hides all running applications""" - if gui_running.showing: - gui_running.hide() - else: - gui_running.show() - - def switcher_hide_running(): - """Hides list of running applications""" - gui_running.hide() - - -@imgui.open() -def gui_running(gui: imgui.GUI): - gui.text("Running applications (with spoken forms)") - gui.line() - running_apps = sorted( - (v.lower(), k, v) for k, v in ctx.lists["self.running"].items() - ) - for _, running_name, full_application_name in running_apps: - gui.text(f"{full_application_name}: {running_name}") - - gui.spacer() - if gui.button("Running close"): - actions.user.switcher_hide_running() - - -def update_launch_list(): - launch = get_apps() - - # actions.user.talon_pretty_print(launch) - - ctx.lists["self.launch"] = actions.user.create_spoken_forms_from_map( - launch, words_to_exclude - ) - - -def ui_event(event, arg): - if event in ("app_launch", "app_close"): - update_running_list() - - -# Talon starts faster if you don't use the `talon.ui` module during launch - - -def on_ready(): - update_overrides(None, None) - fs.watch(overrides_directory, update_overrides) - update_launch_list() - update_running_list() - ui.register("", ui_event) - - -app.register("ready", on_ready) diff --git a/talon/user/community/core/create_spoken_forms.py b/talon/user/community/core/create_spoken_forms.py deleted file mode 100644 index 508e8a9..0000000 --- a/talon/user/community/core/create_spoken_forms.py +++ /dev/null @@ -1,536 +0,0 @@ -import itertools -import re -from collections import defaultdict -from dataclasses import dataclass -from typing import Any, List, Mapping, Optional - -from talon import Module, actions - -from .keys.keys import symbol_key_words -from .numbers.numbers import digits_map, scales, teens, tens -from .user_settings import track_csv_list - -mod = Module() - -DEFAULT_MINIMUM_TERM_LENGTH = 2 -EXPLODE_MAX_LEN = 3 -FANCY_REGULAR_EXPRESSION = r"[A-Z]?[a-z]+|[A-Z]+(?![a-z])|[0-9]+" -SYMBOLS_REGEX = "|".join(re.escape(symbol) for symbol in set(symbol_key_words.values())) -FILE_EXTENSIONS_REGEX = r"^\b$" -file_extensions = {} - - -def update_regex(): - global REGEX_NO_SYMBOLS - global REGEX_WITH_SYMBOLS - REGEX_NO_SYMBOLS = re.compile( - "|".join( - [ - FANCY_REGULAR_EXPRESSION, - FILE_EXTENSIONS_REGEX, - ] - ) - ) - REGEX_WITH_SYMBOLS = re.compile( - "|".join([FANCY_REGULAR_EXPRESSION, FILE_EXTENSIONS_REGEX, SYMBOLS_REGEX]) - ) - - -update_regex() - - -@track_csv_list("file_extensions.csv", headers=("File extension", "Name")) -def on_extensions(values): - global FILE_EXTENSIONS_REGEX - global file_extensions - file_extensions = values - FILE_EXTENSIONS_REGEX = "|".join( - re.escape(file_extension.strip()) + "$" for file_extension in values.values() - ) - update_regex() - - -abbreviations_list = {} - - -@track_csv_list("abbreviations.csv", headers=("Abbreviation", "Spoken Form")) -def on_abbreviations(values): - global abbreviations_list - abbreviations_list = values - - -REVERSE_PRONUNCIATION_MAP = { - **{str(value): key for key, value in digits_map.items()}, - **{value: key for key, value in symbol_key_words.items()}, -} - -# begin: create the lists etc necessary for create_spoken_word_for_number -# by convention, each entry in the list has an append space... until I clean up the function -# the algorithm's expectation is slightly different from numbers.py - -# ["", "one ", "two ",... "nine "] or equivalents -ones = [""] + [ - REVERSE_PRONUNCIATION_MAP[str(index)] for index in range(10) if index != 0 -] - -# ["","","twenty","thirty","forty",..."ninety"] -# or equivalent -twenties = ["", ""] + list(tens) -# print("twenties = " + str(twenties)) - -thousands = [""] + [val for index, val in enumerate(scales) if index != 0] -# print("thousands = " + thousands) -# end: create the lists necessary for create_spoken_word_for_number - - -def create_spoken_form_for_number(num: int): - """Creates a spoken form for an integer""" - - n3 = [] - r1 = "" - # create numeric string - ns = str(num) - for k in range(3, 33, 3): - r = ns[-k:] - q = len(ns) - k - # break if end of ns has been reached - if q < -2: - break - else: - if q >= 0: - n3.append(int(r[:3])) - elif q >= -1: - n3.append(int(r[:2])) - elif q >= -2: - n3.append(int(r[:1])) - r1 = r - - # break each group of 3 digits into - # ones, tens/twenties, hundreds - words = [] - for i, x in enumerate(n3): - b1 = x % 10 - b2 = (x % 100) // 10 - b3 = (x % 1000) // 100 - if x == 0: - continue # skip - else: - t = thousands[i] - - # print(str(b1) + ", " + str(b2) + ", " + str(b3)) - if b2 == 0: - words = [ones[b1], t] + words - elif b2 == 1: - words = [teens[b1], t] + words - elif b2 > 1: - words = [twenties[b2], ones[b1], t] + words - if b3 > 0: - words = [ones[b3], scales[0]] + words - - # filter out the empty strings and join - return " ".join(filter(None, words)) - - -def create_spoken_form_years(num: str): - """Creates spoken forms for numbers 1000 <= num <= 9999. Returns None if not supported""" - - val = int(num) - if val > 9999 or val < 1000: - return None - - centuries = val // 100 - remainder = val % 100 - - words = [] - - if centuries % 10 != 0: - words.append(create_spoken_form_for_number(centuries)) - - # 1900 -> nineteen hundred - if remainder == 0: - words.append(scales[0]) - else: - # 200X -> two thousand x - if remainder < 9: - words.append(REVERSE_PRONUNCIATION_MAP[str(centuries // 10)]) - words.append(scales[1]) - - # 20XX => twenty xx - else: - words.append(create_spoken_form_for_number(str(centuries))) - - if remainder != 0: - # 1906 => "nineteen six" - if remainder < 10: - # todo: decide if we want nineteen oh five" - # todo: decide if we want "and" - # both seem like a waste - # if centuries % 10 != 0: - # words.append("oh") - - words.append(REVERSE_PRONUNCIATION_MAP[str(remainder)]) - else: - words.append(create_spoken_form_for_number(remainder)) - - return " ".join(words) - - -# # ---------- create_spoken_form_years (uncomment to run) ---------- -# def test_year(year: str, expected: str): -# result = create_spoken_form_years(year) -# print(f"test_year: test string = {year}, result = {result}, expected = {expected}") -# assert create_spoken_form_years(year) == expected - - -# print("************* test_year tests ******************") -# test_year("1100", "eleven hundred") -# test_year("1905", "nineteen five") -# test_year("1910", "nineteen ten") -# test_year("1925", "nineteen twenty five") -# test_year("2000", "two thousand") -# test_year("2005", "two thousand five") -# test_year("2020", "twenty twenty") -# test_year("2019", "twenty nineteen") -# test_year("2085", "twenty eighty five") -# test_year("2100", "twenty one hundred") -# test_year("2105", "twenty one five") -# test_year("9999", "ninety nine ninety nine") -# print("************* test_year tests done**************") - - -def create_single_spoken_form(source: str): - """ - Returns a spoken form of a string - (1) Returns the value from REVERSE_PRONUNCIATION_MAP if it exists - (2) Splits allcaps into separate letters ("ABC" -> A B C) - (3) Otherwise, returns the lower case source. - """ - normalized_source = source.lower() - try: - mapped_source = REVERSE_PRONUNCIATION_MAP[normalized_source] - except KeyError: - # Leave completely uppercase words alone, as we can deal with them later. - # Otherwise normalized the rest to help with subsequent abbreviation lookups, - # etc. - if source.isupper(): - mapped_source = source - else: - mapped_source = source.lower() - return mapped_source - - -def create_exploded_forms(spoken_forms: List[str]): - """Exploded common packed words into separate words""" - # TODO: This could be moved somewhere else, possibly seeded from something like - # words to replace... - packed_words = {"readme": "read me"} - - new_spoken_forms = [] - for line in spoken_forms: - exploded_form = [] - # ex: "vm" or "usb" explodes into "V M" or "U S B" - - if ( - " " not in line - and line.islower() - and len(line) > 1 - and len(line) <= EXPLODE_MAX_LEN - ): - new_spoken_forms.append(line) # Keep a regular copy (ie: "nas") - new_spoken_forms.append(" ".join(line.upper())) - # ex: "readme" explodes into "read me" - else: - for word in line.split(" "): - if word in packed_words.keys(): - exploded_form.append(packed_words[word]) - else: - exploded_form.append(word) - new_spoken_forms.append(" ".join(exploded_form)) - return new_spoken_forms - - -def create_extension_forms(spoken_forms: List[str]): - """Add extension forms""" - new_spoken_forms = [] - - file_extensions_map = {v.strip(): k for k, v in file_extensions.items()} - for line in spoken_forms: - have_file_extension = False - file_extension_forms = [] - dotted_extension_form = [] - truncated_forms = [] - for substring in line.split(" "): - # NOTE: If we ever run in to file extensions in the middle of file name, the - # truncated form is going to be busted. ie: foo.md.disabled - - if substring in file_extensions_map.keys(): - file_extension_forms.append(file_extensions_map[substring]) - dotted_extension_form.append(REVERSE_PRONUNCIATION_MAP["."]) - dotted_extension_form.append(file_extensions_map[substring]) - have_file_extension = True - # purposefully down update truncated - else: - file_extension_forms.append(substring) - dotted_extension_form.append(substring) - truncated_forms.append(substring) - # print(file_extension_forms) - if have_file_extension: - new_spoken_forms.append(" ".join(file_extension_forms)) - new_spoken_forms.append(" ".join(dotted_extension_form)) - new_spoken_forms.append(" ".join(truncated_forms)) - - return set(dict.fromkeys(new_spoken_forms)) - - -def create_cased_forms(spoken_forms: List[str]): - """Add lower and upper case forms""" - new_spoken_forms = [] - - for line in spoken_forms: - lower_forms = [] - upper_forms = [] - # print(line) - for substring in line.split(" "): - if substring.isupper(): - lower_forms.append(substring.lower()) - upper_forms.append(" ".join(substring)) - else: - lower_forms.append(substring) - upper_forms.append(substring) - - new_spoken_forms.append(" ".join(lower_forms)) - new_spoken_forms.append(" ".join(upper_forms)) - - return set(dict.fromkeys(new_spoken_forms)) - - -def create_abbreviated_forms(spoken_forms: List[str]): - """Add abbreviated case forms""" - new_spoken_forms = [] - - swapped_abbreviation_map = {v: k for k, v in abbreviations_list.items()} - for line in spoken_forms: - unabbreviated_forms = [] - abbreviated_forms = [] - for substring in line.split(" "): - if substring in swapped_abbreviation_map.keys(): - abbreviated_forms.append(swapped_abbreviation_map[substring]) - else: - abbreviated_forms.append(substring) - unabbreviated_forms.append(substring) - - new_spoken_forms.append(" ".join(abbreviated_forms)) - new_spoken_forms.append(" ".join(unabbreviated_forms)) - - return set(dict.fromkeys(new_spoken_forms)) - - -def create_spoken_number_forms(source: List[str]): - """ - Create a list of spoken forms by transforming numbers in source into spoken forms. - This creates a first pass of spoken forms with numbers translated, but will go - through multiple other passes. - """ - - # list of spoken forms returned - spoken_forms = [] - - # contains the pieces for the spoken form with individual digits - full_form_digit_wise = [] - - # contains the pieces for the spoken form with the spoken version of the number - full_form_fancy_numbers = [] - - # contains the pieces for the spoken form for years like "1900" => nineteen hundred - full_form_spoken_form_years = [] - - # indicates whether or not we processed created a version with the full number (>10) translated - has_fancy_number_version = False - - # indicates whether or not we processed created a version with the year-like ("1900" => nineteen hundred) numbers - has_spoken_form_years = False - - for substring in source: - # for piece in pieces: - # substring = piece.group(0) - length = len(substring) - - # the length is currently capped at 31 digits - if length > 1 and length <= 31 and substring.isnumeric(): - has_fancy_number_version = True - val = int(substring) - spoken_form_years = create_spoken_form_years(val) - spoken_form = create_spoken_form_for_number(val) - - if spoken_form_years: - has_spoken_form_years = True - full_form_spoken_form_years.append(spoken_form_years) - else: - full_form_spoken_form_years.append(spoken_form) - - full_form_fancy_numbers.append(spoken_form) - - # build the serial digit version - for digit in substring: - full_form_digit_wise.append(create_single_spoken_form(digit)) - - else: - spoken_form = create_single_spoken_form(substring) - full_form_digit_wise.append(spoken_form) - full_form_fancy_numbers.append(spoken_form) - full_form_spoken_form_years.append(spoken_form) - - if has_fancy_number_version: - spoken_forms.append(" ".join(full_form_fancy_numbers)) - - if has_spoken_form_years: - result = " ".join(full_form_spoken_form_years) - if result not in spoken_forms: - spoken_forms.append(result) - - spoken_forms.append(" ".join(full_form_digit_wise)) - return set(dict.fromkeys(spoken_forms)) - - -def create_spoken_forms_from_regex(source: str, pattern: re.Pattern): - """ - Creates a list of spoken forms for source using the provided regex pattern. - For numeric pieces detected by the regex, generates both digit-wise and full - spoken forms for the numbers where appropriate. - """ - pieces = list(pattern.finditer(source)) - spoken_forms = list(map(lambda x: x.group(0), pieces)) - - # NOTE: Order is sometimes important - transforms = [ - create_spoken_number_forms, - create_extension_forms, - create_cased_forms, - create_exploded_forms, - create_abbreviated_forms, - create_extension_forms, - ] - - for func in transforms: - spoken_forms = func(spoken_forms) - - return list(dict.fromkeys(spoken_forms)) - - -def generate_string_subsequences( - source: str, - words_to_exclude: list[str], - minimum_term_length: int, -): - # Includes (lower-cased): - # 1. Each word in source, eg "foo bar baz" -> "foo", "bar", "baz". - # 2. Each leading subsequence of words from source, - # eg "foo bar baz" -> "foo", "foo bar", "foo bar baz" - # (but not "bar baz" - TODO: is this intentional?) - # - # Except for: - # 3. strings shorter than minimum_term_length - # 4. strings in words_to_exclude. - term_sequence = source.split(" ") - terms = { - # WARNING: This .lower() version creates unwanted duplication of broken up - # uppercase words, eg 'R E A D M E' -> 'r e a d m e'. Everything else should be - # lower case already - # term.lower().strip() - term.strip() - for term in ( - term_sequence - + list(itertools.accumulate([f"{term} " for term in term_sequence])) - ) - } - return [ - term - for term in terms - if (term not in words_to_exclude and len(term) >= minimum_term_length) - ] - - -@dataclass -class SpeakableItem: - name: str - value: Any - - -@mod.action_class -class Actions: - def create_spoken_forms( - source: str, - words_to_exclude: Optional[list[str]] = None, - minimum_term_length: int = DEFAULT_MINIMUM_TERM_LENGTH, - generate_subsequences: bool = True, - ) -> list[str]: - """Create spoken forms for a given source""" - - spoken_forms_without_symbols = create_spoken_forms_from_regex( - source, REGEX_NO_SYMBOLS - ) - - # todo: this could probably be optimized out if there's no symbols - spoken_forms_with_symbols = create_spoken_forms_from_regex( - source, REGEX_WITH_SYMBOLS - ) - - # some may be identical, so ensure the list is reduced - spoken_forms = set(spoken_forms_with_symbols + spoken_forms_without_symbols) - - # only generate the subsequences if requested - if generate_subsequences: - # todo: do we care about the subsequences that are excluded. - # the only one that seems relevant are the full spoken form for - spoken_forms.update( - generate_string_subsequences( - spoken_forms_without_symbols[-1], - words_to_exclude or [], - minimum_term_length, - ) - ) - - # Avoid empty spoken forms. - return [x for x in spoken_forms if x] - - def create_spoken_forms_from_list( - sources: list[str], - words_to_exclude: Optional[list[str]] = None, - minimum_term_length: int = DEFAULT_MINIMUM_TERM_LENGTH, - generate_subsequences: bool = True, - ) -> dict[str, str]: - """Create spoken forms for all sources in a list, doing conflict resolution""" - return actions.user.create_spoken_forms_from_map( - {source: source for source in sources}, - words_to_exclude, - minimum_term_length, - generate_subsequences, - ) - - def create_spoken_forms_from_map( - sources: Mapping[str, Any], - words_to_exclude: Optional[list[str]] = None, - minimum_term_length: int = DEFAULT_MINIMUM_TERM_LENGTH, - generate_subsequences: bool = True, - ) -> dict[str, Any]: - """Create spoken forms for all sources in a map, doing conflict resolution""" - all_spoken_forms: defaultdict[str, list[SpeakableItem]] = defaultdict(list) - - for name, value in sources.items(): - spoken_forms = actions.user.create_spoken_forms( - name, words_to_exclude, minimum_term_length, generate_subsequences - ) - for spoken_form in spoken_forms: - all_spoken_forms[spoken_form].append(SpeakableItem(name, value)) - - final_spoken_forms = {} - for spoken_form, spoken_form_sources in all_spoken_forms.items(): - if len(spoken_form_sources) > 1: - final_spoken_forms[spoken_form] = min( - spoken_form_sources, - key=lambda speakable_item: len(speakable_item.name), - ).value - else: - final_spoken_forms[spoken_form] = spoken_form_sources[0].value - - return final_spoken_forms diff --git a/talon/user/community/core/deprecations.py b/talon/user/community/core/deprecations.py deleted file mode 100644 index 00abe45..0000000 --- a/talon/user/community/core/deprecations.py +++ /dev/null @@ -1,179 +0,0 @@ -""" -Helpers for deprecating voice commands, actions, and captures. Since Talon can -be an important part of people's workflows providing a warning before removing -functionality is encouraged. - -The normal deprecation process in `community` is as follows: - -1. For 6 months from deprecation a deprecated action or command should - continue working. Put an entry in the BREAKING_CHANGES.txt file in the - project root to mark the deprecation and potentially explain how users can - migrate. Use the user.deprecate_command, user.deprecate_action, or - user.deprecate_capture actions to notify users. -2. After 6 months you can delete the deprecated command, action, or capture. - Leave the note in BREAKING_CHANGES.txt so people who missed the - notifications can see what happened. - -If for some reason you can't keep the functionality working for 6 months, -just put the information in BREAKING_CHANGES.txt so people can look there to -see what happened. - -Usages: - - # myfile.talon - demonstrate voice command deprecation - ... - old legacy command: - # This will show a notification to say use 'new command' instead of - # 'old legacy command'. No removal of functionality is allowed. - user.deprecate_command("2022-11-10", "old legacy command", "new command") - # perform command - - new command: - # perform command - - # myfile.py - demonstrate action deprecation - from talon import actions - - @mod.action_class - class Actions: - def legacy_action(): - actions.user.deprecate_action("2022-10-01", "user.legacy_action") - # Perform action - - # otherfile.py - demostrate capture deprecation - @mod.capture(rule="...") - def legacy_capture(m) -> str: - actions.user.deprecate_capture("2023-09-03", "user.legacy_capture") - # implement capture - -See https://github.com/talonhub/community/issues/940 for original discussion -""" - -import datetime -import os.path -import warnings - -from talon import Module, actions, settings, speech_system - -REPO_DIR = os.path.dirname(os.path.dirname(__file__)) - -mod = Module() -mod.setting( - "deprecate_warning_interval_hours", - type=float, - desc="""How long, in hours, to wait before notifying the user again of a - deprecated action/command/capture.""", - default=24, -) - -# Tells us the last time a notification was shown so we can -# decide when to re-show it without annoying the user too -# much -notification_last_shown = {} - -# This gets reset on every phrase, so we avoid notifying more than once per -# phrase. -notified_in_phrase = set() - - -def calculate_rule_info(): - """ - Try to work out the .talon file and line of the command that is executing - """ - try: - current_command = actions.core.current_command__unstable() - start_line = current_command[0].target.start_line - filename = current_command[0].target.filename - rule = " ".join(current_command[1]._unmapped) - return f'\nTriggered from "{rule}" ({filename}:{start_line})' - except Exception as e: - return "" - - -def deprecate_notify(id: str, message: str): - """ - Notify the user about a deprecation/deactivation. id uniquely - identifies this deprecation. - """ - - maybe_last_shown = notification_last_shown.get(id) - now = datetime.datetime.now() - interval = settings.get("user.deprecate_warning_interval_hours") - threshold = now - datetime.timedelta(hours=interval) - if maybe_last_shown is not None and maybe_last_shown > threshold: - return - - actions.app.notify(message, "Deprecation warning") - notification_last_shown[id] = now - - -def post_phrase(_ignored): - global notified_in_phrase - notified_in_phrase = set() - - -speech_system.register("post:phrase", post_phrase) - - -@mod.action_class -class Actions: - def deprecate_command(time_deprecated: str, name: str, replacement: str): - """ - Notify the user that the given voice command is deprecated and should - not be used into the future; the command `replacement` should be used - instead. - """ - - if name in notified_in_phrase: - return - - # Want to tell users every time they use a deprecated command since - # they should immediately be retraining to use {replacement}. Also - # so if they repeat the command they get another chance to read - # the popup message. - notified_in_phrase.add(name) - msg = ( - f'The "{name}" command is deprecated. Instead, say: "{replacement}".' - f" See log for more." - ) - actions.app.notify(msg, "Deprecation warning") - msg = ( - f'The "{name}" command is deprecated since {time_deprecated}.' - f' Instead, say: "{replacement}".' - f' See {os.path.join(REPO_DIR, "BREAKING_CHANGES.txt")}' - ) - warnings.warn(msg, DeprecationWarning) - - def deprecate_capture(time_deprecated: str, name: str): - """ - Notify the user that the given capture is deprecated and should - not be used into the future. - """ - - id = f"capture.{name}.{time_deprecated}" - - deprecate_notify(id, f"The `{name}` capture is deprecated. See log for more.") - - msg = ( - f"The `{name}` capture is deprecated since {time_deprecated}." - f' See {os.path.join(REPO_DIR, "BREAKING_CHANGES.txt")}' - f"{calculate_rule_info()}" - ) - warnings.warn(msg, DeprecationWarning, stacklevel=3) - - def deprecate_action(time_deprecated: str, name: str): - """ - Notify the user that the given action is deprecated and should - not be used into the future. - """ - - id = f"action.{name}.{time_deprecated}" - - deprecate_notify(id, f"The `{name}` action is deprecated. See log for more.") - - msg = ( - f"The `{name}` action is deprecated since {time_deprecated}." - f' See {os.path.join(REPO_DIR, "BREAKING_CHANGES.txt")}' - f"{calculate_rule_info()}" - ) - warnings.warn(msg, DeprecationWarning, stacklevel=5) diff --git a/talon/user/community/core/dragon_engine.py b/talon/user/community/core/dragon_engine.py deleted file mode 100644 index 1058dc7..0000000 --- a/talon/user/community/core/dragon_engine.py +++ /dev/null @@ -1,22 +0,0 @@ -from talon import Module, speech_system - -mod = Module() - - -@mod.action_class -class Actions: - def dragon_engine_sleep(): - """Sleep the dragon engine""" - speech_system.engine_mimic("go to sleep"), - - def dragon_engine_wake(): - """Wake the dragon engine""" - speech_system.engine_mimic("wake up"), - - def dragon_engine_command_mode(): - """Switch dragon to command mode. Requires Pro.""" - speech_system.engine_mimic("switch to command mode") - - def dragon_engine_normal_mode(): - """Switch dragon to normal mode. Requires Pro.""" - speech_system.engine_mimic("start normal mode") diff --git a/talon/user/community/core/edit/edit.py b/talon/user/community/core/edit/edit.py deleted file mode 100644 index 51bce48..0000000 --- a/talon/user/community/core/edit/edit.py +++ /dev/null @@ -1,148 +0,0 @@ -from talon import Context, Module, actions, clip, settings - -ctx = Context() -mod = Module() - -mod.setting( - "selected_text_timeout", - type=float, - default=0.25, - desc="Time in seconds to wait for the clipboard to change when trying to get selected text", -) - -END_OF_WORD_SYMBOLS = ".!?;:—_/\\|@#$%^&*()[]{}<>=+-~`" - - -@ctx.action_class("edit") -class EditActions: - def selected_text() -> str: - timeout = settings.get("user.selected_text_timeout") - with clip.capture(timeout) as s: - actions.edit.copy() - try: - return s.text() - except clip.NoChange: - return "" - - def line_insert_down(): - actions.edit.line_end() - actions.key("enter") - - def selection_clone(): - actions.edit.copy() - actions.edit.select_none() - actions.edit.paste() - - def line_clone(): - # This may not work if editor auto-indents. Is there a better way? - actions.edit.line_start() - actions.edit.extend_line_end() - actions.edit.copy() - actions.edit.right() - actions.key("enter") - actions.edit.paste() - - # # This simpler implementation of select_word mostly works, but in some apps it doesn't. - # # See https://github.com/talonhub/community/issues/1084. - # def select_word(): - # actions.edit.right() - # actions.edit.word_left() - # actions.edit.extend_word_right() - - def select_word(): - actions.edit.extend_right() - character_to_right_of_initial_caret_position = actions.edit.selected_text() - - # Occasionally apps won't let you edit.extend_right() - # and therefore won't select text if your caret is on the rightmost character - # such as in the Chrome URL bar - did_select_text = character_to_right_of_initial_caret_position != "" - - if did_select_text: - # .strip() turns newline & space characters into empty string; the empty - # string is in any other string, so this works. - if ( - character_to_right_of_initial_caret_position.strip() - in END_OF_WORD_SYMBOLS - ): - # Come out of the highlight in the initial position. - actions.edit.left() - else: - # Come out of the highlight one character - # to the right of the initial position. - actions.edit.right() - - actions.edit.word_left() - actions.edit.extend_word_right() - - -@mod.action_class -class Actions: - def paste(text: str): - """Pastes text and preserves clipboard""" - - with clip.revert(): - clip.set_text(text) - actions.edit.paste() - # sleep here so that clip.revert doesn't revert the clipboard too soon - actions.sleep("150ms") - - def delete_right(): - """Delete character to the right""" - actions.key("delete") - - def words_left(n: int): - """Moves left by n words.""" - for _ in range(n): - actions.edit.word_left() - - def words_right(n: int): - """Moves right by n words.""" - for _ in range(n): - actions.edit.word_right() - - def cut_word_left(): - """Cuts the word to the left.""" - actions.edit.extend_word_left() - actions.edit.cut() - - def cut_word_right(): - """Cuts the word to the right.""" - actions.edit.extend_word_right() - actions.edit.cut() - - def copy_word_left(): - """Copies the word to the left.""" - actions.edit.extend_word_left() - actions.edit.copy() - - def copy_word_right(): - """Copies the word to the right.""" - actions.edit.extend_word_right() - actions.edit.copy() - - # ----- Start / End of line ----- - def select_line_start(): - """Select to start of current line""" - if actions.edit.selected_text(): - actions.edit.left() - actions.edit.extend_line_start() - - def select_line_end(): - """Select to end of current line""" - if actions.edit.selected_text(): - actions.edit.right() - actions.edit.extend_line_end() - - def line_middle(): - """Go to the middle of the line""" - actions.edit.select_line() - half_line_length = int(len(actions.edit.selected_text()) / 2) - actions.edit.left() - for i in range(0, half_line_length): - actions.edit.right() - - def cut_line(): - """Cut current line""" - actions.edit.select_line() - actions.edit.cut() diff --git a/talon/user/community/core/edit/edit.talon b/talon/user/community/core/edit/edit.talon deleted file mode 100644 index bfb3131..0000000 --- a/talon/user/community/core/edit/edit.talon +++ /dev/null @@ -1,142 +0,0 @@ -# Compound of action(select, clear, copy, cut, paste, etc.) and modifier(word, line, etc.) commands for editing text. -# eg: "select line", "clear all" - : user.edit_command(edit_action, edit_modifier) - -# Zoom -zoom in: edit.zoom_in() -zoom out: edit.zoom_out() -zoom reset: edit.zoom_reset() - -# Searching -find it: edit.find() -next one: edit.find_next() - -# Navigation - -# The reason for these spoken forms is that "page up" and "page down" are globally defined as keys. -scroll up: edit.page_up() -scroll down: edit.page_down() - -# go left, go left left down, go 5 left 2 down -# go word left, go 2 words right -go +: user.perform_navigation_steps(navigation_step_list) - -go line start | head: edit.line_start() -go line end | tail: edit.line_end() - -go way left: - edit.line_start() - edit.line_start() -go way right: edit.line_end() -go way up: edit.file_start() -go way down: edit.file_end() - -go top: edit.file_start() -go bottom: edit.file_end() - -go page up: edit.page_up() -go page down: edit.page_down() - -# Selecting - -select left: edit.extend_left() -select right: edit.extend_right() -select up: edit.extend_line_up() -select down: edit.extend_line_down() - -select word left: edit.extend_word_left() -select word right: edit.extend_word_right() - -# Indentation -indent [more]: edit.indent_more() -(indent less | out dent): edit.indent_less() - -# Delete -clear left: edit.delete() -clear right: user.delete_right() - -clear up: - edit.extend_line_up() - edit.delete() - -clear down: - edit.extend_line_down() - edit.delete() - -clear word left: - edit.extend_word_left() - edit.delete() - -clear word right: - edit.extend_word_right() - edit.delete() - -# Copy -copy that: edit.copy() -copy word left: user.copy_word_left() -copy word right: user.copy_word_right() - -#to do: do we want these variants, seem to conflict -# copy left: -# edit.extend_left() -# edit.copy() -# copy right: -# edit.extend_right() -# edit.copy() -# copy up: -# edit.extend_up() -# edit.copy() -# copy down: -# edit.extend_down() -# edit.copy() - -# Cut -cut that: edit.cut() -cut word left: user.cut_word_left() -cut word right: user.cut_word_right() - -#to do: do we want these variants -# cut left: -# edit.select_all() -# edit.cut() -# cut right: -# edit.select_all() -# edit.cut() -# cut up: -# edit.select_all() -# edit.cut() -# cut down: -# edit.select_all() -# edit.cut() - -# Paste -(pace | paste) that: edit.paste() -(pace | paste) enter: - edit.paste() - key(enter) -paste match: edit.paste_match_style() - -# Duplication -clone that: edit.selection_clone() -clone line: edit.line_clone() - -# Insert new line -new line above: edit.line_insert_up() -new line below | slap: edit.line_insert_down() - -# Insert padding with optional symbols -(pad | padding): user.insert_between(" ", " ") -(pad | padding) +: - insert(" ") - user.insert_many(symbol_key_list) - insert(" ") - -# Undo/redo -undo that: edit.undo() -redo that: edit.redo() - -# Save -file save: edit.save() -file save all: edit.save_all() - -[go] line mid: user.line_middle() diff --git a/talon/user/community/core/edit/edit_command.py b/talon/user/community/core/edit/edit_command.py deleted file mode 100644 index f1f7d21..0000000 --- a/talon/user/community/core/edit/edit_command.py +++ /dev/null @@ -1,42 +0,0 @@ -from talon import Module, actions - -from .edit_command_actions import EditAction, run_action_callback -from .edit_command_modifiers import EditModifier, run_modifier_callback - -# In some cases there already is a "compound" talon action for a given action and modifier -compound_actions = { - # Go before - ("goBefore", "line"): actions.edit.line_start, - ("goBefore", "paragraph"): actions.edit.paragraph_start, - ("goBefore", "document"): actions.edit.file_start, - ("goBefore", "fileStart"): actions.edit.file_start, - # Go after - ("goAfter", "line"): actions.edit.line_end, - ("goAfter", "paragraph"): actions.edit.paragraph_end, - ("goAfter", "document"): actions.edit.file_end, - ("goAfter", "fileEnd"): actions.edit.file_end, - # Delete - ("delete", "word"): actions.edit.delete_word, - ("delete", "line"): actions.edit.delete_line, - ("delete", "paragraph"): actions.edit.delete_paragraph, - # ("delete", "document"): actions.edit.delete_all, # Beta only - # Cut to clipboard - ("cutToClipboard", "line"): actions.user.cut_line, -} - - -mod = Module() - - -@mod.action_class -class Actions: - def edit_command(action: EditAction, modifier: EditModifier): - """Perform edit command""" - key = (action.type, modifier.type) - - if key in compound_actions: - compound_actions[key]() - return - - run_modifier_callback(modifier) - run_action_callback(action) diff --git a/talon/user/community/core/edit/edit_command_actions.py b/talon/user/community/core/edit/edit_command_actions.py deleted file mode 100644 index 9239ea5..0000000 --- a/talon/user/community/core/edit/edit_command_actions.py +++ /dev/null @@ -1,70 +0,0 @@ -from dataclasses import dataclass -from typing import Callable - -from talon import Module, actions - - -@dataclass -class EditAction: - type: str - - -@dataclass -class EditInsertAction(EditAction): - type = "insert" - text: str - - -@dataclass -class EditFormatAction(EditAction): - type = "applyFormatter" - formatters: str - - -mod = Module() -mod.list("edit_action", desc="Actions for the edit command") - - -@mod.capture(rule="{user.edit_action}") -def edit_simple_action(m) -> EditAction: - return EditAction(m.edit_action) - - -@mod.capture(rule="") -def edit_action(m) -> EditAction: - return m[0] - - -simple_action_callbacks: dict[str, Callable] = { - "select": actions.skip, - "goBefore": actions.edit.left, - "goAfter": actions.edit.right, - "copyToClipboard": actions.edit.copy, - "cutToClipboard": actions.edit.cut, - "pasteFromClipboard": actions.edit.paste, - "insertLineAbove": actions.edit.line_insert_up, - "insertLineBelow": actions.edit.line_insert_down, - "insertCopyAfter": actions.edit.selection_clone, - "delete": actions.edit.delete, -} - - -def run_action_callback(action: EditAction): - action_type = action.type - - if action_type in simple_action_callbacks: - callback = simple_action_callbacks[action_type] - callback() - return - - match action_type: - case "insert": - assert isinstance(action, EditInsertAction) - actions.insert(action.text) - - case "applyFormatter": - assert isinstance(action, EditFormatAction) - actions.user.formatters_reformat_selection(action.formatters) - - case _: - raise ValueError(f"Unknown edit action: {action_type}") diff --git a/talon/user/community/core/edit/edit_command_actions.talon-list b/talon/user/community/core/edit/edit_command_actions.talon-list deleted file mode 100644 index edd8d63..0000000 --- a/talon/user/community/core/edit/edit_command_actions.talon-list +++ /dev/null @@ -1,13 +0,0 @@ -list: user.edit_action -- - -select: select -go before: goBefore -go after: goAfter - -copy: copyToClipboard -cut: cutToClipboard -paste: pasteFromClipboard -paste to: pasteFromClipboard - -clear: delete diff --git a/talon/user/community/core/edit/edit_command_modifiers.py b/talon/user/community/core/edit/edit_command_modifiers.py deleted file mode 100644 index 2dc647b..0000000 --- a/talon/user/community/core/edit/edit_command_modifiers.py +++ /dev/null @@ -1,39 +0,0 @@ -from dataclasses import dataclass -from typing import Callable - -from talon import Module, actions - -mod = Module() -mod.list("edit_modifier", desc="Modifiers for the edit command") - - -@dataclass -class EditModifier: - type: str - - -@mod.capture(rule="{user.edit_modifier}") -def edit_modifier(m) -> EditModifier: - return EditModifier(m.edit_modifier) - - -modifier_callbacks: dict[str, Callable] = { - "document": actions.edit.select_all, - "paragraph": actions.edit.select_paragraph, - "word": actions.edit.select_word, - "line": actions.edit.select_line, - "lineEnd": actions.user.select_line_end, - "lineStart": actions.user.select_line_start, - "fileStart": actions.edit.extend_file_start, - "fileEnd": actions.edit.extend_file_end, -} - - -def run_modifier_callback(modifier: EditModifier): - modifier_type = modifier.type - - if modifier_type not in modifier_callbacks: - raise ValueError(f"Unknown edit modifier: {modifier_type}") - - callback = modifier_callbacks[modifier_type] - callback() diff --git a/talon/user/community/core/edit/edit_command_modifiers.talon-list b/talon/user/community/core/edit/edit_command_modifiers.talon-list deleted file mode 100644 index 5ce44c9..0000000 --- a/talon/user/community/core/edit/edit_command_modifiers.talon-list +++ /dev/null @@ -1,14 +0,0 @@ -list: user.edit_modifier -- -all: document -paragraph: paragraph -word: word -line: line -line start: lineStart -way left: lineStart -line end: lineEnd -way right: lineEnd -file start: fileStart -way up: fileStart -file end: fileEnd -way down: fileEnd diff --git a/talon/user/community/core/edit/edit_linux.py b/talon/user/community/core/edit/edit_linux.py deleted file mode 100644 index f2e7496..0000000 --- a/talon/user/community/core/edit/edit_linux.py +++ /dev/null @@ -1,194 +0,0 @@ -# defines the default edit actions for linux - -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -os: linux -""" - - -@ctx.action_class("edit") -class EditActions: - def copy(): - actions.key("ctrl-c") - - def cut(): - actions.key("ctrl-x") - - def delete(): - actions.key("backspace") - - def delete_line(): - actions.edit.select_line() - actions.edit.delete() - # action(edit.delete_paragraph): - # action(edit.delete_sentence): - - def delete_word(): - actions.edit.select_word() - actions.edit.delete() - - def down(): - actions.key("down") - # action(edit.extend_again): - # action(edit.extend_column): - - def extend_down(): - actions.key("shift-down") - - def extend_file_end(): - actions.key("shift-ctrl-end") - - def extend_file_start(): - actions.key("shift-ctrl-home") - - def extend_left(): - actions.key("shift-left") - # action(edit.extend_line): - - def extend_line_down(): - actions.key("shift-down") - - def extend_line_end(): - actions.key("shift-end") - - def extend_line_start(): - actions.key("shift-home") - - def extend_line_up(): - actions.key("shift-up") - - def extend_page_down(): - actions.key("shift-pagedown") - - def extend_page_up(): - actions.key("shift-pageup") - # action(edit.extend_paragraph_end): - # action(edit.extend_paragraph_next()): - # action(edit.extend_paragraph_previous()): - # action(edit.extend_paragraph_start()): - - def extend_right(): - actions.key("shift-right") - # action(edit.extend_sentence_end): - # action(edit.extend_sentence_next): - # action(edit.extend_sentence_previous): - # action(edit.extend_sentence_start): - - def extend_up(): - actions.key("shift-up") - - def extend_word_left(): - actions.key("ctrl-shift-left") - - def extend_word_right(): - actions.key("ctrl-shift-right") - - def file_end(): - actions.key("ctrl-end") - - def file_start(): - actions.key("ctrl-home") - - def find(text: str = None): - actions.key("ctrl-f") - if text: - actions.insert(text) - - def find_previous(): - actions.key("shift-f3") - - def find_next(): - actions.key("f3") - - def indent_less(): - actions.key("home delete") - - def indent_more(): - actions.key("home tab") - # action(edit.jump_column(n: int) - # action(edit.jump_line(n: int) - - def left(): - actions.key("left") - - def line_down(): - actions.key("down home") - - def line_end(): - actions.key("end") - - def line_insert_up(): - actions.key("home enter up") - - def line_start(): - actions.key("home") - - def line_up(): - actions.key("up home") - # action(edit.move_again): - - def page_down(): - actions.key("pagedown") - - def page_up(): - actions.key("pageup") - # action(edit.paragraph_end): - # action(edit.paragraph_next): - # action(edit.paragraph_previous): - # action(edit.paragraph_start): - - def paste(): - actions.key("ctrl-v") - # action(paste_match_style): - - def print(): - actions.key("ctrl-p") - - def redo(): - actions.key("ctrl-y") - - def right(): - actions.key("right") - - def save(): - actions.key("ctrl-s") - - def save_all(): - actions.key("ctrl-shift-s") - - def select_all(): - actions.key("ctrl-a") - - def select_line(n: int = None): - if n is not None: - actions.edit.jump_line(n) - actions.key("end shift-home") - # action(edit.select_lines(a: int, b: int)): - - def select_none(): - actions.key("right") - # action(edit.select_paragraph): - # action(edit.select_sentence): - - def undo(): - actions.key("ctrl-z") - - def up(): - actions.key("up") - - def word_left(): - actions.key("ctrl-left") - - def word_right(): - actions.key("ctrl-right") - - def zoom_in(): - actions.key("ctrl-+") - - def zoom_out(): - actions.key("ctrl--") - - def zoom_reset(): - actions.key("ctrl-0") diff --git a/talon/user/community/core/edit/edit_mac.py b/talon/user/community/core/edit/edit_mac.py deleted file mode 100644 index 5466b94..0000000 --- a/talon/user/community/core/edit/edit_mac.py +++ /dev/null @@ -1,194 +0,0 @@ -from talon import Context, actions, clip - -ctx = Context() -ctx.matches = r""" -os: mac -""" - - -@ctx.action_class("edit") -class EditActions: - def copy(): - actions.key("cmd-c") - - def cut(): - actions.key("cmd-x") - - def delete(): - actions.key("backspace") - - def delete_line(): - actions.edit.select_line() - actions.edit.delete() - # action(edit.delete_paragraph): - # action(edit.delete_sentence): - - def delete_word(): - actions.edit.select_word() - actions.edit.delete() - - def down(): - actions.key("down") - # action(edit.extend_again): - # action(edit.extend_column): - - def extend_down(): - actions.key("shift-down") - - def extend_file_end(): - actions.key("cmd-shift-down") - - def extend_file_start(): - actions.key("cmd-shift-up") - - def extend_left(): - actions.key("shift-left") - # action(edit.extend_line): - - def extend_line_down(): - actions.key("shift-down") - - def extend_line_end(): - actions.key("cmd-shift-right") - - def extend_line_start(): - actions.key("cmd-shift-left") - - def extend_line_up(): - actions.key("shift-up") - - def extend_page_down(): - actions.key("cmd-shift-pagedown") - - def extend_page_up(): - actions.key("cmd-shift-pageup") - # action(edit.extend_paragraph_end): - # action(edit.extend_paragraph_next()): - # action(edit.extend_paragraph_previous()): - # action(edit.extend_paragraph_start()): - - def extend_right(): - actions.key("shift-right") - # action(edit.extend_sentence_end): - # action(edit.extend_sentence_next): - # action(edit.extend_sentence_previous): - # action(edit.extend_sentence_start): - - def extend_up(): - actions.key("shift-up") - - def extend_word_left(): - actions.key("shift-alt-left") - - def extend_word_right(): - actions.key("shift-alt-right") - - def file_end(): - actions.key("cmd-down") - - def file_start(): - actions.key("cmd-up") - - def find(text: str = None): - if text is not None: - clip.set_text(text, mode="find") - actions.key("cmd-f") - - def find_next(): - actions.key("cmd-g") - - def find_previous(): - actions.key("cmd-shift-g") - - def indent_less(): - actions.key("cmd-left delete") - - def indent_more(): - actions.key("cmd-left tab") - # action(edit.jump_column(n: int) - # action(edit.jump_line(n: int) - - def left(): - actions.key("left") - - def line_down(): - actions.key("down home") - - def line_end(): - actions.key("cmd-right") - - def line_insert_up(): - actions.key("cmd-left enter up") - - def line_start(): - actions.key("cmd-left") - - def line_up(): - actions.key("up cmd-left") - # action(edit.move_again): - - def page_down(): - actions.key("pagedown") - - def page_up(): - actions.key("pageup") - # action(edit.paragraph_end): - # action(edit.paragraph_next): - # action(edit.paragraph_previous): - # action(edit.paragraph_start): - - def paste(): - actions.key("cmd-v") - - def paste_match_style(): - actions.key("cmd-alt-shift-v") - - def print(): - actions.key("cmd-p") - - def redo(): - actions.key("cmd-shift-z") - - def right(): - actions.key("right") - - def save(): - actions.key("cmd-s") - - def save_all(): - actions.key("cmd-alt-s") - - def select_all(): - actions.key("cmd-a") - - def select_line(n: int = None): - if n is not None: - actions.edit.jump_line(n) - actions.key("cmd-right cmd-shift-left") - # action(edit.select_lines(a: int, b: int)): - - def select_none(): - actions.key("right") - # action(edit.select_paragraph): - # action(edit.select_sentence): - - def undo(): - actions.key("cmd-z") - - def up(): - actions.key("up") - - def word_left(): - actions.key("alt-left") - - def word_right(): - actions.key("alt-right") - - def zoom_in(): - actions.key("cmd-=") - - def zoom_out(): - actions.key("cmd--") - - def zoom_reset(): - actions.key("cmd-0") diff --git a/talon/user/community/core/edit/edit_navigation_steps.py b/talon/user/community/core/edit/edit_navigation_steps.py deleted file mode 100644 index f2b4d4b..0000000 --- a/talon/user/community/core/edit/edit_navigation_steps.py +++ /dev/null @@ -1,61 +0,0 @@ -from contextlib import suppress -from dataclasses import dataclass -from typing import Callable, Literal - -from talon import Module, actions - - -@dataclass -class NavigationStep: - direction: Literal["up", "right", "down", "left"] - type: Literal["word", "character"] - count: int - - -mod = Module() - - -@mod.capture(rule="[] [word | words] {user.arrow_key}") -def navigation_step(m) -> NavigationStep: - type = "character" - count = 1 - - with suppress(IndexError): - if m[-2] in ["word", "words"]: - type = "word" - - with suppress(AttributeError): - count = m.number_small - - return NavigationStep( - direction=m.arrow_key, - type=type, - count=count, - ) - - -@mod.action_class -class Actions: - def perform_navigation_steps(steps: list[NavigationStep]): - """Navigate by a series of steps""" - for step in steps: - match step.direction: - case "up": - repeat_action(actions.edit.up, step.count) - case "down": - repeat_action(actions.edit.down, step.count) - case "left": - if step.type == "word": - repeat_action(actions.edit.word_left, step.count) - else: - repeat_action(actions.edit.left, step.count) - case "right": - if step.type == "word": - repeat_action(actions.edit.word_right, step.count) - else: - repeat_action(actions.edit.right, step.count) - - -def repeat_action(action: Callable, count: int): - for _ in range(count): - action() diff --git a/talon/user/community/core/edit/edit_paragraph.py b/talon/user/community/core/edit/edit_paragraph.py deleted file mode 100644 index 2fb244f..0000000 --- a/talon/user/community/core/edit/edit_paragraph.py +++ /dev/null @@ -1,115 +0,0 @@ -from talon import Context, Module, actions - -ctx = Context() -mod = Module() - - -@ctx.action_class("edit") -class EditActions: - def paragraph_start(): - if extend_paragraph_start_with_success(): - actions.edit.left() - - def paragraph_end(): - if extend_paragraph_end_with_success(): - actions.edit.right() - - def select_paragraph(): - if is_line_empty(): - return - # Search for start of paragraph - actions.edit.extend_paragraph_start() - actions.edit.left() - # Extend to end of paragraph - actions.edit.extend_paragraph_end() - - def extend_paragraph_start(): - # The reason for the wrapper function is a difference in function signature. - # The Talon action has no return value and the below function returns a boolean with success state. - extend_paragraph_start_with_success() - - def extend_paragraph_end(): - extend_paragraph_end_with_success() - - def delete_paragraph(): - actions.edit.select_paragraph() - # Remove selection - actions.edit.delete() - # Remove the empty line containing the cursor - actions.edit.delete() - # Remove leading or trailing empty line - actions.edit.delete_line() - - -@mod.action_class -class Actions: - def cut_paragraph(): - """Cut paragraph under the cursor""" - actions.edit.select_paragraph() - actions.edit.cut() - - def copy_paragraph(): - """Copy paragraph under the cursor""" - actions.edit.select_paragraph() - actions.edit.copy() - - def paste_paragraph(): - """Paste to paragraph under the cursor""" - actions.edit.select_paragraph() - actions.edit.paste() - - -def is_line_empty() -> bool: - """Check if the current line is empty. Return True if empty.""" - actions.edit.extend_line_start() - text = actions.edit.selected_text().strip() - if text: - actions.edit.right() - return False - actions.edit.extend_line_end() - text = actions.edit.selected_text().strip() - if text: - actions.edit.left() - return False - return True - - -def extend_paragraph_start_with_success() -> bool: - """Extend selection to the start of the paragraph. Return True if successful.""" - actions.edit.extend_line_start() - text = actions.edit.selected_text() - length = len(text) - while True: - actions.edit.extend_up() - actions.edit.extend_line_start() - text = actions.edit.selected_text() - new_length = len(text) - if new_length == length: - break - line = text[: new_length - length].strip() - if not line: - actions.edit.extend_down() - break - length = new_length - return text.strip() != "" - - -def extend_paragraph_end_with_success() -> bool: - """Extend selection to the end of the paragraph. Return True if successful.""" - actions.edit.extend_line_end() - text = actions.edit.selected_text() - length = len(text) - while True: - actions.edit.extend_down() - actions.edit.extend_line_end() - text = actions.edit.selected_text() - new_length = len(text) - if new_length == length: - break - line = text[length:].strip() - if not line: - actions.edit.extend_line_start() - actions.edit.extend_left() - break - length = new_length - return text.strip() != "" diff --git a/talon/user/community/core/edit/edit_win.py b/talon/user/community/core/edit/edit_win.py deleted file mode 100644 index e127165..0000000 --- a/talon/user/community/core/edit/edit_win.py +++ /dev/null @@ -1,194 +0,0 @@ -# defines the default edit actions for windows - -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -os: windows -""" - - -@ctx.action_class("edit") -class EditActions: - def copy(): - actions.key("ctrl-c") - - def cut(): - actions.key("ctrl-x") - - def delete(): - actions.key("backspace") - - def delete_line(): - actions.edit.select_line() - actions.edit.delete() - # action(edit.delete_paragraph): - # action(edit.delete_sentence): - - def delete_word(): - actions.edit.select_word() - actions.edit.delete() - - def down(): - actions.key("down") - # action(edit.extend_again): - # action(edit.extend_column): - - def extend_down(): - actions.key("shift-down") - - def extend_file_end(): - actions.key("shift-ctrl-end") - - def extend_file_start(): - actions.key("shift-ctrl-home") - - def extend_left(): - actions.key("shift-left") - # action(edit.extend_line): - - def extend_line_down(): - actions.key("shift-down") - - def extend_line_end(): - actions.key("shift-end") - - def extend_line_start(): - actions.key("shift-home") - - def extend_line_up(): - actions.key("shift-up") - - def extend_page_down(): - actions.key("shift-pagedown") - - def extend_page_up(): - actions.key("shift-pageup") - # action(edit.extend_paragraph_end): - # action(edit.extend_paragraph_next()): - # action(edit.extend_paragraph_previous()): - # action(edit.extend_paragraph_start()): - - def extend_right(): - actions.key("shift-right") - # action(edit.extend_sentence_end): - # action(edit.extend_sentence_next): - # action(edit.extend_sentence_previous): - # action(edit.extend_sentence_start): - - def extend_up(): - actions.key("shift-up") - - def extend_word_left(): - actions.key("ctrl-shift-left") - - def extend_word_right(): - actions.key("ctrl-shift-right") - - def file_end(): - actions.key("ctrl-end") - - def file_start(): - actions.key("ctrl-home") - - def find(text: str = None): - actions.key("ctrl-f") - if text: - actions.insert(text) - - def find_previous(): - actions.key("shift-f3") - - def find_next(): - actions.key("f3") - - def indent_less(): - actions.key("home delete") - - def indent_more(): - actions.key("home tab") - # action(edit.jump_column(n: int) - # action(edit.jump_line(n: int) - - def left(): - actions.key("left") - - def line_down(): - actions.key("down home") - - def line_end(): - actions.key("end") - - def line_insert_up(): - actions.key("home enter up") - - def line_start(): - actions.key("home") - - def line_up(): - actions.key("up home") - # action(edit.move_again): - - def page_down(): - actions.key("pagedown") - - def page_up(): - actions.key("pageup") - # action(edit.paragraph_end): - # action(edit.paragraph_next): - # action(edit.paragraph_previous): - # action(edit.paragraph_start): - - def paste(): - actions.key("ctrl-v") - # action(paste_match_style): - - def print(): - actions.key("ctrl-p") - - def redo(): - actions.key("ctrl-y") - - def right(): - actions.key("right") - - def save(): - actions.key("ctrl-s") - - def save_all(): - actions.key("ctrl-shift-s") - - def select_all(): - actions.key("ctrl-a") - - def select_line(n: int = None): - if n is not None: - actions.edit.jump_line(n) - actions.key("end shift-home") - # action(edit.select_lines(a: int, b: int)): - - def select_none(): - actions.key("right") - # action(edit.select_paragraph): - # action(edit.select_sentence): - - def undo(): - actions.key("ctrl-z") - - def up(): - actions.key("up") - - def word_left(): - actions.key("ctrl-left") - - def word_right(): - actions.key("ctrl-right") - - def zoom_in(): - actions.key("ctrl-+") - - def zoom_out(): - actions.key("ctrl--") - - def zoom_reset(): - actions.key("ctrl-0") diff --git a/talon/user/community/core/edit/insert_between.py b/talon/user/community/core/edit/insert_between.py deleted file mode 100644 index ba957cd..0000000 --- a/talon/user/community/core/edit/insert_between.py +++ /dev/null @@ -1,23 +0,0 @@ -from talon import Module, actions, app - -mod = Module() - - -@mod.action_class -class module_actions: - def insert_between(before: str, after: str): - """Insert `before + after`, leaving cursor between `before` and `after`. Not entirely reliable if `after` contains newlines.""" - actions.insert(before + after) - for _ in after: - actions.edit.left() - - # This is deprecated, please use insert_between instead. - def insert_cursor(text: str): - """Insert a string. Leave the cursor wherever [|] is in the text""" - if "[|]" in text: - actions.user.insert_between(*text.split("[|]", 1)) - else: - actions.insert(text) - app.notify( - "insert_cursor is deprecated, please update your code to use insert_between" - ) diff --git a/talon/user/community/core/edit_text_file.py b/talon/user/community/core/edit_text_file.py deleted file mode 100644 index cab76c9..0000000 --- a/talon/user/community/core/edit_text_file.py +++ /dev/null @@ -1,97 +0,0 @@ -import os -import subprocess - -from talon import Context, Module, app - -# path to community/knausj root directory -REPO_DIR = os.path.dirname(os.path.dirname(__file__)) -SETTINGS_DIR = os.path.join(REPO_DIR, "settings") - -mod = Module() -ctx = Context() -mod.list( - "edit_file", - desc="Absolute paths to frequently edited files (Talon list, CSV, etc.)", -) - -_edit_files = { - "additional words": os.path.join( - REPO_DIR, "core", "vocabulary", "vocabulary.talon-list" - ), - "alphabet": os.path.join(REPO_DIR, "core", "keys", "letter.talon-list"), - "homophones": os.path.join(REPO_DIR, "core", "homophones", "homophones.csv"), - "search engines": os.path.join( - REPO_DIR, "core", "websites_and_search_engines", "search_engine.talon-list" - ), - "unix utilities": os.path.join( - REPO_DIR, "tags", "terminal", "unix_utility.talon-list" - ), - "websites": os.path.join( - REPO_DIR, "core", "websites_and_search_engines", "website.talon-list" - ), -} - -_settings_csvs = { - name: os.path.join(SETTINGS_DIR, file_name) - for name, file_name in { - "abbreviations": "abbreviations.csv", - "file extensions": "file_extensions.csv", - "words to replace": "words_to_replace.csv", - }.items() -} - -_edit_files.update(_settings_csvs) -ctx.lists["self.edit_file"] = _edit_files - - -@mod.action_class -class ModuleActions: - def edit_text_file(path: str): - """Tries to open a file in the user's preferred text editor.""" - - -winctx, linuxctx, macctx = Context(), Context(), Context() -winctx.matches = "os: windows" -linuxctx.matches = "os: linux" -macctx.matches = "os: mac" - - -@winctx.action_class("self") -class WinActions: - def edit_text_file(path): - # If there's no applications registered that can open the given type - # of file, 'edit' will fail, but 'open' always gives the user a - # choice between applications. - try: - os.startfile(path, "edit") - except OSError: - os.startfile(path, "open") - - -@macctx.action_class("self") -class MacActions: - def edit_text_file(path): - # -t means try to open in a text editor. - open_with_subprocess(path, ["/usr/bin/open", "-t", path]) - - -@linuxctx.action_class("self") -class LinuxActions: - def edit_text_file(path): - # we use xdg-open for this even though it might not open a text - # editor. we could use $EDITOR, but that might be something that - # requires a terminal (eg nano, vi). - open_with_subprocess(path, ["/usr/bin/xdg-open", path]) - - -# Helper for linux and mac. -def open_with_subprocess(path, args): - """Tries to open a file using the given subprocess arguments.""" - try: - return subprocess.run(args, timeout=0.5, check=True) - except subprocess.TimeoutExpired: - app.notify(f"Timeout trying to open file for editing: {path}") - raise - except subprocess.CalledProcessError: - app.notify(f"Could not open file for editing: {path}") - raise diff --git a/talon/user/community/core/edit_text_file.talon b/talon/user/community/core/edit_text_file.talon deleted file mode 100644 index 690c462..0000000 --- a/talon/user/community/core/edit_text_file.talon +++ /dev/null @@ -1,4 +0,0 @@ -customize {user.edit_file}: - user.edit_text_file(edit_file) - sleep(500ms) - edit.file_end() diff --git a/talon/user/community/core/file_extension/file_extension.talon b/talon/user/community/core/file_extension/file_extension.talon deleted file mode 100644 index 9213b7d..0000000 --- a/talon/user/community/core/file_extension/file_extension.talon +++ /dev/null @@ -1 +0,0 @@ -{user.file_extension}: "{file_extension}" diff --git a/talon/user/community/core/help/help.talon b/talon/user/community/core/help/help.talon deleted file mode 100644 index 7f61917..0000000 --- a/talon/user/community/core/help/help.talon +++ /dev/null @@ -1,20 +0,0 @@ -help alphabet: user.help_list("user.letter") -help symbols: user.help_list("user.symbol_key") -help numbers: user.help_list("user.number_key") -help punctuation: user.help_list("user.punctuation") -help modifier: user.help_list("user.modifier_key") -help special keys: user.help_list("user.special_key") -help function keys: user.help_list("user.function_key") -help arrows: user.help_list("user.arrow_key") -help context$: user.help_context() -help active$: user.help_context_enabled() -help search $: user.help_search(text) -help context {user.help_contexts}$: user.help_selected_context(help_contexts) -help help: user.help_search("help") -help scope$: user.help_scope_toggle() -help snip: user.help_list("user.snippet") - -(help formatters | help format | format help): - user.help_formatters(user.get_formatters_words(), false) -(help re formatters | help re format | re format help): - user.help_formatters(user.get_reformatters_words(), true) diff --git a/talon/user/community/core/help/help_open.talon b/talon/user/community/core/help/help_open.talon deleted file mode 100644 index f46bde2..0000000 --- a/talon/user/community/core/help/help_open.talon +++ /dev/null @@ -1,8 +0,0 @@ -tag: user.help_open -- -help next$: user.help_next() -help (previous | last)$: user.help_previous() -help $: user.help_select_index(number - 1) -help return$: user.help_return() -help refresh$: user.help_refresh() -help close$: user.help_hide() diff --git a/talon/user/community/core/help/help_scope.py b/talon/user/community/core/help/help_scope.py deleted file mode 100644 index 0478573..0000000 --- a/talon/user/community/core/help/help_scope.py +++ /dev/null @@ -1,70 +0,0 @@ -from talon import Context, Module, actions, imgui, scope, settings, ui - -ctx = Context() -mod = Module() -mod.tag("help_scope_open", "tag for showing the scope help gui") - -mod.setting( - "help_scope_max_length", - type=int, - default=50, -) - - -@imgui.open(x=ui.main_screen().x) -def gui(gui: imgui.GUI): - gui.text("Scope") - gui.line() - gui.spacer() - gui.text("Modes") - gui.line() - for mode in sorted(scope.get("mode")): - gui.text(mode) - gui.spacer() - gui.text("Tags") - gui.line() - for tag in sorted(scope.get("tag")): - gui.text(tag) - gui.spacer() - gui.text("Misc") - gui.line() - ignore = {"main", "mode", "tag"} - keys = {*scope.data.keys(), *scope.data["main"].keys()} - for key in sorted(keys): - if key not in ignore: - value = scope.get(key) - print_value(gui, key, value, ignore) - gui.spacer() - if gui.button("Hide"): - actions.user.help_scope_toggle() - - -def print_value(gui: imgui.GUI, path: str, value, ignore: set[str] = {}): - if isinstance(value, dict): - for key in value: - if key not in ignore: - p = f"{path}.{key}" if path else key - print_value(gui, p, value[key]) - elif value: - gui.text(f"{path}: {format_value(value)}") - - -def format_value(value): - if isinstance(value, (list, set)): - value = ", ".join(sorted(value)) - setting_max_length = settings.get("user.help_scope_max_length") - if isinstance(value, str) and len(value) > setting_max_length + 4: - return f"{value[:setting_max_length]} ..." - return value - - -@mod.action_class -class Actions: - def help_scope_toggle(): - """Toggle help scope gui""" - if gui.showing: - ctx.tags = [] - gui.hide() - else: - ctx.tags = ["user.help_scope_open"] - gui.show() diff --git a/talon/user/community/core/help/help_scope_open.talon b/talon/user/community/core/help/help_scope_open.talon deleted file mode 100644 index 2e42b84..0000000 --- a/talon/user/community/core/help/help_scope_open.talon +++ /dev/null @@ -1,4 +0,0 @@ -tag: user.help_scope_open -- - -scope (hide | close)$: user.help_scope_toggle() diff --git a/talon/user/community/core/homophones/homophones.py b/talon/user/community/core/homophones/homophones.py deleted file mode 100644 index 32cda84..0000000 --- a/talon/user/community/core/homophones/homophones.py +++ /dev/null @@ -1,243 +0,0 @@ -import os - -from talon import Context, Module, actions, app, clip, fs, imgui, ui - -######################################################################## -# global settings -######################################################################## - -# a list of homophones where each line is a comma separated list -# e.g. where,wear,ware -# a suitable one can be found here: -# https://github.com/pimentel/homophones -cwd = os.path.dirname(os.path.realpath(__file__)) -homophones_file = os.path.join(cwd, "homophones.csv") -# if quick_replace, then when a word is selected and only one homophone exists, -# replace it without bringing up the options -quick_replace = True -show_help = False -######################################################################## - -ctx = Context() -mod = Module() - -mod.list("homophones_canonicals", desc="list of words ") -mod.tag( - "homophones_open", - desc="Tag for enabling homophones commands when the associated gui is open", -) - -main_screen = ui.main_screen() - - -def update_homophones(name, flags): - if name != homophones_file: - return - - phones = {} - canonical_list = [] - with open(homophones_file) as f: - for line in f: - words = line.rstrip().split(",") - words = [x for x in words if x.strip() != ""] - canonical_list.append(words[0]) - merged_words = set(words) - for word in words: - old_words = phones.get(word.lower(), []) - merged_words.update(old_words) - merged_words = sorted(merged_words) - for word in merged_words: - phones[word.lower()] = merged_words - - global all_homophones - all_homophones = phones - ctx.lists["self.homophones_canonicals"] = canonical_list - - -update_homophones(homophones_file, None) -fs.watch(cwd, update_homophones) -active_word_list = None -is_selection = False - - -def close_homophones(): - gui.hide() - ctx.tags = [] - - -PHONES_FORMATTERS = [ - lambda word: word.capitalize(), - lambda word: word.upper(), -] - - -def find_matching_format_function(word_with_formatting, format_functions): - """Finds the formatter function from a list of formatter functions which transforms a word into itself. - Returns an identity function if none exists""" - for formatter in format_functions: - formatted_word = formatter(word_with_formatting) - if word_with_formatting == formatted_word: - return formatter - - return lambda word: word - - -def raise_homophones(word_to_find_homophones_for, forced=False, selection=False): - global quick_replace - global active_word_list - global show_help - global force_raise - global is_selection - - force_raise = forced - is_selection = selection - - if is_selection: - word_to_find_homophones_for = word_to_find_homophones_for.strip() - formatter = find_matching_format_function( - word_to_find_homophones_for, PHONES_FORMATTERS - ) - - word_to_find_homophones_for = word_to_find_homophones_for.lower() - - # We support plurals, but very naively. If we can't find your word but your word ends in an s, presume its plural - # and attempt to find the singular, then present the presumed plurals back. This could be improved! - if word_to_find_homophones_for in all_homophones: - valid_homophones = all_homophones[word_to_find_homophones_for] - elif ( - word_to_find_homophones_for.endswith("s") - and word_to_find_homophones_for[:-1] in all_homophones - ): - valid_homophones = map( - lambda w: w + "s", all_homophones[word_to_find_homophones_for[:-1]] - ) - else: - app.notify( - "homophones.py", f'"{word_to_find_homophones_for}" not in homophones list' - ) - return - - # Move current word to end of list to reduce searcher's cognitive load - valid_homophones_reordered = list( - filter( - lambda word_from_list: word_from_list.lower() - != word_to_find_homophones_for, - valid_homophones, - ) - ) + [word_to_find_homophones_for] - active_word_list = list(map(formatter, valid_homophones_reordered)) - - if ( - is_selection - and len(active_word_list) == 2 - and quick_replace - and not force_raise - ): - if word_to_find_homophones_for == active_word_list[0].lower(): - new = active_word_list[1] - else: - new = active_word_list[0] - - clip.set(new) - actions.edit.paste() - return - - ctx.tags = ["user.homophones_open"] - show_help = False - gui.show() - - -@imgui.open(x=main_screen.x + main_screen.width / 2.6, y=main_screen.y) -def gui(gui: imgui.GUI): - global active_word_list - if show_help: - gui.text("Homophone help - todo") - else: - gui.text("Select a homophone") - gui.line() - index = 1 - for word in active_word_list: - if gui.button(f"Choose {index}: {word}"): - actions.insert(actions.user.homophones_select(index)) - actions.user.homophones_hide() - index = index + 1 - - if gui.button("Phones hide"): - actions.user.homophones_hide() - - -def show_help_gui(): - global show_help - show_help = True - gui.show() - - -@mod.capture(rule="{self.homophones_canonicals}") -def homophones_canonical(m) -> str: - "Returns a single string" - return m.homophones_canonicals - - -@mod.action_class -class Actions: - def homophones_hide(): - """Hides the homophones display""" - close_homophones() - - def homophones_show(m: str): - """Show the homophones display""" - raise_homophones(m, False, False) - - def homophones_show_auto(): - """Show homophones for selection, or current word if selection is empty.""" - text = actions.edit.selected_text() - if text: - raise_homophones(text, False, True) - else: - actions.edit.select_word() - actions.user.homophones_show_selection() - - def homophones_show_selection(): - """Show the homophones display for the selected text""" - raise_homophones(actions.edit.selected_text(), False, True) - - def homophones_force_show(m: str): - """Show the homophones display forcibly""" - raise_homophones(m, True, False) - - def homophones_force_show_selection(): - """Show the homophones display for the selected text forcibly""" - raise_homophones(actions.edit.selected_text(), True, True) - - def homophones_select(number: int) -> str: - """selects the homophone by number""" - if number <= len(active_word_list) and number > 0: - return active_word_list[number - 1] - - error = "homophones.py index {} is out of range (1-{})".format( - number, len(active_word_list) - ) - app.notify(error) - raise error - - def homophones_get(word: str) -> [str] or None: - """Get homophones for the given word""" - word = word.lower() - if word in all_homophones: - return all_homophones[word] - return None - - -ctx_homophones_open = Context() -ctx_homophones_open.matches = """ -tag: user.homophones_open -""" - - -@ctx_homophones_open.action_class("user") -class UserActions: - def choose(number_small: int): - """Choose the nth homophone""" - result = actions.user.homophones_select(number_small) - actions.insert(result) - actions.user.homophones_hide() diff --git a/talon/user/community/core/homophones/homophones.talon b/talon/user/community/core/homophones/homophones.talon deleted file mode 100644 index a2189e9..0000000 --- a/talon/user/community/core/homophones/homophones.talon +++ /dev/null @@ -1,19 +0,0 @@ -phones : user.homophones_show(homophones_canonical) -phones that: user.homophones_show_auto() -phones force : - user.homophones_force_show(homophones_canonical) -phones force: user.homophones_force_show_selection() -phones hide: user.homophones_hide() -phones word: - edit.select_word() - user.homophones_show_selection() -phones [] word left: - n = ordinals or 1 - user.words_left(n - 1) - edit.extend_word_left() - user.homophones_show_selection() -phones [] word right: - n = ordinals or 1 - user.words_right(n - 1) - edit.extend_word_right() - user.homophones_show_selection() diff --git a/talon/user/community/core/keys/arrow_key.talon-list b/talon/user/community/core/keys/arrow_key.talon-list deleted file mode 100644 index 42bc477..0000000 --- a/talon/user/community/core/keys/arrow_key.talon-list +++ /dev/null @@ -1,6 +0,0 @@ -list: user.arrow_key -- -down: down -left: left -right: right -up: up diff --git a/talon/user/community/core/keys/function_key.talon-list b/talon/user/community/core/keys/function_key.talon-list deleted file mode 100644 index 936eec4..0000000 --- a/talon/user/community/core/keys/function_key.talon-list +++ /dev/null @@ -1,27 +0,0 @@ -list: user.function_key -- -f one: f1 -f two: f2 -f three: f3 -f four: f4 -f five: f5 -f six: f6 -f seven: f7 -f eight: f8 -f nine: f9 -f ten: f10 -f eleven: f11 -f twelve: f12 -f thirteen: f13 -f fourteen: f14 -f fifteen: f15 -f sixteen: f16 -f seventeen: f17 -f eighteen: f18 -f nineteen: f19 -f twenty: f20 -# these f keys are not supported by all platforms (eg Mac) and are disabled by default -#f twenty one: f21 -#f twenty two: f22 -#f twenty three: f23 -#f twenty four: f24 diff --git a/talon/user/community/core/keys/keypad_key.talon-list b/talon/user/community/core/keys/keypad_key.talon-list deleted file mode 100644 index 286542d..0000000 --- a/talon/user/community/core/keys/keypad_key.talon-list +++ /dev/null @@ -1,21 +0,0 @@ -list: user.keypad_key -- - -key pad zero: keypad_0 -key pad one: keypad_1 -key pad two: keypad_2 -key pad three: keypad_3 -key pad four: keypad_4 -key pad five: keypad_5 -key pad six: keypad_6 -key pad seven: keypad_7 -key pad eight: keypad_8 -key pad nine: keypad_9 -key pad point: keypad_decimal -key pad plus: keypad_plus -key pad minus: keypad_minus -key pad star: keypad_multiply -key pad slash: keypad_divide -key pad equals: keypad_equals -key pad clear: keypad_clear -key pad enter: keypad_enter diff --git a/talon/user/community/core/keys/keys.talon b/talon/user/community/core/keys/keys.talon deleted file mode 100644 index 048cf3f..0000000 --- a/talon/user/community/core/keys/keys.talon +++ /dev/null @@ -1,12 +0,0 @@ -: key(letter) -(ship | uppercase) [(lowercase | sunk)]: - user.insert_formatted(letters, "ALL_CAPS") -: key(symbol_key) -: key(function_key) -: key(special_key) -: key(keypad_key) - : key("{modifiers}-{unmodified_key}") -# for key combos consisting only of modifiers, eg. `press super`. -press : key(modifiers) -# for consistency with dictation mode and explicit arrow keys if you need them. -press : key(keys) diff --git a/talon/user/community/core/keys/letter.talon-list b/talon/user/community/core/keys/letter.talon-list deleted file mode 100644 index 824a293..0000000 --- a/talon/user/community/core/keys/letter.talon-list +++ /dev/null @@ -1,30 +0,0 @@ -list: user.letter -- -# for common alternative spoken forms for letters, visit -# https://talon.wiki/quickstart/improving_recognition_accuracy/#collected-alternatives-to-the-default-alphabet -air: a -bat: b -cap: c -drum: d -each: e -fine: f -gust: g -harp: h -sit: i -jury: j -crunch: k -look: l -made: m -near: n -odd: o -pit: p -quench: q -red: r -sun: s -trap: t -urge: u -vest: v -whale: w -plex: x -yank: y -zip: z diff --git a/talon/user/community/core/keys/mac/modifier_key.talon-list b/talon/user/community/core/keys/mac/modifier_key.talon-list deleted file mode 100644 index a25059a..0000000 --- a/talon/user/community/core/keys/mac/modifier_key.talon-list +++ /dev/null @@ -1,12 +0,0 @@ -list: user.modifier_key -os: mac -- - -alt: alt -control: ctrl -shift: shift -super: cmd -command: cmd -option: alt -function: fn -globe: fn diff --git a/talon/user/community/core/keys/mac/special_key.talon-list b/talon/user/community/core/keys/mac/special_key.talon-list deleted file mode 100644 index 6708750..0000000 --- a/talon/user/community/core/keys/mac/special_key.talon-list +++ /dev/null @@ -1,18 +0,0 @@ -list: user.special_key -os: mac -- - -end: end -home: home -minus: minus -return: return -enter: keypad_enter -page down: pagedown -page up: pageup -escape: escape -space: space -tab: tab -insert: insert -wipe: backspace -delete: backspace -forward delete: delete diff --git a/talon/user/community/core/keys/number_key.talon-list b/talon/user/community/core/keys/number_key.talon-list deleted file mode 100644 index b83e24b..0000000 --- a/talon/user/community/core/keys/number_key.talon-list +++ /dev/null @@ -1,12 +0,0 @@ -list: user.number_key -- -zero: 0 -one: 1 -two: 2 -three: 3 -four: 4 -five: 5 -six: 6 -seven: 7 -eight: 8 -nine: 9 diff --git a/talon/user/community/core/keys/win/modifier_key.talon-list b/talon/user/community/core/keys/win/modifier_key.talon-list deleted file mode 100644 index 4ab343a..0000000 --- a/talon/user/community/core/keys/win/modifier_key.talon-list +++ /dev/null @@ -1,11 +0,0 @@ -list: user.modifier_key -os: windows -os: linux -- -alt: alt -control: ctrl -shift: shift -# super is the windows key -super: super -command: ctrl -option: alt diff --git a/talon/user/community/core/keys/win/special_key.talon-list b/talon/user/community/core/keys/win/special_key.talon-list deleted file mode 100644 index 194fb5a..0000000 --- a/talon/user/community/core/keys/win/special_key.talon-list +++ /dev/null @@ -1,20 +0,0 @@ -list: user.special_key -os: windows -os: linux -- - -end: end -home: home -minus: minus -enter: enter -page down: pagedown -page up: pageup -escape: escape -space: space -tab: tab -insert: insert -wipe: backspace -delete: backspace -forward delete: delete -menu key: menu -print screen: printscr diff --git a/talon/user/community/core/menu_choose/menu_choose.py b/talon/user/community/core/menu_choose/menu_choose.py deleted file mode 100644 index 52bb2eb..0000000 --- a/talon/user/community/core/menu_choose/menu_choose.py +++ /dev/null @@ -1,14 +0,0 @@ -from talon import Context, Module, actions - -mod = Module() - - -@mod.action_class -class Actions: - def choose(number_small: int): - """Choose the nth item""" - actions.key(f"down:{number_small-1} enter") - - def choose_up(number_small: int): - """Choose the nth item up""" - actions.key(f"up:{number_small} enter") diff --git a/talon/user/community/core/menu_choose/menu_choose.talon b/talon/user/community/core/menu_choose/menu_choose.talon deleted file mode 100644 index 9aa2b2e..0000000 --- a/talon/user/community/core/menu_choose/menu_choose.talon +++ /dev/null @@ -1,3 +0,0 @@ -# pick item from a dropdown -choose : user.choose(number_small) -choose up : user.choose_up(number_small) diff --git a/talon/user/community/core/modes/command_and_dictation_mode.talon b/talon/user/community/core/modes/command_and_dictation_mode.talon deleted file mode 100644 index 6f8dba7..0000000 --- a/talon/user/community/core/modes/command_and_dictation_mode.talon +++ /dev/null @@ -1,13 +0,0 @@ -mode: command -mode: dictation -- -^dictation mode$: - mode.disable("sleep") - mode.disable("command") - mode.enable("dictation") - user.code_clear_language_mode() - user.gdb_disable() -^command mode$: - mode.disable("sleep") - mode.disable("dictation") - mode.enable("command") diff --git a/talon/user/community/core/modes/dictation_mode.talon b/talon/user/community/core/modes/dictation_mode.talon deleted file mode 100644 index a1e2436..0000000 --- a/talon/user/community/core/modes/dictation_mode.talon +++ /dev/null @@ -1,76 +0,0 @@ -mode: dictation -- -^press $: key(modifiers) -^press $: key(keys) - -# Everything here should call `user.dictation_insert()` instead of `insert()`, to correctly auto-capitalize/auto-space. -: user.dictation_insert(raw_prose) -cap: user.dictation_format_cap() -# Hyphenated variants are for Dragon. -(no cap | no-caps): user.dictation_format_no_cap() -(no space | no-space): user.dictation_format_no_space() -^cap that$: user.dictation_reformat_cap() -^(no cap | no-caps) that$: user.dictation_reformat_no_cap() -^(no space | no-space) that$: user.dictation_reformat_no_space() - -# Navigation -go up (line | lines): - edit.up() - repeat(number_small - 1) -go down (line | lines): - edit.down() - repeat(number_small - 1) -go left (word | words): - edit.word_left() - repeat(number_small - 1) -go right (word | words): - edit.word_right() - repeat(number_small - 1) -go line start: edit.line_start() -go line end: edit.line_end() - -# Selection -select left (word | words): - edit.extend_word_left() - repeat(number_small - 1) -select right (word | words): - edit.extend_word_right() - repeat(number_small - 1) -select left (character | characters): - edit.extend_left() - repeat(number_small - 1) -select right (character | characters): - edit.extend_right() - repeat(number_small - 1) -clear left (word | words): - edit.extend_word_left() - repeat(number_small - 1) - edit.delete() -clear right (word | words): - edit.extend_word_right() - repeat(number_small - 1) - edit.delete() -clear left (character | characters): - edit.extend_left() - repeat(number_small - 1) - edit.delete() -clear right (character | characters): - edit.extend_right() - repeat(number_small - 1) - edit.delete() - -# Formatting -formatted : user.dictation_insert_raw(format_text) -^format selection $: user.formatters_reformat_selection(formatters) - -# Corrections -nope that | scratch that: user.clear_last_phrase() -(nope | scratch) selection: edit.delete() -select that: user.select_last_phrase() -spell that : user.dictation_insert(letters) -spell that : - result = user.formatted_text(letters, formatters) - user.dictation_insert_raw(result) - -# Escape, type things that would otherwise be commands -^escape $: user.dictation_insert(user.text) diff --git a/talon/user/community/core/modes/language_modes.py b/talon/user/community/core/modes/language_modes.py deleted file mode 100644 index 887e8b2..0000000 --- a/talon/user/community/core/modes/language_modes.py +++ /dev/null @@ -1,148 +0,0 @@ -from talon import Context, Module, actions, app - -# Maps language mode names to the extensions that activate them. Only put things -# here which have a supported language mode; that's why there are so many -# commented out entries. TODO: make this a csv file? -language_extensions = { - # 'assembly': 'asm s', - # 'bash': 'bashbook sh', - "batch": "bat", - "c": "c h", - # 'cmake': 'cmake', - # "cplusplus": "cpp hpp", - "csharp": "cs", - "css": "css", - # 'elisp': 'el', - # 'elm': 'elm', - "gdb": "gdb", - "go": "go", - "java": "java", - "javascript": "js", - "javascriptreact": "jsx", - # "json": "json", - "elixir": "ex", - "kotlin": "kt", - "lua": "lua", - "markdown": "md", - # 'perl': 'pl', - "php": "php", - # 'powershell': 'ps1', - "python": "py", - "protobuf": "proto", - "r": "r", - # 'racket': 'rkt', - "ruby": "rb", - "rust": "rs", - "scala": "scala", - "scss": "scss", - # 'snippets': 'snippets', - "sql": "sql", - "stata": "do ado", - "talon": "talon", - "talonlist": "talon-list", - "terraform": "tf", - "tex": "tex", - "typescript": "ts", - "typescriptreact": "tsx", - # 'vba': 'vba', - "vimscript": "vim vimrc", - # html doesn't actually have a language mode, but we do have snippets. - "html": "html", -} - -# Files without specific extensions but are associated with languages -special_file_map = { - "CMakeLists.txt": "cmake", - "Makefile": "make", - "Dockerfile": "docker", - "meson.build": "meson", - ".bashrc": "bash", - ".zshrc": "zsh", - "PKGBUILD": "pkgbuild", - ".vimrc": "vimscript", - "vimrc": "vimscript", -} - -# Override speakable forms for language modes. If not present, a language mode's -# name is used directly. -language_name_overrides = { - "cplusplus": ["see plus plus"], - "csharp": ["see sharp"], - "css": ["c s s"], - "gdb": ["g d b"], - "go": ["go", "go lang", "go language"], - "r": ["are language"], - "tex": ["tech", "lay tech", "latex"], -} - -mod = Module() -ctx = Context() - -ctx_forced = Context() -ctx_forced.matches = r""" -tag: user.code_language_forced -""" - - -mod.tag("code_language_forced", "This tag is active when a language mode is forced") -mod.list("language_mode", desc="Name of a programming language mode.") - -ctx.lists["self.language_mode"] = { - name: language - for language in language_extensions - for name in language_name_overrides.get(language, [language]) -} - -# Maps extension to languages. -extension_lang_map = { - "." + ext: language - for language, extensions in language_extensions.items() - for ext in extensions.split() -} - -language_ids = set(language_extensions.keys()) - -forced_language = "" - - -@ctx.action_class("code") -class CodeActions: - def language(): - file_name = actions.win.filename() - if file_name in special_file_map: - return special_file_map[file_name] - - file_extension = actions.win.file_ext() - return extension_lang_map.get(file_extension, "") - - -@ctx_forced.action_class("code") -class ForcedCodeActions: - def language(): - return forced_language - - -@mod.action_class -class Actions: - def code_set_language_mode(language: str): - """Sets the active language mode, and disables extension matching""" - global forced_language - assert language in language_extensions - forced_language = language - # Update tags to force a context refresh. Otherwise `code.language` will not update. - # Necessary to first set an empty list otherwise you can't move from one forced language to another. - ctx.tags = [] - ctx.tags = ["user.code_language_forced"] - - def code_clear_language_mode(): - """Clears the active language mode, and re-enables code.language: extension matching""" - global forced_language - forced_language = "" - ctx.tags = [] - - def code_show_forced_language_mode(): - """Show the active language for this context""" - if forced_language: - app.notify(f"Forced language: {forced_language}") - else: - app.notify("No language forced") diff --git a/talon/user/community/core/modes/language_modes.talon b/talon/user/community/core/modes/language_modes.talon deleted file mode 100644 index ac916b9..0000000 --- a/talon/user/community/core/modes/language_modes.talon +++ /dev/null @@ -1,3 +0,0 @@ -^force {user.language_mode}$: user.code_set_language_mode(language_mode) -show [forced] language mode: user.code_show_forced_language_mode() -^clear language mode$: user.code_clear_language_mode() diff --git a/talon/user/community/core/modes/modes_dragon.talon b/talon/user/community/core/modes/modes_dragon.talon deleted file mode 100644 index 5e39689..0000000 --- a/talon/user/community/core/modes/modes_dragon.talon +++ /dev/null @@ -1,25 +0,0 @@ -mode: all -speech.engine: dragon -- -# The optional afterwards allows these to match even if you say arbitrary text -# after this command, without having to wait for the speech timeout. - -# This is handy because you often need to put Talon asleep in order to immediately -# talk to humans, and it's annoying to have to say "sleep all", wait for the timeout, -# and then resume your conversation. - -# With this, you can say "sleep all hey bob" and Talon will immediately go to -# sleep and ignore "hey bob". Note that subtitles will show "sleep all hey bob", -# because it's part of the rule definition, but "hey bob" will be ignored, because -# we don't do anything with the in the body of the command. -^talon sleep []$: speech.disable() -^talon wake []$: speech.enable() - -^sleep all []$: - user.switcher_hide_running() - user.history_disable() - user.homophones_hide() - user.help_hide() - user.mouse_sleep() - speech.disable() - user.dragon_engine_sleep() diff --git a/talon/user/community/core/modes/modes_not_dragon.talon b/talon/user/community/core/modes/modes_not_dragon.talon deleted file mode 100644 index a537671..0000000 --- a/talon/user/community/core/modes/modes_not_dragon.talon +++ /dev/null @@ -1,41 +0,0 @@ -mode: command -mode: dictation -mode: sleep -not speech.engine: dragon -- -# The optional afterwards allows these to match even if you say arbitrary text -# after this command, without having to wait for the speech timeout. - -# This is handy because you often need to put Talon asleep in order to immediately -# talk to humans, and it's annoying to have to say "sleep all", wait for the timeout, -# and then resume your conversation. - -# With this, you can say "sleep all hey bob" and Talon will immediately go to -# sleep and ignore "hey bob". Note that subtitles will show "sleep all hey bob", -# because it's part of the rule definition, but "hey bob" will be ignored, because -# we don't do anything with the in the body of the command. - -^talon wake []$: speech.enable() - -# We define this *only* if the speech engine isn't Dragon, because if you're using Dragon, -# "wake up" is used to specifically control Dragon, and not affect Talon. -# -# It's a useful and well known command, though, so if you're using any other speech -# engine, this controls Talon. -^(wake up)+$: speech.enable() - -# We define this *only* if the speech engine isn't Dragon, because if you're using Dragon, -# "go to sleep" is used to specifically control Dragon, and not affect Talon. -# -# It's a useful and well known command, though, so if you're using any other speech -# engine, this controls Talon. -^go to sleep []$: speech.disable() -^talon sleep []$: speech.disable() - -^sleep all []$: - user.switcher_hide_running() - user.history_disable() - user.homophones_hide() - user.help_hide() - user.mouse_sleep() - speech.disable() diff --git a/talon/user/community/core/modes/sleep_mode.talon b/talon/user/community/core/modes/sleep_mode.talon deleted file mode 100644 index 64a879c..0000000 --- a/talon/user/community/core/modes/sleep_mode.talon +++ /dev/null @@ -1,36 +0,0 @@ -mode: sleep -- -settings(): - # Stop continuous scroll/gaze scroll with a pop - user.mouse_enable_pop_stops_scroll = false - # Stop pop click with 'control mouse' mode - user.mouse_enable_pop_click = 0 - # Stop mouse scroll down using hiss noise - user.mouse_enable_hiss_scroll = false - -#================================================================================ -# Commands to wake Talon -#================================================================================ - -# Note: these have repeaters on them (+) to work around an issue where, in sleep mode, -# you can get into a situation where these commands are difficult to trigger. - -# These commands are fully anchored (^ and $), which means that there must be -# silence before and after saying them in order for them to recognize (this reduces -# false positives during normal sleep mode, normally a good thing). - -# However, ignored background speech during sleep mode also counts as an utterance. - -# Thus, if you say "blah blah blah talon wake", these won't trigger, because "blah -# blah blah" was part of the same utterance. You have to say "blah blah blah" , "talon wake" . - -# Sometimes people would forget the second pause, notice things weren't working, and -# say "talon wake" over and over again before the speech timeout ever gets hit, which -# means that these won't recognize. The (+) handles this case, so if you say -# "talon wake talon wake" , it'll still work. - -^(welcome back)+$: - user.mouse_wake() - user.history_enable() - user.talon_mode() diff --git a/talon/user/community/core/modes/sleep_mode_pop_twice_to_wake.py b/talon/user/community/core/modes/sleep_mode_pop_twice_to_wake.py deleted file mode 100644 index 2fe45f9..0000000 --- a/talon/user/community/core/modes/sleep_mode_pop_twice_to_wake.py +++ /dev/null @@ -1,44 +0,0 @@ -import time - -from talon import Context, Module, actions, settings - -ctx = Context() -mod = Module() - -mod.tag("pop_twice_to_wake", desc="tag for enabling pop twice to wake in sleep mode") - -mod.setting( - "double_pop_speed_minimum", - type=float, - desc="""Shortest time in seconds to accept a second pop to trigger additional actions""", - default=0.1, -) - -mod.setting( - "double_pop_speed_maximum", - type=float, - desc="""Longest time in seconds to accept a second pop to trigger additional actions""", - default=0.3, -) - -ctx.matches = r""" -mode: sleep -and tag: user.pop_twice_to_wake -""" - -time_last_pop = 0 - - -@ctx.action_class("user") -class UserActions: - def noise_trigger_pop(): - # Since zoom mouse is registering against noise.register("pop", on_pop), let that take priority - if actions.tracking.control_zoom_enabled(): - return - global time_last_pop - double_pop_speed_minimum = settings.get("user.double_pop_speed_minimum") - double_pop_speed_maximum = settings.get("user.double_pop_speed_maximum") - delta = time.perf_counter() - time_last_pop - if delta >= double_pop_speed_minimum and delta <= double_pop_speed_maximum: - actions.speech.enable() - time_last_pop = time.perf_counter() diff --git a/talon/user/community/core/mouse_grid/mouse_grid.talon b/talon/user/community/core/mouse_grid/mouse_grid.talon deleted file mode 100644 index 7f4efe6..0000000 --- a/talon/user/community/core/mouse_grid/mouse_grid.talon +++ /dev/null @@ -1,6 +0,0 @@ -tag: user.mouse_grid_enabled -- -M grid: - app.notify("please use the voice command 'mouse grid' instead of 'm grid'") - user.grid_select_screen(1) - user.grid_activate() diff --git a/talon/user/community/core/mouse_grid/mouse_grid_open.talon b/talon/user/community/core/mouse_grid/mouse_grid_open.talon deleted file mode 100644 index a22aaf2..0000000 --- a/talon/user/community/core/mouse_grid/mouse_grid_open.talon +++ /dev/null @@ -1,8 +0,0 @@ -tag: user.mouse_grid_showing -- -: user.grid_narrow(number_key) -grid (off | close | hide): user.grid_close() - -grid reset: user.grid_reset() - -grid back: user.grid_go_back() diff --git a/talon/user/community/core/navigation/navigation.py b/talon/user/community/core/navigation/navigation.py deleted file mode 100644 index 0069efa..0000000 --- a/talon/user/community/core/navigation/navigation.py +++ /dev/null @@ -1,43 +0,0 @@ -from talon import Context, Module, actions - -mod = Module() -mod.tag("navigation") - -ctx_browser = Context() -ctx_browser.matches = r""" -tag: browser -""" - -ctx_mac = Context() -ctx_mac.matches = r""" -os: mac -""" - - -@ctx_browser.action_class("user") -class BrowserActions: - def go_back(): - actions.browser.go_back() - - def go_forward(): - actions.browser.go_forward() - - -@ctx_mac.action_class("user") -class MacActions: - def go_back(): - actions.key("cmd-[") - - def go_forward(): - actions.key("cmd-]") - - -@mod.action_class -class Actions: - def go_back(): - """Navigate back""" - actions.key("alt-left") - - def go_forward(): - """Navigate forward""" - actions.key("alt-right") diff --git a/talon/user/community/core/navigation/navigation.talon b/talon/user/community/core/navigation/navigation.talon deleted file mode 100644 index 18bfd2e..0000000 --- a/talon/user/community/core/navigation/navigation.talon +++ /dev/null @@ -1,5 +0,0 @@ -tag: user.navigation -- - -go back: user.go_back() -go forward: user.go_forward() diff --git a/talon/user/community/core/noise.py b/talon/user/community/core/noise.py deleted file mode 100644 index dacc497..0000000 --- a/talon/user/community/core/noise.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -Map noises (like pop) to actions so they can have contextually differing behavior -""" - -from talon import Module, actions, cron, noise - -mod = Module() -hiss_cron = None - - -@mod.action_class -class Actions: - def noise_trigger_pop(): - """ - Called when the user makes a 'pop' noise. Listen to - https://noise.talonvoice.com/static/previews/pop.mp3 for an - example. - """ - actions.skip() - - def noise_trigger_hiss(active: bool): - """ - Called when the user makes a 'hiss' noise. Listen to - https://noise.talonvoice.com/static/previews/hiss.mp3 for an - example. - """ - actions.skip() - - -def noise_trigger_hiss_debounce(active: bool): - """Since the hiss noise triggers while you're talking we need to debounce it""" - global hiss_cron - if active: - hiss_cron = cron.after("100ms", lambda: actions.user.noise_trigger_hiss(active)) - else: - cron.cancel(hiss_cron) - actions.user.noise_trigger_hiss(active) - - -noise.register("pop", lambda _: actions.user.noise_trigger_pop()) -noise.register("hiss", noise_trigger_hiss_debounce) diff --git a/talon/user/community/core/numbers/numbers.talon b/talon/user/community/core/numbers/numbers.talon deleted file mode 100644 index d3c669b..0000000 --- a/talon/user/community/core/numbers/numbers.talon +++ /dev/null @@ -1,2 +0,0 @@ -numb : "{number_string}" -numb : "{number_decimal_string}" diff --git a/talon/user/community/core/numbers/numbers_unprefixed.talon b/talon/user/community/core/numbers/numbers_unprefixed.talon deleted file mode 100644 index 993a625..0000000 --- a/talon/user/community/core/numbers/numbers_unprefixed.talon +++ /dev/null @@ -1,5 +0,0 @@ -tag: user.unprefixed_numbers -- - -: "{number_string}" -: "{number_decimal_string}" diff --git a/talon/user/community/core/screens/screens.py b/talon/user/community/core/screens/screens.py deleted file mode 100644 index 6587a05..0000000 --- a/talon/user/community/core/screens/screens.py +++ /dev/null @@ -1,64 +0,0 @@ -from talon import Module, cron, ui -from talon.canvas import Canvas - -mod = Module() - - -@mod.action_class -class Actions: - def screens_show_numbering(): - """Show screen number on each screen""" - screens = ui.screens() - number = 1 - for screen in screens: - show_screen_number(screen, number) - number += 1 - - def screens_get_by_number(screen_number: int) -> ui.Screen: - """Get screen by number""" - screens = ui.screens() - length = len(screens) - if screen_number < 1 or screen_number > length: - raise Exception( - f"Non-existing screen {screen_number} in range [1, {length}]" - ) - return screens[screen_number - 1] - - def screens_get_previous(screen: ui.Screen) -> ui.Screen: - """Get the screen before this one""" - return get_screen_by_offset(screen, -1) - - def screens_get_next(screen: ui.Screen) -> ui.Screen: - """Get the screen after this one""" - return get_screen_by_offset(screen, 1) - - -def get_screen_by_offset(screen: ui.Screen, offset: int) -> ui.Screen: - screens = ui.screens() - index = (screens.index(screen) + offset) % len(screens) - return screens[index] - - -def show_screen_number(screen: ui.Screen, number: int): - def on_draw(c): - c.paint.typeface = "arial" - # The min(width, height) is to not get gigantic size on portrait screens - c.paint.textsize = round(min(c.width, c.height) / 2) - text = f"{number}" - rect = c.paint.measure_text(text)[1] - x = c.x + c.width / 2 - rect.x - rect.width / 2 - y = c.y + c.height / 2 + rect.height / 2 - - c.paint.style = c.paint.Style.FILL - c.paint.color = "eeeeee" - c.draw_text(text, x, y) - - c.paint.style = c.paint.Style.STROKE - c.paint.color = "000000" - c.draw_text(text, x, y) - - cron.after("3s", canvas.close) - - canvas = Canvas.from_rect(screen.rect) - canvas.register("draw", on_draw) - canvas.freeze() diff --git a/talon/user/community/core/screens/screens.talon b/talon/user/community/core/screens/screens.talon deleted file mode 100644 index 6748c2a..0000000 --- a/talon/user/community/core/screens/screens.talon +++ /dev/null @@ -1 +0,0 @@ -screen numbers: user.screens_show_numbering() diff --git a/talon/user/community/core/snippets/README.md b/talon/user/community/core/snippets/README.md deleted file mode 100644 index 3d13fed..0000000 --- a/talon/user/community/core/snippets/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Snippets - -Custom format to represent snippets. - -## Features - -- Custom file ending `.snippet`. -- Supports syntax highlighting in VSCode via an [extension](https://marketplace.visualstudio.com/items?itemName=AndreasArvidsson.andreas-talon) -- Supports auto-formatting in VSCode via an [extension](https://marketplace.visualstudio.com/items?itemName=AndreasArvidsson.andreas-talon) -- Support for insertion and wrapper snippets. Note that while the snippet file syntax here supports wrapper snippets, you will need to install [Cursorless](https://www.cursorless.org/) for wrapper snippets to work. You'll also need to add the following line to your `settings.talon` file: - - ```talon - tag(): user.cursorless_use_community_snippets - ``` - - Note that this line will also disable any Cursorless snippets defined in your - Cursorless customization CSVs. You will need to migrate your Cursorless snippets to the new community snippet format described here. If you'd be interested in a tool to help with this migration, please leave a comment on [cursorless-dev/cursorless#2149](https://github.com/cursorless-dev/cursorless/issues/2149), ideally with a link to your custom snippets for us to look at. - -- Support for phrase formatters. - -## Format - -- A `.snippet` file can contain multiple snippet documents separated by `---`. -- Each snippet document has a context and body separated by `-`. -- Optionally a file can have a single context at the top with no body. This is not a snippet in itself, but default values to be inherited by the other snippet documents in the same file. -- Some context keys supports multiple values. These values are separated by `|`. - - For most keys like `language` or `phrase` multiple values means _or_. You can use phrase _1_ or phrase _2_. The snippet is active in language _A_ or language _B_. - - For `insertionFormatter` multiple values means that the formatters will be applied in sequence. - -### Context fields - -| Key | Required | Multiple values | Example | -| -------------- | -------- | --------------- | ------------------------------ | -| name | Yes | No | `name: ifStatement` | -| language | No | Yes | `language: javascript \| java` | -| phrase | No | Yes | `phrase: if \| if state` | -| insertionScope | No | Yes | `insertionScope: statement` | - -### Variables - -It's also possible to set configuration that applies to a specific tab stop (`$0`) or variable (`$try`): - -| Key | Required | Multiple values | Example | -| ------------------ | -------- | --------------- | ----------------------------------- | -| insertionFormatter | No | Yes | `$0.insertionFormatter: SNAKE_CASE` | -| wrapperPhrase | No | Yes | `$0.wrapperPhrase: try \| trying` | -| wrapperScope | No | No | `$0.wrapperScope: statement` | - -## Formatting and syntax highlighting - -To get formatting and syntax highlighting for `.snippet` files install [andreas-talon](https://marketplace.visualstudio.com/items?itemName=AndreasArvidsson.andreas-talon) - -## Examples - -### Single snippet definition - -![snippets1](./images/snippets1.png) - -### Multiple snippet definitions in single file - -![snippets2](./images/snippets2.png) - -### Default context and multiple values - -![snippets3](./images/snippets3.png) diff --git a/talon/user/community/core/snippets/images/snippets1.png b/talon/user/community/core/snippets/images/snippets1.png deleted file mode 100644 index d008926..0000000 Binary files a/talon/user/community/core/snippets/images/snippets1.png and /dev/null differ diff --git a/talon/user/community/core/snippets/images/snippets2.png b/talon/user/community/core/snippets/images/snippets2.png deleted file mode 100644 index 379b779..0000000 Binary files a/talon/user/community/core/snippets/images/snippets2.png and /dev/null differ diff --git a/talon/user/community/core/snippets/images/snippets3.png b/talon/user/community/core/snippets/images/snippets3.png deleted file mode 100644 index 58e0905..0000000 Binary files a/talon/user/community/core/snippets/images/snippets3.png and /dev/null differ diff --git a/talon/user/community/core/snippets/snippet_types.py b/talon/user/community/core/snippets/snippet_types.py deleted file mode 100644 index 1c6c83d..0000000 --- a/talon/user/community/core/snippets/snippet_types.py +++ /dev/null @@ -1,45 +0,0 @@ -from dataclasses import dataclass - - -@dataclass -class SnippetVariable: - name: str - insertion_formatters: list[str] = None - wrapper_phrases: list[str] = None - wrapper_scope: str = None - - -@dataclass -class Snippet: - name: str - body: str - phrases: list[str] = None - insertion_scopes: list[str] = None - languages: list[str] = None - variables: list[SnippetVariable] = None - - def get_variable(self, name: str): - if self.variables: - for var in self.variables: - if var.name == name: - return var - return None - - def get_variable_strict(self, name: str): - variable = self.get_variable(name) - if variable is None: - raise ValueError(f"Snippet '{self.name}' has no variable '{name}'") - return variable - - -@dataclass -class InsertionSnippet: - body: str - scopes: list[str] = None - - -@dataclass -class WrapperSnippet: - body: str - variable_name: str - scope: str = None diff --git a/talon/user/community/core/snippets/snippets.py b/talon/user/community/core/snippets/snippets.py deleted file mode 100644 index e04f10e..0000000 --- a/talon/user/community/core/snippets/snippets.py +++ /dev/null @@ -1,211 +0,0 @@ -import glob -from collections import defaultdict -from pathlib import Path - -from talon import Context, Module, actions, app, fs, settings - -from ..modes.language_modes import language_ids -from .snippet_types import InsertionSnippet, Snippet, WrapperSnippet -from .snippets_parser import create_snippets_from_file - -SNIPPETS_DIR = Path(__file__).parent / "snippets" - -mod = Module() - -mod.list("snippet", "List of insertion snippets") -mod.list("snippet_with_phrase", "List of insertion snippets containing a text phrase") -mod.list("snippet_wrapper", "List of wrapper snippets") - -mod.setting( - "snippets_dir", - type=str, - default=None, - desc="Directory (relative to Talon user) containing additional snippets", -) - -context_map = { - # `_` represents the global context, ie snippets available regardless of language - "_": Context(), -} -snippets_map = {} - -# Create a context for each defined language -for lang in language_ids: - ctx = Context() - ctx.matches = f"code.language: {lang}" - context_map[lang] = ctx - - -def get_setting_dir(): - setting_dir = settings.get("user.snippets_dir") - if not setting_dir: - return None - - dir = Path(setting_dir) - - if not dir.is_absolute(): - user_dir = Path(actions.path.talon_user()) - dir = user_dir / dir - - return dir.resolve() - - -@mod.action_class -class Actions: - def get_snippet(name: str) -> Snippet: - """Get snippet named """ - # Add current code language if not specified - if "." not in name: - lang = actions.code.language() or "_" - name = f"{lang}.{name}" - - if name not in snippets_map: - raise ValueError(f"Unknown snippet '{name}'") - - return snippets_map[name] - - def get_insertion_snippet(name: str) -> InsertionSnippet: - """Get insertion snippet named """ - snippet: Snippet = actions.user.get_snippet(name) - return InsertionSnippet(snippet.body, snippet.insertion_scopes) - - def get_wrapper_snippet(name: str) -> WrapperSnippet: - """Get wrapper snippet named """ - index = name.rindex(".") - snippet_name = name[:index] - variable_name = name[index + 1] - snippet: Snippet = actions.user.get_snippet(snippet_name) - variable = snippet.get_variable_strict(variable_name) - return WrapperSnippet(snippet.body, variable.name, variable.wrapper_scope) - - -def update_snippets(): - language_to_snippets = group_by_language(get_snippets()) - - snippets_map.clear() - - for lang, ctx in context_map.items(): - insertion_map = {} - insertions_phrase_map = {} - wrapper_map = {} - - for lang_super in get_super_languages(lang): - snippets, insertions, insertions_phrase, wrappers = create_lists( - lang, - lang_super, - language_to_snippets.get(lang_super, []), - ) - snippets_map.update(snippets) - insertion_map.update(insertions) - insertions_phrase_map.update(insertions_phrase) - wrapper_map.update(wrappers) - - ctx.lists.update( - { - "user.snippet": insertion_map, - "user.snippet_with_phrase": insertions_phrase_map, - "user.snippet_wrapper": wrapper_map, - } - ) - - -def get_snippets() -> list[Snippet]: - files = glob.glob(f"{SNIPPETS_DIR}/**/*.snippet", recursive=True) - - if get_setting_dir(): - files.extend(glob.glob(f"{get_setting_dir()}/**/*.snippet", recursive=True)) - - result = [] - - for file in files: - result.extend(create_snippets_from_file(file)) - - return result - - -def get_super_languages(language: str) -> list[str]: - """Returns a list of languages that are considered a superset of , including itself. Eg `javascript` will be included in the list when is `typescript`. - Note that the order of languages returned here is very important: more general must precede more specific, so that specific langs can properly override general languages. - """ - match language: - case "_": - return ["_"] - case "typescript": - return ["_", "javascript", "typescript"] - case "javascriptreact": - return ["_", "html", "javascript", "javascriptreact"] - case "typescriptreact": - return [ - "_", - "html", - "javascript", - "typescript", - "javascriptreact", - "typescriptreact", - ] - case _: - return ["_", language] - - -def group_by_language(snippets: list[Snippet]) -> dict[str, list[Snippet]]: - result = defaultdict(list) - for snippet in snippets: - if snippet.languages is not None: - for lang in snippet.languages: - result[lang].append(snippet) - else: - result["_"].append(snippet) - return result - - -def create_lists( - lang_ctx: str, - lang_snippets: str, - snippets: list[Snippet], -) -> tuple[dict[str, list[Snippet]], dict[str, str], dict[str, str], dict[str, str]]: - """Creates the lists for the given language, and returns them as a tuple of (snippets, insertions, insertions_phrase, wrappers) - - Args: - lang_ctx (str): The language of the context match - lang_snippets (str): The language of the snippets - snippets (list[Snippet]): The list of snippets for the given language - """ - snippets_map = {} - insertions = {} - insertions_phrase = {} - wrappers = {} - - for snippet in snippets: - id_ctx = f"{lang_ctx}.{snippet.name}" - id_lang = f"{lang_snippets}.{snippet.name}" - - # Make sure that the snippet is added to the map for the context language - snippets_map[id_ctx] = snippet - - if snippet.phrases is not None: - for phrase in snippet.phrases: - insertions[phrase] = id_lang - - if snippet.variables is not None: - for var in snippet.variables: - if var.insertion_formatters is not None and snippet.phrases is not None: - for phrase in snippet.phrases: - insertions_phrase[phrase] = id_lang - - if var.wrapper_phrases is not None: - for phrase in var.wrapper_phrases: - wrappers[phrase] = f"{id_lang}.{var.name}" - - return snippets_map, insertions, insertions_phrase, wrappers - - -def on_ready(): - fs.watch(str(SNIPPETS_DIR), lambda _1, _2: update_snippets()) - - if get_setting_dir(): - fs.watch(str(get_setting_dir()), lambda _1, _2: update_snippets()) - - update_snippets() - - -app.register("ready", on_ready) diff --git a/talon/user/community/core/snippets/snippets.talon b/talon/user/community/core/snippets/snippets.talon deleted file mode 100644 index 4430693..0000000 --- a/talon/user/community/core/snippets/snippets.talon +++ /dev/null @@ -1,4 +0,0 @@ -snip {user.snippet}: user.insert_snippet_by_name(snippet) - -snip {user.snippet_with_phrase} : - user.insert_snippet_by_name_with_phrase(snippet_with_phrase, text) diff --git a/talon/user/community/core/snippets/snippets/caseStatement.snippet b/talon/user/community/core/snippets/snippets/caseStatement.snippet deleted file mode 100644 index 4b90a87..0000000 --- a/talon/user/community/core/snippets/snippets/caseStatement.snippet +++ /dev/null @@ -1,17 +0,0 @@ -name: caseStatement -phrase: case ---- - -language: javascript | java -insertionScope: branch -- -case $1: - $0 ---- - -language: python -insertionScope: branch -- -case $1: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/catchStatement.snippet b/talon/user/community/core/snippets/snippets/catchStatement.snippet deleted file mode 100644 index 1f482c8..0000000 --- a/talon/user/community/core/snippets/snippets/catchStatement.snippet +++ /dev/null @@ -1,23 +0,0 @@ -name: catchStatement -phrase: catch ---- - -language: javascript -- -catch(error) { - $0 -} ---- - -language: java -- -catch(Exception ex) { - $0 -} ---- - -language: python -- -except Exception as ex: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/classDeclaration.snippet b/talon/user/community/core/snippets/snippets/classDeclaration.snippet deleted file mode 100644 index a2166c6..0000000 --- a/talon/user/community/core/snippets/snippets/classDeclaration.snippet +++ /dev/null @@ -1,33 +0,0 @@ -name: classDeclaration -phrase: class -insertionScope: class | statement - -$0.wrapperPhrase: class -$0.wrapperScope: statement ---- - -language: javascript - -$1.insertionFormatter: PUBLIC_CAMEL_CASE -- -class $1 { - $0 -} ---- - -language: java - -$1.insertionFormatter: PUBLIC_CAMEL_CASE -- -class $1 { - $0 -} ---- - -language: python - -$1.insertionFormatter: PUBLIC_CAMEL_CASE -- -class $1: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/commentBlock.snippet b/talon/user/community/core/snippets/snippets/commentBlock.snippet deleted file mode 100644 index 1b2aa06..0000000 --- a/talon/user/community/core/snippets/snippets/commentBlock.snippet +++ /dev/null @@ -1,21 +0,0 @@ -name: commentBlock -phrase: block comment -insertionScope: statement - -$0.insertionFormatter: CAPITALIZE_FIRST_WORD ---- - -language: c | javascript | java -- -/* $0 */ ---- - -language: python -- -"""$0""" ---- - -language: html -- - ---- diff --git a/talon/user/community/core/snippets/snippets/commentDocumentation.snippet b/talon/user/community/core/snippets/snippets/commentDocumentation.snippet deleted file mode 100644 index 0d2572f..0000000 --- a/talon/user/community/core/snippets/snippets/commentDocumentation.snippet +++ /dev/null @@ -1,16 +0,0 @@ -name: commentDocumentation -phrase: doc comment | doc string -insertionScope: statement - -$0.insertionFormatter: CAPITALIZE_FIRST_WORD ---- - -language: javascript | java -- -/** $0 */ ---- - -language: python -- -"""$0""" ---- diff --git a/talon/user/community/core/snippets/snippets/commentLine.snippet b/talon/user/community/core/snippets/snippets/commentLine.snippet deleted file mode 100644 index df13a01..0000000 --- a/talon/user/community/core/snippets/snippets/commentLine.snippet +++ /dev/null @@ -1,25 +0,0 @@ -name: commentLine -phrase: comment - -$0.insertionFormatter: CAPITALIZE_FIRST_WORD ---- - -language: c | javascript | java | json -- -// $0 ---- - -language: python | talon | csv -- -# $0 ---- - -language: html -- - ---- - -language: scm -- -;; $0 ---- diff --git a/talon/user/community/core/snippets/snippets/constructorDeclaration.snippet b/talon/user/community/core/snippets/snippets/constructorDeclaration.snippet deleted file mode 100644 index 860cb43..0000000 --- a/talon/user/community/core/snippets/snippets/constructorDeclaration.snippet +++ /dev/null @@ -1,24 +0,0 @@ -name: constructorDeclaration -phrase: constructor -insertionScope: namedFunction | statement ---- - -language: javascript -- -constructor($1) { - $0 -} ---- - -language: java -- -$1($2) { - $0 -} ---- - -language: python -- -def __init__(self$1): - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/defaultStatement.snippet b/talon/user/community/core/snippets/snippets/defaultStatement.snippet deleted file mode 100644 index ebbf943..0000000 --- a/talon/user/community/core/snippets/snippets/defaultStatement.snippet +++ /dev/null @@ -1,15 +0,0 @@ -name: defaultStatement -phrase: default ---- - -language: javascript | java -- -default: - $0 ---- - -language: python -- -case _: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/doWhileLoopStatement.snippet b/talon/user/community/core/snippets/snippets/doWhileLoopStatement.snippet deleted file mode 100644 index 4e2df6f..0000000 --- a/talon/user/community/core/snippets/snippets/doWhileLoopStatement.snippet +++ /dev/null @@ -1,11 +0,0 @@ -name: doWhileLoopStatement -phrase: do while -insertionScope: statement ---- - -language: javascript | java -- -do { - $0 -} while($1); ---- diff --git a/talon/user/community/core/snippets/snippets/elseIfStatement.snippet b/talon/user/community/core/snippets/snippets/elseIfStatement.snippet deleted file mode 100644 index 6aec0a3..0000000 --- a/talon/user/community/core/snippets/snippets/elseIfStatement.snippet +++ /dev/null @@ -1,28 +0,0 @@ -name: elseIfStatement -phrase: elif -insertionScope: statement - -$1.wrapperPhrase: elif cond -$1.wrapperScope: statement -$0.wrapperPhrase: elif -$0.wrapperScope: statement ---- - -language: javascript | java -- -else if ($1) { - $0 -} ---- - -language: python -- -elif $1: - $0 ---- - -language: lua -- -elseif $1 then - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/elseStatement.snippet b/talon/user/community/core/snippets/snippets/elseStatement.snippet deleted file mode 100644 index 50d0037..0000000 --- a/talon/user/community/core/snippets/snippets/elseStatement.snippet +++ /dev/null @@ -1,26 +0,0 @@ -name: elseStatement -phrase: else -insertionScope: statement - -$0.wrapperPhrase: else -$0.wrapperScope: statement ---- - -language: javascript | java -- -else { - $0 -} ---- - -language: python -- -else: - $0 ---- - -language: lua -- -else - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/finallyStatement.snippet b/talon/user/community/core/snippets/snippets/finallyStatement.snippet deleted file mode 100644 index 03f0ca5..0000000 --- a/talon/user/community/core/snippets/snippets/finallyStatement.snippet +++ /dev/null @@ -1,16 +0,0 @@ -name: finallyStatement -phrase: finally ---- - -language: javascript | java -- -finally { - $0 -} ---- - -language: python -- -finally: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/forEachStatement.snippet b/talon/user/community/core/snippets/snippets/forEachStatement.snippet deleted file mode 100644 index bd8fdd9..0000000 --- a/talon/user/community/core/snippets/snippets/forEachStatement.snippet +++ /dev/null @@ -1,24 +0,0 @@ -name: forEachStatement -phrase: for each -insertionScope: statement ---- - -language: javascript -- -for (const $1 of $2) { - $0 -} ---- - -language: java -- -for (final $1 : $2) { - $0 -} ---- - -language: python -- -for $1 in $2: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/forLoopStatement.snippet b/talon/user/community/core/snippets/snippets/forLoopStatement.snippet deleted file mode 100644 index aadc281..0000000 --- a/talon/user/community/core/snippets/snippets/forLoopStatement.snippet +++ /dev/null @@ -1,24 +0,0 @@ -name: forLoopStatement -phrase: for loop -insertionScope: statement ---- - -language: javascript -- -for (let i = 0; i < $1; ++i) { - $0 -} ---- - -language: java -- -for (int i = 0; i < $1; ++i) { - $0 -} ---- - -language: python -- -for i in range($1): - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/formatString.snippet b/talon/user/community/core/snippets/snippets/formatString.snippet deleted file mode 100644 index 0f22f94..0000000 --- a/talon/user/community/core/snippets/snippets/formatString.snippet +++ /dev/null @@ -1,18 +0,0 @@ -name: formatString -phrase: format ---- - -language: javascript -- -`$0` ---- - -language: java -- -String.format("$0") ---- - -language: python -- -f"$0" ---- diff --git a/talon/user/community/core/snippets/snippets/functionCall.snippet b/talon/user/community/core/snippets/snippets/functionCall.snippet deleted file mode 100644 index b2f37dc..0000000 --- a/talon/user/community/core/snippets/snippets/functionCall.snippet +++ /dev/null @@ -1,11 +0,0 @@ -name: functionCall -phrase: call -insertionScope: statement - -$0.wrapperPhrase: call ---- - -language: c | javascript | java | python | talon -- -$1($0) ---- diff --git a/talon/user/community/core/snippets/snippets/functionDeclaration.snippet b/talon/user/community/core/snippets/snippets/functionDeclaration.snippet deleted file mode 100644 index 44b5a9e..0000000 --- a/talon/user/community/core/snippets/snippets/functionDeclaration.snippet +++ /dev/null @@ -1,33 +0,0 @@ -name: functionDeclaration -phrase: funk -insertionScope: namedFunction | statement - -$0.wrapperPhrase: funk -$0.wrapperScope: statement ---- - -language: javascript - -$1.insertionFormatter: PRIVATE_CAMEL_CASE -- -function $1($2) { - $0 -} ---- - -language: java - -$1.insertionFormatter: PRIVATE_CAMEL_CASE -- -void $1($2) { - $0 -} ---- - -language: python - -$1.insertionFormatter: SNAKE_CASE -- -def $1($2): - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/global.snippet b/talon/user/community/core/snippets/snippets/global.snippet deleted file mode 100644 index 536bad5..0000000 --- a/talon/user/community/core/snippets/snippets/global.snippet +++ /dev/null @@ -1,7 +0,0 @@ -name: codeQuote -phrase: code -- -``` -$0 -``` ---- diff --git a/talon/user/community/core/snippets/snippets/html.snippet b/talon/user/community/core/snippets/snippets/html.snippet deleted file mode 100644 index 3f928fc..0000000 --- a/talon/user/community/core/snippets/snippets/html.snippet +++ /dev/null @@ -1,46 +0,0 @@ -language: html ---- - -name: element -phrase: element | tag -insertionScope: xmlElement - -$1.insertionFormatter: PRIVATE_CAMEL_CASE -- -<$1> - $0 - ---- - -name: attribute -phrase: attribute - -$1.insertionFormatter: PRIVATE_CAMEL_CASE -- -$1=$0 ---- - -name: table -phrase: table -- - - - - - - - - - - - -
$1
$0
---- - -name: unorderedList -phrase: list -- -
    -
  • $0
  • -
---- diff --git a/talon/user/community/core/snippets/snippets/ifStatement.snippet b/talon/user/community/core/snippets/snippets/ifStatement.snippet deleted file mode 100644 index 9d2cdf0..0000000 --- a/talon/user/community/core/snippets/snippets/ifStatement.snippet +++ /dev/null @@ -1,29 +0,0 @@ -name: ifStatement -phrase: if -insertionScope: statement - -$1.wrapperPhrase: if cond -$1.wrapperScope: statement -$0.wrapperPhrase: if -$0.wrapperScope: statement ---- - -language: javascript | java -- -if ($1) { - $0 -} ---- - -language: python -- -if $1: - $0 ---- - -language: lua -- -if $1 then - $0 -end ---- diff --git a/talon/user/community/core/snippets/snippets/item.snippet b/talon/user/community/core/snippets/snippets/item.snippet deleted file mode 100644 index 41cab31..0000000 --- a/talon/user/community/core/snippets/snippets/item.snippet +++ /dev/null @@ -1,14 +0,0 @@ -name: item -phrase: item -insertionScope: collectionItem ---- - -language: javascript -- -$1: $0, ---- - -language: python -- -"$1": $0, ---- diff --git a/talon/user/community/core/snippets/snippets/javascript.snippet b/talon/user/community/core/snippets/snippets/javascript.snippet deleted file mode 100644 index ac205d4..0000000 --- a/talon/user/community/core/snippets/snippets/javascript.snippet +++ /dev/null @@ -1,56 +0,0 @@ -language: javascript ---- - -name: forInLoopStatement -phrase: for in -insertionScope: statement -- -for (const $1 in $2) { - $0 -} ---- - -name: anonymousFunctionDeclarationAndCall -phrase: self calling -- -(() => { - $0 -})(); ---- - -name: importStarStatement -phrase: import star -- -import * as $0 from "$0"; ---- - -name: importFromStatement -phrase: import from -- -import $0 from "$0"; ---- - -name: namedLambdaExpression -phrase: arrow funk -insertionScope: statement -- -const $1 = ($2) => { - $0 -} ---- - -name: reactUseState -phrase: use state -insertionScope: statement -- -const [$1, set${1/(.)/${1:/capitalize}/}] = useState($0); ---- - -name: reactUseEffect -phrase: use effect -insertionScope: statement -- -useEffect(() => { - $0 -}, []); ---- diff --git a/talon/user/community/core/snippets/snippets/lambdaExpression.snippet b/talon/user/community/core/snippets/snippets/lambdaExpression.snippet deleted file mode 100644 index 4227601..0000000 --- a/talon/user/community/core/snippets/snippets/lambdaExpression.snippet +++ /dev/null @@ -1,25 +0,0 @@ -name: lambdaExpression -phrase: lambda - -$0.wrapperPhrase: lambda -$0.wrapperScope: statement ---- - -language: javascript -- -($1) => { - $0 -} ---- - -language: java -- -($1) -> { - $0 -} ---- - -language: python -- -lambda $1: $0 ---- diff --git a/talon/user/community/core/snippets/snippets/lua.snippet b/talon/user/community/core/snippets/snippets/lua.snippet deleted file mode 100644 index b4be8de..0000000 --- a/talon/user/community/core/snippets/snippets/lua.snippet +++ /dev/null @@ -1,21 +0,0 @@ -language: lua ---- - -name: forInIPairs -phrase: for eye pairs -insertionScope: statement -$1.insertionFormatter: SNAKE_CASE -- -for _, $1 in ipairs($2) do - $0 -end ---- - -name: forInPairs -phrase: for pairs -insertionScope: statement -- -for ${1:k}, ${2:v} in pairs($3) do - $0 -end ---- diff --git a/talon/user/community/core/snippets/snippets/markdown.snippet b/talon/user/community/core/snippets/snippets/markdown.snippet deleted file mode 100644 index 5377dfd..0000000 --- a/talon/user/community/core/snippets/snippets/markdown.snippet +++ /dev/null @@ -1,8 +0,0 @@ -language: markdown ---- - -name: link -phrase: link -- -[$1]($0) ---- diff --git a/talon/user/community/core/snippets/snippets/methodDeclaration.snippet b/talon/user/community/core/snippets/snippets/methodDeclaration.snippet deleted file mode 100644 index 27fed5f..0000000 --- a/talon/user/community/core/snippets/snippets/methodDeclaration.snippet +++ /dev/null @@ -1,13 +0,0 @@ -name: methodDeclaration -phrase: method -insertionScope: namedFunction | statement ---- - -language: javascript - -$1.insertionFormatter: PRIVATE_CAMEL_CASE -- -$1($2) { - $0 -} ---- diff --git a/talon/user/community/core/snippets/snippets/newInstance.snippet b/talon/user/community/core/snippets/snippets/newInstance.snippet deleted file mode 100644 index f7eb4ce..0000000 --- a/talon/user/community/core/snippets/snippets/newInstance.snippet +++ /dev/null @@ -1,13 +0,0 @@ -name: newInstance -phrase: instance ---- - -language: javascript | java -- -new $1($0); ---- - -language: python -- -$1($0) ---- diff --git a/talon/user/community/core/snippets/snippets/printStatement.snippet b/talon/user/community/core/snippets/snippets/printStatement.snippet deleted file mode 100644 index e971a77..0000000 --- a/talon/user/community/core/snippets/snippets/printStatement.snippet +++ /dev/null @@ -1,21 +0,0 @@ -name: printStatement -phrase: print -insertionScope: statement - -$0.wrapperPhrase: print ---- - -language: javascript -- -console.log($0) ---- - -language: java -- -System.out.println($0); ---- - -language: python | talon -- -print($0) ---- diff --git a/talon/user/community/core/snippets/snippets/python.snippet b/talon/user/community/core/snippets/snippets/python.snippet deleted file mode 100644 index 07ef99c..0000000 --- a/talon/user/community/core/snippets/snippets/python.snippet +++ /dev/null @@ -1,47 +0,0 @@ -language: python ---- - -name: talonAppDeclaration -phrase: module app -insertionScope: statement -- -mod.apps.$1 = r""" -$0 -""" ---- - -name: talonModuleClass -phrase: module class -insertionScope: class | statement -- -@mod.action_class -class Actions: - $0 ---- - -name: talonContextMatch -phrase: context match -insertionScope: statement -- -ctx.matches = r""" -$0 -""" ---- - -name: talonContextList -phrase: context list -insertionScope: statement -- -ctx.lists["user.$1"] = { - $0 -} ---- - -name: talonContextClass -phrase: context class -insertionScope: class | statement -- -@ctx.action_class("$1") -class $2Actions: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/switchStatement.snippet b/talon/user/community/core/snippets/snippets/switchStatement.snippet deleted file mode 100644 index 40d1285..0000000 --- a/talon/user/community/core/snippets/snippets/switchStatement.snippet +++ /dev/null @@ -1,17 +0,0 @@ -name: switchStatement -phrase: switch -insertionScope: statement ---- - -language: javascript | java -- -switch ($1) { - $0 -} ---- - -language: python -- -match $1: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/talon.snippet b/talon/user/community/core/snippets/snippets/talon.snippet deleted file mode 100644 index c71db33..0000000 --- a/talon/user/community/core/snippets/snippets/talon.snippet +++ /dev/null @@ -1,11 +0,0 @@ -language: talon ---- - -name: voiceCommandDeclaration -phrase: command -insertionScope: command - -$0.insertionFormatter: NOOP -- -$0: user.vscode("$CLIPBOARD") ---- diff --git a/talon/user/community/core/snippets/snippets/ternary.snippet b/talon/user/community/core/snippets/snippets/ternary.snippet deleted file mode 100644 index 20641f9..0000000 --- a/talon/user/community/core/snippets/snippets/ternary.snippet +++ /dev/null @@ -1,18 +0,0 @@ -name: ternary -phrase: ternary ---- - -language: javascript -- -$1 ? $2 : $0 ---- - -language: python -- -$1 if $2 else $0 ---- - -language: lua -- -$1 and $2 or $0 ---- diff --git a/talon/user/community/core/snippets/snippets/throwException.snippet b/talon/user/community/core/snippets/snippets/throwException.snippet deleted file mode 100644 index 737654a..0000000 --- a/talon/user/community/core/snippets/snippets/throwException.snippet +++ /dev/null @@ -1,18 +0,0 @@ -name: throwException -phrase: exception ---- - -language: javascript -- -throw Error(`$0`) ---- - -language: python -- -raise ValueError(f"$0") ---- - -language: java -- -throw new Exception(String.format("$0")) ---- diff --git a/talon/user/community/core/snippets/snippets/tryCatchStatement.snippet b/talon/user/community/core/snippets/snippets/tryCatchStatement.snippet deleted file mode 100644 index 6c54979..0000000 --- a/talon/user/community/core/snippets/snippets/tryCatchStatement.snippet +++ /dev/null @@ -1,37 +0,0 @@ -name: tryCatchStatement -phrase: try catch -insertionScope: statement - -$1.wrapperPhrase: try -$1.wrapperScope: statement -$0.wrapperPhrase: catch -$0.wrapperScope: statement ---- - -language: javascript -- -try { - $1 -} -catch(error) { - $0 -} ---- - -language: java -- -try { - $1 -} -catch(Exception ex) { - $0 -} ---- - -language: python -- -try: - $1 -except Exception as ex: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/tryStatement.snippet b/talon/user/community/core/snippets/snippets/tryStatement.snippet deleted file mode 100644 index 3ee9ace..0000000 --- a/talon/user/community/core/snippets/snippets/tryStatement.snippet +++ /dev/null @@ -1,24 +0,0 @@ -name: tryStatement -phrase: try -insertionScope: statement ---- - -language: javascript -- -try { - $0 -} ---- - -language: java -- -try { - $1 -} ---- - -language: python -- -try: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets/typescript.snippet b/talon/user/community/core/snippets/snippets/typescript.snippet deleted file mode 100644 index 388e365..0000000 --- a/talon/user/community/core/snippets/snippets/typescript.snippet +++ /dev/null @@ -1,13 +0,0 @@ -language: typescript ---- - -name: interfaceDeclaration -phrase: interface -insertionScope: statement - -$1.insertionFormatter: PUBLIC_CAMEL_CASE -- -interface $1 { - $0 -} ---- diff --git a/talon/user/community/core/snippets/snippets/whileLoopStatement.snippet b/talon/user/community/core/snippets/snippets/whileLoopStatement.snippet deleted file mode 100644 index 71fe5e6..0000000 --- a/talon/user/community/core/snippets/snippets/whileLoopStatement.snippet +++ /dev/null @@ -1,17 +0,0 @@ -name: whileLoopStatement -phrase: while -insertionScope: statement ---- - -language: javascript | java -- -while($1) { - $0 -} ---- - -language: python -- -while $1: - $0 ---- diff --git a/talon/user/community/core/snippets/snippets_insert.py b/talon/user/community/core/snippets/snippets_insert.py deleted file mode 100644 index baa7781..0000000 --- a/talon/user/community/core/snippets/snippets_insert.py +++ /dev/null @@ -1,49 +0,0 @@ -import re - -from talon import Module, actions - -from .snippet_types import Snippet -from .snippets_insert_raw_text import insert_snippet_raw_text - -mod = Module() - - -@mod.action_class -class Actions: - def insert_snippet(body: str): - """Insert snippet""" - insert_snippet_raw_text(body) - - def insert_snippet_by_name(name: str, substitutions: dict[str, str] = None): - """Insert snippet """ - snippet: Snippet = actions.user.get_snippet(name) - body = snippet.body - - if substitutions: - for k, v in substitutions.items(): - reg = re.compile(rf"\${k}|\$\{{{k}\}}") - if not reg.search(body): - raise ValueError( - f"Can't substitute non existing variable '{k}' in snippet '{name}'" - ) - body = reg.sub(v, body) - - actions.user.insert_snippet(body) - - def insert_snippet_by_name_with_phrase(name: str, phrase: str): - """Insert snippet with phrase """ - snippet: Snippet = actions.user.get_snippet(name) - substitutions = {} - - for variable in snippet.variables: - if variable.insertion_formatters is not None: - formatters = ",".join(variable.insertion_formatters) - formatted_phrase = actions.user.formatted_text(phrase, formatters) - substitutions[variable.name] = formatted_phrase - - if not substitutions: - raise ValueError( - f"Can't use snippet phrase. No variable with insertion formatter in snippet '{name}'" - ) - - actions.user.insert_snippet_by_name(name, substitutions) diff --git a/talon/user/community/core/snippets/snippets_insert_raw_text.py b/talon/user/community/core/snippets/snippets_insert_raw_text.py deleted file mode 100644 index 1a30ade..0000000 --- a/talon/user/community/core/snippets/snippets_insert_raw_text.py +++ /dev/null @@ -1,94 +0,0 @@ -import re -from dataclasses import dataclass - -from talon import actions - -INDENTATION = " " -RE_STOP = re.compile(r"\$(\d+|\w+)|\$\{(\d+|\w+)\}|\$\{(\d+|\w+):(.+)\}") - - -@dataclass -class Stop: - name: str - rows_up: int - row: int - col: int - - -def insert_snippet_raw_text(body: str): - """Insert snippet as raw text without editor support""" - updated_snippet, stop = parse_snippet(body) - - actions.insert(updated_snippet) - - if stop: - up(stop.rows_up) - actions.edit.line_start() - right(stop.col) - - -def parse_snippet(body: str): - # Some IM services will send the message on a tab - body = re.sub(r"\t", INDENTATION, body) - - # Replace variable with appropriate value/text - body = re.sub(r"\$TM_SELECTED_TEXT", lambda _: actions.edit.selected_text(), body) - body = re.sub(r"\$CLIPBOARD", lambda _: actions.clip.text(), body) - - lines = body.splitlines() - stops: list[Stop] = [] - - for i, line in enumerate(lines): - match = RE_STOP.search(line) - - while match: - stops.append( - Stop( - name=match.group(1) or match.group(2) or match.group(3), - rows_up=len(lines) - i - 1, - row=i, - col=match.start(), - ) - ) - - # Remove tab stops and variables. - stop_text = match.group(0) - default_value = match.group(4) or "" - line = line.replace(stop_text, default_value, 1) - - # Might have multiple stops on the same line - match = RE_STOP.search(line) - - # Update existing line - lines[i] = line - - updated_snippet = "\n".join(lines) - - return updated_snippet, get_first_stop(stops) - - -def up(n: int): - """Move cursor up rows""" - for _ in range(n): - actions.edit.up() - - -def right(n: int): - """Move cursor right columns""" - for _ in range(n): - actions.edit.right() - - -def key(stop: Stop): - if stop.name == "0": - return 1000 - if stop.name.isdigit(): - return int(stop.name) - return 999 - - -def get_first_stop(stops: list[Stop]): - if not stops: - return None - stops.sort(key=key) - return stops[0] diff --git a/talon/user/community/core/snippets/snippets_parser.py b/talon/user/community/core/snippets/snippets_parser.py deleted file mode 100644 index ee22a41..0000000 --- a/talon/user/community/core/snippets/snippets_parser.py +++ /dev/null @@ -1,336 +0,0 @@ -import re -from pathlib import Path -from typing import Callable, Union - -from .snippet_types import Snippet, SnippetVariable - - -class SnippetDocument: - file: str - line_doc: int - line_body: int - variables: list[SnippetVariable] = [] - name: str | None = None - phrases: list[str] | None = None - insertionScopes: list[str] | None = None - languages: list[str] | None = None - body: str | None = None - - def __init__(self, file: str, line_doc: int, line_body: int): - self.file = file - self.line_doc = line_doc - self.line_body = line_body - - -def create_snippets_from_file(file_path: str) -> list[Snippet]: - documents = parse_file(file_path) - return create_snippets(documents) - - -def create_snippets(documents: list[SnippetDocument]) -> list[Snippet]: - if len(documents) == 0: - return [] - - if documents[0].body is None: - default_context = documents[0] - documents = documents[1:] - else: - default_context = SnippetDocument("", -1, -1) - - snippets: list[Snippet] = [] - - for doc in documents: - snippet = create_snippet(doc, default_context) - if snippet: - snippets.append(snippet) - - return snippets - - -def create_snippet( - document: SnippetDocument, default_context: SnippetDocument -) -> Snippet | None: - snippet = Snippet( - name=document.name or default_context.name, - languages=document.languages or default_context.languages, - phrases=document.phrases or default_context.phrases, - insertion_scopes=document.insertionScopes or default_context.insertionScopes, - variables=combine_variables(default_context.variables, document.variables), - body=normalize_snippet_body_tabs(document.body), - ) - - if not validate_snippet(document, snippet): - return None - - return snippet - - -def validate_snippet(document: SnippetDocument, snippet: Snippet) -> bool: - is_valid = True - - if not snippet.name: - error(document.file, document.line_doc, "Missing snippet name") - is_valid = False - - for variable in snippet.variables: - var_name = f"${variable.name}" - if var_name not in snippet.body: - error( - document.file, - document.line_body, - f"Variable '{var_name}' missing in body '{snippet.body}'", - ) - is_valid = False - - if variable.insertion_formatters is not None and snippet.phrases is None: - error( - document.file, - document.line_doc, - f"Snippet phrase required when using '{var_name}.insertionFormatter'", - ) - is_valid = False - - if variable.wrapper_scope is not None and variable.wrapper_phrases is None: - error( - document.file, - document.line_doc, - f"'{var_name}.wrapperPhrase' required when using '{var_name}.wrapperScope'", - ) - is_valid = False - - return is_valid - - -def combine_variables( - default_variables: list[SnippetVariable], - document_variables: list[SnippetVariable], -) -> list[SnippetVariable]: - variables: dict[str, SnippetVariable] = {} - - for variable in [*default_variables, *document_variables]: - if variable.name not in variables: - variables[variable.name] = SnippetVariable(variable.name) - - new_variable = variables[variable.name] - - if variable.insertion_formatters is not None: - new_variable.insertion_formatters = variable.insertion_formatters - - if variable.wrapper_phrases is not None: - new_variable.wrapper_phrases = variable.wrapper_phrases - - if variable.wrapper_scope is not None: - new_variable.wrapper_scope = variable.wrapper_scope - - return list(variables.values()) - - -def normalize_snippet_body_tabs(body: str) -> str: - # If snippet body already contains tabs. No change. - if not body or "\t" in body: - return body - - lines = [] - smallest_indentation = None - - for line in body.splitlines(): - match = re.search(r"^\s+", line) - indentation = match.group() if match is not None else "" - - # Keep track of smallest non-empty indentation - if len(indentation) > 0 and ( - smallest_indentation is None or len(indentation) < len(smallest_indentation) - ): - smallest_indentation = indentation - - lines.append({"indentation": indentation, "rest": line[len(indentation) :]}) - - # No indentation found in snippet body. No change. - if smallest_indentation is None: - return body - - normalized_lines = [ - reconstruct_line(smallest_indentation, line["indentation"], line["rest"]) - for line in lines - ] - - return "\n".join(normalized_lines) - - -def reconstruct_line(smallest_indentation: str, indentation: str, rest: str) -> str: - # Update indentation by replacing each occurrent of smallest space indentation with a tab - indentation = indentation.replace(smallest_indentation, "\t") - return f"{indentation}{rest}" - - -# ---------- Snippet file parser ---------- - - -def parse_file(file_path: str) -> list[SnippetDocument]: - with open(file_path, encoding="utf-8") as f: - content = f.read() - file_name = Path(file_path).name - return parse_file_content(file_name, content) - - -def parse_file_content(file: str, text: str) -> list[SnippetDocument]: - doc_texts = re.split(r"^---$", text, flags=re.MULTILINE) - documents: list[SnippetDocument] = [] - line = 0 - - for i, doc_text in enumerate(doc_texts): - optional_body = i == 0 and len(doc_texts) > 1 - document = parse_document(file, line, optional_body, doc_text) - if document is not None: - documents.append(document) - line += doc_text.count("\n") + 1 - - return documents - - -def parse_document( - file: str, line: int, optional_body: bool, text: str -) -> Union[SnippetDocument, None]: - parts = re.split(r"^-$", text, maxsplit=1, flags=re.MULTILINE) - line_body = line + parts[0].count("\n") + 1 - org_doc = SnippetDocument(file, line, line_body) - document = parse_context(file, line, org_doc, parts[0]) - - if len(parts) == 2: - body = parse_body(parts[1]) - if body is not None: - if document is None: - document = org_doc - document.body = body - - if document and not document.body and not optional_body: - error(file, line, f"Missing body in snippet document '{text}'") - return None - - return document - - -def parse_context( - file: str, line: int, document: SnippetDocument, text: str -) -> Union[SnippetDocument, None]: - lines = [l.strip() for l in text.splitlines()] - keys: set[str] = set() - variables: dict[str, SnippetVariable] = {} - - def get_variable(name: str) -> SnippetVariable: - if name not in variables: - variables[name] = SnippetVariable(name) - return variables[name] - - for i, line_text in enumerate(lines): - if line_text: - parse_context_line( - file, - line + i, - document, - keys, - get_variable, - line_text, - ) - - if len(keys) == 0: - return None - - document.variables = list(variables.values()) - - return document - - -def parse_context_line( - file: str, - line: int, - document: SnippetDocument, - keys: set[str], - get_variable: Callable[[str], SnippetVariable], - text: str, -): - parts = text.split(":") - - if len(parts) != 2: - error(file, line, f"Invalid line '{text}'") - return - - key = parts[0].strip() - value = parts[1].strip() - - if not key or not value: - error(file, line, f"Invalid line '{text}'") - return - - if key in keys: - error(file, line, f"Duplicate key '{key}'") - - valid_key = True - - match key: - case "name": - document.name = value - case "phrase": - document.phrases = parse_vector_value(value) - case "insertionScope": - document.insertionScopes = parse_vector_value(value) - case "language": - document.languages = parse_vector_value(value) - case _: - if key.startswith("$"): - if not parse_variable(file, line, get_variable, key, value): - valid_key = False - else: - error(file, line, f"Invalid key '{key}'") - valid_key = False - - if valid_key: - keys.add(key) - - -def parse_variable( - file: str, - line_numb: int, - get_variable: Callable[[str], SnippetVariable], - key: str, - value: str, -) -> bool: - parts = key.split(".") - - if len(parts) != 2: - error(file, line_numb, f"Invalid variable key '{key}'") - return False - - name = parts[0][1:] - field = parts[1] - - match field: - case "insertionFormatter": - get_variable(name).insertion_formatters = parse_vector_value(value) - case "wrapperPhrase": - get_variable(name).wrapper_phrases = parse_vector_value(value) - case "wrapperScope": - get_variable(name).wrapper_scope = value - case _: - error(file, line_numb, f"Invalid variable key '{key}'") - return False - - return True - - -def parse_body(text: str) -> Union[str, None]: - # Find first line that is not empty. Preserve indentation. - match_leading = re.search(r"^[ \t]*\S", text, flags=re.MULTILINE) - - if match_leading is None: - return None - - return text[match_leading.start() :].rstrip() - - -def parse_vector_value(value: str) -> list[str]: - return [v.strip() for v in value.split("|")] - - -def error(file: str, line: int, message: str): - print(f"ERROR | {file}:{line+1} | {message}") diff --git a/talon/user/community/core/system_paths.py b/talon/user/community/core/system_paths.py deleted file mode 100644 index 8cf1ae6..0000000 --- a/talon/user/community/core/system_paths.py +++ /dev/null @@ -1,74 +0,0 @@ -""" -This module gives us the list {user.system_paths} and the capture that wraps -the list to easily refer to system paths in talon and python files. It also creates a file -system_paths-.talon-list in the core folder so the user can easily add their own -custom paths. -""" - -from pathlib import Path - -from talon import Context, Module, actions, app, registry - -mod = Module() -mod.list("system_paths", desc="List of system paths") - - -def on_ready(): - # If user.system_paths defined otherwise, don't generate a file - if registry.lists["user.system_paths"][0]: - return - - hostname = actions.user.talon_get_hostname() - system_paths = Path(__file__).with_name(f"system_paths-{hostname}.talon-list") - if system_paths.is_file(): - return - - home = Path.home() - talon_home = Path(actions.path.talon_home()) - - default_system_paths = { - "user": home, - "desktop": home / "Desktop", - "desk": home / "Desktop", - "documents": home / "Documents", - "docks": home / "Documents", - "downloads": home / "Downloads", - "music": home / "Music", - "pictures": home / "Pictures", - "videos": home / "Videos", - "talon home": talon_home, - "talon recordings": talon_home / "recordings", - "talon user": actions.path.talon_user(), - } - - if app.platform == "windows": - default_system_paths["profile"] = home - onedrive_path = home / "OneDrive" - - # this is probably not the correct way to check for OneDrive, quick and dirty - if (onedrive_path / "Desktop").is_dir(): - default_system_paths["desktop"] = onedrive_path / "Desktop" - default_system_paths["documents"] = onedrive_path / "Documents" - default_system_paths["one drive"] = onedrive_path - default_system_paths["pictures"] = onedrive_path / "Pictures" - else: - default_system_paths["home"] = home - - with open(system_paths, "x") as f: - print("list: user.system_paths", file=f) - print(f"hostname: {hostname}", file=f) - print("-", file=f) - for spoken_form, path in default_system_paths.items(): - path = str(path) - if not str.isprintable(path) or "'" in path or '"' in path: - path = repr(path) - - print(f"{spoken_form}: {path}", file=f) - - -@mod.capture(rule="{user.system_paths}") -def system_path(m) -> str: - return m.system_paths - - -app.register("ready", on_ready) diff --git a/talon/user/community/core/text/currency.talon-list b/talon/user/community/core/text/currency.talon-list deleted file mode 100644 index 6c9f6f2..0000000 --- a/talon/user/community/core/text/currency.talon-list +++ /dev/null @@ -1,8 +0,0 @@ -list: user.currency -- -dollar: $ -dollars: $ -euro: € -euros: € -pound: £ -pounds: £ diff --git a/talon/user/community/core/text/formatters.py b/talon/user/community/core/text/formatters.py deleted file mode 100644 index 361cd58..0000000 --- a/talon/user/community/core/text/formatters.py +++ /dev/null @@ -1,488 +0,0 @@ -import logging -import re -from abc import ABC, abstractmethod -from typing import Callable, Optional, Union - -from talon import Context, Module, actions, app -from talon.grammar import Phrase - - -class Formatter(ABC): - def __init__(self, id: str): - self.id = id - - @abstractmethod - def format(self, text: str) -> str: - pass - - @abstractmethod - def unformat(self, text: str) -> str: - pass - - -class CustomFormatter(Formatter): - def __init__( - self, - id: str, - format: Callable[[str], str], - unformat: Optional[Callable[[str], str]] = None, - ): - super().__init__(id) - self._format = format - self._unformat = unformat - - def format(self, text: str) -> str: - return self._format(text) - - def unformat(self, text: str) -> str: - if self._unformat: - return self._unformat(text) - return text - - -class CodeFormatter(Formatter): - def __init__( - self, - id: str, - delimiter: str, - format_first: Callable[[str], str], - format_rest: Callable[[str], str], - ): - super().__init__(id) - self._delimiter = delimiter - self._format_first = format_first - self._format_rest = format_rest - - def format(self, text: str) -> str: - return self._format_delim( - text, self._delimiter, self._format_first, self._format_rest - ) - - def unformat(self, text: str) -> str: - return remove_code_formatting(text) - - def _format_delim( - self, - text: str, - delimiter: str, - format_first: Callable[[str], str], - format_rest: Callable[[str], str], - ): - # Strip anything that is not alpha-num, whitespace, dot or comma - text = re.sub(r"[^\w\d\s.,]+", "", text) - # Split on anything that is not alpha-num - words = re.split(r"([^\w\d]+)", text) - groups = [] - group = [] - first = True - - for word in words: - if word.isspace(): - continue - # Word is number - if word.isnumeric(): - first = True - # Word is symbol - elif not word.isalpha(): - groups.append(delimiter.join(group)) - word = word.strip() - if word != ".": - word += " " - first = True - groups.append(word) - group = [] - continue - elif first: - first = False - if format_first: - word = format_first(word) - elif format_rest: - word = format_rest(word) - group.append(word) - - groups.append(delimiter.join(group)) - return "".join(groups) - - -class TitleFormatter(Formatter): - _words_to_keep_lowercase = ( - "a an and as at but by en for if in nor of on or per the to v via vs".split() - ) - - def format(self, text: str) -> str: - words = [x for x in re.split(r"(\s+)", text) if x] - words = self._title_case_words(words) - return "".join(words) - - def unformat(self, text: str) -> str: - return unformat_upper(text) - - def _title_case_word( - self, word: str, is_first: bool, is_last: bool, following_symbol: bool - ) -> str: - if not word.islower() or ( - word in self._words_to_keep_lowercase - and not is_first - and not is_last - and not following_symbol - ): - return word - - if "-" in word: - words = word.split("-") - words = self._title_case_words(words) - return "-".join(words) - - return word.capitalize() - - def _title_case_words(self, words: list[str]) -> list[str]: - following_symbol = False - for i, word in enumerate(words): - if word.isspace(): - continue - is_first = i == 0 - is_last = i == len(words) - 1 - words[i] = self._title_case_word(word, is_first, is_last, following_symbol) - following_symbol = not word[-1].isalnum() - return words - - -class CapitalizeFormatter(Formatter): - def format(self, text: str) -> str: - return re.sub(r"^\S+", lambda m: capitalize_first(m.group()), text) - - def unformat(self, text: str) -> str: - return unformat_upper(text) - - -class SentenceFormatter(Formatter): - def format(self, text: str) -> str: - """Capitalize first word if it's already all lower case""" - words = [x for x in re.split(r"(\s+)", text) if x] - if words and words[0].islower(): - words[0] = words[0].capitalize() - return "".join(words) - - def unformat(self, text: str) -> str: - return unformat_upper(text) - - -def capitalize_first(text: str) -> str: - return text[:1].upper() + text[1:] - - -def capitalize(text: str) -> str: - return text.capitalize() - - -def lower(text: str) -> str: - return text.lower() - - -def unformat_upper(text: str) -> str: - return text.lower() if text.isupper() else text - - -def remove_code_formatting(text: str) -> str: - """Remove format from text""" - # Split on delimiters. - result = re.sub(r"[-_.:/]+", " ", text) - # Split camel case. Including numbers - result = de_camel(result) - # Delimiter/camel case successfully split. Lower case to restore "original" text. - if text != result: - return result.lower() - return text - - -def de_camel(text: str) -> str: - """Replacing camelCase boundaries with blank space""" - Ll = "a-zåäö" - Lu = "A-ZÅÄÖ" - L = f"{Ll}{Lu}" - low_to_upper = rf"(?<=[{Ll}])(?=[{Lu}])" # camel|Case - upper_to_last_upper = rf"(?<=[L{Lu}])(?=[{Lu}][{Ll}])" # IP|Address - letter_to_digit = rf"(?<=[{L}])(?=[\d])" # version|10 - digit_to_letter = rf"(?<=[\d])(?=[{L}])" # 2|x - return re.sub( - rf"{low_to_upper}|{upper_to_last_upper}|{letter_to_digit}|{digit_to_letter}", - " ", - text, - ) - - -formatter_list = [ - CustomFormatter("NOOP", lambda text: text), - CustomFormatter("TRAILING_SPACE", lambda text: f"{text} "), - CustomFormatter("DOUBLE_QUOTED_STRING", lambda text: f'"{text}"'), - CustomFormatter("SINGLE_QUOTED_STRING", lambda text: f"'{text}'"), - CustomFormatter("SPACE_SURROUNDED_STRING", lambda text: f" {text} "), - CustomFormatter("ALL_CAPS", lambda text: text.upper()), - CustomFormatter("ALL_LOWERCASE", lambda text: text.lower()), - CustomFormatter("COMMA_SEPARATED", lambda text: re.sub(r"\s+", ", ", text)), - CustomFormatter("REMOVE_FORMATTING", remove_code_formatting), - TitleFormatter("CAPITALIZE_ALL_WORDS"), - # The sentence formatter being called `CAPITALIZE_FIRST_WORD` is a bit of a misnomer, but kept for backward compatibility. - SentenceFormatter("CAPITALIZE_FIRST_WORD"), - # This is the formatter that actually just capitalizes the first word - CapitalizeFormatter("CAPITALIZE"), - CodeFormatter("NO_SPACES", "", lower, lower), - CodeFormatter("PRIVATE_CAMEL_CASE", "", lower, capitalize), - CodeFormatter("PUBLIC_CAMEL_CASE", "", capitalize, capitalize), - CodeFormatter("SNAKE_CASE", "_", lower, lower), - CodeFormatter("DASH_SEPARATED", "-", lower, lower), - CodeFormatter("DOT_SEPARATED", ".", lower, lower), - CodeFormatter("SLASH_SEPARATED", "/", lower, lower), - CodeFormatter("ALL_SLASHES", "/", lambda text: f"/{text.lower()}", lower), - CodeFormatter("DOUBLE_UNDERSCORE", "__", lower, lower), - CodeFormatter("DOUBLE_COLON_SEPARATED", "::", lower, lower), -] - -formatters_dict = {f.id: f for f in formatter_list} - - -# Mapping from spoken phrases to formatter names -code_formatter_names = { - "all cap": "ALL_CAPS", - "all down": "ALL_LOWERCASE", - "camel": "PRIVATE_CAMEL_CASE", - "dotted": "DOT_SEPARATED", - "dub string": "DOUBLE_QUOTED_STRING", - "dunder": "DOUBLE_UNDERSCORE", - "hammer": "PUBLIC_CAMEL_CASE", - "kebab": "DASH_SEPARATED", - "packed": "DOUBLE_COLON_SEPARATED", - "padded": "SPACE_SURROUNDED_STRING", - "slasher": "ALL_SLASHES", - "conga": "SLASH_SEPARATED", - "smash": "NO_SPACES", - "snake": "SNAKE_CASE", - "string": "SINGLE_QUOTED_STRING", - "constant": "ALL_CAPS,SNAKE_CASE", -} -prose_formatter_names = { - "say": "NOOP", - "speak": "NOOP", - "sentence": "CAPITALIZE_FIRST_WORD", - "title": "CAPITALIZE_ALL_WORDS", -} -reformatter_names = { - "cap": "CAPITALIZE", - "list": "COMMA_SEPARATED", - "unformat": "REMOVE_FORMATTING", -} -word_formatter_names = { - "word": "ALL_LOWERCASE", - "trot": "TRAILING_SPACE,ALL_LOWERCASE", - "proud": "CAPITALIZE_FIRST_WORD", - "leap": "TRAILING_SPACE,CAPITALIZE_FIRST_WORD", -} - - -all_phrase_formatters = code_formatter_names | prose_formatter_names | reformatter_names - -mod = Module() -mod.list("formatters", desc="list of all formatters (code and prose)") -mod.list("code_formatter", desc="list of formatters typically applied to code") -mod.list( - "prose_formatter", desc="list of prose formatters (words to start dictating prose)" -) -mod.list("word_formatter", "List of word formatters") - -ctx = Context() -ctx.lists["self.formatters"] = all_phrase_formatters -ctx.lists["self.code_formatter"] = code_formatter_names -ctx.lists["self.prose_formatter"] = prose_formatter_names -ctx.lists["user.word_formatter"] = word_formatter_names - - -# The last phrase spoken, without & with formatting. Used for reformatting. -last_phrase = "" -last_phrase_formatted = "" - - -def format_phrase( - m: Union[str, Phrase], formatters: str, unformat: bool = False -) -> str: - global last_phrase, last_phrase_formatted - last_phrase = m - - if isinstance(m, str): - text = m - else: - text = " ".join(actions.dictate.replace_words(actions.dictate.parse_words(m))) - - result = last_phrase_formatted = format_text_without_adding_to_history( - text, formatters, unformat - ) - - actions.user.add_phrase_to_history(result) - # Arguably, we shouldn't be dealing with history here, but somewhere later - # down the line. But we have a bunch of code that relies on doing it this - # way and I don't feel like rewriting it just now. -rntz, 2020-11-04 - return result - - -def format_text_without_adding_to_history( - text: str, formatters: str, unformat: bool = False -) -> str: - """Formats a text according to formatters. formatters is a comma-separated string of formatters (e.g. 'TITLE_CASE,SNAKE_CASE')""" - if not text: - return text - - text, pre, post = shrink_to_string_inside(text) - - for i, formatter_name in enumerate(reversed(formatters.split(","))): - formatter = formatters_dict[formatter_name] - if unformat and i == 0: - text = formatter.unformat(text) - text = formatter.format(text) - - return f"{pre}{text}{post}" - - -string_delimiters = [ - ['"""', '"""'], - ['"', '"'], - ["'", "'"], -] - - -def shrink_to_string_inside(text: str) -> tuple[str, str, str]: - for [left, right] in string_delimiters: - if text.startswith(left) and text.endswith(right): - return text[len(left) : -len(right)], left, right - return text, "", "" - - -@mod.capture(rule="{self.formatters}+") -def formatters(m) -> str: - "Returns a comma-separated string of formatters e.g. 'SNAKE,DUBSTRING'" - return ",".join(m.formatters_list) - - -@mod.capture(rule="{self.code_formatter}+") -def code_formatters(m) -> str: - "Returns a comma-separated string of code formatters e.g. 'SNAKE,DUBSTRING'" - return ",".join(m.code_formatter_list) - - -@mod.capture( - rule=" ( | )*" -) -def format_text(m) -> str: - """Formats text and returns a string""" - out = "" - formatters = m[0] - for chunk in m[1:]: - if isinstance(chunk, ImmuneString): - out += chunk.string - else: - out += format_phrase(chunk, formatters) - return out - - -@mod.capture(rule=" ") -def format_code(m) -> str: - """Formats code and returns a string""" - return format_phrase(m.text, m.code_formatters) - - -class ImmuneString: - """Wrapper that makes a string immune from formatting.""" - - def __init__(self, string): - self.string = string - - -@mod.capture( - # Add anything else into this that you want to have inserted when - # using a prose formatter. - rule="( | (numb | numeral) )" -) -def formatter_immune(m) -> ImmuneString: - """Symbols and numbers that can be interspersed into a prose formatter - (i.e., not dictated immediately after the name of the formatter) - - They will be inserted directly, without being formatted. - - """ - if hasattr(m, "number"): - value = m.number - else: - value = m[0] - return ImmuneString(str(value)) - - -@mod.action_class -class Actions: - def formatted_text(phrase: Union[str, Phrase], formatters: str) -> str: - """Formats a phrase according to formatters. formatters is a comma-separated string of formatters (e.g. 'CAPITALIZE_ALL_WORDS,DOUBLE_QUOTED_STRING')""" - return format_phrase(phrase, formatters) - - def insert_formatted(phrase: Union[str, Phrase], formatters: str): - """Inserts a phrase formatted according to formatters. Formatters is a comma separated list of formatters (e.g. 'CAPITALIZE_ALL_WORDS,DOUBLE_QUOTED_STRING')""" - actions.insert(format_phrase(phrase, formatters)) - - def insert_with_history(text: str): - """Inserts some text, remembering it in the phrase history.""" - actions.user.deprecate_action("2022-12-11", "user.insert_with_history") - - actions.user.add_phrase_to_history(text) - actions.insert(text) - - def formatters_reformat_last(formatters: str): - """Clears and reformats last formatted phrase""" - global last_phrase, last_phrase_formatted - if actions.user.get_last_phrase() != last_phrase_formatted: - # The last thing we inserted isn't the same as the last thing we - # formatted, so abort. - logging.warning( - "formatters_reformat_last(): Last phrase wasn't a formatter!" - ) - return - actions.user.clear_last_phrase() - actions.user.insert_formatted(last_phrase, formatters) - - def reformat_text(text: str, formatters: str) -> str: - """Re-formats as """ - return format_phrase(text, formatters, True) - - def formatters_reformat_selection(formatters: str): - """Reformats the current selection as """ - selected = actions.edit.selected_text() - if not selected: - app.notify("Asked to reformat selection, but nothing selected!") - return - # Delete separately for compatibility with programs that don't overwrite - # selected text (e.g. Emacs) - actions.edit.delete() - text = actions.user.reformat_text(selected, formatters) - actions.insert(text) - - def get_formatters_words() -> dict: - """Returns words currently used as formatters, and a demonstration string using those formatters""" - formatters_help_demo = {} - for phrase in sorted(all_phrase_formatters): - name = all_phrase_formatters[phrase] - demo = format_text_without_adding_to_history("one two three", name) - if phrase in prose_formatter_names: - phrase += " *" - formatters_help_demo[phrase] = demo - return formatters_help_demo - - def get_reformatters_words() -> dict: - """Returns words currently used as re-formatters, and a demonstration string using those re-formatters""" - formatters_help_demo = {} - for phrase in sorted(all_phrase_formatters): - name = all_phrase_formatters[phrase] - demo = format_text_without_adding_to_history("one_two_three", name, True) - if phrase in prose_formatter_names: - phrase += " *" - formatters_help_demo[phrase] = demo - return formatters_help_demo - - def insert_many(strings: list[str]) -> None: - """Insert a list of strings, sequentially.""" - for string in strings: - actions.insert(string) diff --git a/talon/user/community/core/text/phrase_ender.talon-list b/talon/user/community/core/text/phrase_ender.talon-list deleted file mode 100644 index b1a2ac9..0000000 --- a/talon/user/community/core/text/phrase_ender.talon-list +++ /dev/null @@ -1,4 +0,0 @@ -list: user.phrase_ender -- - -over: "" diff --git a/talon/user/community/core/text/text.talon b/talon/user/community/core/text/text.talon deleted file mode 100644 index cea870f..0000000 --- a/talon/user/community/core/text/text.talon +++ /dev/null @@ -1,36 +0,0 @@ -#provide both anchored and unachored commands via 'over' -phrase $: - user.add_phrase_to_history(text) - insert(text) -phrase {user.phrase_ender}: - user.add_phrase_to_history(text) - insert("{text}{phrase_ender}") -{user.prose_formatter} $: user.insert_formatted(prose, prose_formatter) -{user.prose_formatter} {user.phrase_ender}: - user.insert_formatted(prose, prose_formatter) - insert(phrase_ender) -+$: user.insert_many(format_code_list) -+ {user.phrase_ender}: - user.insert_many(format_code_list) - insert(phrase_ender) - that: user.formatters_reformat_selection(user.formatters) -{user.word_formatter} : user.insert_formatted(word, word_formatter) - (pace | paste): user.insert_formatted(clip.text(), formatters) -word : - user.add_phrase_to_history(word) - insert(word) -proud : user.insert_formatted(word, "CAPITALIZE_FIRST_WORD") -recent list: user.toggle_phrase_history() -recent close: user.phrase_history_hide() -recent repeat : - recent_phrase = user.get_recent_phrase(number_small) - user.add_phrase_to_history(recent_phrase) - insert(recent_phrase) -recent copy : clip.set_text(user.get_recent_phrase(number_small)) -select that: user.select_last_phrase() -before that: user.before_last_phrase() -nope that | scratch that: user.clear_last_phrase() -nope that was : user.formatters_reformat_last(formatters) -(abbreviate | abreviate | brief) {user.abbreviation}: "{abbreviation}" - (abbreviate | abreviate | brief) {user.abbreviation}: - user.insert_formatted(abbreviation, formatters) diff --git a/talon/user/community/core/text/text_and_dictation.py b/talon/user/community/core/text/text_and_dictation.py deleted file mode 100644 index b60302b..0000000 --- a/talon/user/community/core/text/text_and_dictation.py +++ /dev/null @@ -1,532 +0,0 @@ -# Descended from https://github.com/dwiel/talon_community/blob/master/misc/dictation.py -import re -from typing import Callable, Optional - -from talon import Context, Module, actions, grammar, settings, ui - -from ..numbers.numbers import get_spoken_form_under_one_hundred - -mod = Module() - -mod.setting( - "context_sensitive_dictation", - type=bool, - default=False, - desc="Look at surrounding text to improve auto-capitalization/spacing in dictation mode. By default, this works by selecting that text & copying it to the clipboard, so it may be slow or fail in some applications.", -) - -mod.list("prose_modifiers", desc="Modifiers that can be used within prose") -mod.list("prose_snippets", desc="Snippets that can be used within prose") -mod.list("phrase_ender", "List of commands that can be used to end a phrase") -mod.list("hours_twelve", desc="Names for hours up to 12") -mod.list("hours", desc="Names for hours up to 24") -mod.list("minutes", desc="Names for minutes, 01 up to 59") -mod.list( - "currency", - desc="Currency types (e.g., dollars, euros) that can be used within prose", -) - -ctx = Context() -# Maps spoken forms to DictationFormat method names (see DictationFormat below). -ctx.lists["user.prose_modifiers"] = { - "cap": "cap", - "no cap": "no_cap", - "no caps": "no_cap", # "no caps" variant for Dragon - "no space": "no_space", -} -ctx.lists["user.prose_snippets"] = { - "spacebar": " ", - "new line": "\n", - "new paragraph": "\n\n", - # Curly quotes are used to obtain proper spacing for left and right quotes, but will later be straightened. - "open quote": "“", - "close quote": "”", - "smiley": ":-)", - "winky": ";-)", - "frowny": ":-(", -} - -ctx.lists["user.hours_twelve"] = get_spoken_form_under_one_hundred( - 1, - 12, - include_oh_variant_for_single_digits=True, - include_default_variant_for_single_digits=True, -) -ctx.lists["user.hours"] = get_spoken_form_under_one_hundred( - 1, - 23, - include_oh_variant_for_single_digits=True, - include_default_variant_for_single_digits=True, -) -ctx.lists["user.minutes"] = get_spoken_form_under_one_hundred( - 1, - 59, - include_oh_variant_for_single_digits=True, - include_default_variant_for_single_digits=False, -) - - -@mod.capture(rule="{user.prose_modifiers}") -def prose_modifier(m) -> Callable: - return getattr(DictationFormat, m.prose_modifiers) - - -@mod.capture(rule="(numb | numeral) ") -def prose_simple_number(m) -> str: - return m.number_string - - -@mod.capture(rule="(numb | numeral) (dot | point) ") -def prose_number_with_dot(m) -> str: - return m.number_string + "." + m.digit_string - - -@mod.capture(rule="(numb | numeral) colon ") -def prose_number_with_colon(m) -> str: - return m.number_string_1 + ":" + m.number_string_2 - - -@mod.capture( - rule=" | | " -) -def prose_number(m) -> str: - return str(m) - - -@mod.capture( - rule=" [(dot | point) ] percent [sign|sine]" -) -def prose_percent(m) -> str: - s = m.number_string - if hasattr(m, "digit_string"): - s += "." + m.digit_string - return s + "%" - - -@mod.capture( - rule=" {user.currency} [[and] [cents|pence]]" -) -def prose_currency(m) -> str: - s = m.currency + m.number_string_1 - if hasattr(m, "number_string_2"): - s += "." + m.number_string_2 - return s - - -@mod.capture(rule="am|pm") -def time_am_pm(m) -> str: - return str(m) - - -# this matches eg "twelve thirty-four" -> 12:34 and "twelve hundred" -> 12:00. hmmmmm. -@mod.capture( - rule="{user.hours} ({user.minutes} | o'clock | hundred hours) []" -) -def prose_time_hours_minutes(m) -> str: - t = m.hours + ":" - if hasattr(m, "minutes"): - t += m.minutes - else: - t += "00" - if hasattr(m, "time_am_pm"): - t += m.time_am_pm - return t - - -@mod.capture(rule="{user.hours_twelve} ") -def prose_time_hours_am_pm(m) -> str: - return m.hours_twelve + m.time_am_pm - - -@mod.capture(rule=" | ") -def prose_time(m) -> str: - return str(m) - - -@mod.capture(rule="({user.vocabulary} | )") -def word(m) -> str: - """A single word, including user-defined vocabulary.""" - try: - return m.vocabulary - except AttributeError: - return " ".join( - actions.dictate.replace_words(actions.dictate.parse_words(m.word)) - ) - - -@mod.capture(rule="({user.vocabulary} | )+") -def text(m) -> str: - """A sequence of words, including user-defined vocabulary.""" - return format_phrase(m) - - -@mod.capture( - rule="( | {user.vocabulary} | {user.punctuation} | {user.prose_snippets} | | | | | )+" -) -def prose(m) -> str: - """Mixed words and punctuation, auto-spaced & capitalized.""" - # Straighten curly quotes that were introduced to obtain proper spacing. - return apply_formatting(m).replace("“", '"').replace("”", '"') - - -@mod.capture( - rule="( | {user.vocabulary} | {user.punctuation} | {user.prose_snippets} | | | | )+" -) -def raw_prose(m) -> str: - """Mixed words and punctuation, auto-spaced & capitalized, without quote straightening and commands (for use in dictation mode).""" - return apply_formatting(m) - - -# ---------- FORMATTING ---------- # -def format_phrase(m): - words = capture_to_words(m) - result = "" - for i, word in enumerate(words): - if i > 0 and needs_space_between(words[i - 1], word): - result += " " - result += word - return result - - -def capture_to_words(m): - words = [] - for item in m: - words.extend( - actions.dictate.replace_words(actions.dictate.parse_words(item)) - if isinstance(item, grammar.vm.Phrase) - else [item] - ) - return words - - -def apply_formatting(m): - formatter = DictationFormat() - formatter.state = None - result = "" - for item in m: - # prose modifiers (cap/no cap/no space) produce formatter callbacks. - if isinstance(item, Callable): - item(formatter) - else: - words = ( - actions.dictate.replace_words(actions.dictate.parse_words(item)) - if isinstance(item, grammar.vm.Phrase) - else [item] - ) - for word in words: - result += formatter.format(word) - return result - - -# There must be a simpler way to do this, but I don't see it right now. -no_space_after = re.compile( - r""" - (?: - [\s\-_/#@([{‘“] # characters that never need space after them - | (? bool: - return not text or no_space_before.search(text) - - -def omit_space_after(text: str) -> bool: - return not text or no_space_after.search(text) - - -def needs_space_between(before: str, after: str) -> bool: - return not (omit_space_after(before) or omit_space_before(after)) - - -# # TESTS, uncomment to enable -# assert needs_space_between("a", "break") -# assert needs_space_between("break", "a") -# assert needs_space_between(".", "a") -# assert needs_space_between("said", "'hello") -# assert needs_space_between("hello'", "said") -# assert needs_space_between("hello.", "'John") -# assert needs_space_between("John.'", "They") -# assert needs_space_between("paid", "$50") -# assert needs_space_between("50$", "payment") -# assert not needs_space_between("", "") -# assert not needs_space_between("a", "") -# assert not needs_space_between("a", " ") -# assert not needs_space_between("", "a") -# assert not needs_space_between(" ", "a") -# assert not needs_space_between("a", ",") -# assert not needs_space_between("'", "a") -# assert not needs_space_between("a", "'") -# assert not needs_space_between("and-", "or") -# assert not needs_space_between("mary", "-kate") -# assert not needs_space_between("$", "50") -# assert not needs_space_between("US", "$") -# assert not needs_space_between("(", ")") -# assert not needs_space_between("(", "e.g.") -# assert not needs_space_between("example", ")") -# assert not needs_space_between("example", '".') -# assert not needs_space_between("example", '."') -# assert not needs_space_between("hello'", ".") -# assert not needs_space_between("hello.", "'") - -no_cap_after = re.compile( - r"""( - e\.g\. - | i\.e\. - )$""", - re.VERBOSE, -) - - -def auto_capitalize(text, state=None): - """ - Auto-capitalizes text. Text must contain complete words, abbreviations, and - formatted expressions. `state` argument means: - - - None: Don't capitalize initial word. - - "sentence start": Capitalize initial word. - - "after newline": Don't capitalize initial word, but we're after a newline. - Used for double-newline detection. - - Returns (capitalized text, updated state). - """ - output = "" - # Imagine a metaphorical "capitalization charge" travelling through the - # string left-to-right. - charge = state == "sentence start" - newline = state == "after newline" - sentence_end = False - for c in text: - # Sentence endings followed by space & double newlines create a charge. - if (sentence_end and c in " \n\t") or (newline and c == "\n"): - charge = True - # Alphanumeric characters and commas/colons absorb charge & try to - # capitalize (for numbers & punctuation this does nothing, which is what - # we want). - elif charge and (c.isalnum() or c in ",:"): - charge = False - c = c.capitalize() - # Otherwise the charge just passes through. - output += c - newline = c == "\n" - sentence_end = c in ".!?" and not no_cap_after.search(output) - return output, ( - "sentence start" - if charge or sentence_end - else "after newline" if newline else None - ) - - -# ---------- DICTATION AUTO FORMATTING ---------- # -class DictationFormat: - def __init__(self): - self.reset() - - def reset(self): - self.reset_context() - self.force_no_space = False - self.force_capitalization = None # Can also be "cap" or "no cap". - - def reset_context(self): - self.before = "" - self.state = "sentence start" - - def update_context(self, before): - if before is None: - return - self.reset_context() - self.pass_through(before) - - def pass_through(self, text): - _, self.state = auto_capitalize(text, self.state) - self.before = text or self.before - - def format(self, text, auto_cap=True): - if not self.force_no_space and needs_space_between(self.before, text): - text = " " + text - self.force_no_space = False - if auto_cap: - text, self.state = auto_capitalize(text, self.state) - if self.force_capitalization == "cap": - text = format_first_letter(text, lambda s: s.capitalize()) - self.force_capitalization = None - if self.force_capitalization == "no cap": - text = format_first_letter(text, lambda s: s.lower()) - self.force_capitalization = None - self.before = text or self.before - return text - - # These are used as callbacks by prose modifiers / dictation_mode commands. - def cap(self): - self.force_capitalization = "cap" - - def no_cap(self): - self.force_capitalization = "no cap" - - def no_space(self): - # This is typically used after repositioning the cursor, so it is helpful to - # reset capitalization as well. - # - # FIXME: this sets state to "sentence start", capitalizing the next - # word. probably undesirable, since most places are not the start of - # sentences? - self.reset_context() - self.force_no_space = True - - -def format_first_letter(text, formatter): - i = -1 - for i, c in enumerate(text): - if c.isalpha(): - break - if i >= 0 and i < len(text): - text = text[:i] + formatter(text[i]) + text[i + 1 :] - return text - - -dictation_formatter = DictationFormat() -ui.register("app_deactivate", lambda app: dictation_formatter.reset()) -ui.register("win_focus", lambda win: dictation_formatter.reset()) - - -def reformat_last_utterance(formatter): - text = actions.user.get_last_phrase() - actions.user.clear_last_phrase() - text = formatter(text) - actions.user.add_phrase_to_history(text) - actions.insert(text) - - -@mod.action_class -class Actions: - def dictation_format_reset(): - """Resets the dictation formatter""" - return dictation_formatter.reset() - - def dictation_format_cap(): - """Sets the dictation formatter to capitalize""" - dictation_formatter.cap() - - def dictation_format_no_cap(): - """Sets the dictation formatter to not capitalize""" - dictation_formatter.no_cap() - - def dictation_format_no_space(): - """Sets the dictation formatter to not prepend a space""" - dictation_formatter.no_space() - - def dictation_reformat_cap(): - """Capitalizes the last utterance""" - reformat_last_utterance( - lambda s: format_first_letter(s, lambda c: c.capitalize()) - ) - - def dictation_reformat_no_cap(): - """Lowercases the last utterance""" - reformat_last_utterance(lambda s: format_first_letter(s, lambda c: c.lower())) - - def dictation_reformat_no_space(): - """Removes space before the last utterance""" - reformat_last_utterance(lambda s: s[1:] if s.startswith(" ") else s) - - def dictation_insert_raw(text: str): - """Inserts text as-is, without invoking the dictation formatter.""" - actions.user.dictation_insert(text, auto_cap=False) - - def dictation_insert(text: str, auto_cap: bool = True) -> str: - """Inserts dictated text, formatted appropriately.""" - add_space_after = False - if settings.get("user.context_sensitive_dictation"): - # Peek left if we might need leading space or auto-capitalization; - # peek right if we might need trailing space. NB. We peek right - # BEFORE insertion to avoid breaking the undo-chain between the - # inserted text and the trailing space. - need_left = not omit_space_before(text) or ( - auto_cap and text != auto_capitalize(text, "sentence start")[0] - ) - need_right = not omit_space_after(text) - before, after = actions.user.dictation_peek(need_left, need_right) - dictation_formatter.update_context(before) - add_space_after = after is not None and needs_space_between(text, after) - text = dictation_formatter.format(text, auto_cap) - # Straighten curly quotes that were introduced to obtain proper - # spacing. The formatter context still has the original curly quotes - # so that future dictation is properly formatted. - text = text.replace("“", '"').replace("”", '"') - actions.user.add_phrase_to_history(text) - actions.user.insert_between(text, " " if add_space_after else "") - - def dictation_peek(left: bool, right: bool) -> tuple[Optional[str], Optional[str]]: - """ - Gets text around the cursor to inform auto-spacing and -capitalization. - Returns (before, after), where `before` is some text before the cursor, - and `after` some text after it. Results are not guaranteed; `before` - and/or `after` may be None, indicating no information. If `before` is - the empty string, this means there is nothing before the cursor (we are - at the beginning of the document); likewise for `after`. - - To optimize performance, pass `left = False` if you won't need - `before`, and `right = False` if you won't need `after`. - - dictation_peek() is intended for use before inserting text, so it may - delete any currently selected text. - """ - if not (left or right): - return None, None - before, after = None, None - # Inserting a space ensures we select something even if we're at - # document start; some editors 'helpfully' copy the current line if we - # edit.copy() while nothing is selected. - actions.insert(" ") - if left: - # In principle the previous word should suffice, but some applications - # have a funny concept of what the previous word is (for example, they - # may only take the "`" at the end of "`foo`"). To be double sure we - # take two words left. I also tried taking a line up + a word left, but - # edit.extend_up() = key(shift-up) doesn't work consistently in the - # Slack webapp (sometimes escapes the text box). - actions.edit.extend_word_left() - actions.edit.extend_word_left() - before = actions.edit.selected_text()[:-1] - # Unfortunately, in web Slack, if our selection ends at newline, - # this will go right over the newline. Argh. - actions.edit.right() - if not right: - actions.key("backspace") # remove the space - else: - actions.edit.left() # go left before space - # We want to select at least two characters to the right, plus the space - # we inserted, because no_space_before needs two characters in the worst - # case -- for example, inserting before "' hello" we don't want to add - # space, while inserted before "'hello" we do. - # - # We use 2x extend_word_right() because it's fewer keypresses (lower - # latency) than 3x extend_right(). Other options all seem to have - # problems. For instance, extend_line_end() might not select all the way - # to the next newline if text has been wrapped across multiple lines; - # extend_line_down() sometimes escapes the current text box (eg. in a - # browser address bar). 1x extend_word_right() _usually_ works, but on - # Windows in Firefox it doesn't always select enough characters. - actions.edit.extend_word_right() - actions.edit.extend_word_right() - after = actions.edit.selected_text()[1:] - actions.edit.left() - actions.key("delete") # remove space - return before, after diff --git a/talon/user/community/core/user_settings.py b/talon/user/community/core/user_settings.py deleted file mode 100644 index 6e08cfe..0000000 --- a/talon/user/community/core/user_settings.py +++ /dev/null @@ -1,107 +0,0 @@ -import csv -import os -from pathlib import Path -from typing import IO, Callable - -from talon import resource - -# NOTE: This method requires this module to be one folder below the top-level -# community/knausj folder. -SETTINGS_DIR = Path(__file__).parents[1] / "settings" -SETTINGS_DIR.mkdir(exist_ok=True) - -CallbackT = Callable[[dict[str, str]], None] -DecoratorT = Callable[[CallbackT], CallbackT] - - -def read_csv_list( - f: IO, headers: tuple[str, str], is_spoken_form_first: bool = False -) -> dict[str, str]: - rows = list(csv.reader(f)) - - # print(str(rows)) - mapping = {} - if len(rows) >= 2: - actual_headers = rows[0] - if not actual_headers == list(headers): - print( - f'"{f.name}": Malformed headers - {actual_headers}.' - + f" Should be {list(headers)}. Ignoring row." - ) - for row in rows[1:]: - if len(row) == 0: - # Windows newlines are sometimes read as empty rows. :champagne: - continue - if len(row) == 1: - output = spoken_form = row[0] - else: - if is_spoken_form_first: - spoken_form, output = row[:2] - else: - output, spoken_form = row[:2] - - if len(row) > 2: - print( - f'"{f.name}": More than two values in row: {row}.' - + " Ignoring the extras." - ) - # Leading/trailing whitespace in spoken form can prevent recognition. - spoken_form = spoken_form.strip() - mapping[spoken_form] = output - - return mapping - - -def write_csv_defaults( - path: Path, - headers: tuple[str, str], - default: dict[str, str] = None, - is_spoken_form_first: bool = False, -) -> None: - if not path.is_file() and default is not None: - with open(path, "w", encoding="utf-8") as file: - writer = csv.writer(file) - writer.writerow(headers) - for key, value in default.items(): - if key == value: - writer.writerow([key]) - elif is_spoken_form_first: - writer.writerow([key, value]) - else: - writer.writerow([value, key]) - - -def track_csv_list( - filename: str, - headers: tuple[str, str], - default: dict[str, str] = None, - is_spoken_form_first: bool = False, -) -> DecoratorT: - assert filename.endswith(".csv") - path = SETTINGS_DIR / filename - write_csv_defaults(path, headers, default, is_spoken_form_first) - - def decorator(fn: CallbackT) -> CallbackT: - @resource.watch(str(path)) - def on_update(f): - data = read_csv_list(f, headers, is_spoken_form_first) - fn(data) - - return decorator - - -def append_to_csv(filename: str, rows: dict[str, str]): - path = SETTINGS_DIR / filename - assert filename.endswith(".csv") - - with open(str(path)) as file: - line = None - for line in file: - pass - needs_newline = line is not None and not line.endswith("\n") - with open(path, "a", encoding="utf-8", newline="") as file: - writer = csv.writer(file) - if needs_newline: - writer.writerow([]) - for key, value in rows.items(): - writer.writerow([key] if key == value else [value, key]) diff --git a/talon/user/community/core/vocabulary/edit_vocabulary.talon b/talon/user/community/core/vocabulary/edit_vocabulary.talon deleted file mode 100644 index af01012..0000000 --- a/talon/user/community/core/vocabulary/edit_vocabulary.talon +++ /dev/null @@ -1,17 +0,0 @@ -mode: command -mode: dictation -- -copy to vocab [as ]$: user.add_selection_to_vocabulary(phrase or "") -# Automatically adds possessive form by appending "'s". -copy name to vocab [as ]$: - user.add_selection_to_vocabulary(phrase or "", "name") -# Automatically adds plural form by simply appending "s". -copy noun to vocab [as ]$: - user.add_selection_to_vocabulary(phrase or "", "noun") -copy to replacements as $: user.add_selection_to_words_to_replace(phrase) -# Automatically adds possessive form by appending "'s". -copy name to replacements as $: - user.add_selection_to_words_to_replace(phrase, "name") -# Automatically adds plural form by simply appending "s". -copy noun to replacements as $: - user.add_selection_to_words_to_replace(phrase, "noun") diff --git a/talon/user/community/core/vocabulary/vocabulary.py b/talon/user/community/core/vocabulary/vocabulary.py deleted file mode 100644 index 3792138..0000000 --- a/talon/user/community/core/vocabulary/vocabulary.py +++ /dev/null @@ -1,279 +0,0 @@ -import logging -import os -import re -from typing import Sequence, Union - -from talon import Context, Module, actions -from talon.grammar import Phrase - -from ..user_settings import append_to_csv, track_csv_list - -mod = Module() -ctx = Context() - -mod.list("vocabulary", desc="additional vocabulary words") - -# Default words that will need to be capitalized. -# DON'T EDIT THIS. Edit settings/words_to_replace.csv instead. -# These defaults and those later in this file are ONLY used when -# auto-creating the corresponding settings/*.csv files. Those csv files -# determine the contents of user.vocabulary and dictate.word_map. Once they -# exist, the contents of the lists/dictionaries below are irrelevant. -_capitalize_defaults = [ - # NB. the lexicon now capitalizes January/February by default, but not the - # others below. Not sure why. - "January", - "February", - # March omitted because it's a regular word too - "April", - # May omitted because it's a regular word too - "June", - "July", - "August", # technically also an adjective but the month is far more common - "September", - "October", - "November", - "December", -] - -# Default words that need to be remapped. -_word_map_defaults = { - # E.g: - # "cash": "cache", - # This is the opposite ordering to words_to_replace.csv (the latter has the target word first) -} -_word_map_defaults.update({word.lower(): word for word in _capitalize_defaults}) -phrases_to_replace = {} - - -class PhraseReplacer: - """Utility for replacing phrases by other phrases inside text or word lists. - - Replacing longer phrases has priority. - - Args: - - phrase_dict: dictionary mapping recognized/spoken forms to written forms - """ - - def __init__(self): - self.phrase_index = {} - - def update(self, phrase_dict: dict[str, str]): - # Index phrases by first word, then number of subsequent words n_next - phrase_index = dict() - for spoken_form, written_form in phrase_dict.items(): - words = spoken_form.split() - if not words: - logging.warning( - "Found empty spoken form for written form" - f"{written_form}, ignored" - ) - continue - first_word, n_next = words[0], len(words) - 1 - phrase_index.setdefault(first_word, {}).setdefault(n_next, {})[ - tuple(words[1:]) - ] = written_form - - # Sort n_next index so longer phrases have priority - self.phrase_index = { - first_word: sorted(same_first_word.items(), key=lambda x: -x[0]) - for first_word, same_first_word in phrase_index.items() - } - - def replace(self, input_words: Sequence[str]) -> Sequence[str]: - input_words = tuple(input_words) # tuple to ensure hashability of slices - output_words = [] - first_word_i = 0 - while first_word_i < len(input_words): - first_word = input_words[first_word_i] - next_word_i = first_word_i + 1 - # Could this word be the first of a phrase we should replace? - for n_next, phrases_n_next in self.phrase_index.get(first_word, []): - # Yes. Perhaps a phrase with n_next subsequent words? - continuation = input_words[next_word_i : next_word_i + n_next] - if continuation in phrases_n_next: - # Found a match! - output_words.append(phrases_n_next[continuation]) - first_word_i += 1 + n_next - break - else: - # No match, just add the word to the result - output_words.append(first_word) - first_word_i += 1 - return output_words - - # Wrapper used for testing. - def replace_string(self, text: str) -> str: - return " ".join(self.replace(text.split())) - - -# Unit tests for PhraseReplacer -rep = PhraseReplacer() -rep.update( - { - "this": "foo", - "that": "bar", - "this is": "stopping early", - "this is a test": "it worked!", - } -) -assert rep.replace_string("gnork") == "gnork" -assert rep.replace_string("this") == "foo" -assert rep.replace_string("this that this") == "foo bar foo" -assert rep.replace_string("this is a test") == "it worked!" -assert rep.replace_string("well this is a test really") == "well it worked! really" -assert rep.replace_string("try this is too") == "try stopping early too" -assert rep.replace_string("this is a tricky one") == "stopping early a tricky one" - -phrase_replacer = PhraseReplacer() - - -# phrases_to_replace is a spoken form -> written form map, used by our -# implementation of `dictate.replace_words` (at bottom of file) to rewrite words -# and phrases Talon recognized. This does not change the priority with which -# Talon recognizes particular phrases over others. -@track_csv_list( - "words_to_replace.csv", - headers=("Replacement", "Original"), - default=_word_map_defaults, -) -def on_word_map(values): - global phrases_to_replace - phrases_to_replace = values - phrase_replacer.update(values) - - # "dictate.word_map" is used by Talon's built-in default implementation of - # `dictate.replace_words`, but supports only single-word replacements. - # Multi-word phrases are ignored. - ctx.settings["dictate.word_map"] = values - - -@ctx.action_class("dictate") -class OverwrittenActions: - def replace_words(words: Sequence[str]) -> Sequence[str]: - try: - return phrase_replacer.replace(words) - except: - # fall back to default implementation for error-robustness - logging.error("phrase replacer failed!") - return actions.next(words) - - -def _create_vocabulary_entries(spoken_form, written_form, type): - """Expands the provided spoken form and written form into multiple variants based on - the provided type, which can be either "name" to add a possessive variant or "noun" - to add plural. - """ - entries = {spoken_form: written_form} - if type == "name": - # Note that we use the spoken form without apostrophe because this seems to generally lead - # to better recognition on Conformer b108. - entries[f"{spoken_form}s"] = f"{written_form}'s" - elif type == "noun": - # Note that we simply append an "s", but we could use something more sophisticated like - # https://github.com/jpvanhal/inflection. The downside is that this is less predictable, - # and this feature is likely to be used in ways that are unlike common English prose, which - # is already included in the lexicon. For example, made up identifiers used in programming. - entries[f"{spoken_form}s"] = f"{written_form}s" - return entries - - -# See https://github.com/wolfmanstout/talon-vocabulary-editor for an experimental version -# of this which tests if the default spoken form can be used instead of the provided phrase. -def _add_selection_to_file( - phrase: Union[Phrase, str], - type: str, - file_name: str, - file_contents: dict[str, str], - skip_identical_replacement: bool, -): - written_form = actions.edit.selected_text().strip() - if phrase: - spoken_form = " ".join(actions.dictate.parse_words(phrase)) - else: - is_acronym = re.fullmatch(r"[A-Z]+", written_form) - spoken_form = " ".join(written_form) if is_acronym else written_form - entries = _create_vocabulary_entries(spoken_form, written_form, type) - added_some_phrases = False - - # until we add support for parsing or otherwise getting the active - # vocabulary.talon-list, skip the logic for checking for duplicates etc - if file_contents: - # clear the new entries dictionary - new_entries = {} - for spoken_form, written_form in entries.items(): - if skip_identical_replacement and spoken_form == written_form: - actions.app.notify(f'Skipping identical replacement: "{spoken_form}"') - elif spoken_form in file_contents: - actions.app.notify( - f'Spoken form "{spoken_form}" is already in {file_name}' - ) - else: - new_entries[spoken_form] = written_form - added_some_phrases = True - else: - new_entries = entries - added_some_phrases = True - - if file_name.endswith(".csv"): - append_to_csv(file_name, new_entries) - elif file_name == "vocabulary.talon-list": - append_to_vocabulary(new_entries) - - if added_some_phrases: - actions.app.notify(f"Added to {file_name}: {new_entries}") - - -def append_to_vocabulary(rows: dict[str, str]): - vocabulary_file_path = actions.user.get_vocabulary_file_path() - with open(str(vocabulary_file_path)) as file: - line = None - for line in file: - pass - needs_newline = line is not None and not line.endswith("\n") - - with open(vocabulary_file_path, "a", encoding="utf-8") as file: - if needs_newline: - file.write("\n") - for key, value in rows.items(): - if key == value: - file.write(f"{key}\n") - else: - value = repr(value) - file.write(f"{key}: {value}\n") - - -@mod.action_class -class Actions: - # this is implemented as an action so it may be overridden in other contexts - def get_vocabulary_file_path(): - """Returns the path for the active vocabulary file""" - vocabulary_directory = os.path.dirname(os.path.realpath(__file__)) - vocabulary_file_path = os.path.join( - vocabulary_directory, "vocabulary.talon-list" - ) - return vocabulary_file_path - - def add_selection_to_vocabulary(phrase: Union[Phrase, str] = "", type: str = ""): - """Permanently adds the currently selected text to the vocabulary with the provided - spoken form and adds variants based on the type ("noun" or "name"). - """ - _add_selection_to_file( - phrase, - type, - "vocabulary.talon-list", - None, - False, - ) - - def add_selection_to_words_to_replace(phrase: Phrase, type: str = ""): - """Permanently adds the currently selected text as replacement for the provided - original form and adds variants based on the type ("noun" or "name"). - """ - _add_selection_to_file( - phrase, - type, - "words_to_replace.csv", - phrases_to_replace, - True, - ) diff --git a/talon/user/community/core/vocabulary/vocabulary.talon-list b/talon/user/community/core/vocabulary/vocabulary.talon-list deleted file mode 100644 index 63b7c79..0000000 --- a/talon/user/community/core/vocabulary/vocabulary.talon-list +++ /dev/null @@ -1,11 +0,0 @@ -list: user.vocabulary -- -N map: nmap -under documented: under-documented -nmap -admin -Cisco -Citrix -VPN -DNS -Minecraft diff --git a/talon/user/community/core/websites_and_search_engines/search_engine.talon-list b/talon/user/community/core/websites_and_search_engines/search_engine.talon-list deleted file mode 100644 index 8f0ba95..0000000 --- a/talon/user/community/core/websites_and_search_engines/search_engine.talon-list +++ /dev/null @@ -1,7 +0,0 @@ -list: user.search_engine -- -amazon: https://www.amazon.com/s/?field-keywords=%s -google: https://www.google.com/search?q=%s -map: https://maps.google.com/maps?q=%s -scholar: https://scholar.google.com/scholar?q=%s -wiki: https://en.wikipedia.org/w/index.php?search=%s diff --git a/talon/user/community/core/websites_and_search_engines/website.talon-list b/talon/user/community/core/websites_and_search_engines/website.talon-list deleted file mode 100644 index cff1c1a..0000000 --- a/talon/user/community/core/websites_and_search_engines/website.talon-list +++ /dev/null @@ -1,18 +0,0 @@ -list: user.website -- -talon home page: http://talonvoice.com -talon slack: http://talonvoice.slack.com/messages/help -talon wiki: https://talon.wiki/ -talon practice: https://chaosparrot.github.io/talon_practice/ -talon repository search: https://search.talonvoice.com/search/ -amazon: https://www.amazon.com/ -dropbox: https://dropbox.com/ -google: https://www.google.com/ -google calendar: https://calendar.google.com -google maps: https://maps.google.com/ -google scholar: https://scholar.google.com/ -gmail: https://mail.google.com/ -github: https://github.com/ -gist: https://gist.github.com/ -wikipedia: https://en.wikipedia.org/ -youtube: https://www.youtube.com/ diff --git a/talon/user/community/core/websites_and_search_engines/websites_and_search_engines.py b/talon/user/community/core/websites_and_search_engines/websites_and_search_engines.py deleted file mode 100644 index 1b5f934..0000000 --- a/talon/user/community/core/websites_and_search_engines/websites_and_search_engines.py +++ /dev/null @@ -1,23 +0,0 @@ -import webbrowser -from urllib.parse import quote_plus - -from talon import Module - -mod = Module() -mod.list("website", desc="A website.") -mod.list( - "search_engine", - desc="A search engine. Any instance of %s will be replaced by query text", -) - - -@mod.action_class -class Actions: - def open_url(url: str): - """Visit the given URL.""" - webbrowser.open(url) - - def search_with_search_engine(search_template: str, search_text: str): - """Search a search engine for given text""" - url = search_template.replace("%s", quote_plus(search_text)) - webbrowser.open(url) diff --git a/talon/user/community/core/websites_and_search_engines/websites_and_search_engines.talon b/talon/user/community/core/websites_and_search_engines/websites_and_search_engines.talon deleted file mode 100644 index eab5887..0000000 --- a/talon/user/community/core/websites_and_search_engines/websites_and_search_engines.talon +++ /dev/null @@ -1,10 +0,0 @@ -open {user.website}: user.open_url(website) -open that: user.open_url(edit.selected_text()) -open paste: user.open_url(clip.text()) - -{user.search_engine} hunt $: - user.search_with_search_engine(search_engine, user.text) -{user.search_engine} (that | this): - text = edit.selected_text() - user.search_with_search_engine(search_engine, text) -{user.search_engine} paste: user.search_with_search_engine(search_engine, clip.text()) diff --git a/talon/user/community/core/windows_and_tabs/tabs.py b/talon/user/community/core/windows_and_tabs/tabs.py deleted file mode 100644 index 6939821..0000000 --- a/talon/user/community/core/windows_and_tabs/tabs.py +++ /dev/null @@ -1,21 +0,0 @@ -from talon import Module, actions, app - -mod = Module() - - -@mod.action_class -class tab_actions: - def tab_jump(number: int): - """Jumps to the specified tab""" - - def tab_final(): - """Jumps to the final tab""" - - def tab_close_wrapper(): - """Closes the current tab. - Exists so that apps can implement their own delay before running tab_close() to handle repetitions better. - """ - actions.app.tab_close() - - def tab_duplicate(): - """Duplicates the current tab.""" diff --git a/talon/user/community/core/windows_and_tabs/tabs.talon b/talon/user/community/core/windows_and_tabs/tabs.talon deleted file mode 100644 index fe1d05e..0000000 --- a/talon/user/community/core/windows_and_tabs/tabs.talon +++ /dev/null @@ -1,10 +0,0 @@ -tag: user.tabs -- -tab (open | new): app.tab_open() -tab (last | previous): app.tab_previous() -tab next: app.tab_next() -tab close: user.tab_close_wrapper() -tab (reopen | restore): app.tab_reopen() -go tab : user.tab_jump(number) -go tab final: user.tab_final() -tab (duplicate | clone): user.tab_duplicate() diff --git a/talon/user/community/core/windows_and_tabs/window_management.talon b/talon/user/community/core/windows_and_tabs/window_management.talon deleted file mode 100644 index f0175cd..0000000 --- a/talon/user/community/core/windows_and_tabs/window_management.talon +++ /dev/null @@ -1,24 +0,0 @@ -window (new | open): app.window_open() -window next: app.window_next() -window last: app.window_previous() -window close: app.window_close() -window hide: app.window_hide() -focus : user.switcher_focus(running_applications) -# following only works on windows. Can't figure out how to make it work for mac. No idea what the equivalent for linux would be. -focus$: user.switcher_menu() -focus last: user.switcher_focus_last() -running list: user.switcher_toggle_running() -running close: user.switcher_hide_running() -launch : user.switcher_launch(launch_applications) - -snap : user.snap_window(window_snap_position) -snap next [screen]: user.move_window_next_screen() -snap last [screen]: user.move_window_previous_screen() -snap screen : user.move_window_to_screen(number) -snap : - user.snap_app(running_applications, window_snap_position) -# is here twice to require at least two applications. -snap +: - user.snap_layout(window_split_position, running_applications_list) -snap [screen] : - user.move_app_to_screen(running_applications, number) diff --git a/talon/user/community/core/windows_and_tabs/window_snap.py b/talon/user/community/core/windows_and_tabs/window_snap.py deleted file mode 100644 index f23b26f..0000000 --- a/talon/user/community/core/windows_and_tabs/window_snap.py +++ /dev/null @@ -1,369 +0,0 @@ -"""Tools for voice-driven window management. - -Originally from dweil/talon_community - modified for newapi by jcaw. - -""" - -# TODO: Map keyboard shortcuts to this manager once Talon has key hooks on all -# platforms - -import logging -from typing import Dict, Optional - -from talon import Context, Module, actions, settings, ui - -mod = Module() -mod.list( - "window_snap_positions", - "Predefined window positions for the current window. See `RelativeScreenPos`.", -) -mod.list( - "window_split_positions", - "Predefined window positions when splitting the screen between three applications.", -) -mod.setting( - "window_snap_screen", - type=str, - default="proportional", - desc="""How to position and size windows when snapping across different physical screens. Options: - - "proportional" (default): Preserve the window's relative position and size proportional to the screen. - - "size aware": Preserve position relative to the screen, but keep absolute size the same, except if window is full-height or -width, keep it so. -""", -) - - -def _set_window_pos(window, x, y, width, height): - """Helper to set the window position.""" - window.rect = ui.Rect(round(x), round(y), round(width), round(height)) - - -def _bring_forward(window): - current_window = ui.active_window() - try: - window.focus() - current_window.focus() - except Exception as e: - # We don't want to block if this fails. - print(f"Couldn't bring window to front: {e}") - - -def _get_app_window(app_name: str) -> ui.Window: - return actions.self.get_running_app(app_name).active_window - - -def interpolate_interval(w0, w1, s0, s1, d0, d1): - """ - Interpolates an interval (w0, w1) which is within (s0, s1) so that it lies - within (d0, d1). Returns (r0, r1). Tries to preserve absolute interval size, - w1 - w0, while maintaining its relative 'position' within (s0, s1). For - instance, if w0 == s0 then r0 == d0. - - Use-case: fix a window w, a source screen s, and a destination screen d. - Let w0 = w.left, w1 = window.right, s0 = s.left, s1 = s.right, d0 = d.left, d1 = d.right. - """ - wsize, ssize, dsize = w1 - w0, s1 - s0, d1 - d0 - assert wsize > 0 and ssize > 0 and dsize > 0 - before = max(0, (w0 - s0) / ssize) - after = max(0, (s1 - w1) / ssize) - # If we're within 5% of maximized, preserve this. - if before + after <= 0.05: - return (d0, d1) - # If before is 0 (eg. window is left-aligned), we want to preserve before. - # If after is 0 (eg. window is right-aligned), we want to preserve after. - # In between, we linearly interpolate. - beforeness = before / (before + after) - afterness = after / (before + after) - a0, b1 = d0 + before * dsize, d1 - after * dsize - a1, b0 = a0 + wsize, b1 - wsize - r0 = a0 * afterness + b0 * beforeness - r1 = a1 * afterness + b1 * beforeness - return (max(d0, r0), min(d1, r1)) # clamp to destination - - -def _move_to_screen( - window: ui.Window, offset: Optional[int] = None, screen_number: Optional[int] = None -): - """Move a window to a different screen. - - Provide one of `offset` or `screen_number` to specify a target screen. - - Provide `window` to move a specific window, otherwise the current window is - moved. - - """ - assert ( - screen_number or offset and not (screen_number and offset) - ), "Provide exactly one of `screen_number` or `offset`." - - src_screen = window.screen - - if offset: - if offset < 0: - dest_screen = actions.user.screens_get_previous(src_screen) - else: - dest_screen = actions.user.screens_get_next(src_screen) - else: - dest_screen = actions.user.screens_get_by_number(screen_number) - - if src_screen == dest_screen: - return - - dest = dest_screen.visible_rect - src = src_screen.visible_rect - maximized = window.maximized - how = settings.get("user.window_snap_screen") - if how == "size aware": - r = window.rect - left, right = interpolate_interval( - r.left, r.right, src.left, src.right, dest.left, dest.right - ) - top, bot = interpolate_interval( - r.top, r.bot, src.top, src.bot, dest.top, dest.bot - ) - r.x, r.y = left, top - r.width = right - left - r.height = bot - top - window.rect = r - if maximized: - window.maximized = True - return - - # TODO: Test vertical screen with different aspect ratios - # Does the orientation between the screens change? (vertical/horizontal) - if how != "proportional": - logging.warning( - f"Unrecognized 'window_snap_screen' setting: {how!r}. Using default 'proportional'." - ) - if (src.width / src.height > 1) != (dest.width / dest.height > 1): - # Horizontal -> vertical or vertical -> horizontal - # Retain proportional window size, but flip x/y of the vertical monitor to account for the monitors rotation. - if src.width / src.height > 1: - # horizontal -> vertical - width = window.rect.width * dest.height / src.width - height = window.rect.height * dest.width / src.height - else: - # vertical -> horizontal - width = window.rect.width * dest.width / src.height - height = window.rect.height * dest.height / src.width - # Deform window if width or height is bigger than the target monitors while keeping the window area the same. - if width > dest.width: - over = (width - dest.width) * height - width = dest.width - height += over / width - if height > dest.height: - over = (height - dest.height) * width - height = dest.height - width += over / height - # Proportional position: - # Since the window size in respect to the monitor size is not proportional (x/y was flipped), - # the positioning is more complicated than proportionally scaling the x/y coordinates. - # It is computed by keeping the free space to the left of the window proportional to the right - # and respectively for the top/bottom free space. - # The if conditions account for division by 0. TODO: Refactor positioning without division by 0 - if src.height == window.rect.height: - x = dest.left + (dest.width - width) / 2 - else: - x = dest.left + (window.rect.top - src.top) * (dest.width - width) / ( - src.height - window.rect.height - ) - if src.width == window.rect.width: - y = dest.top + (dest.height - height) / 2 - else: - y = dest.top + (window.rect.left - src.left) * (dest.height - height) / ( - src.width - window.rect.width - ) - else: - # Horizontal -> horizontal or vertical -> vertical - # Retain proportional size and position - proportional_width = dest.width / src.width - proportional_height = dest.height / src.height - x = dest.left + (window.rect.left - src.left) * proportional_width - y = dest.top + (window.rect.top - src.top) * proportional_height - width = window.rect.width * proportional_width - height = window.rect.height * proportional_height - _set_window_pos(window, x=x, y=y, width=width, height=height) - if maximized: - window.maximized = True - - -def _snap_window_helper(window, pos): - screen = window.screen.visible_rect - - _set_window_pos( - window, - x=screen.x + (screen.width * pos.left), - y=screen.y + (screen.height * pos.top), - width=screen.width * (pos.right - pos.left), - height=screen.height * (pos.bottom - pos.top), - ) - - -class RelativeScreenPos: - """Represents a window position as a fraction of the screen.""" - - def __init__(self, left, top, right, bottom): - self.left = left - self.top = top - self.bottom = bottom - self.right = right - - -_snap_positions = { - # Halves - # .---.---. .-------. - # | | | & |-------| - # '---'---' '-------' - "left": RelativeScreenPos(0, 0, 0.5, 1), - "right": RelativeScreenPos(0.5, 0, 1, 1), - "top": RelativeScreenPos(0, 0, 1, 0.5), - "bottom": RelativeScreenPos(0, 0.5, 1, 1), - # Thirds - # .--.--.--. - # | | | | - # '--'--'--' - "center third": RelativeScreenPos(1 / 3, 0, 2 / 3, 1), - "left third": RelativeScreenPos(0, 0, 1 / 3, 1), - "right third": RelativeScreenPos(2 / 3, 0, 1, 1), - "left two thirds": RelativeScreenPos(0, 0, 2 / 3, 1), - "right two thirds": RelativeScreenPos(1 / 3, 0, 1, 1), - # Alternate (simpler) spoken forms for thirds - "center small": RelativeScreenPos(1 / 3, 0, 2 / 3, 1), - "left small": RelativeScreenPos(0, 0, 1 / 3, 1), - "right small": RelativeScreenPos(2 / 3, 0, 1, 1), - "left large": RelativeScreenPos(0, 0, 2 / 3, 1), - "right large": RelativeScreenPos(1 / 3, 0, 1, 1), - # Quarters - # .---.---. - # |---|---| - # '---'---' - "top left": RelativeScreenPos(0, 0, 0.5, 0.5), - "top right": RelativeScreenPos(0.5, 0, 1, 0.5), - "bottom left": RelativeScreenPos(0, 0.5, 0.5, 1), - "bottom right": RelativeScreenPos(0.5, 0.5, 1, 1), - # Sixths - # .--.--.--. - # |--|--|--| - # '--'--'--' - "top left third": RelativeScreenPos(0, 0, 1 / 3, 0.5), - "top right third": RelativeScreenPos(2 / 3, 0, 1, 0.5), - "top left two thirds": RelativeScreenPos(0, 0, 2 / 3, 0.5), - "top right two thirds": RelativeScreenPos(1 / 3, 0, 1, 0.5), - "top center third": RelativeScreenPos(1 / 3, 0, 2 / 3, 0.5), - "bottom left third": RelativeScreenPos(0, 0.5, 1 / 3, 1), - "bottom right third": RelativeScreenPos(2 / 3, 0.5, 1, 1), - "bottom left two thirds": RelativeScreenPos(0, 0.5, 2 / 3, 1), - "bottom right two thirds": RelativeScreenPos(1 / 3, 0.5, 1, 1), - "bottom center third": RelativeScreenPos(1 / 3, 0.5, 2 / 3, 1), - # Alternate (simpler) spoken forms for sixths - "top left small": RelativeScreenPos(0, 0, 1 / 3, 0.5), - "top right small": RelativeScreenPos(2 / 3, 0, 1, 0.5), - "top left large": RelativeScreenPos(0, 0, 2 / 3, 0.5), - "top right large": RelativeScreenPos(1 / 3, 0, 1, 0.5), - "top center small": RelativeScreenPos(1 / 3, 0, 2 / 3, 0.5), - "bottom left small": RelativeScreenPos(0, 0.5, 1 / 3, 1), - "bottom right small": RelativeScreenPos(2 / 3, 0.5, 1, 1), - "bottom left large": RelativeScreenPos(0, 0.5, 2 / 3, 1), - "bottom right large": RelativeScreenPos(1 / 3, 0.5, 1, 1), - "bottom center small": RelativeScreenPos(1 / 3, 0.5, 2 / 3, 1), - # Special - "center": RelativeScreenPos(1 / 8, 1 / 6, 7 / 8, 5 / 6), - "full": RelativeScreenPos(0, 0, 1, 1), - "fullscreen": RelativeScreenPos(0, 0, 1, 1), -} - -_split_positions = { - "split": { - 2: [_snap_positions["left"], _snap_positions["right"]], - 3: [ - _snap_positions["left third"], - _snap_positions["center third"], - _snap_positions["right third"], - ], - }, - "clock": { - 3: [ - _snap_positions["left"], - _snap_positions["top right"], - _snap_positions["bottom right"], - ], - }, - "counterclock": { - 3: [ - _snap_positions["right"], - _snap_positions["top left"], - _snap_positions["bottom left"], - ], - }, -} - - -@mod.capture(rule="{user.window_snap_positions}") -def window_snap_position(m) -> RelativeScreenPos: - return _snap_positions[m.window_snap_positions] - - -@mod.capture(rule="{user.window_split_positions}") -def window_split_position(m) -> Dict[int, list[RelativeScreenPos]]: - return _split_positions[m.window_split_positions] - - -ctx = Context() -ctx.lists["user.window_snap_positions"] = _snap_positions.keys() -ctx.lists["user.window_split_positions"] = _split_positions.keys() - - -@mod.action_class -class Actions: - def snap_window(position: RelativeScreenPos) -> None: - """Move the active window to a specific position on its current screen, given a `RelativeScreenPos` object.""" - _snap_window_helper(ui.active_window(), position) - - def snap_window_to_position(position_name: str) -> None: - """Move the active window to a specifically named position on its current screen, using a key from `_snap_positions`.""" - actions.user.snap_window(_snap_positions[position_name]) - - def move_window_next_screen() -> None: - """Move the active window to a specific screen.""" - _move_to_screen(ui.active_window(), offset=1) - - def move_window_previous_screen() -> None: - """Move the active window to the previous screen.""" - _move_to_screen(ui.active_window(), offset=-1) - - def move_window_to_screen(screen_number: int) -> None: - """Move the active window leftward by one.""" - _move_to_screen(ui.active_window(), screen_number=screen_number) - - def snap_app(app_name: str, position: RelativeScreenPos): - """Snap a specific application to another screen.""" - window = _get_app_window(app_name) - _bring_forward(window) - _snap_window_helper(window, position) - - def snap_layout( - positions_by_count: Dict[int, list[RelativeScreenPos]], - apps: list[str], - ): - """Split the screen between multiple applications.""" - try: - positions = positions_by_count[len(apps)] - except KeyError: - supported_layouts = ", ".join(map(str, positions_by_count.keys())) - message = f"{len(apps)} applications given but chosen layout only supports {supported_layouts}" - actions.app.notify(message, "Cannot arrange") - raise NotImplementedError(message) - for index, app in enumerate(apps): - window = _get_app_window(app) - _snap_window_helper(window, positions[index]) - window.focus() - - def move_app_to_screen(app_name: str, screen_number: int): - """Move a specific application to another screen.""" - window = _get_app_window(app_name) - _bring_forward(window) - _move_to_screen( - window, - screen_number=screen_number, - ) diff --git a/talon/user/community/core/windows_and_tabs/windows_and_tabs.py b/talon/user/community/core/windows_and_tabs/windows_and_tabs.py deleted file mode 100644 index a14c627..0000000 --- a/talon/user/community/core/windows_and_tabs/windows_and_tabs.py +++ /dev/null @@ -1,40 +0,0 @@ -from talon import Context, actions, ui - -ctx = Context() - - -@ctx.action_class("app") -class AppActions: - def window_previous(): - cycle_windows(ui.active_app(), -1) - - def window_next(): - cycle_windows(ui.active_app(), 1) - - -def cycle_windows(app: ui.App, diff: int): - """Cycle windows backwards or forwards for the given application""" - active = app.active_window - windows = [w for w in app.windows() if w == active or is_window_valid(w)] - windows.sort(key=lambda w: w.id) - current = windows.index(active) - i = (current + diff) % len(windows) - - while i != current: - try: - actions.user.switcher_focus_window(windows[i]) - break - except Exception: - i = (i + diff) % len(windows) - - -def is_window_valid(window: ui.Window) -> bool: - """Returns true if this window is valid for focusing""" - return ( - not window.hidden - # On Windows, there are many fake windows with empty titles -- this excludes them. - and window.title != "" - # This excludes many tiny windows that are not actual windows, and is a rough heuristic. - and window.rect.width > window.screen.dpi - and window.rect.height > window.screen.dpi - ) diff --git a/talon/user/community/core/windows_and_tabs/windows_and_tabs_linux.py b/talon/user/community/core/windows_and_tabs/windows_and_tabs_linux.py deleted file mode 100644 index 8ca2bd7..0000000 --- a/talon/user/community/core/windows_and_tabs/windows_and_tabs_linux.py +++ /dev/null @@ -1,46 +0,0 @@ -# defines the default app actions for linux - -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -os: linux -""" - - -@ctx.action_class("app") -class AppActions: - # app.preferences() - - def tab_close(): - actions.key("ctrl-w") - - def tab_next(): - actions.key("ctrl-tab") - - def tab_open(): - actions.key("ctrl-t") - - def tab_previous(): - actions.key("ctrl-shift-tab") - - def tab_reopen(): - actions.key("ctrl-shift-t") - - def window_close(): - actions.key("alt-f4") - - def window_hide(): - actions.key("alt-space n") - - def window_hide_others(): - actions.key("win-d alt-tab") - - def window_open(): - actions.key("ctrl-n") - - -@ctx.action_class("user") -class UserActions: - def switcher_focus_last(): - actions.key("alt-tab") diff --git a/talon/user/community/core/windows_and_tabs/windows_and_tabs_mac.py b/talon/user/community/core/windows_and_tabs/windows_and_tabs_mac.py deleted file mode 100644 index 3fac8ab..0000000 --- a/talon/user/community/core/windows_and_tabs/windows_and_tabs_mac.py +++ /dev/null @@ -1,51 +0,0 @@ -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -os: mac -""" - - -@ctx.action_class("app") -class AppActions: - def preferences(): - actions.key("cmd-,") - - def tab_close(): - actions.key("cmd-w") - - def tab_next(): - actions.key("ctrl-tab") - - def tab_open(): - actions.key("cmd-t") - - def tab_previous(): - actions.key("ctrl-shift-tab") - - def tab_reopen(): - actions.key("cmd-shift-t") - - def window_close(): - actions.key("cmd-w") - - def window_hide(): - actions.key("cmd-m") - - def window_hide_others(): - actions.key("cmd-alt-h") - - def window_open(): - actions.key("cmd-n") - - def window_previous(): - actions.key("cmd-shift-`") - - def window_next(): - actions.key("cmd-`") - - -@ctx.action_class("user") -class UserActions: - def switcher_focus_last(): - actions.key("cmd-tab") diff --git a/talon/user/community/core/windows_and_tabs/windows_and_tabs_win.py b/talon/user/community/core/windows_and_tabs/windows_and_tabs_win.py deleted file mode 100644 index f440246..0000000 --- a/talon/user/community/core/windows_and_tabs/windows_and_tabs_win.py +++ /dev/null @@ -1,46 +0,0 @@ -# defines the default app actions for windows - -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -os: windows -""" - - -@ctx.action_class("app") -class AppActions: - # app.preferences() - - def tab_close(): - actions.key("ctrl-w") - - def tab_next(): - actions.key("ctrl-tab") - - def tab_open(): - actions.key("ctrl-t") - - def tab_previous(): - actions.key("ctrl-shift-tab") - - def tab_reopen(): - actions.key("ctrl-shift-t") - - def window_close(): - actions.key("alt-f4") - - def window_hide(): - actions.key("alt-space n") - - def window_hide_others(): - actions.key("win-d alt-tab") - - def window_open(): - actions.key("ctrl-n") - - -@ctx.action_class("user") -class UserActions: - def switcher_focus_last(): - actions.key("alt-tab") diff --git a/talon/user/community/lang/batch/batch.py b/talon/user/community/lang/batch/batch.py deleted file mode 100644 index 3a4b67f..0000000 --- a/talon/user/community/lang/batch/batch.py +++ /dev/null @@ -1,12 +0,0 @@ -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -code.language: batch -""" - - -@ctx.action_class("user") -class UserActions: - def code_comment_line_prefix(): - actions.auto_insert("REM ") diff --git a/talon/user/community/lang/c/c.talon b/talon/user/community/lang/c/c.talon deleted file mode 100644 index 40200f7..0000000 --- a/talon/user/community/lang/c/c.talon +++ /dev/null @@ -1,93 +0,0 @@ -code.language: c -- -tag(): user.code_imperative - -tag(): user.code_block_c_like -tag(): user.code_comment_line -tag(): user.code_comment_block_c_like -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_functions_common -tag(): user.code_libraries -tag(): user.code_libraries_gui -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_math -tag(): user.code_operators_pointer - -settings(): - user.code_private_function_formatter = "SNAKE_CASE" - user.code_protected_function_formatter = "SNAKE_CASE" - user.code_public_function_formatter = "SNAKE_CASE" - user.code_private_variable_formatter = "SNAKE_CASE" - user.code_protected_variable_formatter = "SNAKE_CASE" - user.code_public_variable_formatter = "SNAKE_CASE" - -# NOTE: migrated from generic, as they were only used here, though once cpp support is added, perhaps these should be migrated to a tag together with the commands below -state include: insert("#include ") -state include system: user.insert_between("#include <", ">") -state include local: user.insert_between('#include "', '"') -state type deaf: insert("typedef ") -state type deaf struct: - insert("typedef struct") - insert("{\n\n}") - edit.up() - key('tab') - -# XXX - create a preprocessor tag for these, as they will match cpp, etc -state define: "#define " -state (undefine | undeaf): "#undef " -state if (define | deaf): "#ifdef " -[state] define $: - "#define {user.formatted_text(text, 'ALL_CAPS,SNAKE_CASE')}" -[state] (undefine | undeaf) $: - "#undef {user.formatted_text(text, 'ALL_CAPS,SNAKE_CASE')}" -[state] if (define | deaf) $: - "#ifdef {user.formatted_text(text, 'ALL_CAPS,SNAKE_CASE')}" - -# XXX - preprocessor instead of pre? -state pre if: "#if " -state error: "#error " -state pre else if: "#elif " -state pre end: "#endif " -state pragma: "#pragma " -state default: "default:\nbreak;" - -#control flow -#best used with a push like command -#the below example may not work in editors that automatically add the closing brace -#if so uncomment the two lines and comment out the rest accordingly -push braces: - edit.line_end() - #insert("{") - #key(enter) - insert("{}") - edit.left() - key(enter) - key(enter) - edit.up() - -# Declare variables or structs etc. -# Ex. * int myList - : - insert("{c_variable} ") - insert(user.formatted_text(phrase, "PRIVATE_CAMEL_CASE,NO_SPACES")) - - : insert("{c_variable} {letter} ") - -# Ex. (int *) -cast to : "{c_cast}" -standard cast to : "{stdint_cast}" -: "{c_types}" -: "{c_pointers}" -: "{c_keywords}" -: "{c_signed}" -standard : "{stdint_types}" -int main: user.insert_between("int main(", ")") - -toggle includes: user.code_toggle_libraries() -include : - user.code_insert_library(code_libraries, "") - key(end enter) diff --git a/talon/user/community/lang/csharp/csharp.py b/talon/user/community/lang/csharp/csharp.py deleted file mode 100644 index 105a18b..0000000 --- a/talon/user/community/lang/csharp/csharp.py +++ /dev/null @@ -1,250 +0,0 @@ -from talon import Context, actions, settings - -ctx = Context() -ctx.matches = r""" -code.language: csharp -""" -ctx.lists["user.code_common_function"] = { - "integer": "int.TryParse", - "print": "Console.WriteLine", - "string": ".ToString", -} - - -@ctx.action_class("user") -class UserActions: - def code_operator_indirection(): - actions.auto_insert("*") - - def code_operator_address_of(): - actions.auto_insert("&") - - def code_operator_structure_dereference(): - actions.auto_insert("->") - - def code_operator_lambda(): - actions.auto_insert("=>") - - def code_operator_subscript(): - actions.user.insert_between("[", "]") - - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_subtraction_assignment(): - actions.auto_insert(" -= ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_addition_assignment(): - actions.auto_insert(" += ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_multiplication_assignment(): - actions.auto_insert(" *= ") - - # action(user.code_operator_exponent): " ** " - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_division_assignment(): - actions.auto_insert(" /= ") - - def code_operator_modulo(): - actions.auto_insert(" % ") - - def code_operator_modulo_assignment(): - actions.auto_insert(" %= ") - - def code_operator_equal(): - actions.auto_insert(" == ") - - def code_operator_not_equal(): - actions.auto_insert(" != ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" && ") - - def code_operator_or(): - actions.auto_insert(" || ") - - def code_operator_bitwise_and(): - actions.auto_insert(" & ") - - def code_operator_bitwise_and_assignment(): - actions.auto_insert(" &= ") - - def code_operator_bitwise_or(): - actions.auto_insert(" | ") - - def code_operator_bitwise_or_assignment(): - actions.auto_insert(" |= ") - - def code_operator_bitwise_exclusive_or(): - actions.auto_insert(" ^ ") - - def code_operator_bitwise_exclusive_or_assignment(): - actions.auto_insert(" ^= ") - - def code_operator_bitwise_left_shift(): - actions.auto_insert(" << ") - - def code_operator_bitwise_left_shift_assignment(): - actions.auto_insert(" <<= ") - - def code_operator_bitwise_right_shift(): - actions.auto_insert(" >> ") - - def code_operator_bitwise_right_shift_assignment(): - actions.auto_insert(" >>= ") - - def code_self(): - actions.auto_insert("this") - - def code_operator_object_accessor(): - actions.auto_insert(".") - - def code_insert_null(): - actions.auto_insert("null") - - def code_insert_is_null(): - actions.auto_insert(" == null ") - - def code_insert_is_not_null(): - actions.auto_insert(" != null") - - def code_state_if(): - actions.user.insert_between("if(", ")") - - def code_state_else_if(): - actions.user.insert_between("else if(", ")") - - def code_state_else(): - actions.insert("else\n{\n}\n") - actions.key("up") - - def code_state_switch(): - actions.user.insert_between("switch(", ")") - - def code_state_case(): - actions.insert("case \nbreak;") - actions.edit.up() - - def code_state_for(): - actions.auto_insert("for ") - - def code_state_for_each(): - actions.insert("foreach() ") - actions.key("left") - actions.edit.word_left() - actions.key("space") - actions.edit.left() - - def code_state_go_to(): - actions.auto_insert("go to ") - - def code_state_while(): - actions.user.insert_between("while(", ")") - - def code_state_return(): - actions.auto_insert("return ") - - def code_break(): - actions.auto_insert("break;") - - def code_next(): - actions.auto_insert("continue;") - - def code_insert_true(): - actions.auto_insert("true") - - def code_insert_false(): - actions.auto_insert("false") - - def code_define_class(): - actions.auto_insert("class ") - - def code_import(): - actions.auto_insert("using ") - - def code_comment_line_prefix(): - actions.auto_insert("//") - - def code_insert_function(text: str, selection: str): - text += f"({selection or ''})" - actions.user.paste(text) - actions.edit.left() - - def code_private_function(text: str): - """Inserts private function declaration""" - result = "private void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_private_static_function(text: str): - """Inserts private static function""" - result = "private static void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_protected_function(text: str): - result = "private void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_protected_static_function(text: str): - result = "protected static void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_public_function(text: str): - result = "public void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_public_static_function(text: str): - result = "public static void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) diff --git a/talon/user/community/lang/csharp/csharp.talon b/talon/user/community/lang/csharp/csharp.talon deleted file mode 100644 index cf8e9f7..0000000 --- a/talon/user/community/lang/csharp/csharp.talon +++ /dev/null @@ -1,27 +0,0 @@ -code.language: csharp -- -tag(): user.code_imperative -tag(): user.code_object_oriented - -tag(): user.code_block_c_like -tag(): user.code_comment_line -tag(): user.code_comment_block_c_like -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_functions_common -tag(): user.code_libraries -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_lambda -tag(): user.code_operators_math -tag(): user.code_operators_pointer - -settings(): - user.code_private_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_function_formatter = "PUBLIC_CAMEL_CASE" - user.code_public_function_formatter = "PUBLIC_CAMEL_CASE" - user.code_private_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_variable_formatter = "PUBLIC_CAMEL_CASE" - user.code_public_variable_formatter = "PUBLIC_CAMEL_CASE" diff --git a/talon/user/community/lang/css/css.py b/talon/user/community/lang/css/css.py deleted file mode 100644 index 26d4457..0000000 --- a/talon/user/community/lang/css/css.py +++ /dev/null @@ -1,145 +0,0 @@ -from talon import Context, Module, actions - -mod = Module() -global_ctx = Context() -ctx = Context() -ctx.matches = """ -code.language: css -code.language: scss -""" - -mod.list("css_at_rule", desc="List of CSS @rules") -mod.list("css_unit", desc="List of CSS units") -mod.list("css_global_value", desc="CSS-wide values") - -global_ctx.lists["self.css_unit"] = { - # distance (length) - "char": "ch", - "em": "em", - "rem": "rem", - "pixels": "px", - "points": "pt", - "view height": "vh", - "view width": "vw", - # angle - "degrees": "deg", - "radians": "rad", - "turn": "turn", - # duration (time) - "seconds": "s", - "millis": "ms", - # resolution - "dots per pixel": "dppx", - # flexible length (flex) - grid - "fraction": "fr", -} - -global_ctx.lists["self.css_at_rule"] = { - # regular - "charset": "charset", - "import": "import", - "namespace": "namespace", - # conditional group - "media": "media", - "supports": "supports", - # other nested - "page": "page", - "font face": "font-face", - "keyframes": "keyframes", - # CSS Modules - "value": "value", -} - -global_ctx.lists["self.css_global_value"] = ["initial", "inherit", "unset", "revert"] - -ctx.lists["user.code_common_function"] = { - # reference - "attribute": "attr", - "env": "env", - "url": "url", - "var": "var", - "variable": "var", - # mathematical - "calc": "calc", - "calculate": "calc", - "clamp": "clamp", - "max": "max", - "min": "min", - # color - "HSL": "hsl", - "hue sat light": "hsl", - "HSLA": "hsla", - "lab": "lab", - "LCH": "lch", - "RGB": "rgb", - "red green blue": "rgb", - "RGBA": "rgba", - "color": "color", - # image functions - "linear gradient": "linear-gradient", - # counter functions - "counter": "counter", - "counters": "counters", - "symbols": "symbols", - # filter - "blur": "blur", - "brightness": "brightness", - "contrast": "contrast", - "drop shadow": "drop-shadow", - "grayscale": "grayscale", - "hue rotate": "hue-rotate", - "invert": "invert", - "opacity": "opacity", - "saturate": "saturate", - "sepia": "sepia", - # grid - "fit content": "fit-content", - "min max": "minmax", - "repeat": "repeat", - # transform - "matrix": "matrix", - "rotate": "rotate", - "scale": "scale", - "skew": "skew", - "translate": "translate", -} - - -@ctx.action_class("user") -class UserActions: - def code_operator_addition(): - actions.insert(" + ") - - def code_operator_subtraction(): - actions.insert(" - ") - - def code_operator_multiplication(): - actions.insert(" * ") - - def code_operator_division(): - actions.insert(" / ") - - def code_operator_and(): - actions.insert(" and ") - - def code_operator_or(): - actions.insert(" or ") - - def code_operator_greater_than(): - actions.insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.insert(" >= ") - - def code_operator_less_than(): - actions.insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.insert(" <= ") - - def code_import(): - actions.insert("@import ") - - def code_insert_function(text: str, selection: str): - actions.user.paste(f"{text}({selection})") - actions.edit.left() diff --git a/talon/user/community/lang/css/css.talon b/talon/user/community/lang/css/css.talon deleted file mode 100644 index 8c5b023..0000000 --- a/talon/user/community/lang/css/css.talon +++ /dev/null @@ -1,45 +0,0 @@ -code.language: css -code.language: scss -- -tag(): user.code_block_c_like -tag(): user.code_comment_block_c_like -tag(): user.code_functions_common -tag(): user.code_libraries -tag(): user.code_operators_math - -settings(): - user.code_public_variable_formatter = "DASH_SEPARATED" - -block: user.code_block() - -attribute []: - name = user.formatted_text(text or "", "DASH_SEPARATED") - user.insert_between("[{name}", "]") - -prop : - name = user.formatted_text(text, "DASH_SEPARATED") - user.insert_between("{name}: ", ";") - -# for media/supports queries, or if you don't like prop -rule : - name = user.formatted_text(text, "DASH_SEPARATED") - insert("{name}: ") - -value [{user.css_unit}]: "{number_string}{css_unit or ''}" -value point [{user.css_unit}]: - "{number_string}.{digit_string}{css_unit or ''}" - -(value | state) {user.css_global_value}: "{css_global_value}" -value : user.insert_formatted(text, "DASH_SEPARATED") - -variable : - name = user.formatted_text(text, "DASH_SEPARATED") - insert("var(--{name})") - -op var: user.insert_between("var(--", ")") - -at {user.css_at_rule}: "@{css_at_rule} " -unit {user.css_unit}: insert(css_unit) - -[value] current color: "currentColor" -op important: " !important" diff --git a/talon/user/community/lang/elixir/elixir.py b/talon/user/community/lang/elixir/elixir.py deleted file mode 100644 index 76fde79..0000000 --- a/talon/user/community/lang/elixir/elixir.py +++ /dev/null @@ -1,168 +0,0 @@ -from talon import Context, Module, actions, settings - -ctx = Context() -mod = Module() -ctx.matches = r""" -code.language: elixir -""" - -# Elixir keywords and constructs -ctx.lists["user.code_keyword"] = { - "def": "def ", - "def p": "defp ", - "def module": "defmodule ", - "do": "do ", - "end": "end", - "if": "if ", - "else": "else ", - "cond": "cond ", - "case": "case ", - "when": "when ", - "f n": "fn ", - "receive": "receive ", - "after": "after ", - "try": "try ", - "catch": "catch ", - "rescue": "rescue ", - "raise": "raise ", - "with": "with ", - "unless": "unless ", - "import": "import ", - "alias": "alias ", - "require": "require ", - "use": "use ", -} - - -@ctx.action_class("user") -class UserActions: - def code_comment_line_prefix(): - actions.insert("# ") - - def code_operator_lambda(): - actions.auto_insert("->") - - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_equal(): - actions.auto_insert(" == ") - - def code_operator_not_equal(): - actions.auto_insert(" != ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" and ") - - def code_operator_or(): - actions.auto_insert(" or ") - - def code_self(): - actions.auto_insert("self") - - def code_insert_true(): - actions.auto_insert("true") - - def code_insert_false(): - actions.auto_insert("false") - - def code_insert_null(): - actions.insert("nil") - - def code_insert_is_null(): - actions.insert(" == nil") - - def code_insert_is_not_null(): - actions.insert(" != nil") - - def code_state_if(): - actions.user.insert_between("if ", " do\nend") - - def code_state_else_if(): - actions.user.insert_between("else if ", " do\nend") - - def code_state_else(): - actions.insert("else\nend") - actions.key("enter") - - def code_state_case(): - actions.user.insert_between("case ", " do\nend") - - def code_state_for(): - actions.user.insert_between("for ", " do\nend") - - def code_state_while(): - actions.user.insert_between("while ", " do\nend") - - def code_define_class(): - # Elixir doesn't have classes, so this is not applicable - pass - - def code_state_return(): - # Elixir functions automatically return the last evaluated expression - pass - - def code_insert_function(text: str, selection: str): - text += f"({selection or ''})" - actions.user.paste(text) - actions.edit.left() - - def code_default_function(text: str): - result = "def {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - actions.user.code_insert_function(result, None) - - def code_public_function(text: str): - actions.user.code_default_function(text) - - def code_private_function(text: str): - """Inserts private function declaration""" - result = "defp {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - actions.user.code_insert_function(result, None) - - def code_import_module(text: str): - actions.auto_insert("import ") - actions.insert(text) - - def code_alias_module(text: str): - actions.auto_insert("alias ") - actions.insert(text) - - def code_require_module(text: str): - actions.auto_insert("require ") - actions.insert(text) - - def code_use_module(text: str): - actions.auto_insert("use ") - actions.insert(text) diff --git a/talon/user/community/lang/elixir/elixir.talon b/talon/user/community/lang/elixir/elixir.talon deleted file mode 100644 index 31927ef..0000000 --- a/talon/user/community/lang/elixir/elixir.talon +++ /dev/null @@ -1,40 +0,0 @@ -code.language: elixir -- -tag(): user.code_functional -tag(): user.code_concurrent - -tag(): user.code_comment_line -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_keywords -tag(): user.code_libraries -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_math -tag(): user.code_operators_lambda - -settings(): - user.code_private_function_formatter = "SNAKE_CASE" - user.code_public_function_formatter = "SNAKE_CASE" - user.code_private_variable_formatter = "SNAKE_CASE" - user.code_public_variable_formatter = "SNAKE_CASE" - -# Elixir-specific grammars -state def: "def " -state defp: "defp " -state if: "if " -state else: "else" -state case: "case " -state cond: "cond do" -state try: "try do" -state rescue: "rescue" -state after: "after" -state end: "end" - -op pipe: " |> " - -# Elixir-specific keywords and symbols -[state] raise {user.elixir_exception}: user.insert_between("raise ", "") - -[state] rescue {user.elixir_exception}: "rescue {elixir_exception}" diff --git a/talon/user/community/lang/html/html.talon b/talon/user/community/lang/html/html.talon deleted file mode 100644 index d3096fb..0000000 --- a/talon/user/community/lang/html/html.talon +++ /dev/null @@ -1,4 +0,0 @@ -code.language: html -code.language: javascriptreact -code.language: typescriptreact -- diff --git a/talon/user/community/lang/java/java.py b/talon/user/community/lang/java/java.py deleted file mode 100644 index e5a569f..0000000 --- a/talon/user/community/lang/java/java.py +++ /dev/null @@ -1,307 +0,0 @@ -from talon import Context, Module, actions, settings - -ctx = Context() -mod = Module() -ctx.matches = r""" -code.language: java -""" - -# Primitive Types -java_primitive_types = { - "boolean": "boolean", - "int": "int", - "float": "float", - "byte": "byte", - "double": "double", - "short": "short", - "long": "long", - "char": "char", - "void": "void", -} - -# Java Boxed Types -java_boxed_types = { - "Byte": "Byte", - "Integer": "Integer", - "Double": "Double", - "Short": "Short", - "Float": "Float", - "Long": "Long", - "Boolean": "Boolean", - "Character": "Character", - "Void": "Void", -} - -mod.list("java_boxed_type", desc="Java Boxed Types") -ctx.lists["self.java_boxed_type"] = java_boxed_types - -# Common Classes -java_common_classes = { - "Object": "Object", - "string": "String", - "thread": "Thread", - "exception": "Exception", -} - -mod.list("java_common_class", desc="Java Common Classes") -ctx.lists["self.java_common_class"] = java_common_classes - - -# Java Generic Data Structures -java_generic_data_structures = { - # Interfaces - "set": "Set", - "list": "List", - "queue": "Queue", - "deque": "Deque", - "map": "Map", - # Classes - "hash set": "HashSet", - "array list": "ArrayList", - "hash map": "HashMap", -} - -unboxed_types = java_primitive_types.copy() -unboxed_types.update(java_common_classes) -unboxed_types.update(java_generic_data_structures) - -ctx.lists["user.code_type"] = unboxed_types - -mod.list("java_generic_data_structure", desc="Java Generic Data Structures") -ctx.lists["self.java_generic_data_structure"] = java_generic_data_structures - -# Java Modifies -java_modifiers = { - "public": "public", - "private": "private", - "protected": "protected", - "static": "static", - "synchronized": "synchronized", - "volatile": "volatile", - "transient": "transient", - "abstract": "abstract", - "interface": "interface", - "final": "final", -} - -mod.list("java_modifier", desc="Java Modifiers") -ctx.lists["self.java_modifier"] = java_modifiers - - -@ctx.action_class("user") -class UserActions: - def code_operator_lambda(): - actions.insert(" -> ") - - def code_operator_subscript(): - actions.user.insert_between("[", "]") - - def code_operator_assignment(): - actions.insert(" = ") - - def code_operator_subtraction(): - actions.insert(" - ") - - def code_operator_subtraction_assignment(): - actions.insert(" -= ") - - def code_operator_addition(): - actions.insert(" + ") - - def code_operator_addition_assignment(): - actions.insert(" += ") - - def code_operator_multiplication(): - actions.insert(" * ") - - def code_operator_multiplication_assignment(): - actions.insert(" *= ") - - def code_operator_exponent(): - actions.insert(" ^ ") - - def code_operator_division(): - actions.insert(" / ") - - def code_operator_division_assignment(): - actions.insert(" /= ") - - def code_operator_modulo(): - actions.insert(" % ") - - def code_operator_modulo_assignment(): - actions.insert(" %= ") - - def code_operator_equal(): - actions.insert(" == ") - - def code_operator_not_equal(): - actions.insert(" != ") - - def code_operator_greater_than(): - actions.insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.insert(" >= ") - - def code_operator_less_than(): - actions.insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.insert(" <= ") - - def code_operator_and(): - actions.insert(" && ") - - def code_operator_or(): - actions.insert(" || ") - - def code_operator_bitwise_and(): - actions.insert(" & ") - - def code_operator_bitwise_and_assignment(): - actions.insert(" &= ") - - def code_operator_increment(): - actions.insert("++") - - def code_operator_bitwise_or(): - actions.insert(" | ") - - def code_operator_bitwise_exclusive_or(): - actions.insert(" ^ ") - - def code_operator_bitwise_left_shift(): - actions.insert(" << ") - - def code_operator_bitwise_left_shift_assignment(): - actions.insert(" <<= ") - - def code_operator_bitwise_right_shift(): - actions.insert(" >> ") - - def code_operator_bitwise_right_shift_assignment(): - actions.insert(" >>= ") - - def code_self(): - actions.insert("this") - - def code_operator_object_accessor(): - actions.insert(".") - - def code_insert_null(): - actions.insert("null") - - def code_insert_is_null(): - actions.insert(" == null") - - def code_insert_is_not_null(): - actions.insert(" != null") - - def code_state_if(): - actions.user.insert_between("if (", ") ") - - def code_state_else_if(): - actions.user.insert_between("else if (", ") ") - - def code_state_else(): - actions.insert("else ") - actions.key("enter") - - def code_state_switch(): - actions.user.insert_between("switch (", ") ") - - def code_state_case(): - actions.insert("case \nbreak;") - actions.edit.up() - - def code_state_for(): - actions.user.insert_between("for (", ") ") - - def code_state_while(): - actions.user.insert_between("while (", ") ") - - def code_break(): - actions.insert("break;") - - def code_next(): - actions.insert("continue;") - - def code_insert_true(): - actions.insert("true") - - def code_insert_false(): - actions.insert("false") - - def code_define_class(): - actions.insert("class ") - - def code_import(): - actions.insert("import ") - - def code_state_return(): - actions.insert("return ") - - def code_comment_line_prefix(): - actions.insert("// ") - - def code_insert_function(text: str, selection: str): - text += f"({selection or ''})" - actions.user.paste(text) - actions.edit.left() - - def code_private_function(text: str): - """Inserts private function declaration""" - result = "private void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_private_static_function(text: str): - """Inserts private static function""" - result = "private static void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_protected_function(text: str): - result = "void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_protected_static_function(text: str): - result = "static void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_public_function(text: str): - result = "public void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_public_static_function(text: str): - result = "public static void {}".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) diff --git a/talon/user/community/lang/java/java.talon b/talon/user/community/lang/java/java.talon deleted file mode 100644 index c5a0e18..0000000 --- a/talon/user/community/lang/java/java.talon +++ /dev/null @@ -1,44 +0,0 @@ -code.language: java -- -tag(): user.code_imperative -tag(): user.code_object_oriented - -tag(): user.code_block_c_like -tag(): user.code_comment_line -tag(): user.code_comment_block_c_like -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_libraries -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_lambda -tag(): user.code_operators_math - -settings(): - user.code_private_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_public_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_private_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_public_variable_formatter = "PRIVATE_CAMEL_CASE" - -state var: "var " - -# Types Commands -boxed [type] {user.java_boxed_type}: insert(user.java_boxed_type + " ") - -generic [type] {user.java_generic_data_structure}: - user.insert_between(java_generic_data_structure + "<", ">") - -# Arrays -type {user.code_type} array: - insert(user.code_type) - user.code_operator_subscript() - -[state] {user.java_modifier}: insert(user.java_modifier + " ") - -op array: user.code_operator_subscript() - -op new: insert("new ") diff --git a/talon/user/community/lang/javascript/javascript.py b/talon/user/community/lang/javascript/javascript.py deleted file mode 100644 index 40d89f6..0000000 --- a/talon/user/community/lang/javascript/javascript.py +++ /dev/null @@ -1,317 +0,0 @@ -from talon import Context, Module, actions, settings - -mod = Module() -ctx = Context() -ctx.matches = r""" -code.language: javascript -code.language: typescript -code.language: javascriptreact -code.language: typescriptreact -""" - -ctx.lists["user.code_common_function"] = { - "abs": "Math.abs", - "entries": "Object.entries", - "fetch": "fetch", - "floor": "Math.floor", - "from entries": "Object.fromEntries", - "keys": "Object.keys", - "log": "console.log", - "max": "Math.max", - "min": "Math.min", - "print": "console.log", - "round": "Math.round", - "values": "Object.values", -} - -mod.list("code_common_member_function", "Function to use in a dotted chain, eg .foo()") - -ctx.lists["user.code_common_member_function"] = { - "catch": "catch", - "concat": "concat", - "filter": "filter", - "finally": "finally", - "find": "find", - "flat map": "flatMap", - "for each": "forEach", - "join": "join", - "includes": "includes", - "map": "map", - "pop": "pop", - "push": "push", - "reduce": "reduce", - "slice": "slice", - "some": "some", - "split": "split", - "substring": "substring", - "then": "then", -} - -ctx.lists["user.code_keyword"] = { - "a sink": "async ", - "await": "await ", - "break": "break", - "class": "class ", - "const": "const ", - "continue": "continue", - "default": "default ", - "export": "export ", - "false": "false", - "function": "function ", - "import": "import ", - "let": "let ", - "new": "new ", - "null": "null", - "private": "private ", - "protected": "protected ", - "public": "public ", - "return": "return ", - "throw": "throw ", - "true": "true", - "try": "try ", - "undefined": "undefined", - "yield": "yield ", -} - - -@ctx.action_class("user") -class UserActions: - def code_insert_is_not_null(): - actions.auto_insert(" !== null") - - def code_insert_is_null(): - actions.auto_insert(" === null") - - def code_state_if(): - actions.user.insert_between("if (", ")") - - def code_state_else_if(): - actions.user.insert_between(" else if (", ")") - - def code_state_else(): - actions.user.insert_between(" else {", "}") - actions.key("enter") - - def code_self(): - actions.auto_insert("this") - - def code_operator_object_accessor(): - actions.auto_insert(".") - - def code_state_while(): - actions.user.insert_between("while (", ")") - - def code_state_do(): - actions.auto_insert("do ") - - def code_state_return(): - actions.insert("return ") - - def code_state_for(): - actions.user.insert_between("for (", ")") - - def code_state_switch(): - actions.user.insert_between("switch (", ")") - - def code_state_case(): - actions.user.insert_between("case ", ":") - - def code_state_go_to(): - actions.auto_insert("") - - def code_import(): - actions.auto_insert("import ") - - def code_define_class(): - actions.auto_insert("class ") - - def code_state_for_each(): - actions.user.insert_between(".forEach(", ")") - - def code_break(): - actions.auto_insert("break;") - - def code_next(): - actions.auto_insert("continue;") - - def code_insert_true(): - actions.auto_insert("true") - - def code_insert_false(): - actions.auto_insert("false") - - def code_insert_null(): - actions.auto_insert("null") - - def code_operator_lambda(): - actions.auto_insert(" => ") - - def code_operator_subscript(): - actions.user.insert_between("[", "]") - - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_subtraction_assignment(): - actions.auto_insert(" -= ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_addition_assignment(): - actions.auto_insert(" += ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_multiplication_assignment(): - actions.auto_insert(" *= ") - - def code_operator_exponent(): - actions.auto_insert(" ** ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_division_assignment(): - actions.auto_insert(" /= ") - - def code_operator_modulo(): - actions.auto_insert(" % ") - - def code_operator_modulo_assignment(): - actions.auto_insert(" %= ") - - def code_operator_equal(): - actions.auto_insert(" == ") - - def code_or_operator_assignment(): - actions.auto_insert(" ||= ") - - def code_operator_not_equal(): - actions.auto_insert(" != ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" && ") - - def code_operator_or(): - actions.auto_insert(" || ") - - def code_operator_bitwise_and(): - actions.auto_insert(" & ") - - def code_operator_bitwise_and_assignment(): - actions.auto_insert(" &= ") - - def code_operator_bitwise_or(): - actions.auto_insert(" | ") - - def code_operator_bitwise_or_assignment(): - actions.auto_insert(" |= ") - - def code_operator_bitwise_exclusive_or(): - actions.auto_insert(" ^ ") - - def code_operator_bitwise_exclusive_or_assignment(): - actions.auto_insert(" ^= ") - - def code_operator_bitwise_left_shift(): - actions.auto_insert(" << ") - - def code_operator_bitwise_left_shift_assignment(): - actions.auto_insert(" <<= ") - - def code_operator_bitwise_right_shift(): - actions.auto_insert(" >> ") - - def code_operator_bitwise_right_shift_assignment(): - actions.auto_insert(" >>= ") - - def code_comment_line_prefix(): - actions.auto_insert("//") - - def code_insert_function(text: str, selection: str): - text += f"({selection or ''})" - actions.user.paste(text) - actions.edit.left() - - def code_default_function(text: str): - """Inserts function declaration without modifiers""" - result = "function {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_private_function(text: str): - """Inserts private function declaration""" - result = "function {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - # def code_private_static_function(text: str): - # """Inserts private static function""" - # result = "private static void {}".format( - # actions.user.formatted_text( - # text, settings.get("user.code_private_function_formatter") - # ) - # ) - - # actions.user.code_insert_function(result, None) - - def code_protected_function(text: str): - result = "function {}".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - # def code_protected_static_function(text: str): - # result = "protected static void {}".format( - # actions.user.formatted_text( - # text, settings.get("user.code_protected_function_formatter") - # ) - # ) - - # actions.user.code_insert_function(result, None) - - def code_public_function(text: str): - result = "function {}".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - # def code_public_static_function(text: str): - # result = "public static void {}".format( - # actions.user.formatted_text( - # text, settings.get("user.code_public_function_formatter") - # ) - # ) - - # actions.user.code_insert_function(result, None) diff --git a/talon/user/community/lang/javascript/javascript.talon b/talon/user/community/lang/javascript/javascript.talon deleted file mode 100644 index 54de1d0..0000000 --- a/talon/user/community/lang/javascript/javascript.talon +++ /dev/null @@ -1,57 +0,0 @@ -code.language: javascript -code.language: typescript -code.language: javascriptreact -code.language: typescriptreact -- -tag(): user.code_imperative -tag(): user.code_object_oriented - -tag(): user.code_block_c_like -tag(): user.code_comment_line -tag(): user.code_comment_block_c_like -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_functions_common -tag(): user.code_keywords -tag(): user.code_libraries -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_lambda -tag(): user.code_operators_math - -settings(): - user.code_private_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_public_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_private_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_public_variable_formatter = "PRIVATE_CAMEL_CASE" - -(op | is) strict equal: " === " -(op | is) strict not equal: " !== " -op null else: " ?? " - -state const: "const " - -state let: "let " - -state var: "var " - -state export: "export " - -state async: "async " - -state await: "await " - -dot {user.code_common_member_function}: - user.insert_between(".{code_common_member_function}(", ")") - -state map: app.notify('ERROR: Command deprecated; please use "dot map"') -state filter: app.notify('ERROR: Command deprecated; please use "dot filter"') -state reduce: app.notify('ERROR: Command deprecated; please use "dot reduce"') - -state spread: "..." - -from import: user.insert_between(' from "', '"') diff --git a/talon/user/community/lang/kotlin/kotlin.py b/talon/user/community/lang/kotlin/kotlin.py deleted file mode 100644 index f644317..0000000 --- a/talon/user/community/lang/kotlin/kotlin.py +++ /dev/null @@ -1,182 +0,0 @@ -from talon import Context, Module, actions, settings - -ctx = Context() -mod = Module() -ctx.matches = r""" -code.language: kotlin -""" - -ctx.lists["user.code_keyword"] = { - "var": "var ", - "val": "val ", - "lateinit": "lateinit ", - "public": "public ", - "private": "private ", - "protected": "protected ", - "companion object": "companion object ", - "synchronized": "synchronized ", - "volatile": "volatile ", - "transient": "transient ", - "abstract": "abstract ", - "interface": "interface ", - "final": "final ", -} - - -@ctx.action_class("user") -class UserActions: - def code_comment_line_prefix(): - actions.insert("// ") - - def code_operator_lambda(): - actions.auto_insert(" -> ") - - def code_operator_subscript(): - actions.user.insert_between("[", "]") - - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_subtraction_assignment(): - actions.auto_insert(" -= ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_addition_assignment(): - actions.auto_insert(" += ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_multiplication_assignment(): - actions.auto_insert(" *= ") - - def code_operator_exponent(): - actions.auto_insert(" ^ ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_division_assignment(): - actions.auto_insert(" /= ") - - def code_operator_modulo(): - actions.auto_insert(" % ") - - def code_operator_modulo_assignment(): - actions.auto_insert(" %= ") - - def code_operator_equal(): - actions.auto_insert(" == ") - - def code_operator_not_equal(): - actions.auto_insert(" != ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" && ") - - def code_operator_or(): - actions.auto_insert(" || ") - - def code_operator_bitwise_and(): - actions.auto_insert(" & ") - - def code_operator_bitwise_or(): - actions.auto_insert(" | ") - - def code_operator_bitwise_exclusive_or(): - actions.auto_insert(" ^ ") - - def code_operator_bitwise_left_shift(): - actions.auto_insert(" << ") - - def code_operator_bitwise_left_shift_assignment(): - actions.auto_insert(" <<= ") - - def code_operator_bitwise_right_shift(): - actions.auto_insert(" >> ") - - def code_operator_bitwise_right_shift_assignment(): - actions.auto_insert(" >>= ") - - def code_self(): - actions.auto_insert("this") - - def code_insert_null(): - actions.insert("null") - - def code_insert_is_null(): - actions.insert(" == null") - - def code_insert_is_not_null(): - actions.insert(" != null") - - def code_state_if(): - actions.user.insert_between("if (", ") ") - - def code_state_else_if(): - actions.user.insert_between("else if (", ") ") - - def code_state_else(): - actions.user.insert_between("else {", "}") - actions.key("enter") - - def code_state_switch(): - actions.user.insert_between("when (", ") ") # Kotlin uses 'when' for switch - - def code_state_case(): - actions.insert("case \nbreak;") - actions.edit.up() - - def code_state_for(): - actions.user.insert_between("for (", ") ") - - def code_state_while(): - actions.user.insert_between("while (", ") ") - - def code_define_class(): - actions.auto_insert("class ") - - def code_state_return(): - actions.insert("return ") - - def code_insert_function(text: str, selection: str): - text += f"({selection or ''})" - actions.user.paste(text) - actions.edit.left() - - def code_default_function(text: str): - result = "fun {}".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - actions.user.code_insert_function(result, None) - - def code_public_function(text: str): - actions.user.code_default_function(text) - - def code_private_function(text: str): - """Inserts private function declaration""" - result = "private fun {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - actions.user.code_insert_function(result, None) diff --git a/talon/user/community/lang/kotlin/kotlin.talon b/talon/user/community/lang/kotlin/kotlin.talon deleted file mode 100644 index fb5601a..0000000 --- a/talon/user/community/lang/kotlin/kotlin.talon +++ /dev/null @@ -1,25 +0,0 @@ -code.language: kotlin -- -tag(): user.code_imperative -tag(): user.code_object_oriented - -tag(): user.code_comment_line -tag(): user.code_comment_block_c_like -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_libraries -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_lambda -tag(): user.code_operators_math -tag(): user.code_keywords - -settings(): - user.code_private_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_public_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_private_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_public_variable_formatter = "PRIVATE_CAMEL_CASE" diff --git a/talon/user/community/lang/lua/lua.py b/talon/user/community/lang/lua/lua.py deleted file mode 100644 index 8205c12..0000000 --- a/talon/user/community/lang/lua/lua.py +++ /dev/null @@ -1,308 +0,0 @@ -from talon import Context, Module, actions, settings - -mod = Module() -ctx = Context() -ctx.matches = r""" -code.language: lua -""" - -mod.setting( - "lua_version", - type=float, - default=5.1, - desc="The default lua version to use. Dictates certain operators", -) -mod.tag("stylua", desc="Tag for stylua linting commands") - -ctx.lists["user.code_common_function"] = { - "to number": "tonumber", - "I pairs": "ipairs", - "print": "print", - "print F": "printf", - "type": "type", - "assert": "assert", - "get meta table": "getmetatable", - "set meta table": "setmetatable", - # io - "I O write": "io.write", - "I O read": "io.read", - "I O open": "io.open", - # string - "format": "string.format", - "string G find": "string.gfind", - "string find": "string.strfind", - "string len": "string.strlen", - "string upper": "string.strupper", - "string lower": "string.strlower", - "string sub": "string.strsub", - "string G sub": "string.gsub", - "string match": "string.match", - "string G match": "string.gmatch", - # table - "table unpack": "table.unpack", - "table insert": "table.insert", - "tabel get N": "table.getn", - "tabel sort": "table.sort", - # math - "math max": "math.max", - # json - "jason parse": "json.parse", - # http - "H T T P get": "http.get", - "web get": "http.get", - # os - "O S date": "os.date", - "O S time": "os.time", - "O S clock": "os.clock", - "O S rename": "os.rename", - "O S remove": "os.remove", - "O S getenv": "os.getenv", - "O S execute": "os.execute", -} - -ctx.lists["user.code_libraries"] = { - "bit": "bit", - "I O": "io", - "string": "string", - "U T F eight": "utf8", - "table": "table", - "math": "math", - "O S": "os", - "debug": "debug", - "L F S": "lfs", - "socket": "socket", - "H T T P": "http", - "web": "http", - "jason": "json", -} - - -@mod.capture(rule="{self.lua_functions}") -def lua_functions(m) -> str: - "Returns a string" - return m.lua_functions - - -@ctx.action_class("user") -class UserActions: - # tag-related actions listed first, indicated by comment. corresponds to - # the tag(): user.code_imperative style declaration in the language .talon - # file - - ## - # code_imperative - ## - def code_state_if(): - actions.user.insert_between("if ", " then") - - def code_state_else_if(): - actions.user.insert_between("elseif ", " then") - - def code_state_else(): - actions.insert("else\n") - - def code_state_do(): - actions.insert("repeat\n") - - def code_state_for(): - actions.user.insert_between("for ", " do") - - def code_state_go_to(): - actions.insert("goto ") - - def code_state_while(): - actions.user.insert_between("while ", " do") - - def code_state_return(): - actions.insert("return ") - - def code_break(): - actions.insert("break ") - - # Assumes a ::continue:: label - def code_next(): - actions.insert("goto continue") - - def code_try_catch(): - actions.user.insert_between("pcall(", ")") - - ## - # code_comment_line - ## - def code_comment_line_prefix(): - actions.insert("-- ") - - ## - # code_comment_block - ## - def code_comment_block(): - actions.insert("--[[\n\n--]]") - actions.edit.up() - - def code_comment_block_prefix(): - actions.insert("--[[") - - def code_comment_block_suffix(): - actions.insert("--]]") - - ## - # code_data_bool - ## - def code_insert_true(): - actions.insert("true") - - def code_insert_false(): - actions.insert("false") - - ## - # code_data_null - ## - def code_insert_null(): - actions.insert("nil") - - def code_insert_is_null(): - actions.insert(" == nil") - - def code_insert_is_not_null(): - actions.insert(" ~= nil") - - ## - # code_functions - ## - def code_private_function(text: str): - """Inserts private function declaration""" - result = "local function {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - - actions.insert("\n\nend") - actions.key("up:2") - actions.user.code_insert_function(result, None) - - def code_public_function(text: str): - result = "function {}".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - - actions.insert("\n\nend") - actions.key("up:2") - actions.user.code_insert_function(result, None) - - def code_insert_function(text: str, selection: str): - if selection: - text = text + f"({selection})" - else: - text = text + "()" - - actions.user.paste(text) - actions.edit.left() - - ## - # code_libraries - ## - def code_import(): - actions.user.insert_between("local ", " = require('')") - - ## - # code_libraries_gui - ## - def code_insert_library(text: str, selection: str): - actions.insert(f"local {selection} = require('{selection}')") - - ## - # code_operators_array - ## - def code_operator_subscript(): - actions.user.insert_between("[", "]") - - ## - # code_operators_assignment - ## - def code_operator_assignment(): - actions.insert(" = ") - - ## - # code_operators_math - ## - def code_operator_subtraction(): - actions.insert(" - ") - - def code_operator_addition(): - actions.insert(" + ") - - def code_operator_multiplication(): - actions.insert(" * ") - - def code_operator_division(): - actions.insert(" / ") - - def code_operator_modulo(): - actions.insert(" % ") - - def code_operator_equal(): - actions.insert(" == ") - - def code_operator_not_equal(): - actions.insert(" ~= ") - - def code_operator_greater_than(): - actions.insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.insert(" >= ") - - def code_operator_less_than(): - actions.insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.insert(" <= ") - - def code_operator_and(): - actions.insert(" and ") - - def code_operator_or(): - actions.insert(" or ") - - ### - # code_operators_bitwise - ### - - # NOTE: < 5.3 assumes Lua BitOp usage - # > 5.2 assumes native bitwise operators - # TODO: Possibly add settings to define which library to use, as 5.2 - # includes bit32. Neovim uses luajit, which uses Lua BitOp - def code_operator_bitwise_and(): - if settings.get("user.lua_version") > 5.2: - actions.insert(" & ") - else: - actions.insert(" bit.band() ") - - def code_operator_bitwise_or(): - if settings.get("user.lua_version") > 5.2: - actions.insert(" | ") - else: - actions.insert(" bit.bor() ") - - def code_operator_bitwise_exclusive_or(): - if settings.get("user.lua_version") > 5.2: - actions.insert(" ~ ") - else: - actions.insert(" bit.xor() ") - - def code_operator_bitwise_left_shift(): - if settings.get("user.lua_version") > 5.2: - actions.insert(" << ") - else: - actions.insert(" bit.lshift() ") - - def code_operator_bitwise_right_shift(): - if settings.get("user.lua_version") > 5.2: - actions.insert(" >> ") - else: - actions.insert(" bit.rshift() ") - - # non-tag related actions diff --git a/talon/user/community/lang/lua/lua.talon b/talon/user/community/lang/lua/lua.talon deleted file mode 100644 index cfbbd26..0000000 --- a/talon/user/community/lang/lua/lua.talon +++ /dev/null @@ -1,66 +0,0 @@ -code.language: lua -- - -tag(): user.code_imperative - -tag(): user.code_comment_line -tag(): user.code_comment_block -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_functions_common -tag(): user.code_libraries -tag(): user.code_libraries_gui -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_math -tag(): user.code_operators_pointer -# Use this tag if you use the stylua linter -settings(): - user.code_private_function_formatter = "SNAKE_CASE" - user.code_public_function_formatter = "SNAKE_CASE" - user.code_private_variable_formatter = "SNAKE_CASE" - user.code_public_variable_formatter = "SNAKE_CASE" - -state local: "local" -state end: "end" -state then: "then" -state repeat: "repeat" -state until: "until" -state return (null | nil): "return nil" -state return true: "return true" -state return false: "return false" -state return table: user.insert_between("return {", "}") -state append string: " .. " - -state label : - insert("::") - user.insert_formatted(text, "SNAKE_CASE") - insert("::") - -require : - user.code_insert_library("", code_libraries) - key(end enter) - -state (variable | var) [] [over]: user.code_public_variable_formatter(text) - -state local (variable | var) [] [over]: - insert("local ") - user.code_private_variable_formatter(text) - -# for built in object methods, ex: foo:gsub() -method : - insert(":") - user.code_public_function_formatter(text) - insert("()") - edit.left() - -self dot: "self." - -index : '["{word}"]' -index (var | variable) : - var = user.formatted_text(text, "SNAKE_CASE") - insert("[{var}]") - -state return dick: user.insert_between("return {", "}") diff --git a/talon/user/community/lang/lua/stylua.talon b/talon/user/community/lang/lua/stylua.talon deleted file mode 100644 index 4dae9a4..0000000 --- a/talon/user/community/lang/lua/stylua.talon +++ /dev/null @@ -1,6 +0,0 @@ -tag: user.stylua -- - -lint ignore: "-- stylua: ignore" -lint ignore start: "-- stylua: ignore start" -lint ignore end: "-- stylua: ignore end" diff --git a/talon/user/community/lang/markdown/markdown.py b/talon/user/community/lang/markdown/markdown.py deleted file mode 100644 index 3a38bcb..0000000 --- a/talon/user/community/lang/markdown/markdown.py +++ /dev/null @@ -1,19 +0,0 @@ -from talon import Context, Module - -mod = Module() -ctx = Context() - -ctx.matches = r""" -code.language: markdown -""" - -mod.list("markdown_code_block_language", desc="Languages for code blocks") -ctx.lists["user.markdown_code_block_language"] = { - "typescript": "typescript", - "python": "python", - "code": "", - "ruby": "ruby", - "shell": "shell", - "bash": "bash", - "json": "json", -} diff --git a/talon/user/community/lang/markdown/markdown.talon b/talon/user/community/lang/markdown/markdown.talon deleted file mode 100644 index 101933d..0000000 --- a/talon/user/community/lang/markdown/markdown.talon +++ /dev/null @@ -1,46 +0,0 @@ -code.language: markdown -- -(level | heading | header) one: - edit.line_start() - "# " -(level | heading | header) two: - edit.line_start() - "## " -(level | heading | header) three: - edit.line_start() - "### " -(level | heading | header) four: - edit.line_start() - "#### " -(level | heading | header) five: - edit.line_start() - "##### " -(level | heading | header) six: - edit.line_start() - "###### " - -list [one]: - edit.line_start() - "- " -list two: - edit.line_start() - " - " -list three: - edit.line_start() - " - " -list four: - edit.line_start() - " - " -list five: - edit.line_start() - " - " -list six: - edit.line_start() - " - " - -{user.markdown_code_block_language} block: - user.insert_snippet("```{markdown_code_block_language}\n$0\n```") - -link: - "[]()" - key(left:3) diff --git a/talon/user/community/lang/php/php.py b/talon/user/community/lang/php/php.py deleted file mode 100644 index d3d245a..0000000 --- a/talon/user/community/lang/php/php.py +++ /dev/null @@ -1,244 +0,0 @@ -from talon import Context, actions, settings - -ctx = Context() -ctx.matches = r""" -code.language: php -""" - -ctx.lists["user.code_type"] = { - "int": "int", - "float": "float", - "string": "string", - "bool": "bool", - "array": "array", - "null": "null", - "void": "void", -} - - -@ctx.action_class("user") -class UserActions: - def code_self(): - actions.auto_insert("$this") - - def code_operator_object_accessor(): - actions.auto_insert("->") - - def code_define_class(): - actions.auto_insert("class ") - - def code_import(): - actions.auto_insert("use ;") - actions.edit.left() - - def code_comment_line_prefix(): - actions.auto_insert("// ") - - def code_comment_block(): - actions.user.code_comment_block_prefix() - actions.key("enter") - actions.key("enter") - actions.user.code_comment_block_suffix() - actions.edit.up() - - def code_comment_block_prefix(): - actions.auto_insert("/*") - - def code_comment_block_suffix(): - actions.auto_insert("*/") - - def code_comment_documentation(): - actions.insert("/**") - - def code_insert_true(): - actions.auto_insert("true") - - def code_insert_false(): - actions.auto_insert("false") - - def code_insert_null(): - actions.auto_insert("null") - - def code_insert_is_null(): - actions.auto_insert("is_null()") - actions.edit.left() - - def code_insert_is_not_null(): - actions.auto_insert("isset()") - actions.edit.left() - - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_subtraction_assignment(): - actions.auto_insert(" -= ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_addition_assignment(): - actions.auto_insert(" += ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_multiplication_assignment(): - actions.auto_insert(" *= ") - - def code_operator_exponent(): - actions.auto_insert(" ** ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_division_assignment(): - actions.auto_insert(" /= ") - - def code_operator_modulo(): - actions.auto_insert(" % ") - - def code_operator_modulo_assignment(): - actions.auto_insert(" %= ") - - def code_operator_equal(): - actions.auto_insert(" === ") - - def code_operator_not_equal(): - actions.auto_insert(" !== ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" && ") - - def code_operator_or(): - actions.auto_insert(" || ") - - def code_state_if(): - actions.insert("if ()") - actions.edit.left() - - def code_state_else_if(): - actions.insert("elseif ()") - actions.edit.left() - - def code_state_else(): - actions.insert("else {") - actions.key("enter") - - def code_state_while(): - actions.insert("while ()") - actions.edit.left() - - def code_state_for(): - actions.insert("for ()") - actions.edit.left() - - def code_state_for_each(): - actions.insert("foreach ()") - actions.edit.left() - - def code_state_switch(): - actions.insert("switch ()") - actions.edit.left() - - def code_state_case(): - actions.insert("case :") - actions.edit.left() - - def code_state_do(): - actions.insert("do {") - actions.key("enter") - - def code_state_go_to(): - actions.insert("goto ;") - actions.edit.left() - - def code_state_return(): - actions.insert("return ;") - actions.edit.left() - - def code_break(): - actions.insert("break;") - - def code_next(): - actions.insert("continue;") - - def code_default_function(text: str): - actions.user.code_public_function(text) - - def code_protected_function(text: str): - """Inserts protected function declaration""" - result = "protected function {}()".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - actions.user.paste(result) - actions.edit.left() - - def code_public_function(text: str): - """Inserts public function declaration""" - result = "public function {}()".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - actions.user.paste(result) - actions.edit.left() - - def code_private_function(text: str): - """Inserts private function declaration""" - result = "private function {}()".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - actions.user.paste(result) - actions.edit.left() - - def code_private_static_function(text: str): - """Inserts private static function declaration""" - result = "private static function {}()".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - actions.user.paste(result) - actions.edit.left() - - def code_protected_static_function(text: str): - """Inserts protected static function declaration""" - result = "protected static function {}()".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - actions.user.paste(result) - actions.edit.left() - - def code_public_static_function(text: str): - """Inserts public static function declaration""" - result = "public static function {}()".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - actions.user.paste(result) - actions.edit.left() - - def code_insert_return_type(type: str): - actions.insert(f": {type}") diff --git a/talon/user/community/lang/php/php.talon b/talon/user/community/lang/php/php.talon deleted file mode 100644 index df40fd4..0000000 --- a/talon/user/community/lang/php/php.talon +++ /dev/null @@ -1,33 +0,0 @@ -code.language: php -- -tag(): user.code_imperative -tag(): user.code_object_oriented -tag(): user.code_libraries - -tag(): user.code_block_c_like -tag(): user.code_comment_line -tag(): user.code_comment_block -tag(): user.code_comment_documentation -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_operators_assignment -tag(): user.code_operators_math -tag(): user.code_functions - -settings(): - user.code_private_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_public_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_private_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_public_variable_formatter = "PRIVATE_CAMEL_CASE" - -(op | is) loosely equal: " == " -(op | is) loosely not equal: " != " - -state try: "try {\n" -state catch: "catch (\\Throwable $exception) {\n" - -var [over]: - insert("$") - insert(user.formatted_text(phrase, "PRIVATE_CAMEL_CASE")) diff --git a/talon/user/community/lang/proto/proto.py b/talon/user/community/lang/proto/proto.py deleted file mode 100644 index a193efe..0000000 --- a/talon/user/community/lang/proto/proto.py +++ /dev/null @@ -1,26 +0,0 @@ -from talon import Context, Module - -mod = Module() - -ctx = Context() -ctx.matches = r""" -code.language: protobuf -""" - -ctx.lists["user.code_type"] = { - "string": "string", - "bytes": "bytes", - "you sixty four": "uint64", - "you thirty two": "uint32", - "eye sixty four": "int64", - "eye thirty two": "int32", - "sin sixty four": "sint64", - "sin thirty two": "sint32", - "fixed sixty four": "fixed64", - "fixed thirty two": "fixed32", - "as fixed sixty four": "sfixed64", - "as fixed thirty two": "sfixed32", - "boolean": "bool", - "double": "double", - "float": "float", -} diff --git a/talon/user/community/lang/proto/proto.talon b/talon/user/community/lang/proto/proto.talon deleted file mode 100644 index 0c909ca..0000000 --- a/talon/user/community/lang/proto/proto.talon +++ /dev/null @@ -1,19 +0,0 @@ -code.language: protobuf -- -tag(): user.code_block_c_like - -# this is pretty bare-bones, further contributions welcome -block: user.code_block() - -state message: "message " -state package: "package " -state reserved: "reserved " -state enum: "enum " -op equals: " = " -state import: "import " -state import public: "import public " -state option: "option " -state repeated: "repeated " - -type {user.code_type}: "{code_type}" -repeated type {user.code_type}: "repeated {code_type}" diff --git a/talon/user/community/lang/python/python.py b/talon/user/community/lang/python/python.py deleted file mode 100644 index 3945bc0..0000000 --- a/talon/user/community/lang/python/python.py +++ /dev/null @@ -1,360 +0,0 @@ -import re - -from talon import Context, Module, actions, settings - -mod = Module() -ctx = Context() -ctx.matches = r""" -code.language: python -""" -ctx.lists["user.code_common_function"] = { - "enumerate": "enumerate", - "integer": "int", - "length": "len", - "list": "list", - "print": "print", - "range": "range", - "set": "set", - "split": "split", - "string": "str", - "update": "update", -} - -"""a set of fields used in python docstrings that will follow the -reStructuredText format""" -docstring_fields = { - "class": ":class:", - "function": ":func:", - "parameter": ":param:", - "raise": ":raise:", - "returns": ":return:", - "type": ":type:", - "return type": ":rtype:", - # these are sphinx-specific - "see also": ".. seealso:: ", - "notes": ".. notes:: ", - "warning": ".. warning:: ", - "todo": ".. todo:: ", -} - -mod.list("python_docstring_fields", desc="python docstring fields") -ctx.lists["user.python_docstring_fields"] = docstring_fields - -ctx.lists["user.code_type"] = { - "boolean": "bool", - "integer": "int", - "string": "str", - "none": "None", - "dick": "Dict", - "float": "float", - "any": "Any", - "tuple": "Tuple", - "union": "UnionAny", - "iterable": "Iterable", - "vector": "Vector", - "bytes": "bytes", - "sequence": "Sequence", - "callable": "Callable", - "list": "List", - "no return": "NoReturn", -} - -ctx.lists["user.code_keyword"] = { - "break": "break", - "continue": "continue", - "class": "class ", - "return": "return ", - "import": "import ", - "null": "None", - "none": "None", - "true": "True", - "false": "False", - "yield": "yield ", - "from": "from ", -} - -exception_list = [ - "BaseException", - "SystemExit", - "KeyboardInterrupt", - "GeneratorExit", - "Exception", - "StopIteration", - "StopAsyncIteration", - "ArithmeticError", - "FloatingPointError", - "OverflowError", - "ZeroDivisionError", - "AssertionError", - "AttributeError", - "BufferError", - "EOFError", - "ImportError", - "ModuleNotFoundError", - "LookupError", - "IndexError", - "KeyError", - "MemoryError", - "NameError", - "UnboundLocalError", - "OSError", - "BlockingIOError", - "ChildProcessError", - "ConnectionError", - "BrokenPipeError", - "ConnectionAbortedError", - "ConnectionRefusedError", - "ConnectionResetError", - "FileExistsError", - "FileNotFoundError", - "InterruptedError", - "IsADirectoryError", - "NotADirectoryError", - "PermissionError", - "ProcessLookupError", - "TimeoutError", - "ReferenceError", - "RuntimeError", - "NotImplementedError", - "RecursionError", - "SyntaxError", - "IndentationError", - "TabError", - "SystemError", - "TypeError", - "ValueError", - "UnicodeError", - "UnicodeDecodeError", - "UnicodeEncodeError", - "UnicodeTranslateError", - "Warning", - "DeprecationWarning", - "PendingDeprecationWarning", - "RuntimeWarning", - "SyntaxWarning", - "UserWarning", - "FutureWarning", - "ImportWarning", - "UnicodeWarning", - "BytesWarning", - "ResourceWarning", -] -mod.list("python_exception", desc="python exceptions") -ctx.lists["user.python_exception"] = { - " ".join(re.findall("[A-Z][^A-Z]*", exception)).lower(): exception - for exception in exception_list -} - - -@ctx.action_class("user") -class UserActions: - def code_operator_lambda(): - actions.user.insert_between("lambda ", ": ") - - def code_operator_subscript(): - actions.user.insert_between("[", "]") - - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_subtraction_assignment(): - actions.auto_insert(" -= ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_addition_assignment(): - actions.auto_insert(" += ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_multiplication_assignment(): - actions.auto_insert(" *= ") - - def code_operator_exponent(): - actions.auto_insert(" ** ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_division_assignment(): - actions.auto_insert(" /= ") - - def code_operator_modulo(): - actions.auto_insert(" % ") - - def code_operator_modulo_assignment(): - actions.auto_insert(" %= ") - - def code_operator_equal(): - actions.auto_insert(" == ") - - def code_operator_not_equal(): - actions.auto_insert(" != ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" and ") - - def code_operator_or(): - actions.auto_insert(" or ") - - def code_operator_not(): - actions.auto_insert("not ") - - def code_operator_in(): - actions.auto_insert(" in ") - - def code_operator_not_in(): - actions.auto_insert(" not in ") - - def code_operator_bitwise_and(): - actions.auto_insert(" & ") - - def code_operator_bitwise_and_assignment(): - actions.auto_insert(" &= ") - - def code_operator_bitwise_or(): - actions.auto_insert(" | ") - - def code_operator_bitwise_or_assignment(): - actions.auto_insert(" |= ") - - def code_operator_bitwise_exclusive_or(): - actions.auto_insert(" ^ ") - - def code_operator_bitwise_exclusive_or_assignment(): - actions.auto_insert(" ^= ") - - def code_operator_bitwise_not(): - actions.auto_insert("~") - - def code_operator_bitwise_left_shift(): - actions.auto_insert(" << ") - - def code_operator_bitwise_left_shift_assignment(): - actions.auto_insert(" <<= ") - - def code_operator_bitwise_right_shift(): - actions.auto_insert(" >> ") - - def code_operator_bitwise_right_shift_assignment(): - actions.auto_insert(" >>= ") - - def code_self(): - actions.auto_insert("self") - - def code_operator_object_accessor(): - actions.auto_insert(".") - - def code_insert_null(): - actions.auto_insert("None") - - def code_insert_is_null(): - actions.auto_insert(" is None") - - def code_insert_is_not_null(): - actions.auto_insert(" is not None") - - def code_state_if(): - actions.user.insert_between("if ", ":") - - def code_state_else_if(): - actions.user.insert_between("elif ", ":") - - def code_state_else(): - actions.insert("else:") - actions.key("enter") - - def code_state_switch(): - actions.user.insert_between("match ", ":") - - def code_state_case(): - actions.user.insert_between("case ", ":") - - def code_state_for(): - actions.auto_insert("for ") - - def code_state_for_each(): - actions.user.insert_between("for ", " in ") - - def code_state_while(): - actions.user.insert_between("while ", ":") - - def code_define_class(): - actions.auto_insert("class ") - - def code_import(): - actions.auto_insert("import ") - - def code_comment_line_prefix(): - actions.auto_insert("# ") - - def code_state_return(): - actions.insert("return ") - - def code_insert_true(): - actions.auto_insert("True") - - def code_insert_false(): - actions.auto_insert("False") - - def code_comment_documentation(): - actions.user.insert_between('"""', '"""') - - def code_insert_function(text: str, selection: str): - text += f"({selection or ''})" - actions.user.paste(text) - actions.edit.left() - - def code_default_function(text: str): - actions.user.code_public_function(text) - - def code_private_function(text: str): - """Inserts private function declaration""" - result = "def _{}():".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - - actions.user.paste(result) - actions.edit.left() - actions.edit.left() - - def code_public_function(text: str): - result = "def {}():".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - actions.user.paste(result) - actions.edit.left() - actions.edit.left() - - def code_insert_type_annotation(type: str): - actions.insert(f": {type}") - - def code_insert_return_type(type: str): - actions.insert(f" -> {type}") - - def code_break(): - actions.insert("break") - - def code_next(): - actions.insert("continue") diff --git a/talon/user/community/lang/python/python.talon b/talon/user/community/lang/python/python.talon deleted file mode 100644 index 6584965..0000000 --- a/talon/user/community/lang/python/python.talon +++ /dev/null @@ -1,55 +0,0 @@ -code.language: python -- -tag(): user.code_imperative -tag(): user.code_object_oriented - -tag(): user.code_comment_documentation -tag(): user.code_comment_line -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_functions_common -tag(): user.code_keywords -tag(): user.code_libraries -tag(): user.code_libraries_gui -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_lambda -tag(): user.code_operators_math - -settings(): - user.code_private_function_formatter = "SNAKE_CASE" - user.code_protected_function_formatter = "SNAKE_CASE" - user.code_public_function_formatter = "SNAKE_CASE" - user.code_private_variable_formatter = "SNAKE_CASE" - user.code_protected_variable_formatter = "SNAKE_CASE" - user.code_public_variable_formatter = "SNAKE_CASE" - -#python-specific grammars -dunder in it: "__init__" -state (def | deaf | deft): "def " -state try: "try:\n" -state except: "except " -state raise: "raise " -self taught: "self." -pie test: "pytest" -state past: "pass" - -[state] raise {user.python_exception}: - user.insert_between("raise {python_exception}(", ")") -[state] except {user.python_exception}: "except {python_exception}:" - -dock string: user.code_comment_documentation() -dock {user.python_docstring_fields}: - insert("{python_docstring_fields}") - edit.left() -dock type {user.code_type}: user.insert_between(":type ", ": {code_type}") -dock returns type {user.code_type}: user.insert_between(":rtype ", ": {code_type}") - -toggle imports: user.code_toggle_libraries() -import : - user.code_insert_library(code_libraries, "") - key(end enter) - -from import: user.insert_between("from ", " import ") diff --git a/talon/user/community/lang/r/r.talon b/talon/user/community/lang/r/r.talon deleted file mode 100644 index 042132e..0000000 --- a/talon/user/community/lang/r/r.talon +++ /dev/null @@ -1,44 +0,0 @@ -code.language: r -- -tag(): user.code_imperative - -tag(): user.code_block_c_like -tag(): user.code_comment_line -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_functions_common -tag(): user.code_libraries -tag(): user.code_libraries_gui -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_math - -settings(): - user.code_private_function_formatter = "SNAKE_CASE" - user.code_protected_function_formatter = "SNAKE_CASE" - user.code_public_function_formatter = "SNAKE_CASE" - user.code_private_variable_formatter = "SNAKE_CASE" - user.code_protected_variable_formatter = "SNAKE_CASE" - user.code_public_variable_formatter = "SNAKE_CASE" - -# NOTE: migrated from generic as it is only use here -(op | is) in: " %in% " - -toggle library: user.code_toggle_libraries() -library : - user.code_insert_library(code_libraries, "") - key(end enter) - -# R specific commands -(chain | pipe that): - key(end) - " %>%" - key(enter) -state na: insert("NA") - -# TODO: migrate to function tag -^function define $: user.code_private_function(text) - -named arg {user.code_parameter_name}: - user.code_insert_named_argument(code_parameter_name) diff --git a/talon/user/community/lang/ruby/ruby.py b/talon/user/community/lang/ruby/ruby.py deleted file mode 100644 index f471b70..0000000 --- a/talon/user/community/lang/ruby/ruby.py +++ /dev/null @@ -1,183 +0,0 @@ -from talon import Context, actions, settings - -ctx = Context() -ctx.matches = r""" -code.language: ruby -""" - - -@ctx.action_class("user") -class UserActions: - def code_operator_lambda(): - actions.auto_insert("->") - - def code_operator_subscript(): - actions.insert("[]") - actions.key("left") - - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_or_operator_assignment(): - actions.auto_insert(" ||= ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_subtraction_assignment(): - actions.auto_insert(" -= ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_addition_assignment(): - actions.auto_insert(" += ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_multiplication_assignment(): - actions.auto_insert(" *= ") - - def code_operator_exponent(): - actions.auto_insert(" ** ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_division_assignment(): - actions.auto_insert(" /= ") - - def code_operator_modulo(): - actions.auto_insert(" % ") - - def code_operator_modulo_assignment(): - actions.auto_insert(" %= ") - - def code_operator_equal(): - actions.auto_insert(" == ") - - def code_operator_not_equal(): - actions.auto_insert(" != ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" && ") - - def code_operator_or(): - actions.auto_insert(" || ") - - def code_operator_bitwise_and(): - actions.auto_insert(" & ") - - def code_operator_bitwise_and_assignment(): - actions.auto_insert(" &= ") - - def code_operator_bitwise_or(): - actions.auto_insert(" | ") - - def code_operator_bitwise_or_assignment(): - actions.auto_insert(" |= ") - - def code_operator_bitwise_exclusive_or(): - actions.auto_insert(" ^ ") - - def code_operator_bitwise_exclusive_or_assignment(): - actions.auto_insert(" ^= ") - - def code_operator_bitwise_left_shift(): - actions.auto_insert(" << ") - - def code_operator_bitwise_left_shift_assignment(): - actions.auto_insert(" <<= ") - - def code_operator_bitwise_right_shift(): - actions.auto_insert(" >> ") - - def code_operator_bitwise_right_shift_assignment(): - actions.auto_insert(" >>= ") - - def code_self(): - actions.auto_insert("self") - - def code_operator_object_accessor(): - actions.auto_insert(".") - - def code_insert_null(): - actions.auto_insert("nil") - - def code_insert_is_null(): - actions.auto_insert(".nil?") - - # Technically .present? is provided by Rails - def code_insert_is_not_null(): - actions.auto_insert(".present?") - - def code_state_do(): - actions.insert("do ") - - def code_state_if(): - actions.insert("if ") - - def code_state_else_if(): - actions.insert("elsif ") - - def code_state_else(): - actions.insert("else") - actions.key("enter") - - def code_state_switch(): - actions.insert("case ") - - def code_state_case(): - actions.insert("when ") - - def code_state_for_each(): - actions.insert(".each do ||") - actions.key("left") - - def code_define_class(): - actions.auto_insert("class ") - - def code_import(): - actions.auto_insert('require ""') - actions.key("left") - - def code_comment_line_prefix(): - actions.auto_insert("# ") - - def code_state_return(): - actions.insert("return ") - - def code_insert_true(): - actions.auto_insert("true") - - def code_insert_false(): - actions.auto_insert("false") - - def code_comment_documentation(): - actions.insert("##") - actions.key("enter") - actions.key("space") - ### Extra non-standard things - - def code_default_function(text: str): - """Inserts function definition""" - - result = "def {}".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - actions.user.paste(result) diff --git a/talon/user/community/lang/ruby/ruby.talon b/talon/user/community/lang/ruby/ruby.talon deleted file mode 100644 index af8dcf0..0000000 --- a/talon/user/community/lang/ruby/ruby.talon +++ /dev/null @@ -1,38 +0,0 @@ -code.language: ruby -- -tag(): user.code_imperative -tag(): user.code_object_oriented - -tag(): user.code_comment_line -tag(): user.code_comment_documentation -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_libraries -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_lambda -tag(): user.code_operators_math - -settings(): - user.code_private_function_formatter = "SNAKE_CASE" - user.code_protected_function_formatter = "SNAKE_CASE" - user.code_public_function_formatter = "SNAKE_CASE" - user.code_private_variable_formatter = "SNAKE_CASE" - user.code_protected_variable_formatter = "SNAKE_CASE" - user.code_public_variable_formatter = "SNAKE_CASE" - -args pipe: user.insert_between("|", "|") - -# NOTE: this command is created for backward compatibility, but the documentation comments are not actually strings in Ruby. -dock string: user.code_comment_documentation() - -state end: "end" -state begin: "begin" -state rescue: "rescue " -state module: "module " - -^instance $: - insert("@") - user.code_public_variable_formatter(text) diff --git a/talon/user/community/lang/rust/rust.py b/talon/user/community/lang/rust/rust.py deleted file mode 100644 index dcb0e85..0000000 --- a/talon/user/community/lang/rust/rust.py +++ /dev/null @@ -1,516 +0,0 @@ -from typing import Any, Callable, TypeVar - -from talon import Context, Module, actions, settings - -mod = Module() -# rust specific grammar -mod.list("code_type_modifier", desc="List of type modifiers for active language") -mod.list("code_macros", desc="List of macros for active language") -mod.list("code_trait", desc="List of traits for active language") - - -@mod.action_class -class Actions: - def code_state_implements(): - """Inserts implements block, positioning the cursor appropriately""" - - def code_insert_macro(text: str, selection: str): - """Inserts a macro and positions the cursor appropriately""" - - def code_insert_macro_array(text: str, selection: str): - """Inserts a macro array and positions the cursor appropriately""" - - def code_insert_macro_block(text: str, selection: str): - """Inserts a macro block and positions the cursor appropriately""" - - def code_state_unsafe(): - """Inserts an unsafe block and positions the cursor appropriately""" - - def code_comment_documentation_block(): - """Inserts a block document comment and positions the cursor appropriately""" - - def code_comment_documentation_inner(): - """Inserts an inner document comment and positions the cursor appropriately""" - - def code_comment_documentation_block_inner(): - """Inserts an inner block document comment and positions the cursor appropriately""" - - -ctx = Context() -ctx.matches = r""" -code.language: rust -""" - -scalar_types = { - "eye eight": "i8", - "you eight": "u8", - "bytes": "u8", - "eye sixteen": "i16", - "you sixteen": "u16", - "eye thirty two": "i32", - "you thirty two": "u32", - "eye sixty four": "i64", - "you sixty four": "u64", - "eye one hundred and twenty eight": "i128", - "you one hundred and twenty eight": "u128", - "eye size": "isize", - "you size": "usize", - "float thirty two": "f32", - "float sixty four": "f64", - "boolean": "bool", - "character": "char", -} - -compound_types = { - "tuple": "()", - "array": "[]", -} - -standard_library_types = { - "box": "Box", - "vector": "Vec", - "string": "String", - "string slice": "&str", - "os string": "OsString", - "os string slice": "&OsStr", - "see string": "CString", - "see string slice": "&CStr", - "option": "Option", - "result": "Result", - "hashmap": "HashMap", - "hash set": "HashSet", - "reference count": "Rc", -} - -standard_sync_types = { - "arc": "Arc", - "barrier": "Barrier", - "condition variable": "Condvar", - "mutex": "Mutex", - "once": "Once", - "read write lock": "RwLock", - "receiver": "Receiver", - "sender": "Sender", - "sink sender": "SyncSender", -} - -all_types = { - **scalar_types, - **compound_types, - **standard_library_types, - **standard_sync_types, -} - -standard_function_macros = { - "panic": "panic!", - "format": "format!", - "concatenate": "concat!", - "print": "print!", - "print line": "println!", - "error print line": "eprintln!", - "to do": "todo!", -} - -standard_array_macros = { - "vector": "vec!", -} - -standard_block_macros = { - "macro rules": "macro_rules!", -} - -logging_macros = { - "debug": "debug!", - "info": "info!", - "warning": "warn!", - "error": "error!", -} - -testing_macros = { - "assert": "assert!", - "assert equal": "assert_eq!", - "assert not equal": "assert_ne!", -} - -all_function_macros = { - **standard_function_macros, - **logging_macros, - **testing_macros, -} - -all_array_macros = { - **standard_array_macros, -} - -all_block_macros = { - **standard_block_macros, -} - -all_macros = { - **all_function_macros, - **all_array_macros, - **all_block_macros, -} - -all_function_macro_values = set(all_function_macros.values()) -all_array_macro_values = set(all_array_macros.values()) -all_block_macro_values = set(all_block_macros.values()) - -closure_traits = { - "closure": "Fn", - "closure once": "FnOnce", - "closure mutable": "FnMut", -} - -conversion_traits = { - "into": "Into", - "from": "From", -} - -iterator_traits = { - "iterator": "Iterator", -} - -all_traits = { - **closure_traits, - **conversion_traits, - **iterator_traits, -} - - -# tag: libraries_gui -ctx.lists["user.code_libraries"] = { - "eye oh": "std::io", - "file system": "std::fs", - "envy": "std::env", - "collections": "std::collections", -} - -# tag: functions_common -ctx.lists["user.code_common_function"] = { - "drop": "drop", - "catch unwind": "catch_unwind", - "iterator": "iter", - "into iterator": "into_iter", - "from iterator": "from_iter", - **all_macros, -} - -# tag: functions -ctx.lists["user.code_type"] = all_types - -# rust specific grammar -ctx.lists["user.code_type_modifier"] = { - "mutable": "mut ", - "mute": "mut ", - "borrowed": "&", - "borrowed mutable": "&mut ", - "borrowed mute": "&mut ", - "mutable borrowed": "&mut ", - "mute borrowed": "&mut ", -} - - -@ctx.capture("user.code_type", rule="[{user.code_type_modifier}] {user.code_type}") -def code_type(m) -> str: - """Returns a macro name""" - return "".join(m) - - -ctx.lists["user.code_macros"] = all_macros - -ctx.lists["user.code_trait"] = all_traits - - -@ctx.action_class("user") -class UserActions: - # tag: comment_line - - def code_comment_line_prefix(): - actions.auto_insert("// ") - - # tag: comment_documentation - - def code_comment_documentation(): - actions.auto_insert("/// ") - - # tag: imperative - - def code_state_if(): - actions.auto_insert("if ") - - def code_state_else_if(): - actions.auto_insert(" else if ") - - def code_state_else(): - actions.user.insert_between(" else { ", " }") - - def code_state_switch(): - actions.auto_insert("match ") - - def code_state_for(): - actions.auto_insert("for in {}") - actions.edit.left() - actions.key("enter") - actions.edit.up() - actions.edit.line_end() - repeat_call(6, actions.edit.left) - - def code_state_while(): - actions.auto_insert("while {}") - actions.edit.left() - actions.key("enter") - actions.edit.up() - actions.edit.line_end() - repeat_call(2, actions.edit.left) - - def code_state_infinite_loop(): - actions.user.insert_between("loop {", "}") - actions.key("enter") - - def code_state_return(): - actions.auto_insert("return ") - - def code_break(): - actions.auto_insert("break;") - - def code_next(): - actions.auto_insert("continue;") - - # tag: object_oriented - - def code_operator_object_accessor(): - actions.auto_insert(".") - - def code_self(): - actions.auto_insert("self") - - def code_define_class(): - actions.auto_insert("struct ") - - # tag: data_bool - - def code_insert_true(): - actions.auto_insert("true") - - def code_insert_false(): - actions.auto_insert("false") - - # tag: data_null - - def code_insert_null(): - actions.auto_insert("None") - - def code_insert_is_null(): - actions.auto_insert(".is_none()") - - def code_insert_is_not_null(): - actions.auto_insert(".is_some()") - - # tag: functions - - def code_default_function(text: str): - actions.user.code_private_function(text) - - def code_private_function(text: str): - actions.auto_insert("fn ") - formatter = settings.get("user.code_private_function_formatter") - function_name = actions.user.formatted_text(text, formatter) - actions.user.code_insert_function(function_name, None) - - def code_protected_function(text: str): - actions.auto_insert("pub(crate) fn ") - formatter = settings.get("user.code_protected_function_formatter") - function_name = actions.user.formatted_text(text, formatter) - actions.user.code_insert_function(function_name, None) - - def code_public_function(text: str): - actions.auto_insert("pub fn ") - formatter = settings.get("user.code_public_function_formatter") - function_name = actions.user.formatted_text(text, formatter) - actions.user.code_insert_function(function_name, None) - - def code_insert_type_annotation(type: str): - actions.auto_insert(f": {type}") - - def code_insert_return_type(type: str): - actions.auto_insert(f" -> {type}") - - # tag: functions_gui - - def code_insert_function(text: str, selection: str): - code_insert_function_or_macro(text, selection, "(", ")") - - # tag: libraries - - def code_import(): - actions.auto_insert("use ") - - # tag: libraries_gui - - def code_insert_library(text: str, selection: str): - actions.user.paste(f"use {text}") - - # tag: operators_array - - def code_operator_subscript(): - actions.auto_insert("[]") - actions.edit.left() - - # tag: code_operators_assignment - - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_operator_subtraction_assignment(): - actions.auto_insert(" -= ") - - def code_operator_addition_assignment(): - actions.auto_insert(" += ") - - def code_operator_multiplication_assignment(): - actions.auto_insert(" *= ") - - def code_operator_division_assignment(): - actions.auto_insert(" /= ") - - def code_operator_modulo_assignment(): - actions.auto_insert(" %= ") - - def code_operator_bitwise_and_assignment(): - actions.auto_insert(" &= ") - - def code_operator_bitwise_or_assignment(): - actions.auto_insert(" |= ") - - def code_operator_bitwise_exclusive_or_assignment(): - actions.auto_insert(" ^= ") - - def code_operator_bitwise_left_shift_assignment(): - actions.auto_insert(" <<= ") - - def code_operator_bitwise_right_shift_assignment(): - actions.auto_insert(" >>= ") - - # tag: operators_bitwise - - def code_operator_bitwise_and(): - actions.auto_insert(" & ") - - def code_operator_bitwise_or(): - actions.auto_insert(" | ") - - def code_operator_bitwise_exclusive_or(): - actions.auto_insert(" ^ ") - - def code_operator_bitwise_left_shift(): - actions.auto_insert(" << ") - - def code_operator_bitwise_right_shift(): - actions.auto_insert(" >> ") - - # tag: operators_math - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_exponent(): - actions.auto_insert(".pow()") - actions.edit.left() - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_modulo(): - actions.auto_insert(" % ") - - def code_operator_equal(): - actions.auto_insert(" == ") - - def code_operator_not_equal(): - actions.auto_insert(" != ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" && ") - - def code_operator_or(): - actions.auto_insert(" || ") - - def code_operator_increment(): - actions.auto_insert(" += 1") - - # rust specific grammar - - def code_operator_structure_dereference(): - actions.auto_insert("*") - - def code_state_implements(): - actions.auto_insert("impl {}") - actions.edit.left() - actions.key("enter") - actions.edit.up() - actions.edit.line_end() - repeat_call(2, actions.edit.left) - - def code_insert_macro(text: str, selection: str): - if text in all_array_macro_values: - code_insert_function_or_macro(text, selection, "[", "]") - elif text in all_block_macro_values: - code_insert_function_or_macro(text, selection, "{", "}") - else: - code_insert_function_or_macro(text, selection, "(", ")") - - def code_state_unsafe(): - actions.user.insert_between("unsafe {", "}") - actions.key("enter") - - def code_comment_documentation_block(): - actions.user.insert_between("/**", "*/") - actions.key("enter") - - def code_comment_documentation_inner(): - actions.auto_insert("//! ") - - def code_comment_documentation_block_inner(): - actions.user.insert_between("/*!", "*/") - actions.key("enter") - - -def code_insert_function_or_macro( - text: str, - selection: str, - left_delim: str, - right_delim: str, -): - if selection: - out_text = text + f"{left_delim}{selection}{right_delim}" - else: - out_text = text + f"{left_delim}{right_delim}" - actions.user.paste(out_text) - actions.edit.left() - - -RT = TypeVar("RT") # return type - - -def repeat_call(n: int, f: Callable[..., RT], *args: Any, **kwargs: Any): - for i in range(n): - f(*args, **kwargs) diff --git a/talon/user/community/lang/rust/rust.talon b/talon/user/community/lang/rust/rust.talon deleted file mode 100644 index dd2911c..0000000 --- a/talon/user/community/lang/rust/rust.talon +++ /dev/null @@ -1,106 +0,0 @@ -code.language: rust -- -tag(): user.code_comment_line -tag(): user.code_comment_block_c_like -tag(): user.code_comment_documentation - -tag(): user.code_block_c_like -tag(): user.code_imperative -tag(): user.code_object_oriented - -tag(): user.code_data_bool -tag(): user.code_data_null - -tag(): user.code_functions -tag(): user.code_functions_common -tag(): user.code_libraries -tag(): user.code_libraries_gui - -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_math - -settings(): - user.code_private_function_formatter = "SNAKE_CASE" - user.code_protected_function_formatter = "SNAKE_CASE" - user.code_public_function_formatter = "SNAKE_CASE" - user.code_private_variable_formatter = "SNAKE_CASE" - user.code_protected_variable_formatter = "SNAKE_CASE" - user.code_public_variable_formatter = "SNAKE_CASE" - -# rust-specific grammars - -## for unsafe rust -state unsafe: "unsafe " -unsafe block: user.code_state_unsafe() - -## rust centric struct and enum definitions -state (struct | structure) : - insert("struct ") - insert(user.formatted_text(text, "PUBLIC_CAMEL_CASE")) - -state enum : - insert("enum ") - insert(user.formatted_text(text, "PUBLIC_CAMEL_CASE")) - -toggle use: user.code_toggle_libraries() - -## Simple aliases -borrow: "&" -borrow mutable: "&mut " -state (a sink | async | asynchronous): "async " -state (pub | public): "pub " -state (pub | public) crate: "pub(crate) " -state (dyn | dynamic): "dyn " -state constant: "const " -state (funk | func | function): "fn " -state (imp | implements): "impl " -state let mute: "let mut " -state let: "let " -state (mute | mutable): "mut " -state (mod | module): "mod " -state ref (mute | mutable): "ref mut " -state ref: "ref " -state trait: "trait " -state match: user.code_state_switch() -state (some | sum): "Some" -state static: "static " -self taught: "self." -state use: user.code_import() - -use : - user.code_insert_library(code_libraries, "") - key(; enter) - -## specialist flow control -state if let some: user.insert_between("if let Some(", ")") -state if let (ok | okay): user.insert_between("if let Ok(", ")") -state if let error: user.insert_between("if let Err(", ")") - -## rust centric synonyms -is some: user.code_insert_is_not_null() - -## for implementing -implement (struct | structure): user.code_state_implements() - -## for annotating function parameters -is implemented trait {user.code_trait}: ": impl {code_trait}" -is implemented trait: ": impl " -returns implemented trait {user.code_trait}: " -> impl {code_trait}" -returns implemented trait: " -> impl " - -## for generic reference of traits -trait {user.code_trait}: insert("{code_trait}") -implemented trait {user.code_trait}: insert("impl {code_trait}") -dynamic trait {user.code_trait}: insert("dyn {code_trait}") - -## for generic reference of macro -macro {user.code_macros}: user.code_insert_macro(code_macros, "") -macro wrap {user.code_macros}: - user.code_insert_macro(code_macros, edit.selected_text()) - -## rust specific document comments -block dock comment: user.code_comment_documentation_block() -inner dock comment: user.code_comment_documentation_inner() -inner block dock comment: user.code_comment_documentation_block_inner() diff --git a/talon/user/community/lang/scala/scala.py b/talon/user/community/lang/scala/scala.py deleted file mode 100644 index cae483e..0000000 --- a/talon/user/community/lang/scala/scala.py +++ /dev/null @@ -1,313 +0,0 @@ -from talon import Context, Module, actions, settings - -ctx = Context() -mod = Module() -ctx.matches = r""" -code.language: scala -""" - -# Scala Common Types -scala_common_types = { - "boolean": "Boolean", - "int": "Int", - "float": "Float", - "byte": "Byte", - "double": "Double", - "short": "Short", - "long": "Long", - "char": "Char", - "unit": "Unit", - "any": "Any", - "any val": "AnyVal", - "string": "String", - "thread": "Thread", - "exception": "Exception", - "throwable": "Throwable", - "none": "None", - "success": "Success", - "failure": "Failure", -} - -# Scala Common Generic Types -scala_common_generic_types = { - "array": "Array", - "deck": "Deque", - "future": "Future", - "list": "List", - "map": "Map", - "nil": "Nil", - "option": "Option", - "queue": "Queue", - "seek": "Seq", - "set": "Set", - "some": "Some", - "stack": "Stack", - "try": "Try", -} - -scala_types = scala_common_types.copy() -scala_types.update(scala_common_generic_types) -ctx.lists["user.code_type"] = scala_types - -# Scala Modifies -scala_modifiers = { - "public": "public", - "private": "private", - "protected": "protected", -} - -mod.list("scala_modifier", desc="Scala Modifiers") -ctx.lists["user.scala_modifier"] = scala_modifiers - -scala_keywords = { - "abstract": "abstract", - "case class": "case class", - "def": "def", - "extends": "extends", - "implicit": "implicit", - "lazy val": "lazy val", - "new": "new", - "object": "object", - "override": "override", - "package": "package", - "sealed": "sealed", - "throw": "throw", - "trait": "trait", - "type": "type", - "val": "val", - "var": "var", - "with": "with", - "yield": "yield", -} - -mod.list("scala_keyword", desc="Scala Keywords") -ctx.lists["user.scala_keyword"] = scala_keywords - - -@ctx.action_class("user") -class UserActions: - def code_operator_lambda(): - actions.insert(" => ") - - def code_operator_subscript(): - actions.insert("()") - actions.edit.left() - - def code_operator_assignment(): - actions.insert(" = ") - - def code_operator_subtraction(): - actions.insert(" - ") - - def code_operator_subtraction_assignment(): - actions.insert(" -= ") - - def code_operator_addition(): - actions.insert(" + ") - - def code_operator_addition_assignment(): - actions.insert(" += ") - - def code_operator_multiplication(): - actions.insert(" * ") - - def code_operator_multiplication_assignment(): - actions.insert(" *= ") - - def code_operator_exponent(): - actions.insert(" ^ ") - - def code_operator_division(): - actions.insert(" / ") - - def code_operator_division_assignment(): - actions.insert(" /= ") - - def code_operator_modulo(): - actions.insert(" % ") - - def code_operator_modulo_assignment(): - actions.insert(" %= ") - - def code_operator_equal(): - actions.insert(" == ") - - def code_operator_not_equal(): - actions.insert(" != ") - - def code_operator_greater_than(): - actions.insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.insert(" >= ") - - def code_operator_less_than(): - actions.insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.insert(" <= ") - - def code_operator_and(): - actions.insert(" && ") - - def code_operator_or(): - actions.insert(" || ") - - def code_operator_bitwise_and(): - actions.insert(" & ") - - def code_operator_bitwise_and_assignment(): - actions.insert(" &= ") - - def code_operator_increment(): - actions.insert("++") - - def code_operator_bitwise_or(): - actions.insert(" | ") - - def code_operator_bitwise_exclusive_or(): - actions.insert(" ^ ") - - def code_operator_bitwise_left_shift(): - actions.insert(" << ") - - def code_operator_bitwise_left_shift_assignment(): - actions.insert(" <<= ") - - def code_operator_bitwise_right_shift(): - actions.insert(" >> ") - - def code_operator_bitwise_right_shift_assignment(): - actions.insert(" >>= ") - - def code_self(): - actions.insert("this") - - def code_insert_null(): - actions.insert("null") - - def code_insert_is_null(): - actions.insert(" == null") - - def code_insert_is_not_null(): - actions.insert(" != null") - - def code_state_if(): - actions.insert("if () ") - actions.edit.left() - actions.edit.left() - - def code_state_else_if(): - actions.insert("else if () ") - actions.edit.left() - actions.edit.left() - - def code_state_else(): - actions.insert("else ") - - def code_state_switch(): - actions.insert("match {\n") - - def code_state_case(): - actions.insert("case => ") - actions.edit.left() - actions.edit.left() - actions.edit.left() - actions.edit.left() - - def code_state_for(): - actions.insert("for () ") - actions.edit.left() - actions.edit.left() - - def code_state_while(): - actions.insert("while () ") - actions.edit.left() - actions.edit.left() - - def code_break(): - actions.insert("break") - - def code_next(): - actions.insert("continue") - - def code_insert_true(): - actions.insert("true") - - def code_insert_false(): - actions.insert("false") - - def code_define_class(): - actions.insert("class ") - - def code_import(): - actions.insert("import ") - - def code_state_return(): - actions.insert("return ") - - def code_comment_line_prefix(): - actions.insert("// ") - - def code_comment_block(): - actions.insert("/*") - actions.key("enter") - actions.key("enter") - actions.insert("*/") - actions.edit.up() - - def code_comment_block_prefix(): - actions.insert("/*") - - def code_comment_block_suffix(): - actions.insert("*/") - - def code_insert_type_annotation(type: str): - actions.insert(f": {type}") - - def code_insert_return_type(type: str): - actions.insert(f": {type}") - - def code_operator_object_accessor(): - actions.insert(".") - - def code_default_function(text: str): - """Inserts function declaration""" - actions.user.code_public_function(text) - - def code_insert_function(text: str, selection: str): - if selection: - text = text + f"({selection})" - else: - text = text + "()" - - actions.user.paste(text) - actions.edit.left() - - def code_private_function(text: str): - """Inserts private function declaration""" - result = "private def {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_protected_function(text: str): - result = "protected def {}".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_public_function(text: str): - result = "def {}".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) diff --git a/talon/user/community/lang/scala/scala.talon b/talon/user/community/lang/scala/scala.talon deleted file mode 100644 index 795900f..0000000 --- a/talon/user/community/lang/scala/scala.talon +++ /dev/null @@ -1,40 +0,0 @@ -code.language: scala -- -tag(): user.code_imperative -tag(): user.code_object_oriented - -tag(): user.code_block_c_like -tag(): user.code_comment_line -tag(): user.code_comment_block_c_like -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_functions -tag(): user.code_libraries -tag(): user.code_operators_array -tag(): user.code_operators_assignment -tag(): user.code_operators_bitwise -tag(): user.code_operators_lambda -tag(): user.code_operators_math - -settings(): - user.code_private_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_public_function_formatter = "PRIVATE_CAMEL_CASE" - user.code_private_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_protected_variable_formatter = "PRIVATE_CAMEL_CASE" - user.code_public_variable_formatter = "PRIVATE_CAMEL_CASE" - -state {user.scala_modifier}: insert("{user.scala_modifier} ") - -state {user.scala_keyword}: insert("{scala_keyword} ") - -op right arrow: " -> " -op left arrow: " <- " -op plus plus: " ++ " -op subtype: " <: " - -state match: user.code_state_switch() - -block string: - insert('""""""') - key("left left left") diff --git a/talon/user/community/lang/sql/sql.py b/talon/user/community/lang/sql/sql.py deleted file mode 100644 index 1812ed1..0000000 --- a/talon/user/community/lang/sql/sql.py +++ /dev/null @@ -1,69 +0,0 @@ -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -code.language: sql -""" - -# these vary by dialect -ctx.lists["user.code_common_function"] = {"count": "Count", "min": "Min", "max": "Max"} - - -@ctx.action_class("user") -class UserActions: - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_equal(): - actions.auto_insert(" = ") - - def code_operator_not_equal(): - actions.auto_insert(" <> ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_in(): - actions.user.insert_between(" IN (", ")") - - def code_operator_not_in(): - actions.user.insert_between(" NOT IN (", ")") - - def code_operator_and(): - actions.auto_insert("AND ") - - def code_operator_or(): - actions.auto_insert("OR ") - - def code_insert_null(): - actions.auto_insert("NULL") - - def code_insert_is_null(): - actions.auto_insert(" IS NULL") - - def code_insert_is_not_null(): - actions.auto_insert(" IS NOT NULL") - - def code_comment_line_prefix(): - actions.auto_insert("-- ") - - def code_insert_function(text: str, selection: str): - actions.user.insert_between(f"{text}({selection or ''}", ")") diff --git a/talon/user/community/lang/sql/sql.talon b/talon/user/community/lang/sql/sql.talon deleted file mode 100644 index 6e94c08..0000000 --- a/talon/user/community/lang/sql/sql.talon +++ /dev/null @@ -1,42 +0,0 @@ -code.language: sql -- -tag(): user.code_operators_math -tag(): user.code_comment_line -tag(): user.code_comment_block_c_like -tag(): user.code_data_null -tag(): user.code_functions_common - -select: "SELECT " -distinct: "DISTINCT " -from: "FROM " -select star from: "SELECT *\nFROM " -where: "WHERE " -order by: "ORDER BY " -group by: "GROUP BY " -having: "HAVING " -descending: " DESC" -ascending: " ASC" -dot i d: ".id" -inner join: user.insert_between("INNER JOIN ", " ON ") -inner join using: user.insert_between("INNER JOIN ", " USING ") -left outer join: user.insert_between("LEFT OUTER JOIN ", " ON ") -right outer join: user.insert_between("RIGHT OUTER JOIN ", " ON ") - -with: - key(enter up) - "WITH AS (" - key(enter tab) - "SELECT " - key(enter shift-tab) - edit.extend_line_end() - edit.delete() - ") " - key(delete up:2 right:3) - -column: - key(return) - ", " - -count: user.code_insert_function("Count", "") - -date: user.insert_between("DATE '", "'") diff --git a/talon/user/community/lang/stata/stata.py b/talon/user/community/lang/stata/stata.py deleted file mode 100644 index 30821c3..0000000 --- a/talon/user/community/lang/stata/stata.py +++ /dev/null @@ -1,167 +0,0 @@ -from talon import Context, actions, settings - -ctx = Context() - -ctx.matches = r""" -code.language: stata -""" - -# functions.py -ctx.lists["user.code_parameter_name"] = { - # regressions - "V C E cluster": "vce(cluster)", - "V C E robust": "vce(robust)", -} - -# functions_common.py -ctx.lists["user.code_common_function"] = { - # base stata - "global": "global", - "local": "local", - "reg": "reg", - "regress": "reg", - # packages - "estadd": "estadd", - "estout": "estout", - "estpost": "estpost", - "eststo": "eststo", - "esttab": "esttab", -} - -# libraries_gui.py -ctx.lists["user.code_libraries"] = { - "estout": "estout", -} - - -@ctx.action_class("user") -class UserActions: - # comment_line.py - def code_comment_line_prefix(): - actions.auto_insert("* ") - - # functions.py - def code_private_function(text: str): - result = "program {} \n\nend".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - actions.user.paste(result) - actions.edit.up() - actions.key("tab") - - def code_default_function(text: str): - actions.user.code_private_function(text) - - def code_insert_named_argument(parameter_name: str): - actions.insert(f"{parameter_name} ") - - # functions_common.py - def code_insert_function(text: str, selection: str): - text += f" {selection or ''}" - actions.user.paste(text) - - # imperative.py - def code_block(): - actions.auto_insert("\n") - - def code_state_if(): - actions.insert("if {\n\n}") - actions.key("up tab up") - actions.edit.line_end() - actions.key("left:2") - - def code_state_else_if(): - actions.insert("else if {\n\n}") - actions.key("up tab up") - actions.edit.line_end() - actions.key("left:2") - - def code_state_else(): - actions.insert("else {\n\n}") - actions.key("up tab") - - def code_state_for(): - actions.insert("forval {\n\n}") - actions.key("up tab up") - actions.edit.line_end() - actions.key("left:2") - - def code_state_for_each(): - actions.insert("foreach in {\n\n}") - actions.key("up tab up") - actions.edit.line_end() - actions.key("left:2") - - def code_state_while(): - actions.insert("while {\n\n}") - actions.key("up tab up") - actions.edit.line_end() - actions.key("left:2") - - def code_break(): - actions.insert("break") - - def code_next(): - actions.insert("continue") - - # libraries.py - def code_import(): - actions.auto_insert("ssc install ") - - # libraries_gui.py - def code_insert_library(text: str, selection: str): - actions.auto_insert("ssc install ") - actions.user.paste(text + selection) - - # operators_array.py - def code_operator_subscript(): - actions.user.insert_between("[", "]") - - # operators_assignment.py - def code_operator_assignment(): - actions.auto_insert(" = ") - - # operators_math.py - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_modulo(): - actions.user.insert_between("mod(", ")") - - def code_operator_exponent(): - actions.auto_insert(" ^ ") - - def code_operator_equal(): - actions.auto_insert(" == ") - - def code_operator_not_equal(): - actions.auto_insert(" != ") - - def code_operator_greater_than(): - actions.auto_insert(" > ") - - def code_operator_less_than(): - actions.auto_insert(" < ") - - def code_operator_greater_than_or_equal_to(): - actions.auto_insert(" >= ") - - def code_operator_less_than_or_equal_to(): - actions.auto_insert(" <= ") - - def code_operator_and(): - actions.auto_insert(" & ") - - def code_operator_or(): - actions.auto_insert(" | ") diff --git a/talon/user/community/lang/stata/stata.talon b/talon/user/community/lang/stata/stata.talon deleted file mode 100644 index 13975e6..0000000 --- a/talon/user/community/lang/stata/stata.talon +++ /dev/null @@ -1,28 +0,0 @@ -code.language: stata -- -tag(): user.code_imperative - -tag(): user.code_comment_block_c_like -tag(): user.code_comment_block -tag(): user.code_comment_line -tag(): user.code_functions -tag(): user.code_functions_common -tag(): user.code_libraries -tag(): user.code_libraries_gui -tag(): user.code_operators_array -tag(): user.code_operators_assignment - -settings(): - user.code_private_function_formatter = "SNAKE_CASE" - -arg {user.code_parameter_name}: user.code_insert_named_argument(code_parameter_name) - -state for val: user.code_state_for() - -# alternative to saying ""state import"" -s s c install: user.code_import() - -s s c install : user.code_insert_library(code_libraries, "") - -toggle imports: user.code_toggle_libraries() -toggle packages: user.code_toggle_libraries() diff --git a/talon/user/community/lang/tags/comment_block.py b/talon/user/community/lang/tags/comment_block.py deleted file mode 100644 index a83963b..0000000 --- a/talon/user/community/lang/tags/comment_block.py +++ /dev/null @@ -1,37 +0,0 @@ -from talon import Context, Module, actions - -c_like_ctx = Context() -mod = Module() - -mod.tag("code_comment_block", desc="Tag for enabling generic block comment commands") -mod.tag("code_comment_block_c_like", desc="Denotes usage of C-style block comments") - -c_like_ctx.matches = """ -tag: user.code_comment_block_c_like -""" -c_like_ctx.tags = ["user.code_comment_block"] - - -@mod.action_class -class Actions: - def code_comment_block(): - """Block comment""" - - def code_comment_block_prefix(): - """Block comment start syntax""" - - def code_comment_block_suffix(): - """Block comment end syntax""" - - -@c_like_ctx.action_class("user") -class CActions: - def code_comment_block(): - actions.insert("/*\n\n*/") - actions.edit.up() - - def code_comment_block_prefix(): - actions.auto_insert("/*") - - def code_comment_block_suffix(): - actions.auto_insert("*/") diff --git a/talon/user/community/lang/tags/comment_documentation.py b/talon/user/community/lang/tags/comment_documentation.py deleted file mode 100644 index 870a43d..0000000 --- a/talon/user/community/lang/tags/comment_documentation.py +++ /dev/null @@ -1,14 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -mod.tag( - "code_comment_documentation", desc="Tag for enabling generic documentation commands" -) - - -@mod.action_class -class Actions: - def code_comment_documentation(): - """Inserts a document comment and positions the cursor appropriately""" diff --git a/talon/user/community/lang/tags/comment_documentation.talon b/talon/user/community/lang/tags/comment_documentation.talon deleted file mode 100644 index 7c57601..0000000 --- a/talon/user/community/lang/tags/comment_documentation.talon +++ /dev/null @@ -1,3 +0,0 @@ -tag: user.code_comment_documentation -- -dock comment: user.code_comment_documentation() diff --git a/talon/user/community/lang/tags/comment_line.py b/talon/user/community/lang/tags/comment_line.py deleted file mode 100644 index ebeb059..0000000 --- a/talon/user/community/lang/tags/comment_line.py +++ /dev/null @@ -1,12 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -mod.tag("code_comment_line", desc="Tag for enabling generic line comment commands") - - -@mod.action_class -class Actions: - def code_comment_line_prefix(): - """Inserts line comment prefix at current cursor location""" diff --git a/talon/user/community/lang/tags/comment_line.talon b/talon/user/community/lang/tags/comment_line.talon deleted file mode 100644 index 921b8bb..0000000 --- a/talon/user/community/lang/tags/comment_line.talon +++ /dev/null @@ -1,38 +0,0 @@ -tag: user.code_comment_line -- -comment: user.code_comment_line_prefix() -comment line: - #todo: this should probably be a single function once - #.talon supports implementing actions with parameters? - edit.line_start() - user.code_comment_line_prefix() -#adds comment to the start of the line -comment line over: - #todo: this should probably be a single function once - #.talon supports implementing actions with parameters? - edit.line_start() - user.code_comment_line_prefix() - insert(user.text) - insert(" ") -comment over: - #todo: this should probably be a single function once - #.talon supports implementing actions with parameters? - user.code_comment_line_prefix() - insert(user.text) -comment $: - #todo: this should probably be a single function once - #.talon supports implementing actions with parameters? - user.code_comment_line_prefix() - insert(user.text) -(line | inline) comment over: - #todo: this should probably be a single function once - #.talon supports implementing actions with parameters? - edit.line_end() - user.code_comment_line_prefix() - insert(user.text) -(line | inline) comment $: - #todo: this should probably be a single function once - #.talon supports implementing actions with parameters? - edit.line_end() - user.code_comment_line_prefix() - insert(user.text) diff --git a/talon/user/community/lang/tags/data_bool.py b/talon/user/community/lang/tags/data_bool.py deleted file mode 100644 index 27e9fae..0000000 --- a/talon/user/community/lang/tags/data_bool.py +++ /dev/null @@ -1,15 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -mod.tag("code_data_bool", desc="Tag for enabling commands for inserting Boolean data") - - -@mod.action_class -class Actions: - def code_insert_true(): - """Insert True value""" - - def code_insert_false(): - """Insert False value""" diff --git a/talon/user/community/lang/tags/data_bool.talon b/talon/user/community/lang/tags/data_bool.talon deleted file mode 100644 index a291e5e..0000000 --- a/talon/user/community/lang/tags/data_bool.talon +++ /dev/null @@ -1,4 +0,0 @@ -tag: user.code_data_bool -- -state true: user.code_insert_true() -state false: user.code_insert_false() diff --git a/talon/user/community/lang/tags/data_null.py b/talon/user/community/lang/tags/data_null.py deleted file mode 100644 index 33a5d6a..0000000 --- a/talon/user/community/lang/tags/data_null.py +++ /dev/null @@ -1,18 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -mod.tag("code_data_null", desc="Tag for enabling commands relating to null") - - -@mod.action_class -class Actions: - def code_insert_null(): - """Inserts null""" - - def code_insert_is_null(): - """Inserts check for null""" - - def code_insert_is_not_null(): - """Inserts check for non-null""" diff --git a/talon/user/community/lang/tags/data_null.talon b/talon/user/community/lang/tags/data_null.talon deleted file mode 100644 index c8f5f2f..0000000 --- a/talon/user/community/lang/tags/data_null.talon +++ /dev/null @@ -1,5 +0,0 @@ -tag: user.code_data_null -- -state (no | none | nil | null): user.code_insert_null() -is not (none | null): user.code_insert_is_not_null() -is (none | null): user.code_insert_is_null() diff --git a/talon/user/community/lang/tags/functions.py b/talon/user/community/lang/tags/functions.py deleted file mode 100644 index f25fee0..0000000 --- a/talon/user/community/lang/tags/functions.py +++ /dev/null @@ -1,149 +0,0 @@ -from typing import Union - -from talon import Context, Module, actions, settings - -ctx = Context() -mod = Module() - -# TODO: abstract visibilities using a list (#663) - -mod.tag("code_functions", desc="Tag for enabling commands for functions") - -mod.list("code_type", desc="List of types for active language") -mod.list( - "code_parameter_name", desc="List of common parameter names for active language" -) -mod.list( - "code_function_modifier", - desc="List of function modifiers (e.g. private, async, static)", -) - -ctx.lists["user.code_function_modifier"] = { - "pub": "public", - "pro": "protected", - "private": "private", - "static": "static", -} - - -@mod.capture(rule="{user.code_type}") -def code_type(m) -> str: - """Returns a macro name""" - return m.code_type - - -mod.setting("code_private_function_formatter", str) -mod.setting("code_protected_function_formatter", str) -mod.setting("code_public_function_formatter", str) -mod.setting("code_private_variable_formatter", str) -mod.setting("code_protected_variable_formatter", str) -mod.setting("code_public_variable_formatter", str) - - -@mod.action_class -class Actions: - def code_modified_function(modifiers: Union[list[str], int], text: str): - """ - Inserts function declaration with the given modifiers. modifiers == 0 - implies no modifiers (.talon files don't have empty list literal - syntax) - """ - mods = {} if modifiers == 0 else set(modifiers) - - if mods == {}: - return actions.user.code_default_function(text) - elif mods == {"static"}: - return actions.user.code_private_static_function(text) - elif mods == {"private"}: - return actions.user.code_private_function(text) - elif mods == {"private", "static"}: - return actions.user.code_private_static_function(text) - elif mods == {"protected"}: - return actions.user.code_protected_function(text) - elif mods == {"protected", "static"}: - return actions.user.code_protected_static_function(text) - elif mods == {"public"}: - return actions.user.code_public_function(text) - elif mods == {"public", "static"}: - return actions.user.code_public_static_function(text) - else: - raise RuntimeError(f"Unhandled modifier set: {mods}") - - def code_default_function(text: str): - """Inserts function declaration""" - actions.user.code_private_function(text) - - def code_private_function(text: str): - """Inserts private function declaration""" - - def code_private_static_function(text: str): - """Inserts private static function""" - - def code_protected_function(text: str): - """Inserts protected function declaration""" - - def code_protected_static_function(text: str): - """Inserts public function""" - - def code_public_function(text: str): - """Inserts public function""" - - def code_public_static_function(text: str): - """Inserts public function""" - - def code_private_function_formatter(name: str): - """Inserts private function name with formatter""" - actions.insert( - actions.user.formatted_text( - name, settings.get("user.code_private_function_formatter") - ) - ) - - def code_protected_function_formatter(name: str): - """inserts properly formatted private function name""" - actions.insert( - actions.user.formatted_text( - name, settings.get("user.code_protected_function_formatter") - ) - ) - - def code_public_function_formatter(name: str): - """inserts properly formatted private function name""" - actions.insert( - actions.user.formatted_text( - name, settings.get("user.code_public_function_formatter") - ) - ) - - def code_private_variable_formatter(name: str): - """inserts properly formatted private function name""" - actions.insert( - actions.user.formatted_text( - name, settings.get("user.code_private_variable_formatter") - ) - ) - - def code_protected_variable_formatter(name: str): - """inserts properly formatted private function name""" - actions.insert( - actions.user.formatted_text( - name, settings.get("user.code_protected_variable_formatter") - ) - ) - - def code_public_variable_formatter(name: str): - """inserts properly formatted private function name""" - actions.insert( - actions.user.formatted_text( - name, settings.get("user.code_public_variable_formatter") - ) - ) - - def code_insert_type_annotation(type: str): - """Inserts a type annotation""" - - def code_insert_return_type(type: str): - """Inserts a return type""" - - def code_insert_named_argument(parameter_name: str): - """Inserts a named argument""" diff --git a/talon/user/community/lang/tags/functions.talon b/talon/user/community/lang/tags/functions.talon deleted file mode 100644 index f8979e5..0000000 --- a/talon/user/community/lang/tags/functions.talon +++ /dev/null @@ -1,26 +0,0 @@ -tag: user.code_functions -- -# Default implementation of capture listens for the following keywords in any -# order: private pro pub static -# -# The default action implementation looks for the token combination on the left -# (funky is added here for searchability) and calls the function on the right: -# -# * funky -> code_default_function -# * private funky -> code_private_function -# * pro funky -> code_protected_function -# * pub funky -> code_public_function -# * static funky -> code_private_static_function -# * private static funky -> code_private_static_function -# * pro static funky -> code_protected_static_function -# * pub static funky -> code_public_static_function -# -^{user.code_function_modifier}* funky $: - user.code_modified_function(code_function_modifier_list or 0, text) - -# for annotating function parameters -is type : user.code_insert_type_annotation(code_type) -returns [type] : user.code_insert_return_type(code_type) - -# for generic reference of types -type : insert(code_type) diff --git a/talon/user/community/lang/tags/functions_common.py b/talon/user/community/lang/tags/functions_common.py deleted file mode 100644 index d581ee7..0000000 --- a/talon/user/community/lang/tags/functions_common.py +++ /dev/null @@ -1,85 +0,0 @@ -from talon import Context, Module, actions, imgui, registry - -ctx = Context() -mod = Module() - -mod.list("code_common_function", desc="List of common functions for active language") - -# global -function_list = [] - - -@mod.capture(rule="{user.code_common_function}") -def code_common_function(m) -> str: - """Returns a function name""" - return m.code_common_function - - -mod.tag("code_functions_common", desc="Tag for enabling support for common functions") -mod.tag( - "code_functions_common_gui_active", - desc="Active when the function picker GUI is showing", -) - - -@mod.action_class -class Actions: - def code_toggle_functions(): - """GUI: List functions for active language""" - global function_list - if gui_functions.showing: - function_list = [] - gui_functions.hide() - ctx.tags = [] - else: - update_function_list_and_freeze() - - def code_select_function(number: int, selection: str): - """Inserts the selected function when the imgui is open""" - if gui_functions.showing and number < len(function_list): - actions.user.code_insert_function( - registry.lists["user.code_common_function"][0][function_list[number]], - selection, - ) - - # TODO: clarify the relation between `code_insert_function` - # and the various functions declared in the functions - - def code_insert_function(text: str, selection: str): - """Inserts a function and positions the cursor appropriately""" - - -def update_function_list_and_freeze(): - global function_list - if "user.code_common_function" in registry.lists: - function_list = sorted(registry.lists["user.code_common_function"][0].keys()) - else: - function_list = [] - - gui_functions.show() - ctx.tags = ["user.code_functions_common_gui_active"] - - -@imgui.open() -def gui_functions(gui: imgui.GUI): - gui.text("Functions") - gui.line() - - # print(str(registry.lists["user.code_functions"])) - for i, entry in enumerate(function_list, 1): - if entry in registry.lists["user.code_common_function"][0]: - gui.text( - f"{i}. {entry}: {registry.lists['user.code_common_function'][0][entry]}" - ) - - gui.spacer() - if gui.button("Toggle funk (close window)"): - actions.user.code_toggle_functions() - - -def commands_updated(_): - if gui_functions.showing: - update_function_list_and_freeze() - - -registry.register("update_commands", commands_updated) diff --git a/talon/user/community/lang/tags/functions_common.talon b/talon/user/community/lang/tags/functions_common.talon deleted file mode 100644 index 7e24f99..0000000 --- a/talon/user/community/lang/tags/functions_common.talon +++ /dev/null @@ -1,8 +0,0 @@ -tag: user.code_functions_common -- -toggle funk: user.code_toggle_functions() -funk : user.code_insert_function(code_common_function, "") -funk cell : user.code_select_function(number - 1, "") -funk wrap : - user.code_insert_function(code_common_function, edit.selected_text()) -funk wrap : user.code_select_function(number - 1, edit.selected_text()) diff --git a/talon/user/community/lang/tags/functions_common_gui_active.talon b/talon/user/community/lang/tags/functions_common_gui_active.talon deleted file mode 100644 index c598564..0000000 --- a/talon/user/community/lang/tags/functions_common_gui_active.talon +++ /dev/null @@ -1,4 +0,0 @@ -tag: user.code_functions_common_gui_active -- -# Toggle prefix to be mentally similar to the 'toggle funk' show command -toggle funk: user.code_toggle_functions() diff --git a/talon/user/community/lang/tags/imperative.py b/talon/user/community/lang/tags/imperative.py deleted file mode 100644 index 66f4518..0000000 --- a/talon/user/community/lang/tags/imperative.py +++ /dev/null @@ -1,72 +0,0 @@ -from talon import Context, Module, actions - -c_like_ctx = Context() -mod = Module() - -mod.tag( - "code_imperative", - desc="Tag for enabling basic imperative programming commands (loops, functions, etc)", -) -mod.tag("code_block_c_like", desc="Language uses C style code blocks, i.e. braces") - -c_like_ctx.matches = """ -tag: self.code_block_c_like -""" - - -@mod.action_class -class Actions: - def code_block(): - """Inserts equivalent of {\n} for the active language, and places the cursor appropriately""" - - def code_state_if(): - """Inserts if statement""" - - def code_state_else_if(): - """Inserts else if statement""" - - def code_state_else(): - """Inserts else statement""" - - def code_state_do(): - """Inserts do statement""" - - def code_state_switch(): - """Inserts switch statement""" - - def code_state_case(): - """Inserts case statement""" - - def code_state_for(): - """Inserts for statement""" - - def code_state_for_each(): - """Inserts for each equivalent statement""" - - def code_state_go_to(): - """inserts go-to statement""" - - def code_state_while(): - """Inserts while statement""" - - def code_state_infinite_loop(): - """Inserts infinite loop statement""" - - def code_state_return(): - """Inserts return statement""" - - def code_break(): - """Inserts break statement""" - - def code_next(): - """Inserts next/continue statement""" - - def code_try_catch(): - """Inserts try/catch. If selection is true, does so around the selection""" - - -@c_like_ctx.action_class("self") -class CActions: - def code_block(): - actions.user.insert_between("{", "}") - actions.key("enter") diff --git a/talon/user/community/lang/tags/imperative.talon b/talon/user/community/lang/tags/imperative.talon deleted file mode 100644 index 7657445..0000000 --- a/talon/user/community/lang/tags/imperative.talon +++ /dev/null @@ -1,17 +0,0 @@ -tag: user.code_imperative -- -block: user.code_block() -state if: user.code_state_if() -state else if: user.code_state_else_if() -state else: user.code_state_else() -state while: user.code_state_while() -state loop: user.code_state_infinite_loop() -state for: user.code_state_for() -state for in: user.code_state_for_each() -state (switch | match): user.code_state_switch() -state case: user.code_state_case() -state do: user.code_state_do() -state goto: user.code_state_go_to() -state return: user.code_state_return() -state break: user.code_break() -state (continue | next): user.code_next() diff --git a/talon/user/community/lang/tags/keywords.py b/talon/user/community/lang/tags/keywords.py deleted file mode 100644 index bb31fec..0000000 --- a/talon/user/community/lang/tags/keywords.py +++ /dev/null @@ -1,20 +0,0 @@ -from talon import Module, actions - -mod = Module() - -mod.tag("code_keywords", desc="Tag for enabling commands for keywords") - -mod.list("code_keyword", desc="List of keywords for active language") - - -@mod.capture(rule=("{user.code_keyword}")) -def code_keyword(m) -> str: - return str(m) - - -@mod.action_class -class Actions: - def code_keyword(keywords: list[str]): - """Adds keywords""" - for keyword in keywords: - actions.insert(keyword) diff --git a/talon/user/community/lang/tags/keywords.talon b/talon/user/community/lang/tags/keywords.talon deleted file mode 100644 index 02bd072..0000000 --- a/talon/user/community/lang/tags/keywords.talon +++ /dev/null @@ -1,3 +0,0 @@ -tag: user.code_keywords -- -keyword (+): user.code_keyword(code_keyword_list) diff --git a/talon/user/community/lang/tags/libraries.py b/talon/user/community/lang/tags/libraries.py deleted file mode 100644 index b5717fc..0000000 --- a/talon/user/community/lang/tags/libraries.py +++ /dev/null @@ -1,15 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -mod.tag( - "code_libraries", - desc="Tag for enabling commands for importing libraries", -) - - -@mod.action_class -class Actions: - def code_import(): - """import/using equivalent""" diff --git a/talon/user/community/lang/tags/libraries.talon b/talon/user/community/lang/tags/libraries.talon deleted file mode 100644 index 91d9acc..0000000 --- a/talon/user/community/lang/tags/libraries.talon +++ /dev/null @@ -1,3 +0,0 @@ -tag: user.code_libraries -- -state import: user.code_import() diff --git a/talon/user/community/lang/tags/libraries_gui.py b/talon/user/community/lang/tags/libraries_gui.py deleted file mode 100644 index 797ab61..0000000 --- a/talon/user/community/lang/tags/libraries_gui.py +++ /dev/null @@ -1,80 +0,0 @@ -from talon import Context, Module, actions, imgui, registry - -ctx = Context() -mod = Module() - -mod.list("code_libraries", desc="List of libraries for active language") -mod.tag( - "code_libraries_gui_showing", desc="Active when the library picker GUI is showing" -) - -# global -library_list = [] - - -@mod.capture(rule="{user.code_libraries}") -def code_libraries(m) -> str: - """Returns a type""" - return m.code_libraries - - -mod.tag("code_libraries_gui", desc="Tag for enabling GUI support for common libraries") - - -@mod.action_class -class Actions: - def code_toggle_libraries(): - """GUI: List libraries for active language""" - global library_list - if gui_libraries.showing: - library_list = [] - gui_libraries.hide() - ctx.tags.discard("user.code_libraries_gui_showing") - else: - update_library_list_and_freeze() - - def code_select_library(number: int, selection: str): - """Inserts the selected library when the imgui is open""" - if gui_libraries.showing and number < len(library_list): - actions.user.code_insert_library( - registry.lists["user.code_libraries"][0][library_list[number]], - selection, - ) - - # TODO: clarify the relation between `code_insert_library` - # and `code_import` - - def code_insert_library(text: str, selection: str): - """Inserts a library and positions the cursor appropriately""" - - -@imgui.open() -def gui_libraries(gui: imgui.GUI): - gui.text("Libraries") - gui.line() - - for i, entry in enumerate(library_list, 1): - gui.text(f"{i}. {entry}: {registry.lists['user.code_libraries'][0][entry]}") - - gui.spacer() - if gui.button("Toggle libraries close"): - actions.user.code_toggle_libraries_hide() - - -def update_library_list_and_freeze(): - global library_list - if "user.code_libraries" in registry.lists: - library_list = sorted(registry.lists["user.code_libraries"][0].keys()) - else: - library_list = [] - - gui_libraries.show() - ctx.tags.add("user.code_libraries_gui_showing") - - -def commands_updated(_): - if gui_libraries.showing: - update_library_list_and_freeze() - - -registry.register("update_commands", commands_updated) diff --git a/talon/user/community/lang/tags/libraries_gui.talon b/talon/user/community/lang/tags/libraries_gui.talon deleted file mode 100644 index 3ed8b83..0000000 --- a/talon/user/community/lang/tags/libraries_gui.talon +++ /dev/null @@ -1,8 +0,0 @@ -tag: user.code_libraries_gui -- -# NOTE: This file does not define any commands, as the commands vary from -# language to language, e.g., Python uses 'import', C uses 'include', -# R uses 'library', etcetera. - -# TODO: If this ever becomes possible, we should abstract over these commands -# using a variable which can be set to the context-specific word. diff --git a/talon/user/community/lang/tags/library_gui_open.talon b/talon/user/community/lang/tags/library_gui_open.talon deleted file mode 100644 index 6558dbc..0000000 --- a/talon/user/community/lang/tags/library_gui_open.talon +++ /dev/null @@ -1,6 +0,0 @@ -tag: user.code_libraries_gui_showing -- -# The show functions for this have language specific names, e.g. toggle imports for Python -# but let's use a generic name for the close one. Having it behind this tag allows it to be closed -# even if your editor isn't visible. -toggle libraries close: user.code_toggle_libraries() diff --git a/talon/user/community/lang/tags/object_oriented.py b/talon/user/community/lang/tags/object_oriented.py deleted file mode 100644 index b4d7dd6..0000000 --- a/talon/user/community/lang/tags/object_oriented.py +++ /dev/null @@ -1,21 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -mod.tag( - "code_object_oriented", - desc="Tag for enabling basic object oriented programming commands (objects, classes, etc)", -) - - -@mod.action_class -class Actions: - def code_operator_object_accessor(): - """Inserts the object accessor operator (e.g., Java's "." or PHP's "->)""" - - def code_self(): - """Inserts a reference to the current object (e.g., C++ "this" or Python's "self")""" - - def code_define_class(): - """Starts a class definition (e.g., Java's "class" keyword)""" diff --git a/talon/user/community/lang/tags/object_oriented.talon b/talon/user/community/lang/tags/object_oriented.talon deleted file mode 100644 index 150b886..0000000 --- a/talon/user/community/lang/tags/object_oriented.talon +++ /dev/null @@ -1,10 +0,0 @@ -tag: user.code_object_oriented -- - -self dot: - user.code_self() - user.code_operator_object_accessor() - -state self: user.code_self() - -state class: user.code_define_class() diff --git a/talon/user/community/lang/tags/operators_array.py b/talon/user/community/lang/tags/operators_array.py deleted file mode 100644 index a61dcfa..0000000 --- a/talon/user/community/lang/tags/operators_array.py +++ /dev/null @@ -1,12 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -mod.tag("code_operators_array", desc="Tag for enabling array operator commands") - - -@mod.action_class -class Actions: - def code_operator_subscript(): - """code_operator_subscript (e.g., C++ [])""" diff --git a/talon/user/community/lang/tags/operators_array.talon b/talon/user/community/lang/tags/operators_array.talon deleted file mode 100644 index c90d35e..0000000 --- a/talon/user/community/lang/tags/operators_array.talon +++ /dev/null @@ -1,5 +0,0 @@ -tag: user.code_operators_array -- - -# array subscription -op subscript: user.code_operator_subscript() diff --git a/talon/user/community/lang/tags/operators_assignment.py b/talon/user/community/lang/tags/operators_assignment.py deleted file mode 100644 index b218502..0000000 --- a/talon/user/community/lang/tags/operators_assignment.py +++ /dev/null @@ -1,48 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -mod.tag("code_operators_assignment", desc="Tag for enabling assignment commands") - - -@mod.action_class -class Actions: - def code_operator_assignment(): - """code_operator_assignment""" - - def code_or_operator_assignment(): - """code_operator_assignment""" - - def code_operator_subtraction_assignment(): - """code_operator_subtraction_assignment""" - - def code_operator_addition_assignment(): - """code_operator_addition_assignment""" - - def code_operator_increment(): - """code_operator_increment""" - - def code_operator_multiplication_assignment(): - """code_operator_multiplication_assignment""" - - def code_operator_division_assignment(): - """code_operator_division_assignment""" - - def code_operator_modulo_assignment(): - """code_operator_modulo_assignment""" - - def code_operator_bitwise_and_assignment(): - """code_operator_and_assignment""" - - def code_operator_bitwise_or_assignment(): - """code_operator_or_assignment""" - - def code_operator_bitwise_exclusive_or_assignment(): - """code_operator_bitwise_exclusive_or_assignment""" - - def code_operator_bitwise_left_shift_assignment(): - """code_operator_bitwise_left_shift_assigment""" - - def code_operator_bitwise_right_shift_assignment(): - """code_operator_bitwise_right_shift_assignment""" diff --git a/talon/user/community/lang/tags/operators_assignment.talon b/talon/user/community/lang/tags/operators_assignment.talon deleted file mode 100644 index 1f8f337..0000000 --- a/talon/user/community/lang/tags/operators_assignment.talon +++ /dev/null @@ -1,26 +0,0 @@ -tag: user.code_operators_assignment -- -tag(): user.code_operators_math -tag(): user.code_operators_bitwise - -# assignment -op (equals | assign): user.code_operator_assignment() -op or equals: user.code_or_operator_assignment() - -# combined computation and assignment -op (minus | subtract) equals: user.code_operator_subtraction_assignment() -op (plus | add) equals: user.code_operator_addition_assignment() -op (times | multiply) equals: user.code_operator_multiplication_assignment() -op divide equals: user.code_operator_division_assignment() -op mod equals: user.code_operator_modulo_assignment() -[op] increment: user.code_operator_increment() - -#bitwise operators -[op] bit [wise] and equals: user.code_operator_bitwise_and_assignment() -[op] bit [wise] or equals: user.code_operator_bitwise_or_assignment() -(op | logical | bitwise) (ex | exclusive) or equals: - user.code_operator_bitwise_exclusive_or_assignment() -[(op | logical | bitwise)] (left shift | shift left) equals: - user.code_operator_bitwise_left_shift_assignment() -[(op | logical | bitwise)] (right shift | shift right) equals: - user.code_operator_bitwise_right_shift_assignment() diff --git a/talon/user/community/lang/tags/operators_bitwise.py b/talon/user/community/lang/tags/operators_bitwise.py deleted file mode 100644 index d5f7797..0000000 --- a/talon/user/community/lang/tags/operators_bitwise.py +++ /dev/null @@ -1,27 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -mod.tag("code_operators_bitwise", desc="Tag for enabling bitwise operator commands") - - -@mod.action_class -class Actions: - def code_operator_bitwise_and(): - """code_operator_bitwise_and""" - - def code_operator_bitwise_or(): - """code_operator_bitwise_or""" - - def code_operator_bitwise_not(): - """code_operator_bitwise_not""" - - def code_operator_bitwise_exclusive_or(): - """code_operator_bitwise_exclusive_or""" - - def code_operator_bitwise_left_shift(): - """code_operator_bitwise_left_shift""" - - def code_operator_bitwise_right_shift(): - """code_operator_bitwise_right_shift""" diff --git a/talon/user/community/lang/tags/operators_bitwise.talon b/talon/user/community/lang/tags/operators_bitwise.talon deleted file mode 100644 index e403e66..0000000 --- a/talon/user/community/lang/tags/operators_bitwise.talon +++ /dev/null @@ -1,15 +0,0 @@ -tag: user.code_operators_bitwise -- - -#bitwise operators -[op] bitwise and: user.code_operator_bitwise_and() -[op] bitwise or: user.code_operator_bitwise_or() -[op] bitwise not: user.code_operator_bitwise_not() - -# TODO: split these out into separate logical and bitwise operator commands - -(op | logical | bitwise) (ex | exclusive) or: user.code_operator_bitwise_exclusive_or() -(op | logical | bitwise) (left shift | shift left): - user.code_operator_bitwise_left_shift() -(op | logical | bitwise) (right shift | shift right): - user.code_operator_bitwise_right_shift() diff --git a/talon/user/community/lang/tags/operators_lambda.py b/talon/user/community/lang/tags/operators_lambda.py deleted file mode 100644 index 9cc8ede..0000000 --- a/talon/user/community/lang/tags/operators_lambda.py +++ /dev/null @@ -1,16 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -# TODO: this probably shouldn't be in operators - -mod.tag( - "code_operators_lambda", desc="Tag for enabling commands for anonymous functions" -) - - -@mod.action_class -class Actions: - def code_operator_lambda(): - """code_operator_lambda""" diff --git a/talon/user/community/lang/tags/operators_lambda.talon b/talon/user/community/lang/tags/operators_lambda.talon deleted file mode 100644 index 263450e..0000000 --- a/talon/user/community/lang/tags/operators_lambda.talon +++ /dev/null @@ -1,13 +0,0 @@ -tag: user.code_operators_lambda -- - -# In many languages, anonymous functions aren't merely infix syntax: -# -# Haskell '\x -> bla' -# OCaml 'fun x -> bla' -# Rust '|x| { bla }' -# -# Therefore a revision of this command may be in order. - -# syntax for anonymous functions -op lambda: user.code_operator_lambda() diff --git a/talon/user/community/lang/tags/operators_math.py b/talon/user/community/lang/tags/operators_math.py deleted file mode 100644 index 34507bf..0000000 --- a/talon/user/community/lang/tags/operators_math.py +++ /dev/null @@ -1,63 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - - -# TODO: Could split into numeric, comparison, and logic? - -mod.tag("code_operators_math", desc="Tag for enabling mathematical operator commands") - - -@mod.action_class -class Actions: - def code_operator_subtraction(): - """code_operator_subtraction""" - - def code_operator_addition(): - """code_operator_addition""" - - def code_operator_multiplication(): - """code_operator_multiplication""" - - def code_operator_exponent(): - """code_operator_exponent""" - - def code_operator_division(): - """code_operator_division""" - - def code_operator_modulo(): - """code_operator_modulo""" - - def code_operator_equal(): - """code_operator_equal""" - - def code_operator_not_equal(): - """code_operator_not_equal""" - - def code_operator_greater_than(): - """code_operator_greater_than""" - - def code_operator_greater_than_or_equal_to(): - """code_operator_greater_than_or_equal_to""" - - def code_operator_less_than(): - """code_operator_less_than""" - - def code_operator_less_than_or_equal_to(): - """code_operator_less_than_or_equal_to""" - - def code_operator_and(): - """code_operator_and""" - - def code_operator_or(): - """code_operator_or""" - - def code_operator_not(): - """code_operator_not""" - - def code_operator_in(): - """code_operator_in""" - - def code_operator_not_in(): - """code_operator_not_in""" diff --git a/talon/user/community/lang/tags/operators_math.talon b/talon/user/community/lang/tags/operators_math.talon deleted file mode 100644 index 57d590e..0000000 --- a/talon/user/community/lang/tags/operators_math.talon +++ /dev/null @@ -1,30 +0,0 @@ -tag: user.code_operators_math -- - -# math operators -op (minus | subtract): user.code_operator_subtraction() -op (plus | add): user.code_operator_addition() -op (times | multiply): user.code_operator_multiplication() -op divide: user.code_operator_division() -op mod: user.code_operator_modulo() -(op (power | exponent) | to the power [of]): user.code_operator_exponent() - -# comparison operators -(op | is) equal: user.code_operator_equal() -(op | is) not equal: user.code_operator_not_equal() -(op | is) (greater | more): user.code_operator_greater_than() -(op | is) (less | below) [than]: user.code_operator_less_than() -(op | is) greater [than] or equal: user.code_operator_greater_than_or_equal_to() -(op | is) less [than] or equal: user.code_operator_less_than_or_equal_to() - -# logical operators -(op | logical) and: user.code_operator_and() -(op | logical) or: user.code_operator_or() -(op | logical) not: user.code_operator_not() - -# set operators -(op | is) in: user.code_operator_in() -(op | is) not in: user.code_operator_not_in() - -# TODO: This operator should either be abstracted into a function or removed. -(op | pad) colon: " : " diff --git a/talon/user/community/lang/tags/operators_pointer.py b/talon/user/community/lang/tags/operators_pointer.py deleted file mode 100644 index a663e91..0000000 --- a/talon/user/community/lang/tags/operators_pointer.py +++ /dev/null @@ -1,18 +0,0 @@ -from talon import Context, Module - -ctx = Context() -mod = Module() - -mod.tag("code_operators_pointer", desc="Tag for enabling pointer operator commands") - - -@mod.action_class -class Actions: - def code_operator_indirection(): - """code_operator_indirection""" - - def code_operator_address_of(): - """code_operator_address_of (e.g., C++ & op)""" - - def code_operator_structure_dereference(): - """code_operator_structure_dereference (e.g., C++ -> op)""" diff --git a/talon/user/community/lang/tags/operators_pointer.talon b/talon/user/community/lang/tags/operators_pointer.talon deleted file mode 100644 index 29d3b85..0000000 --- a/talon/user/community/lang/tags/operators_pointer.talon +++ /dev/null @@ -1,6 +0,0 @@ -tag: user.code_operators_pointer -- -# pointer operators -op dereference: user.code_operator_indirection() -op address of: user.code_operator_address_of() -op arrow: user.code_operator_structure_dereference() diff --git a/talon/user/community/lang/talon/talon-list.talon b/talon/user/community/lang/talon/talon-list.talon deleted file mode 100644 index 14e2d4d..0000000 --- a/talon/user/community/lang/talon/talon-list.talon +++ /dev/null @@ -1,5 +0,0 @@ -code.language: talonlist -- -# requires user.talon_populate_lists tag. do not use with dragon -list [require] {user.talon_lists}: "list: {talon_lists}" -list [require]: "list: " diff --git a/talon/user/community/lang/talon/talon.py b/talon/user/community/lang/talon/talon.py deleted file mode 100644 index e5c7319..0000000 --- a/talon/user/community/lang/talon/talon.py +++ /dev/null @@ -1,148 +0,0 @@ -from talon import Context, Module, actions, app, registry - -mod = Module() -ctx_talon = Context() -ctx_talon_python = Context() -ctx_talon_lists = Context() - -# restrict all the talon_* lists to when the user.talon_populate_lists tag -# is active to prevent them from being active in contexts where they are not wanted. -# Do not enable this tag with dragon, as it will be unusable. -# with conformer, the latency increase may also be unacceptable depending on your cpu -# see https://github.com/talonhub/community/issues/600 -ctx_talon_lists.matches = r""" -tag: user.talon_populate_lists -""" - -mod.tag("talon_python", "Tag to activate talon-specific python commands") -mod.tag( - "talon_populate_lists", - "Tag to activate talon-specific lists of actions, scopes, modes etcetera. Do not use this tag with dragon", -) -mod.list("talon_actions") -mod.list("talon_lists") -mod.list("talon_captures") -mod.list("talon_apps") -mod.list("talon_tags") -mod.list("talon_modes") -mod.list("talon_settings") -mod.list("talon_scopes") -mod.list("talon_modes") - -ctx_talon.matches = r""" -code.language: talon -""" - -ctx_talon_python.matches = r""" -tag: user.talon_python -""" - - -def on_update_decls(decls): - # todo modes? - for thing in [ - "actions", - "lists", - "captures", - "tags", - "apps", - "settings", - "scopes", - "modes", - ]: - l = getattr(decls, thing) - ctx_talon_lists.lists[f"user.talon_{thing}"] = ( - actions.user.create_spoken_forms_from_list( - l.keys(), generate_subsequences=False - ) - ) - # print( - # "List: {} \n {}".format(thing, str(ctx_talon_lists.lists[f"user.talon_{thing}"])) - # ) - - -def on_ready(): - # print("on_ready") - on_update_decls(registry.decls) - registry.register("update_decls", on_update_decls) - - -app.register("ready", on_ready) - - -@mod.action_class -class Actions: - def talon_code_insert_action_call(text: str, selection: str): - """inserts talon-specific action call""" - actions.user.code_insert_function(text, selection) - - def talon_code_enable_tag(tag: str): - """enables tag in either python or talon files""" - - def talon_code_enable_setting(setting: str): - """asserts setting in either python or talon files""" - - -@ctx_talon.action_class("user") -class TalonActions: - def talon_code_enable_tag(tag: str): - """enables tag in either python or talon files""" - actions.user.paste(f"tag(): {tag}") - - def talon_code_enable_setting(setting: str): - """asserts setting in either python or talon files""" - actions.user.paste(f"{setting} = ") - - -@ctx_talon_python.action_class("user") -class TalonPythonActions: - def talon_code_insert_action_call(text: str, selection: str): - text = f"actions.{text}({selection or ''})" - actions.user.paste(text) - actions.edit.left() - - def talon_code_enable_tag(tag: str): - """enables tag in either python or talon files""" - actions.user.paste(f'ctx.tags = ["{tag}"]') - if not tag: - actions.edit.left() - actions.edit.left() - - def talon_code_enable_setting(setting: str): - """asserts setting in either python or talon files""" - if not setting: - actions.user.insert_between('ctx.settings["', '"] = ') - else: - actions.user.paste(f'ctx.settings["{setting}"] = ') - - -@ctx_talon.action_class("user") -class UserActions: - def code_operator_and(): - actions.auto_insert(" and ") - - def code_operator_or(): - actions.auto_insert(" or ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_comment_line_prefix(): - actions.auto_insert("#") - - def code_insert_function(text: str, selection: str): - text += f"({selection or ''})" - actions.user.paste(text) - actions.edit.left() diff --git a/talon/user/community/lang/talon/talon.talon b/talon/user/community/lang/talon/talon.talon deleted file mode 100644 index df2b7e4..0000000 --- a/talon/user/community/lang/talon/talon.talon +++ /dev/null @@ -1,14 +0,0 @@ -code.language: talon -- -tag(): user.code_operators_math -tag(): user.code_operators_assignment -tag(): user.code_comment_line -tag(): user.code_functions_common -# uncomment user.talon_populate_lists tag to activate talon-specific lists of actions, scopes, modes etcetera. -# Do not enable this tag with dragon, as it will be unusable. -# with conformer, the latency increase may also be unacceptable depending on your cpu -# see https://github.com/talonhub/community/issues/600 -# tag(): user.talon_populate_lists - -#defintion blocks for the context -setting block: insert("settings():\n\t") diff --git a/talon/user/community/lang/talon/talon_code_common_function.talon-list b/talon/user/community/lang/talon/talon_code_common_function.talon-list deleted file mode 100644 index addc97d..0000000 --- a/talon/user/community/lang/talon/talon_code_common_function.talon-list +++ /dev/null @@ -1,7 +0,0 @@ -list: user.code_common_function -code.language: talon -- -insert -key -print -repeat diff --git a/talon/user/community/lang/talon/talon_common.talon b/talon/user/community/lang/talon/talon_common.talon deleted file mode 100644 index 3bcd4cb..0000000 --- a/talon/user/community/lang/talon/talon_common.talon +++ /dev/null @@ -1,22 +0,0 @@ -#Defines commands common to both python and talon files -code.language: talon -code.language: python -and tag: user.talon_python -- -tag set [{user.talon_tags}]: - tag = talon_tags or "" - user.talon_code_enable_tag(tag) - -# requires user.talon_populate_lists tag. do not use with dragon -list {user.talon_lists}: "{{{talon_lists}}}" -# requires user.talon_populate_lists tag. do not use with dragon -capture {user.talon_captures}: "<{talon_captures}>" - -setting {user.talon_settings}: user.talon_code_enable_setting(talon_settings) - -#commands for dictating key combos -key over: "{keys}" -key over: "{modifiers}" - -action {user.talon_actions}: - user.talon_code_insert_action_call(talon_actions, edit.selected_text()) diff --git a/talon/user/community/lang/talon/talon_context.talon b/talon/user/community/lang/talon/talon_context.talon deleted file mode 100644 index d001cba..0000000 --- a/talon/user/community/lang/talon/talon_context.talon +++ /dev/null @@ -1,22 +0,0 @@ -code.language: talon -code.language: talonlist -code.language: python -and tag: user.talon_python -- -#context requirements -win require: insert("os: windows\n") -mac require: insert("os: mac\n") -linux require: insert("os: linux\n") -title require: insert("win.title: ") -application [require] [{user.talon_apps}]: - app = "{talon_apps}\n" or "" - insert("app: {app}") -mode require [{user.talon_modes}]: - mode = "{talon_modes}\n" or "" - insert("mode: {mode}") -tag require [{user.talon_tags}]: - tag = "{talon_tags}\n" or "" - insert("tag: {tag}") -host require: - hostname = user.talon_get_hostname() - insert("hostname: {hostname}\n") diff --git a/talon/user/community/lang/talon/talon_python_activator.talon b/talon/user/community/lang/talon/talon_python_activator.talon deleted file mode 100644 index 3f82ea7..0000000 --- a/talon/user/community/lang/talon/talon_python_activator.talon +++ /dev/null @@ -1,14 +0,0 @@ -# This file activates talon-specific python commands -# by default, it simply looks for the python tag to be active -# lines 7-11 provide examples to make the activation more specific -# which may be preferred by people who code in other python projects -# app: vscode -# Mac VSCode uses an em-dash -# win.title: /— user/ -# win.title: /— community/ -# windows VSCode uses an en-dash -# win.title: / - user - Visual Studio Code/ -# win.title: / - community - Visual Studio Code/ -code.language: python -- -tag(): user.talon_python diff --git a/talon/user/community/lang/terraform/terraform.py b/talon/user/community/lang/terraform/terraform.py deleted file mode 100644 index 5589d67..0000000 --- a/talon/user/community/lang/terraform/terraform.py +++ /dev/null @@ -1,142 +0,0 @@ -from talon import Context, Module, actions - -ctx = Context() -mod = Module() -ctx.matches = r""" -code.language: terraform -""" - -types = { - "string": "string", - "number": "number", - "bool": "bool", - "list": "list", - "map": "map", - "null": "null", -} - -ctx.lists["user.code_type"] = types - -common_properties = { - "name": "name", - "type": "type", - "description": "description", - "default": "default", - "for each": "for_each", - "count": "count", - "prevent destroy": "prevent_destroy", - "nullable": "nullable", - "sensitive": "sensitive", - "depends on": "depends_on", - "provider": "provider", - "source": "source", -} - -mod.list("terraform_common_property", desc="Terraform Modifier") -ctx.lists["self.terraform_common_property"] = common_properties - -module_blocks = { - "variable": "variable", - "output": "output", - "provider": "provider", - "module": "module", -} - -mod.list("terraform_module_block", desc="Simple Terraform Block") -ctx.lists["self.terraform_module_block"] = module_blocks - - -@mod.action_class -class Actions: - def code_terraform_module_block(text: str): - """Inserts a new module-related block of a given type (e.g. variable, output, provider...)""" - - def code_terraform_resource(text: str): - """Inserts a new resource block with given name""" - - def code_terraform_data_source(text: str): - """Inserts a new data block with given name""" - - -@ctx.action_class("user") -class UserActions: - def code_terraform_module_block(text: str): - actions.user.insert_between(text + ' "', '"') - - def code_terraform_resource(text: str): - result = f"resource \"{actions.user.formatted_text(text, 'SNAKE_CASE')}\" \"\"" - - actions.insert(result) - actions.key("left") - - def code_terraform_data_source(text: str): - result = f"data \"{actions.user.formatted_text(text, 'SNAKE_CASE')}\" \"\"" - - actions.insert(result) - actions.key("left") - - def code_operator_assignment(): - actions.insert(" = ") - - def code_operator_subtraction(): - actions.insert(" - ") - - def code_operator_addition(): - actions.insert(" + ") - - def code_operator_multiplication(): - actions.insert(" * ") - - def code_operator_division(): - actions.insert(" / ") - - def code_operator_modulo(): - actions.insert(" % ") - - def code_operator_equal(): - actions.insert(" == ") - - def code_operator_not_equal(): - actions.insert(" != ") - - def code_operator_greater_than(): - actions.insert(" > ") - - def code_operator_greater_than_or_equal_to(): - actions.insert(" >= ") - - def code_operator_less_than(): - actions.insert(" < ") - - def code_operator_less_than_or_equal_to(): - actions.insert(" <= ") - - def code_operator_and(): - actions.insert(" && ") - - def code_operator_or(): - actions.insert(" || ") - - def code_insert_true(): - actions.insert("true") - - def code_insert_false(): - actions.insert("false") - - def code_operator_lambda(): - actions.insert(" => ") - - def code_insert_null(): - actions.insert("null") - - def code_insert_is_null(): - actions.insert(" == null") - - def code_insert_is_not_null(): - actions.insert(" != null") - - def code_comment_line_prefix(): - actions.insert("# ") - - def code_state_for(): - actions.user.insert_between("for ", " in") diff --git a/talon/user/community/lang/terraform/terraform.talon b/talon/user/community/lang/terraform/terraform.talon deleted file mode 100644 index e27751e..0000000 --- a/talon/user/community/lang/terraform/terraform.talon +++ /dev/null @@ -1,26 +0,0 @@ -code.language: terraform -- -tag(): user.code_block_c_like -tag(): user.code_comment_block_c_like -tag(): user.code_comment_line -tag(): user.code_data_bool -tag(): user.code_data_null -tag(): user.code_imperative -tag(): user.code_operators_assignment -tag(): user.code_operators_lambda -tag(): user.code_operators_math - -block: user.code_block() - -state {user.terraform_module_block}: - user.code_terraform_module_block(user.terraform_module_block) - -resource : user.code_terraform_resource(text) - -data [source] : user.code_terraform_data_source(text) - -[state] prop {user.terraform_common_property}: - insert(user.terraform_common_property) - user.code_operator_assignment() - -type {user.code_type}: insert("{code_type}") diff --git a/talon/user/community/lang/typescript/typescript.py b/talon/user/community/lang/typescript/typescript.py deleted file mode 100644 index 18c8069..0000000 --- a/talon/user/community/lang/typescript/typescript.py +++ /dev/null @@ -1,56 +0,0 @@ -from talon import Context, actions, settings - -ctx = Context() -ctx.matches = r""" -code.language: typescript -code.language: typescriptreact -# Make typescript win over javascript -mode: command -""" - -ctx.lists["user.code_type"] = { - "boolean": "boolean", - "integer": "int", - "string": "string", - "null": "null", - "undefined": "undefined", - "number": "number", - "any": "any", -} - - -@ctx.action_class("user") -class UserActions: - def code_private_function(text: str): - """Inserts private function declaration""" - result = "private function {}".format( - actions.user.formatted_text( - text, settings.get("user.code_private_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_protected_function(text: str): - result = "protected function {}".format( - actions.user.formatted_text( - text, settings.get("user.code_protected_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_public_function(text: str): - result = "public function {}".format( - actions.user.formatted_text( - text, settings.get("user.code_public_function_formatter") - ) - ) - - actions.user.code_insert_function(result, None) - - def code_insert_type_annotation(type: str): - actions.insert(f": {type}") - - def code_insert_return_type(type: str): - actions.insert(f": {type}") diff --git a/talon/user/community/lang/typescript/typescript.talon b/talon/user/community/lang/typescript/typescript.talon deleted file mode 100644 index 9e7f314..0000000 --- a/talon/user/community/lang/typescript/typescript.talon +++ /dev/null @@ -1,10 +0,0 @@ -code.language: typescript -code.language: typescriptreact -- - -type union []: " | {code_type or ''}" -type intersect []: " & {code_type or ''}" - -state type: user.insert_between("type ", " = ") - -as const: " as const" diff --git a/talon/user/community/lang/vimscript/vimscript.py b/talon/user/community/lang/vimscript/vimscript.py deleted file mode 100644 index aa67d1a..0000000 --- a/talon/user/community/lang/vimscript/vimscript.py +++ /dev/null @@ -1,94 +0,0 @@ -from talon import Context, Module, actions - -mod = Module() -ctx = Context() -ctx.matches = r""" -code.language: vimscript -""" - -ctx.lists["self.vimscript_functions"] = { - "string len": "strlen", - "get line": "getline", - "set line": "setline", - "length": "len", -} - -ctx.lists["self.vimscript_scope"] = { - "argument": "a:", - "arg": "a:", - "buffer": "b:", - "buf": "b:", - "window": "w:", - "win": "w:", - "tab": "t:", - "special": "v:", - "global": "g:", - "local": "l:", - "script local": "s:", -} - -mod.list("vimscript_functions", desc="Standard built-in vimscript functions") -mod.list("vimscript_scope", desc="vimscript scoping types for functions and variables") - - -@mod.capture(rule="{self.vimscript_functions}") -def vimscript_functions(m) -> str: - "Returns a string" - return m.vimscript_functions - - -@mod.capture(rule="{self.vimscript_scope}") -def vimscript_scope(m) -> str: - "Returns a string" - return m.vimscript_scope - - -@ctx.action_class("user") -class UserActions: - def code_operator_assignment(): - actions.auto_insert(" = ") - - def code_operator_subtraction(): - actions.auto_insert(" - ") - - def code_operator_subtraction_assignment(): - actions.auto_insert(" -= ") - - def code_operator_addition(): - actions.auto_insert(" + ") - - def code_operator_addition_assignment(): - actions.auto_insert(" += ") - - def code_operator_multiplication(): - actions.auto_insert(" * ") - - def code_operator_multiplication_assignment(): - actions.auto_insert(" *= ") - - def code_operator_division(): - actions.auto_insert(" / ") - - def code_operator_division_assignment(): - actions.auto_insert(" /= ") - - def code_comment_line_prefix(): - actions.auto_insert('"') - - def code_state_if(): - actions.insert("if ") - - def code_state_else_if(): - actions.insert("elseif ") - - def code_state_else(): - actions.insert("else") - - def code_private_function(text: str): - actions.auto_insert("function ") - - def code_protected_function(text: str): - actions.auto_insert("function ") - - def code_public_function(text: str): - actions.auto_insert("function ") diff --git a/talon/user/community/lang/vimscript/vimscript.talon b/talon/user/community/lang/vimscript/vimscript.talon deleted file mode 100644 index 03e3fa8..0000000 --- a/talon/user/community/lang/vimscript/vimscript.talon +++ /dev/null @@ -1,38 +0,0 @@ -code.language: vimscript -- -tag(): user.code_imperative -tag(): user.code_operators_assignment -tag(): user.code_operators_math -tag(): user.code_comment_line - -# XXX - revisit these -settings(): - user.code_private_function_formatter = "SNAKE_CASE" - user.code_protected_function_formatter = "SNAKE_CASE" - user.code_public_function_formatter = "SNAKE_CASE" - user.code_private_variable_formatter = "SNAKE_CASE" - user.code_protected_variable_formatter = "SNAKE_CASE" - user.code_public_variable_formatter = "SNAKE_CASE" - -### -# VIM Script Specific -### -assign [] (variable | var) [] [over]: - insert("let ") - insert(vimscript_scope or "") - user.code_private_variable_formatter(text) - -[] (variable | var) [] [over]: - insert(vimscript_scope or "") - user.code_private_variable_formatter(text) - -# see lang/vimscript/vimscript.py for list -: insert("{vimscript_functions} ") - -# XXX - possibly overlap with some programming.talon -state command: "command! " -state end if: "endif" -state end for: "endfor" -state end while: "endwhile" -state end function: "endfunction" -state continue: "continue" diff --git a/talon/user/community/migration_helpers/migration_helpers.py b/talon/user/community/migration_helpers/migration_helpers.py deleted file mode 100644 index 0c32f82..0000000 --- a/talon/user/community/migration_helpers/migration_helpers.py +++ /dev/null @@ -1,271 +0,0 @@ -import csv -import os -import re -from dataclasses import dataclass -from pathlib import Path -from typing import Union - -from talon import Module, actions, app - -mod = Module() - - -@dataclass -class CSVData: - """Class to track CSV-related data necessary for conversion to .talon-list""" - - # name of the list - name: str - # Path to the CSV file - path: str - # path to the generated .talon-list - newpath: Union[str, callable] = None - # Indicates whether the first line of the CSV file is a header - # that should be ignored - is_first_line_header: bool = True - # Indicates whether the spoken form or value is first in the CSV file - is_spoken_form_first: bool = False - # An optional callable for generating a custom header for - # generated .talon-list - custom_header: callable = None - # An optional callable for custom processing of the value for - # generated .talon-list - custom_value_converter: callable = None - - -# Note: homophones, emacs_commands, file_extensions, words_to_replace, abbreviations, and app name overrides -# are intentionally omitted, as their use cases are not compatible with .talon-list conversions -supported_csv_files = [ - CSVData( - "user.git_argument", - os.path.join("apps", "git", "git_arguments.csv"), - os.path.join("apps", "git", "git_argument.talon-list"), - ), - CSVData( - "user.git_command", - os.path.join("apps", "git", "git_commands.csv"), - os.path.join("apps", "git", "git_command.talon-list"), - ), - CSVData( - "user.vocabulary", - os.path.join("settings", "additional_words.csv"), - os.path.join("core", "vocabulary", "vocabulary.talon-list"), - ), - CSVData( - "user.letter", - os.path.join("settings", "alphabet.csv"), - os.path.join("core", "keys", "letter.talon-list"), - ), - CSVData( - "user.system_paths", - os.path.join("settings", "system_paths.csv"), - lambda: os.path.join( - "core", f"system_paths-{actions.user.talon_get_hostname()}.talon-list" - ), - custom_header=(lambda: f"hostname: {actions.user.talon_get_hostname()}"), - ), - CSVData( - "user.search_engine", - os.path.join("settings", "search_engines.csv"), - os.path.join("core", "websites_and_search_engines", "search_engine.talon-list"), - ), - CSVData( - "user.unix_utility", - os.path.join("settings", "unix_utilities.csv"), - os.path.join("tags", "terminal", "unix_utility.talon-list"), - ), - CSVData( - "user.website", - os.path.join("settings", "websites.csv"), - os.path.join("core", "websites_and_search_engines", "website.talon-list"), - ), - CSVData( - "user.emoji", - os.path.join("tags", "emoji", "emoji.csv"), - os.path.join("tags", "emoji", "emoji.talon-list"), - is_first_line_header=False, - is_spoken_form_first=True, - ), - CSVData( - "user.emoticon", - os.path.join("tags", "emoji", "emoticon.csv"), - os.path.join("tags", "emoji", "emoticon.talon-list"), - is_first_line_header=False, - is_spoken_form_first=True, - ), - CSVData( - "user.kaomoji", - os.path.join("tags", "emoji", "kaomoji.csv"), - os.path.join("tags", "emoji", "kaomoji.talon-list"), - is_first_line_header=False, - is_spoken_form_first=True, - ), -] - - -def convert_csv_to_talonlist(input_csv: csv.reader, config: CSVData): - """ - Convert a 1 or 2 column CSV into a talon list. - Empty lines, lines containing only whitespace or starting with a # are skipped. - - Args: - - input_csv: A csv.reader instance - - config: A CSVData instance - - Returns: - - str: The contents of a talon list file - - Raises: - - ValueError: If any line in the input CSV contains more than 2 columns. - """ - rows = list(input_csv) - - is_spoken_form_first = config.is_spoken_form_first - is_first_line_header = config.is_first_line_header - start_index = 1 if is_first_line_header else 0 - output = [] - - output.append(f"list: {config.name}") - if config.custom_header and callable(config.custom_header): - output.append(config.custom_header()) - - output.append("-") - - for row in rows[start_index:]: - # Remove trailing whitespace for each cell - row = [col.rstrip() for col in row] - cols = len(row) - - # Check columns - if cols > 2: - raise ValueError("Expected only 1 or 2 columns, got {cols}:", row) - - # Exclude empty or comment rows - if cols == 0 or (cols == 1 and row[0] == "") or row[0].startswith("#"): - continue - - if cols == 2: - if is_spoken_form_first: - spoken_form, value = row - else: - value, spoken_form = row - - if config.custom_value_converter: - value = config.custom_value_converter(value) - - else: - spoken_form = value = row[0] - - if spoken_form != value: - if not str.isprintable(value) or "'" in value or '"' in value: - value = repr(value) - - output.append(f"{spoken_form}: {value}") - else: - output.append(f"{spoken_form}") - - # Terminate file in newline - output.append("") - return "\n".join(output) - - -def convert_files(csv_files_list): - known_csv_files = {str(item.path): item for item in csv_files_list} - - conversion_count = 0 - base_path = Path(__file__).resolve().parent.parent - - for csv_path in base_path.rglob("*.csv"): - csv_relative_path = csv_path.relative_to(base_path) - migrated_csv_path = csv_path.with_suffix(".csv-converted-to-talon-list") - - config = known_csv_files.get(str(csv_relative_path)) - if not config: - continue - - if callable(config.newpath): - talonlist_relative_path = config.newpath() - else: - talonlist_relative_path = config.newpath - - talonlist_path = base_path / talonlist_relative_path - - if talonlist_path.is_file() and not csv_path.is_file(): - print(f"Skipping existing Talon list file {talonlist_relative_path}") - continue - - if migrated_csv_path.is_file(): - print(f"Skipping existing renamed CSV {migrated_csv_path}") - continue - - print( - f"Converting CSV {csv_relative_path} to Talon list {talonlist_relative_path}" - ) - - conversion_count += 1 - with open(csv_path, newline="") as csv_file: - csv_reader = csv.reader(csv_file, skipinitialspace=True) - talonlist_content = convert_csv_to_talonlist(csv_reader, config) - - print( - f"Renaming converted CSV to {migrated_csv_path.name}. This file may be deleted if no longer needed; it's preserved in case there's an issue with conversion." - ) - if talonlist_path.is_file(): - backup_path = talonlist_path.with_suffix(".bak") - print( - f"Migration target {talonlist_relative_path} already exists; backing up to {backup_path}" - ) - talonlist_path.rename(backup_path) - - with open(talonlist_path, "w") as talonlist_file: - talonlist_file.write(talonlist_content) - csv_path.rename(migrated_csv_path) - - return conversion_count - - -@mod.action_class -class Actions: - def migrate_known_csv_files(): - """Migrate known CSV files to .talon-list""" - conversion_count = convert_files(supported_csv_files) - if conversion_count > 0: - notification_text = f"migration_helpers.py converted {conversion_count} CSVs. See Talon log for more details.\n" - print(notification_text) - actions.app.notify(notification_text) - - def migrate_custom_csv( - path: str, - new_path: str, - list_name: str, - is_first_line_header: bool, - spoken_form_first: bool, - ): - """Migrate a custom CSV file""" - csv_file = CSVData( - list_name, - path, - new_path, - is_first_line_header, - spoken_form_first, - None, - None, - ) - convert_files([csv_file]) - - -def on_ready(): - try: - actions.user.migrate_known_csv_files() - except KeyError: - # Due to a core Talon bug, the above action may not be available when a ready callback is invoked. - # (see https://github.com/talonhub/community/pull/1268#issuecomment-2325721706) - notification = ( - "Unable to migrate CSVs to Talon lists.", - "Please quit and restart Talon.", - ) - app.notify(*notification) - print(*notification) - - -app.register("ready", on_ready) diff --git a/talon/user/community/plugin/README.md b/talon/user/community/plugin/README.md deleted file mode 100644 index dc6a5f4..0000000 --- a/talon/user/community/plugin/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# plugin - -The plugin folder has several other subfolders containing various commands: - -- `cancel` contains commands to make talon ignore a command -- `command_history` has commands to see previous commands -- `datetimeinsert` has commands to automatically write the current date and time -- `desktops` has commands to navigate between the different computer desktops -- `draft_editor` has some of the commands to open and use a built-in pop-up text editor -- `dropdown` has commands to select an option from a dropdown menu -- `macro` has commands to use macros -- `media` has commands for video and volume control -- `microphone_selection` has commands for selecting a microphone to use -- `mode_indicator` does not have commands, but has settings for enabling a graphical mode indicator -- `mouse` has commands to click, drag, scroll, and use an eye tracker -- `repeater` has commands for repeating other commands, described briefly in the top level [README](https://github.com/talonhub/community?tab=readme-ov-file#repeating-commands) -- `screenshot` has commands for taking screenshots -- `symbols` has commands for inserting certain symbols, like pairs of parentheses or quotation marks -- `talon_draft_window` has the rest of the commands for using the draft editor window -- `talon_helpers` has commands helpful for debugging, opening the talon directory, and getting updates -- `text_navigation` has commands for navigating the cursor in text diff --git a/talon/user/community/plugin/are_you_sure/README.md b/talon/user/community/plugin/are_you_sure/README.md deleted file mode 100644 index 4a0c743..0000000 --- a/talon/user/community/plugin/are_you_sure/README.md +++ /dev/null @@ -1,28 +0,0 @@ -# Are You Sure Dialog - -This lets you require confirmation before executing an action, which can be useful for potentially destructive commands like shutting down your computer or exiting talon. - -To require confirmation for an action, you use the user.are_you_sure_set_on_confirmation_action function that receives a message to display for the dialogue and the action to perform on confirmation. An optional action to perform on cancelling the action can be provided as the third argument. As this is intended to work with particularly destructive actions, this only supports executing a single action at a time and does not work with chaining. - -You confirm an action by saying "yes I am sure" and cancel it by saying "cancel". - -## Example - -```python -from talon import actions, Module, app - -mod = Module() -@mod.action_class -class Actions: - def test_are_you_sure(): - '''A simple test for the are you sure dialog''' - def on_confirm(): - app.notify('Confirmed') - def on_cancel(): - app.notify('Cancelled') - actions.user.are_you_sure_set_on_confirmation_action('Would you like to receive the on confirm message?', on_confirm, on_cancel) -``` - -```talon -test are you sure: user.test_are_you_sure() -``` diff --git a/talon/user/community/plugin/are_you_sure/are_you_sure.py b/talon/user/community/plugin/are_you_sure/are_you_sure.py deleted file mode 100644 index ca1e442..0000000 --- a/talon/user/community/plugin/are_you_sure/are_you_sure.py +++ /dev/null @@ -1,73 +0,0 @@ -from typing import Callable - -from talon import Context, Module, actions, imgui - -mod = Module() -mod.tag("are_you_sure", desc="Activates are you sure commands") - - -class ConfirmationState: - def __init__(self): - self.context = Context() - - def request_confirmation(self, message: str, on_confirmation, on_disconfirmation): - self.on_confirmation = on_confirmation - self.on_cancel = on_disconfirmation - self.message = message - self.context.tags = ["user.are_you_sure"] - gui.show() - - def confirm(self): - self.on_confirmation() - self.cleanup() - - def cancel(self): - if self.on_cancel: - self.on_cancel() - self.cleanup() - - def cleanup(self): - self.context.tags = [] - self.on_confirmation = None - self.on_cancel = None - self.message = None - gui.hide() - - def get_message(self) -> str: - return self.message - - -confirmation = ConfirmationState() - - -@imgui.open(y=0) -def gui(gui: imgui.GUI): - gui.text(confirmation.get_message()) - gui.line() - if gui.button("Yes I am sure"): - actions.user.are_you_sure_confirm() - if gui.button("Cancel"): - actions.user.are_you_sure_cancel() - - -@mod.action_class -class Actions: - def are_you_sure_confirm(): - """Performs the registered are you sure action""" - confirmation.confirm() - - def are_you_sure_cancel(): - """Cancels the registered are you sure action""" - confirmation.cancel() - - def are_you_sure_set_on_confirmation_action( - message: str, on_confirmation: Callable, on_cancel: Callable = None - ): - """Sets the action to be performed on user confirmation. - message: the message to display to the user - on_confirmation: the action to perform if the user confirms - on_cancel: (optional) the action to perform if the user cancels - This only supports working with a single action at a time and - does not work with chaining as it is intended to be used with particularly destructive actions. - """ - confirmation.request_confirmation(message, on_confirmation, on_cancel) diff --git a/talon/user/community/plugin/are_you_sure/are_you_sure.talon b/talon/user/community/plugin/are_you_sure/are_you_sure.talon deleted file mode 100644 index 84c79b0..0000000 --- a/talon/user/community/plugin/are_you_sure/are_you_sure.talon +++ /dev/null @@ -1,4 +0,0 @@ -tag: user.are_you_sure -- -yes I am sure: user.are_you_sure_confirm() -cancel: user.are_you_sure_cancel() diff --git a/talon/user/community/plugin/cancel/cancel.py b/talon/user/community/plugin/cancel/cancel.py deleted file mode 100644 index dcbbc79..0000000 --- a/talon/user/community/plugin/cancel/cancel.py +++ /dev/null @@ -1,72 +0,0 @@ -# to disable command cancellation, comment out this entire file. -# you may also wish to adjust the commands in misc/cancel.talon. - -import time - -from talon import Context, Module, actions, speech_system -from talon.grammar import Phrase - -# To change the phrase used to cancel commands, you must also adjust misc/cancel.talon -cancel_phrase = "cancel cancel".split() - -mod = Module() -ctx = Context() - -ts_threshold: float = 0 - - -@ctx.action_class("speech") -class SpeechActions: - # When Talon wakes we set the timestamp threshold. On the next command we - # will compare the phrase timestamp to the threshold and cancel any phrase - # started before wakeup. This is to prevent speech said before wake-up to - # be interpreted as a command if the user wakes Talon using a noise or - # keypress. - def enable(): - actions.user.cancel_current_phrase() - actions.next() - - -@mod.action_class -class Actions: - def cancel_current_phrase(): - """Cancel/abort current spoken phrase""" - global ts_threshold - ts_threshold = time.perf_counter() - - -def pre_phrase(phrase: Phrase): - global ts_threshold - - words = phrase["phrase"] - - if not words: - return - - # Check if the phrase is before the threshold - if ts_threshold != 0: - start = getattr(words[0], "start", phrase["_ts"]) - phrase_starts_before_threshold = start < ts_threshold - ts_threshold = 0 - # Start of phrase is before threshold timestamp - if phrase_starts_before_threshold: - print(f"Canceled phrase: {' '.join(words)}") - cancel_entire_phrase(phrase) - return - - # Check if the phrase is a cancel command - n = len(cancel_phrase) - before, after = words[:-n], words[-n:] - if after == cancel_phrase: - actions.app.notify(f"Command canceled: {' '.join(before)!r}") - cancel_entire_phrase(phrase) - return - - -def cancel_entire_phrase(phrase: Phrase): - phrase["phrase"] = [] - if "parsed" in phrase: - phrase["parsed"]._sequence = [] - - -speech_system.register("pre:phrase", pre_phrase) diff --git a/talon/user/community/plugin/cancel/cancel.talon b/talon/user/community/plugin/cancel/cancel.talon deleted file mode 100644 index 08798fb..0000000 --- a/talon/user/community/plugin/cancel/cancel.talon +++ /dev/null @@ -1,6 +0,0 @@ -# allows you to prevent a command executing by ending it with "cancel cancel" -cancel cancel$: skip() -# the actual behavior of "cancel cancel" is implemented in code/cancel.py; if you want to use a different phrase you must also change cancel_phrase there. - -# allows you to say something (eg to a human) that you don't want talon to hear, eg "ignore hey Jerry" -ignore []$: app.notify("Command ignored") diff --git a/talon/user/community/plugin/command_history/command_history.py b/talon/user/community/plugin/command_history/command_history.py deleted file mode 100644 index 564f4f4..0000000 --- a/talon/user/community/plugin/command_history/command_history.py +++ /dev/null @@ -1,92 +0,0 @@ -from typing import Optional - -from talon import Module, actions, imgui, settings, speech_system - -# We keep command_history_size lines of history, but by default display only -# command_history_display of them. -mod = Module() -mod.setting("command_history_size", type=int, default=50) -mod.setting("command_history_display", type=int, default=10) - -hist_more = False -history = [] - - -def on_phrase(j): - global history - - words = j.get("text") - - text = actions.user.history_transform_phrase_text(words) - - if text is not None: - history.append(text) - history = history[-settings.get("user.command_history_size") :] - - -# todo: dynamic rect? -@imgui.open(y=0) -def gui(gui: imgui.GUI): - global history - gui.text("Command History") - gui.line() - text = ( - history[:] - if hist_more - else history[-settings.get("user.command_history_display") :] - ) - for line in text: - gui.text(line) - - gui.spacer() - if gui.button("Command history close"): - actions.user.history_disable() - - -speech_system.register("phrase", on_phrase) - - -@mod.action_class -class Actions: - def history_toggle(): - """Toggles viewing the history""" - if gui.showing: - gui.hide() - else: - gui.show() - - def history_enable(): - """Enables the history""" - gui.show() - - def history_disable(): - """Disables the history""" - gui.hide() - - def history_clear(): - """Clear the history""" - global history - history = [] - - def history_more(): - """Show more history""" - global hist_more - hist_more = True - - def history_less(): - """Show less history""" - global hist_more - hist_more = False - - def history_get(number: int) -> str: - """returns the history entry at the specified index""" - num = (0 - number) - 1 - return history[num] - - def history_transform_phrase_text(words: list[str]) -> Optional[str]: - """Transforms phrase text for presentation in history. Return `None` to omit from history""" - - if not actions.speech.enabled(): - return None - - return " ".join(words) if words else None diff --git a/talon/user/community/plugin/datetimeinsert/datetimeinsert.py b/talon/user/community/plugin/datetimeinsert/datetimeinsert.py deleted file mode 100644 index 6a7adbc..0000000 --- a/talon/user/community/plugin/datetimeinsert/datetimeinsert.py +++ /dev/null @@ -1,24 +0,0 @@ -import datetime - -from talon import Module - -mod = Module() - - -@mod.action_class -class Actions: - def time_format(fmt: str = None) -> str: - """Return the current time, formatted. - fmt: strftime()-style format string, defaults to ISO format.""" - now = datetime.datetime.now() - if fmt is None: - return now.isoformat() - return now.strftime(fmt) - - def time_format_utc(fmt: str = None) -> str: - """Return the current UTC time, formatted. - fmt: strftime()-style format string, defaults to ISO format.""" - now = datetime.datetime.utcnow() - if fmt is None: - return now.isoformat() - return now.strftime(fmt) diff --git a/talon/user/community/plugin/datetimeinsert/datetimeinsert.talon b/talon/user/community/plugin/datetimeinsert/datetimeinsert.talon deleted file mode 100644 index 8b62cfa..0000000 --- a/talon/user/community/plugin/datetimeinsert/datetimeinsert.talon +++ /dev/null @@ -1,7 +0,0 @@ -date insert: insert(user.time_format("%Y-%m-%d")) -date insert UTC: insert(user.time_format_utc("%Y-%m-%d")) -timestamp insert: insert(user.time_format("%Y-%m-%d %H:%M:%S")) -timestamp insert high resolution: insert(user.time_format("%Y-%m-%d %H:%M:%S.%f")) -timestamp insert UTC: insert(user.time_format_utc("%Y-%m-%d %H:%M:%S")) -timestamp insert UTC high resolution: - insert(user.time_format_utc("%Y-%m-%d %H:%M:%S.%f")) diff --git a/talon/user/community/plugin/desktops/desktops.py b/talon/user/community/plugin/desktops/desktops.py deleted file mode 100644 index a6d8f07..0000000 --- a/talon/user/community/plugin/desktops/desktops.py +++ /dev/null @@ -1,34 +0,0 @@ -from talon import Module, app - -mod = Module() - - -@mod.action_class -class Actions: - def desktop(number: int): - """change the current desktop""" - app.notify("Not supported on this operating system") - - def desktop_show(): - """shows the current desktops""" - app.notify("Not supported on this operating system") - - def desktop_next(): - """move to next desktop""" - app.notify("Not supported on this operating system") - - def desktop_last(): - """move to previous desktop""" - app.notify("Not supported on this operating system") - - def window_move_desktop_left(): - """move the current window to the desktop to the left""" - app.notify("Not supported on this operating system") - - def window_move_desktop_right(): - """move the current window to the desktop to the right""" - app.notify("Not supported on this operating system") - - def window_move_desktop(desktop_number: int): - """move the current window to a different desktop""" - app.notify("Not supported on this operating system") diff --git a/talon/user/community/plugin/desktops/desktops_linux.py b/talon/user/community/plugin/desktops/desktops_linux.py deleted file mode 100644 index 888eb3e..0000000 --- a/talon/user/community/plugin/desktops/desktops_linux.py +++ /dev/null @@ -1,31 +0,0 @@ -from talon import Context, actions, ui - -ctx = Context() -ctx.matches = r""" -os: linux -""" - - -@ctx.action_class("user") -class Actions: - def desktop(number: int): - ui.switch_workspace(number) - - def desktop_next(): - actions.user.desktop(ui.active_workspace() + 1) - - def desktop_last(): - actions.user.desktop(ui.active_workspace() - 1) - - def desktop_show(): - actions.key("super") - - def window_move_desktop(desktop_number: int): - ui.active_window().workspace = desktop_number - actions.user.desktop(desktop_number) - - def window_move_desktop_left(): - actions.user.window_move_desktop(ui.active_workspace() - 1) - - def window_move_desktop_right(): - actions.user.window_move_desktop(ui.active_workspace() + 1) diff --git a/talon/user/community/plugin/desktops/desktops_win.py b/talon/user/community/plugin/desktops/desktops_win.py deleted file mode 100644 index aa742c7..0000000 --- a/talon/user/community/plugin/desktops/desktops_win.py +++ /dev/null @@ -1,26 +0,0 @@ -from talon import Context, actions - -ctx = Context() -ctx.matches = r""" -os: windows -""" - - -@ctx.action_class("user") -class Actions: - # def desktop(number: int): - - def desktop_next(): - actions.key("super-ctrl-right") - - def desktop_last(): - actions.key("super-ctrl-left") - - def desktop_show(): - actions.key("super-tab") - - # def window_move_desktop_left(): - - # def window_move_desktop_right(): - - # def window_move_desktop(desktop_number: int): diff --git a/talon/user/community/plugin/draft_editor/draft_editor.py b/talon/user/community/plugin/draft_editor/draft_editor.py deleted file mode 100644 index dbb1c5f..0000000 --- a/talon/user/community/plugin/draft_editor/draft_editor.py +++ /dev/null @@ -1,129 +0,0 @@ -from talon import Context, Module, actions, app, settings, ui - -mod = Module() -mod.tag("draft_editor_active", "Indicates whether the draft editor has been activated") -mod.tag( - "draft_editor_app_focused", - "Indicates that the draft editor app currently has focus", -) - -ctx = Context() -tags: set[str] = set() - - -def add_tag(tag: str): - tags.add(tag) - ctx.tags = list(tags) - - -def remove_tag(tag: str): - tags.discard(tag) - ctx.tags = list(tags) - - -default_names = ["Visual Studio Code", "Code", "VSCodium", "Codium", "code-oss"] - -mod.setting( - "draft_editor", - type=str, - default=None, - desc="List of application names to use for draft editor", -) - - -def get_editor_names(): - names_csv = settings.get("user.draft_editor") - return names_csv.split(", ") if names_csv else default_names - - -@mod.scope -def scope(): - editor_names = get_editor_names() - - for app in ui.apps(background=False): - if app.name in editor_names: - return {"draft_editor_running": True} - - return {"draft_editor_running": False} - - -def handle_app_activate(app): - if app.name in get_editor_names(): - add_tag("user.draft_editor_app_focused") - else: - remove_tag("user.draft_editor_app_focused") - - -ui.register("app_launch", scope.update) -ui.register("app_close", scope.update) -ui.register("app_activate", handle_app_activate) - - -original_window = None - -last_draft = None - - -@mod.action_class -class Actions: - def draft_editor_open(): - """Open draft editor""" - global original_window - original_window = ui.active_window() - editor_app = get_editor_app() - selected_text = actions.edit.selected_text() - actions.user.switcher_focus_app(editor_app) - # Wait additional time for talon context to update. - actions.sleep("200ms") - actions.app.tab_open() - if selected_text != "": - actions.user.paste(selected_text) - add_tag("user.draft_editor_active") - - def draft_editor_submit(): - """Submit/save draft editor""" - close_editor(submit_draft=True) - - def draft_editor_discard(): - """Discard draft editor""" - close_editor(submit_draft=False) - - def draft_editor_paste_last(): - """Paste last submitted draft""" - if last_draft: - actions.user.paste(last_draft) - - -def get_editor_app() -> ui.App: - editor_names = get_editor_names() - - for app in ui.apps(background=False): - if app.name in editor_names: - return app - - raise RuntimeError("Draft editor is not running") - - -def close_editor(submit_draft: bool) -> None: - global last_draft - remove_tag("user.draft_editor_active") - actions.edit.select_all() - if submit_draft: - last_draft = actions.edit.selected_text() - actions.edit.delete() - actions.app.tab_close() - if submit_draft: - try: - actions.user.switcher_focus_window(original_window) - except Exception: - app.notify( - "Failed to focus on window to submit draft, manually focus on intended destination and use draft submit again" - ) - else: - actions.sleep("300ms") - actions.user.paste(last_draft) - else: - try: - actions.user.switcher_focus_window(original_window) - except Exception: - app.notify("Failed to focus on previous window, leaving editor open") diff --git a/talon/user/community/plugin/draft_editor/draft_editor.talon b/talon/user/community/plugin/draft_editor/draft_editor.talon deleted file mode 100644 index 63360da..0000000 --- a/talon/user/community/plugin/draft_editor/draft_editor.talon +++ /dev/null @@ -1,23 +0,0 @@ -user.draft_editor_running: True -not tag: user.draft_editor_app_focused -- - -draft this: user.draft_editor_open() - -draft all: - edit.select_all() - user.draft_editor_open() - -draft line: - edit.select_line() - user.draft_editor_open() - -draft top: - edit.extend_file_start() - user.draft_editor_open() - -draft bottom: - edit.extend_file_end() - user.draft_editor_open() - -draft submit: user.draft_editor_paste_last() diff --git a/talon/user/community/plugin/draft_editor/draft_editor_open.talon b/talon/user/community/plugin/draft_editor/draft_editor_open.talon deleted file mode 100644 index aadcfff..0000000 --- a/talon/user/community/plugin/draft_editor/draft_editor_open.talon +++ /dev/null @@ -1,6 +0,0 @@ -tag: user.draft_editor_active -and tag: user.draft_editor_app_focused -- - -draft submit: user.draft_editor_submit() -draft discard: user.draft_editor_discard() diff --git a/talon/user/community/plugin/dropdown/dropdown.talon b/talon/user/community/plugin/dropdown/dropdown.talon deleted file mode 100644 index e8161bf..0000000 --- a/talon/user/community/plugin/dropdown/dropdown.talon +++ /dev/null @@ -1,4 +0,0 @@ -# DEPRECATED -drop down : user.deprecate_command("2024-05-29", "drop down", "choose") -drop down up : - user.deprecate_command("2024-05-29", "drop down up", "choose up") diff --git a/talon/user/community/plugin/gamepad/README.md b/talon/user/community/plugin/gamepad/README.md deleted file mode 100644 index 6add6b7..0000000 --- a/talon/user/community/plugin/gamepad/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# Gamepad - -Some predefined gamepad bindings for doing tasks like clicking, scrolling and moving your cursor. - -### Usage - -To enable the gamepad bindings activate tag `user.gamepad` in [gamepad_settings.talon](./gamepad_settings.talon) - -## Demo - Using gamepad - -[YouTube - Gamepad demo](https://youtu.be/zNeiZ9nnK_A) - -## Gamepad tester - -![Gamepad tester](./gamepad_tester.png) - -### Usage - -1. Say `"gamepad tester"` to open gamepad tester UI. -1. Press buttons on actual gamepad and see interaction in UI. -1. Close gamepad tester by saying `"gamepad tester"` again. - -### Demo - Gamepad tester - -[YouTube - Gamepad tester demo](https://youtu.be/FzfIlaHm8_w) - -### Conflict with existing gamepad implementations - -The gamepad tester doesn't disable your existing gamepad implementations. If you don't want your existing gamepad implementations to trigger during the testing phase you can add `not tag: user.gamepad_tester` at the top of your gamepad Talon files. diff --git a/talon/user/community/plugin/gamepad/gamepad.py b/talon/user/community/plugin/gamepad/gamepad.py deleted file mode 100644 index 165dc90..0000000 --- a/talon/user/community/plugin/gamepad/gamepad.py +++ /dev/null @@ -1,304 +0,0 @@ -from talon import Module, actions, ctrl, ui -from talon.screen import Screen - -screen: Screen = ui.main_screen() -slow_scroll = False -slow_mouse_move = False - -mod = Module() -mod.tag("gamepad", desc="Activate tag to enable gamepad bindings") - - -@mod.action_class -class Actions: - # DPAD buttons - - def gamepad_press_dpad_left(): - """Gamepad press button dpad left""" - gamepad_mouse_jump("left") - - def gamepad_release_dpad_left(): - """Gamepad release button dpad left""" - actions.skip() - - def gamepad_press_dpad_up(): - """Gamepad press button dpad up""" - gamepad_mouse_jump("up") - - def gamepad_release_dpad_up(): - """Gamepad release button dpad up""" - actions.skip() - - def gamepad_press_dpad_right(): - """Gamepad press button dpad right""" - gamepad_mouse_jump("right") - - def gamepad_release_dpad_right(): - """Gamepad release button dpad right""" - actions.skip() - - def gamepad_press_dpad_down(): - """Gamepad press button dpad down""" - gamepad_mouse_jump("down") - - def gamepad_release_dpad_down(): - """Gamepad release button dpad down""" - actions.skip() - - # Compass / ABXY buttons - - def gamepad_press_west(): - """Gamepad press button west""" - actions.mouse_drag(0) - - def gamepad_release_west(): - """Gamepad release button west""" - actions.mouse_release(0) - - def gamepad_press_north(): - """Gamepad press button north""" - actions.mouse_drag(1) - - def gamepad_release_north(): - """Gamepad release button north""" - actions.mouse_release(1) - - def gamepad_press_east(): - """Gamepad press button east""" - actions.key("ctrl:down") - actions.mouse_click() - actions.key("ctrl:up") - - def gamepad_release_east(): - """Gamepad release button east""" - actions.skip() - - def gamepad_press_south(): - """Gamepad press button south""" - actions.mouse_drag(2) - - def gamepad_release_south(): - """Gamepad release button south""" - actions.mouse_release(2) - - # Select / Start buttons - - def gamepad_press_select(): - """Gamepad press button select""" - actions.skip() - - def gamepad_release_select(): - """Gamepad release button select""" - actions.skip() - - def gamepad_press_start(): - """Gamepad press button start""" - actions.speech.toggle() - - def gamepad_release_start(): - """Gamepad release button start""" - actions.skip() - - # Shoulder buttons - - def gamepad_press_left_shoulder(): - """Gamepad press button left shoulder""" - actions.user.go_back() - - def gamepad_release_left_shoulder(): - """Gamepad release button left shoulder""" - actions.skip() - - def gamepad_press_right_shoulder(): - """Gamepad press button right shoulder""" - actions.user.go_forward() - - def gamepad_release_right_shoulder(): - """Gamepad release button right shoulder""" - actions.skip() - - # Stick buttons - - def gamepad_press_left_stick(): - """Gamepad press button left thumb stick""" - gamepad_scroll_slow_toggle() - - def gamepad_release_left_stick(): - """Gamepad release button left thumb stick""" - actions.skip() - - def gamepad_press_right_stick(): - """Gamepad press button right thumb stick""" - gamepad_mouse_move_slow_toggle() - - def gamepad_release_right_stick(): - """Gamepad release button right thumb stick""" - actions.skip() - - # Analog triggers - - def gamepad_trigger_left(value: float): - """Gamepad trigger left movement""" - gamepad_scroll(0, value * -1) - - def gamepad_trigger_right(value: float): - """Gamepad trigger right movement""" - gamepad_scroll(0, value) - - # Analog thumb sticks - - def gamepad_stick_left(x: float, y: float): - """Gamepad left stick movement""" - gamepad_scroll(x, y) - - def gamepad_stick_right(x: float, y: float): - """Gamepad right stick movement""" - gamepad_mouse_move(x, y) - - # Scaffolding actions used by the Talon file - - def gamepad_button_down(button: str): - """Gamepad press button