diff --git a/Cargo.lock b/Cargo.lock index 7dd90c45..269437f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -166,7 +166,7 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] @@ -263,17 +263,18 @@ dependencies = [ [[package]] name = "crossterm" -version = "0.27.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ "bitflags 2.6.0", "crossterm_winapi", "filedescriptor", "futures-core", "libc", - "mio 0.8.11", + "mio", "parking_lot", + "rustix", "signal-hook", "signal-hook-mio", "winapi", @@ -356,9 +357,9 @@ dependencies = [ [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "either" @@ -1405,7 +1406,7 @@ dependencies = [ name = "helix-lsp-types" version = "0.95.1" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "serde", "serde_json", "serde_repr", @@ -1428,7 +1429,7 @@ dependencies = [ "rustix", "tempfile", "which", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1699,7 +1700,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.0", + "windows-targets 0.48.0", ] [[package]] @@ -1766,18 +1767,6 @@ dependencies = [ "adler", ] -[[package]] -name = "mio" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys 0.48.0", -] - [[package]] name = "mio" version = "1.0.1" @@ -1786,6 +1775,7 @@ checksum = "4569e456d394deccd22ce1c1913e6ea0e54519f577285001215d33557431afe4" dependencies = [ "hermit-abi 0.3.9", "libc", + "log", "wasi", "windows-sys 0.52.0", ] @@ -2034,9 +2024,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.5" +version = "1.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" dependencies = [ "aho-corasick", "memchr", @@ -2152,9 +2142,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.121" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab380d7d9f22ef3f21ad3e6c1ebe8e4fc7a2000ccba2e4d71fc96f15b2cb609" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", "memchr", @@ -2164,9 +2154,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.12" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", @@ -2206,12 +2196,12 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", - "mio 0.8.11", + "mio", "signal-hook", ] @@ -2323,12 +2313,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "b8fcd239983515c23a32fb82099f97d0b11b8c72f654ed659363a95c3dad7a53" dependencies = [ "cfg-if", "fastrand", + "once_cell", "rustix", "windows-sys 0.52.0", ] @@ -2448,7 +2439,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.1", + "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -2481,9 +2472,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.16" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81967dd0dd2c1ab0bc3468bd7caecc32b8a4aa47d0c8c695d8c2b2108168d62c" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", @@ -2493,18 +2484,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fb9f64314842840f1d940ac544da178732128f1c78c21772e876579e0da1db" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.22.17" +version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9f8729f5aea9562aac1cc0441f5d6de3cff1ee0c5d67293eeca5eb36ee7c16" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ "indexmap", "serde", @@ -2673,9 +2664,9 @@ checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] name = "which" -version = "6.0.1" +version = "6.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8211e4f58a2b2805adfbefbc07bab82958fc91e3836339b1ab7ae32465dce0d7" +checksum = "3d9c5ed668ee1f17edb3b627225343d210006a90bb1e3745ce1f30b1fb115075" dependencies = [ "either", "home", @@ -2747,7 +2738,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -2782,17 +2782,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -2809,9 +2810,9 @@ checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -2827,9 +2828,9 @@ checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -2845,9 +2846,15 @@ checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2863,9 +2870,9 @@ checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2881,9 +2888,9 @@ checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2899,9 +2906,9 @@ checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2917,15 +2924,15 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" -version = "0.6.5" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" dependencies = [ "memchr", ] diff --git a/book/src/generated/lang-support.md b/book/src/generated/lang-support.md index 9c88b7a2..4ce9edb9 100644 --- a/book/src/generated/lang-support.md +++ b/book/src/generated/lang-support.md @@ -1,6 +1,6 @@ | Language | Syntax Highlighting | Treesitter Textobjects | Auto Indent | Default LSP | | --- | --- | --- | --- | --- | -| ada | ✓ | ✓ | | `ada_language_server`, `ada_language_server` | +| ada | ✓ | ✓ | | `ada_language_server` | | adl | ✓ | ✓ | ✓ | | | agda | ✓ | | | | | astro | ✓ | | | | @@ -58,6 +58,7 @@ | gas | ✓ | ✓ | | | | gdscript | ✓ | ✓ | ✓ | | | gemini | ✓ | | | | +| gherkin | ✓ | | | | | git-attributes | ✓ | | | | | git-commit | ✓ | ✓ | | | | git-config | ✓ | | | | @@ -97,6 +98,7 @@ | javascript | ✓ | ✓ | ✓ | `typescript-language-server` | | jinja | ✓ | | | | | jjdescription | ✓ | | | | +| jq | ✓ | ✓ | | `jq-lsp` | | jsdoc | ✓ | | | | | json | ✓ | ✓ | ✓ | `vscode-json-language-server` | | json5 | ✓ | | | | @@ -125,7 +127,7 @@ | markdown.inline | ✓ | | | | | matlab | ✓ | ✓ | ✓ | | | mermaid | ✓ | | | | -| meson | ✓ | | ✓ | | +| meson | ✓ | | ✓ | `mesonlsp` | | mint | | | | `mint` | | mojo | ✓ | ✓ | ✓ | `mojo-lsp-server` | | move | ✓ | | | | @@ -133,7 +135,7 @@ | nasm | ✓ | ✓ | | | | nickel | ✓ | | ✓ | `nls` | | nim | ✓ | ✓ | ✓ | `nimlangserver` | -| nix | ✓ | ✓ | | `nil` | +| nix | ✓ | ✓ | | `nil`, `nixd` | | nu | ✓ | | | `nu` | | nunjucks | ✓ | | | | | ocaml | ✓ | | ✓ | `ocamllsp` | @@ -199,12 +201,14 @@ | tcl | ✓ | | ✓ | | | templ | ✓ | | | `templ` | | tfvars | ✓ | | ✓ | `terraform-ls` | +| thrift | ✓ | | | | | todotxt | ✓ | | | | | toml | ✓ | ✓ | | `taplo` | | tsq | ✓ | | | | | tsx | ✓ | ✓ | ✓ | `typescript-language-server` | | twig | ✓ | | | | | typescript | ✓ | ✓ | ✓ | `typescript-language-server` | +| typespec | ✓ | ✓ | ✓ | `tsp-server` | | typst | ✓ | | | `tinymist`, `typst-lsp` | | ungrammar | ✓ | | | | | unison | ✓ | | ✓ | | diff --git a/book/src/install.md b/book/src/install.md index debc82b0..387b8b65 100644 --- a/book/src/install.md +++ b/book/src/install.md @@ -14,6 +14,10 @@ Note that: ## Pre-built binaries Download pre-built binaries from the [GitHub Releases page](https://github.com/helix-editor/helix/releases). -Add the `hx` binary to your system's `$PATH` to use it from the command line, and copy the `runtime` directory into the config directory (for example `~/.config/helix/runtime` on Linux/macOS). -The runtime location can be overriden via the HELIX_RUNTIME environment variable. +The tarball contents include an `hx` binary and a `runtime` directory. +To set up Helix: +1. Add the `hx` binary to your system's `$PATH` to allow it to be used from the command line. +2. Copy the `runtime` directory to a location that `hx` searches for runtime files. A typical location on Linux/macOS is `~/.config/helix/runtime`. + +To see the runtime directories that `hx` searches, run `hx --health`. If necessary, you can override the default runtime location by setting the `HELIX_RUNTIME` environment variable. diff --git a/book/src/keymap.md b/book/src/keymap.md index 0e60f282..e7ae6ae4 100644 --- a/book/src/keymap.md +++ b/book/src/keymap.md @@ -320,10 +320,14 @@ Displays documentation for item under cursor. Remapping currently not supported. Displays documentation for the selected completion item. Remapping currently not supported. -| Key | Description | -| ---- | ----------- | -| `Shift-Tab`, `Ctrl-p`, `Up` | Previous entry | -| `Tab`, `Ctrl-n`, `Down` | Next entry | +| Key | Description | +| ---- | ----------- | +| `Shift-Tab`, `Ctrl-p`, `Up` | Previous entry | +| `Tab`, `Ctrl-n`, `Down` | Next entry | +| `Enter` | Close menu and accept completion | +| `Ctrl-c` | Close menu and reject completion | + +Any other keypresses result in the completion being accepted. ##### Signature-help Popup diff --git a/book/src/remapping.md b/book/src/remapping.md index 863c5557..e3efdf16 100644 --- a/book/src/remapping.md +++ b/book/src/remapping.md @@ -75,5 +75,20 @@ Ctrl, Shift and Alt modifiers are encoded respectively with the prefixes Keys can be disabled by binding them to the `no_op` command. -A list of commands is available in the [Keymap](https://docs.helix-editor.com/keymap.html) documentation - and in the source code at [`helix-term/src/commands.rs`](https://github.com/helix-editor/helix/blob/master/helix-term/src/commands.rs) at the invocation of `static_commands!` macro and the `TypableCommandList`. +## Commands + +There are three kinds of commands that can be used in keymaps: + +* Static commands: commands like `move_char_right` which are usually bound to + keys and used for movement and editing. A list of static commands is + available in the [Keymap](./keymap.html) documentation and in the source code + in [`helix-term/src/commands.rs`](https://github.com/helix-editor/helix/blob/master/helix-term/src/commands.rs) + at the invocation of `static_commands!` macro and the `TypableCommandList`. +* Typable commands: commands that can be executed from command mode (`:`), for + example `:write!`. See the [Commands](./commands.html) documentation for a + list of available typeable commands. +* Macros: sequences of keys that are executed in order. These keybindings + start with `@` and then list any number of keys to be executed. For example + `@miw` can be used to select the surrounding word. For now, macro keybindings + are not allowed in keybinding sequences due to limitations in the way that + command sequences are executed. diff --git a/helix-core/src/movement.rs b/helix-core/src/movement.rs index f5c2b2ed..e446d8cc 100644 --- a/helix-core/src/movement.rs +++ b/helix-core/src/movement.rs @@ -197,13 +197,31 @@ pub fn move_prev_long_word_end(slice: RopeSlice, range: Range, count: usize) -> word_move(slice, range, count, WordMotionTarget::PrevLongWordEnd) } +pub fn move_next_sub_word_start(slice: RopeSlice, range: Range, count: usize) -> Range { + word_move(slice, range, count, WordMotionTarget::NextSubWordStart) +} + +pub fn move_next_sub_word_end(slice: RopeSlice, range: Range, count: usize) -> Range { + word_move(slice, range, count, WordMotionTarget::NextSubWordEnd) +} + +pub fn move_prev_sub_word_start(slice: RopeSlice, range: Range, count: usize) -> Range { + word_move(slice, range, count, WordMotionTarget::PrevSubWordStart) +} + +pub fn move_prev_sub_word_end(slice: RopeSlice, range: Range, count: usize) -> Range { + word_move(slice, range, count, WordMotionTarget::PrevSubWordEnd) +} + fn word_move(slice: RopeSlice, range: Range, count: usize, target: WordMotionTarget) -> Range { let is_prev = matches!( target, WordMotionTarget::PrevWordStart | WordMotionTarget::PrevLongWordStart + | WordMotionTarget::PrevSubWordStart | WordMotionTarget::PrevWordEnd | WordMotionTarget::PrevLongWordEnd + | WordMotionTarget::PrevSubWordEnd ); // Special-case early-out. @@ -383,6 +401,12 @@ pub enum WordMotionTarget { NextLongWordEnd, PrevLongWordStart, PrevLongWordEnd, + // A sub word is similar to a regular word, except it is also delimited by + // underscores and transitions from lowercase to uppercase. + NextSubWordStart, + NextSubWordEnd, + PrevSubWordStart, + PrevSubWordEnd, } pub trait CharHelpers { @@ -398,8 +422,10 @@ impl CharHelpers for Chars<'_> { target, WordMotionTarget::PrevWordStart | WordMotionTarget::PrevLongWordStart + | WordMotionTarget::PrevSubWordStart | WordMotionTarget::PrevWordEnd | WordMotionTarget::PrevLongWordEnd + | WordMotionTarget::PrevSubWordEnd ); // Reverse the iterator if needed for the motion direction. @@ -476,6 +502,25 @@ fn is_long_word_boundary(a: char, b: char) -> bool { } } +fn is_sub_word_boundary(a: char, b: char, dir: Direction) -> bool { + match (categorize_char(a), categorize_char(b)) { + (CharCategory::Word, CharCategory::Word) => { + if (a == '_') != (b == '_') { + return true; + } + + // Subword boundaries are directional: in 'fooBar', there is a + // boundary between 'o' and 'B', but not between 'B' and 'a'. + match dir { + Direction::Forward => a.is_lowercase() && b.is_uppercase(), + Direction::Backward => a.is_uppercase() && b.is_lowercase(), + } + } + (a, b) if a != b => true, + _ => false, + } +} + fn reached_target(target: WordMotionTarget, prev_ch: char, next_ch: char) -> bool { match target { WordMotionTarget::NextWordStart | WordMotionTarget::PrevWordEnd => { @@ -494,6 +539,22 @@ fn reached_target(target: WordMotionTarget, prev_ch: char, next_ch: char) -> boo is_long_word_boundary(prev_ch, next_ch) && (!prev_ch.is_whitespace() || char_is_line_ending(next_ch)) } + WordMotionTarget::NextSubWordStart => { + is_sub_word_boundary(prev_ch, next_ch, Direction::Forward) + && (char_is_line_ending(next_ch) || !(next_ch.is_whitespace() || next_ch == '_')) + } + WordMotionTarget::PrevSubWordEnd => { + is_sub_word_boundary(prev_ch, next_ch, Direction::Backward) + && (char_is_line_ending(next_ch) || !(next_ch.is_whitespace() || next_ch == '_')) + } + WordMotionTarget::NextSubWordEnd => { + is_sub_word_boundary(prev_ch, next_ch, Direction::Forward) + && (!(prev_ch.is_whitespace() || prev_ch == '_') || char_is_line_ending(next_ch)) + } + WordMotionTarget::PrevSubWordStart => { + is_sub_word_boundary(prev_ch, next_ch, Direction::Backward) + && (!(prev_ch.is_whitespace() || prev_ch == '_') || char_is_line_ending(next_ch)) + } } } @@ -1012,6 +1073,178 @@ mod test { } } + #[test] + fn test_behaviour_when_moving_to_start_of_next_sub_words() { + let tests = [ + ( + "NextSubwordStart", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 11)), + ], + ), + ( + "next_subword_start", + vec![ + (1, Range::new(0, 0), Range::new(0, 5)), + (1, Range::new(4, 4), Range::new(5, 13)), + ], + ), + ( + "Next_Subword_Start", + vec![ + (1, Range::new(0, 0), Range::new(0, 5)), + (1, Range::new(4, 4), Range::new(5, 13)), + ], + ), + ( + "NEXT_SUBWORD_START", + vec![ + (1, Range::new(0, 0), Range::new(0, 5)), + (1, Range::new(4, 4), Range::new(5, 13)), + ], + ), + ( + "next subword start", + vec![ + (1, Range::new(0, 0), Range::new(0, 5)), + (1, Range::new(4, 4), Range::new(5, 13)), + ], + ), + ( + "Next Subword Start", + vec![ + (1, Range::new(0, 0), Range::new(0, 5)), + (1, Range::new(4, 4), Range::new(5, 13)), + ], + ), + ( + "NEXT SUBWORD START", + vec![ + (1, Range::new(0, 0), Range::new(0, 5)), + (1, Range::new(4, 4), Range::new(5, 13)), + ], + ), + ( + "next__subword__start", + vec![ + (1, Range::new(0, 0), Range::new(0, 6)), + (1, Range::new(4, 4), Range::new(4, 6)), + (1, Range::new(5, 5), Range::new(6, 15)), + ], + ), + ( + "Next__Subword__Start", + vec![ + (1, Range::new(0, 0), Range::new(0, 6)), + (1, Range::new(4, 4), Range::new(4, 6)), + (1, Range::new(5, 5), Range::new(6, 15)), + ], + ), + ( + "NEXT__SUBWORD__START", + vec![ + (1, Range::new(0, 0), Range::new(0, 6)), + (1, Range::new(4, 4), Range::new(4, 6)), + (1, Range::new(5, 5), Range::new(6, 15)), + ], + ), + ]; + + for (sample, scenario) in tests { + for (count, begin, expected_end) in scenario.into_iter() { + let range = move_next_sub_word_start(Rope::from(sample).slice(..), begin, count); + assert_eq!(range, expected_end, "Case failed: [{}]", sample); + } + } + } + + #[test] + fn test_behaviour_when_moving_to_end_of_next_sub_words() { + let tests = [ + ( + "NextSubwordEnd", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 11)), + ], + ), + ( + "next subword end", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 12)), + ], + ), + ( + "Next Subword End", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 12)), + ], + ), + ( + "NEXT SUBWORD END", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 12)), + ], + ), + ( + "next_subword_end", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 12)), + ], + ), + ( + "Next_Subword_End", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 12)), + ], + ), + ( + "NEXT_SUBWORD_END", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 12)), + ], + ), + ( + "next__subword__end", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 13)), + (1, Range::new(5, 5), Range::new(5, 13)), + ], + ), + ( + "Next__Subword__End", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 13)), + (1, Range::new(5, 5), Range::new(5, 13)), + ], + ), + ( + "NEXT__SUBWORD__END", + vec![ + (1, Range::new(0, 0), Range::new(0, 4)), + (1, Range::new(4, 4), Range::new(4, 13)), + (1, Range::new(5, 5), Range::new(5, 13)), + ], + ), + ]; + + for (sample, scenario) in tests { + for (count, begin, expected_end) in scenario.into_iter() { + let range = move_next_sub_word_end(Rope::from(sample).slice(..), begin, count); + assert_eq!(range, expected_end, "Case failed: [{}]", sample); + } + } + } + #[test] fn test_behaviour_when_moving_to_start_of_next_long_words() { let tests = [ @@ -1181,6 +1414,92 @@ mod test { } } + #[test] + fn test_behaviour_when_moving_to_start_of_previous_sub_words() { + let tests = [ + ( + "PrevSubwordEnd", + vec![ + (1, Range::new(13, 13), Range::new(14, 11)), + (1, Range::new(11, 11), Range::new(11, 4)), + ], + ), + ( + "prev subword end", + vec![ + (1, Range::new(15, 15), Range::new(16, 13)), + (1, Range::new(12, 12), Range::new(13, 5)), + ], + ), + ( + "Prev Subword End", + vec![ + (1, Range::new(15, 15), Range::new(16, 13)), + (1, Range::new(12, 12), Range::new(13, 5)), + ], + ), + ( + "PREV SUBWORD END", + vec![ + (1, Range::new(15, 15), Range::new(16, 13)), + (1, Range::new(12, 12), Range::new(13, 5)), + ], + ), + ( + "prev_subword_end", + vec![ + (1, Range::new(15, 15), Range::new(16, 13)), + (1, Range::new(12, 12), Range::new(13, 5)), + ], + ), + ( + "Prev_Subword_End", + vec![ + (1, Range::new(15, 15), Range::new(16, 13)), + (1, Range::new(12, 12), Range::new(13, 5)), + ], + ), + ( + "PREV_SUBWORD_END", + vec![ + (1, Range::new(15, 15), Range::new(16, 13)), + (1, Range::new(12, 12), Range::new(13, 5)), + ], + ), + ( + "prev__subword__end", + vec![ + (1, Range::new(17, 17), Range::new(18, 15)), + (1, Range::new(13, 13), Range::new(14, 6)), + (1, Range::new(14, 14), Range::new(15, 6)), + ], + ), + ( + "Prev__Subword__End", + vec![ + (1, Range::new(17, 17), Range::new(18, 15)), + (1, Range::new(13, 13), Range::new(14, 6)), + (1, Range::new(14, 14), Range::new(15, 6)), + ], + ), + ( + "PREV__SUBWORD__END", + vec![ + (1, Range::new(17, 17), Range::new(18, 15)), + (1, Range::new(13, 13), Range::new(14, 6)), + (1, Range::new(14, 14), Range::new(15, 6)), + ], + ), + ]; + + for (sample, scenario) in tests { + for (count, begin, expected_end) in scenario.into_iter() { + let range = move_prev_sub_word_start(Rope::from(sample).slice(..), begin, count); + assert_eq!(range, expected_end, "Case failed: [{}]", sample); + } + } + } + #[test] fn test_behaviour_when_moving_to_start_of_previous_long_words() { let tests = [ @@ -1444,6 +1763,92 @@ mod test { } } + #[test] + fn test_behaviour_when_moving_to_end_of_previous_sub_words() { + let tests = [ + ( + "PrevSubwordEnd", + vec![ + (1, Range::new(13, 13), Range::new(14, 11)), + (1, Range::new(11, 11), Range::new(11, 4)), + ], + ), + ( + "prev subword end", + vec![ + (1, Range::new(15, 15), Range::new(16, 12)), + (1, Range::new(12, 12), Range::new(12, 4)), + ], + ), + ( + "Prev Subword End", + vec![ + (1, Range::new(15, 15), Range::new(16, 12)), + (1, Range::new(12, 12), Range::new(12, 4)), + ], + ), + ( + "PREV SUBWORD END", + vec![ + (1, Range::new(15, 15), Range::new(16, 12)), + (1, Range::new(12, 12), Range::new(12, 4)), + ], + ), + ( + "prev_subword_end", + vec![ + (1, Range::new(15, 15), Range::new(16, 12)), + (1, Range::new(12, 12), Range::new(12, 4)), + ], + ), + ( + "Prev_Subword_End", + vec![ + (1, Range::new(15, 15), Range::new(16, 12)), + (1, Range::new(12, 12), Range::new(12, 4)), + ], + ), + ( + "PREV_SUBWORD_END", + vec![ + (1, Range::new(15, 15), Range::new(16, 12)), + (1, Range::new(12, 12), Range::new(12, 4)), + ], + ), + ( + "prev__subword__end", + vec![ + (1, Range::new(17, 17), Range::new(18, 13)), + (1, Range::new(13, 13), Range::new(13, 4)), + (1, Range::new(14, 14), Range::new(15, 13)), + ], + ), + ( + "Prev__Subword__End", + vec![ + (1, Range::new(17, 17), Range::new(18, 13)), + (1, Range::new(13, 13), Range::new(13, 4)), + (1, Range::new(14, 14), Range::new(15, 13)), + ], + ), + ( + "PREV__SUBWORD__END", + vec![ + (1, Range::new(17, 17), Range::new(18, 13)), + (1, Range::new(13, 13), Range::new(13, 4)), + (1, Range::new(14, 14), Range::new(15, 13)), + ], + ), + ]; + + for (sample, scenario) in tests { + for (count, begin, expected_end) in scenario.into_iter() { + let range = move_prev_sub_word_end(Rope::from(sample).slice(..), begin, count); + assert_eq!(range, expected_end, "Case failed: [{}]", sample); + } + } + } + #[test] fn test_behaviour_when_moving_to_end_of_next_long_words() { let tests = [ diff --git a/helix-core/src/selection.rs b/helix-core/src/selection.rs index eff1fcd7..a382a718 100644 --- a/helix-core/src/selection.rs +++ b/helix-core/src/selection.rs @@ -184,16 +184,16 @@ impl Range { let positions_to_map = match self.anchor.cmp(&self.head) { Ordering::Equal => [ - (&mut self.anchor, Assoc::After), - (&mut self.head, Assoc::After), + (&mut self.anchor, Assoc::AfterSticky), + (&mut self.head, Assoc::AfterSticky), ], Ordering::Less => [ - (&mut self.anchor, Assoc::After), - (&mut self.head, Assoc::Before), + (&mut self.anchor, Assoc::AfterSticky), + (&mut self.head, Assoc::BeforeSticky), ], Ordering::Greater => [ - (&mut self.head, Assoc::After), - (&mut self.anchor, Assoc::Before), + (&mut self.head, Assoc::AfterSticky), + (&mut self.anchor, Assoc::BeforeSticky), ], }; changes.update_positions(positions_to_map.into_iter()); @@ -482,16 +482,16 @@ impl Selection { range.old_visual_position = None; match range.anchor.cmp(&range.head) { Ordering::Equal => [ - (&mut range.anchor, Assoc::After), - (&mut range.head, Assoc::After), + (&mut range.anchor, Assoc::AfterSticky), + (&mut range.head, Assoc::AfterSticky), ], Ordering::Less => [ - (&mut range.anchor, Assoc::After), - (&mut range.head, Assoc::Before), + (&mut range.anchor, Assoc::AfterSticky), + (&mut range.head, Assoc::BeforeSticky), ], Ordering::Greater => [ - (&mut range.head, Assoc::After), - (&mut range.anchor, Assoc::Before), + (&mut range.head, Assoc::AfterSticky), + (&mut range.anchor, Assoc::BeforeSticky), ], } }); diff --git a/helix-core/src/transaction.rs b/helix-core/src/transaction.rs index f24f2094..c5c94b75 100644 --- a/helix-core/src/transaction.rs +++ b/helix-core/src/transaction.rs @@ -29,6 +29,12 @@ pub enum Assoc { /// Acts like `Before` if a word character is inserted /// before the position, otherwise acts like `After` BeforeWord, + /// Acts like `Before` but if the position is within an exact replacement + /// (exact size) the offset to the start of the replacement is kept + BeforeSticky, + /// Acts like `After` but if the position is within an exact replacement + /// (exact size) the offset to the start of the replacement is kept + AfterSticky, } impl Assoc { @@ -40,13 +46,17 @@ impl Assoc { fn insert_offset(self, s: &str) -> usize { let chars = s.chars().count(); match self { - Assoc::After => chars, + Assoc::After | Assoc::AfterSticky => chars, Assoc::AfterWord => s.chars().take_while(|&c| char_is_word(c)).count(), // return position before inserted text - Assoc::Before => 0, + Assoc::Before | Assoc::BeforeSticky => 0, Assoc::BeforeWord => chars - s.chars().rev().take_while(|&c| char_is_word(c)).count(), } } + + pub fn sticky(self) -> bool { + matches!(self, Assoc::BeforeSticky | Assoc::AfterSticky) + } } #[derive(Debug, Default, Clone, PartialEq, Eq)] @@ -456,8 +466,14 @@ impl ChangeSet { if pos == old_pos && assoc.stay_at_gaps() { new_pos } else { - // place to end of insert - new_pos + assoc.insert_offset(s) + let ins = assoc.insert_offset(s); + // if the deleted and inserted text have the exact same size + // keep the relative offset into the new text + if *len == ins && assoc.sticky() { + new_pos + (pos - old_pos) + } else { + new_pos + assoc.insert_offset(s) + } } }), i diff --git a/helix-loader/Cargo.toml b/helix-loader/Cargo.toml index d15d87f9..6b63f03e 100644 --- a/helix-loader/Cargo.toml +++ b/helix-loader/Cargo.toml @@ -30,8 +30,8 @@ log = "0.4" # cloning/compiling tree-sitter grammars cc = { version = "1" } threadpool = { version = "1.0" } -tempfile = "3.10.1" -dunce = "1.0.4" +tempfile = "3.11.0" +dunce = "1.0.5" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] libloading = "0.8" diff --git a/helix-loader/src/lib.rs b/helix-loader/src/lib.rs index badb9bd6..f36c76c4 100644 --- a/helix-loader/src/lib.rs +++ b/helix-loader/src/lib.rs @@ -225,13 +225,16 @@ pub fn merge_toml_values(left: toml::Value, right: toml::Value, merge_depth: usi /// Used as a ceiling dir for LSP root resolution, the filepicker and potentially as a future filewatching root /// /// This function starts searching the FS upward from the CWD -/// and returns the first directory that contains either `.git` or `.helix`. +/// and returns the first directory that contains either `.git`, `.svn` or `.helix`. /// If no workspace was found returns (CWD, true). /// Otherwise (workspace, false) is returned pub fn find_workspace() -> (PathBuf, bool) { let current_dir = current_working_dir(); for ancestor in current_dir.ancestors() { - if ancestor.join(".git").exists() || ancestor.join(".helix").exists() { + if ancestor.join(".git").exists() + || ancestor.join(".svn").exists() + || ancestor.join(".helix").exists() + { return (ancestor.to_owned(), false); } } diff --git a/helix-lsp-types/Cargo.toml b/helix-lsp-types/Cargo.toml index 18cc8b14..1abd7bca 100644 --- a/helix-lsp-types/Cargo.toml +++ b/helix-lsp-types/Cargo.toml @@ -21,9 +21,9 @@ keywords = ["language", "server", "lsp", "vscode", "lsif"] license = "MIT" [dependencies] -bitflags = "1.0.1" +bitflags = "2.6.0" serde = { version = "1.0.34", features = ["derive"] } -serde_json = "1.0.50" +serde_json = "1.0.122" serde_repr = "0.1" url = {version = "2.0.0", features = ["serde"]} diff --git a/helix-lsp-types/src/lib.rs b/helix-lsp-types/src/lib.rs index 68d58704..3ea1c0cd 100644 --- a/helix-lsp-types/src/lib.rs +++ b/helix-lsp-types/src/lib.rs @@ -2495,6 +2495,7 @@ pub struct RelativePattern { pub type Pattern = String; bitflags! { +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct WatchKind: u8 { /// Interested in create events. const Create = 1; diff --git a/helix-stdx/Cargo.toml b/helix-stdx/Cargo.toml index 5aef6590..8d8f6b84 100644 --- a/helix-stdx/Cargo.toml +++ b/helix-stdx/Cargo.toml @@ -20,10 +20,10 @@ regex-cursor = "0.1.4" bitflags = "2.6" [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.52", features = ["Win32_Security", "Win32_Security_Authorization", "Win32_System_Threading"] } +windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_Security", "Win32_Security_Authorization", "Win32_Storage_FileSystem", "Win32_System_Threading"] } [target.'cfg(unix)'.dependencies] rustix = { version = "0.38", features = ["fs"] } [dev-dependencies] -tempfile = "3.10" +tempfile = "3.11" diff --git a/helix-stdx/src/faccess.rs b/helix-stdx/src/faccess.rs index 6be6bdd8..e4c3daf2 100644 --- a/helix-stdx/src/faccess.rs +++ b/helix-stdx/src/faccess.rs @@ -85,7 +85,7 @@ mod imp { #[cfg(windows)] mod imp { - use windows_sys::Win32::Foundation::{CloseHandle, LocalFree, ERROR_SUCCESS, HANDLE, PSID}; + use windows_sys::Win32::Foundation::{CloseHandle, LocalFree, ERROR_SUCCESS, HANDLE}; use windows_sys::Win32::Security::Authorization::{ GetNamedSecurityInfoW, SetNamedSecurityInfoW, SE_FILE_OBJECT, }; @@ -95,7 +95,7 @@ mod imp { SecurityImpersonation, ACCESS_ALLOWED_CALLBACK_ACE, ACL, ACL_SIZE_INFORMATION, DACL_SECURITY_INFORMATION, GENERIC_MAPPING, GROUP_SECURITY_INFORMATION, INHERITED_ACE, LABEL_SECURITY_INFORMATION, OBJECT_SECURITY_INFORMATION, OWNER_SECURITY_INFORMATION, - PRIVILEGE_SET, PROTECTED_DACL_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, + PRIVILEGE_SET, PROTECTED_DACL_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, PSID, SID_IDENTIFIER_AUTHORITY, TOKEN_DUPLICATE, TOKEN_QUERY, }; use windows_sys::Win32::Storage::FileSystem::{ @@ -419,7 +419,7 @@ mod imp { pub fn hardlink_count(p: &Path) -> std::io::Result { let file = std::fs::File::open(p)?; - let handle = file.as_raw_handle() as isize; + let handle = file.as_raw_handle(); let mut info: BY_HANDLE_FILE_INFORMATION = unsafe { std::mem::zeroed() }; if unsafe { GetFileInformationByHandle(handle, &mut info) } == 0 { diff --git a/helix-term/Cargo.toml b/helix-term/Cargo.toml index 4887b0a2..6e27b00f 100644 --- a/helix-term/Cargo.toml +++ b/helix-term/Cargo.toml @@ -37,7 +37,7 @@ once_cell = "1.19" tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] } tui = { path = "../helix-tui", package = "helix-tui", default-features = false, features = ["crossterm"] } -crossterm = { version = "0.27", features = ["event-stream"] } +crossterm = { version = "0.28", features = ["event-stream"] } signal-hook = "0.3" tokio-stream = "0.1" futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false } @@ -77,7 +77,7 @@ signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] } libc = "0.2.155" [target.'cfg(target_os = "macos")'.dependencies] -crossterm = { version = "0.27", features = ["event-stream", "use-dev-tty"] } +crossterm = { version = "0.28", features = ["event-stream", "use-dev-tty", "libc"] } [build-dependencies] helix-loader = { path = "../helix-loader" } @@ -85,5 +85,5 @@ helix-loader = { path = "../helix-loader" } [dev-dependencies] smallvec = "1.13" indoc = "2.0.5" -tempfile = "3.10.1" +tempfile = "3.11.0" same-file = "1.0.1" diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 4e97f36b..9ccd8ea1 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -176,9 +176,16 @@ where use helix_view::{align_view, Align}; -/// A MappableCommand is either a static command like "jump_view_up" or a Typable command like -/// :format. It causes a side-effect on the state (usually by creating and applying a transaction). -/// Both of these types of commands can be mapped with keybindings in the config.toml. +/// MappableCommands are commands that can be bound to keys, executable in +/// normal, insert or select mode. +/// +/// There are three kinds: +/// +/// * Static: commands usually bound to keys and used for editing, movement, +/// etc., for example `move_char_left`. +/// * Typable: commands executable from command mode, prefixed with a `:`, +/// for example `:write!`. +/// * Macro: a sequence of keys to execute, for example `@miw`. #[derive(Clone)] pub enum MappableCommand { Typable { @@ -191,6 +198,10 @@ pub enum MappableCommand { fun: fn(cx: &mut Context), doc: &'static str, }, + Macro { + name: String, + keys: Vec, + }, } macro_rules! static_commands { @@ -227,6 +238,23 @@ impl MappableCommand { } } Self::Static { fun, .. } => (fun)(cx), + Self::Macro { keys, .. } => { + // Protect against recursive macros. + if cx.editor.macro_replaying.contains(&'@') { + cx.editor.set_error( + "Cannot execute macro because the [@] register is already playing a macro", + ); + return; + } + cx.editor.macro_replaying.push('@'); + let keys = keys.clone(); + cx.callback.push(Box::new(move |compositor, cx| { + for key in keys.into_iter() { + compositor.handle_event(&compositor::Event::Key(key), cx); + } + cx.editor.macro_replaying.pop(); + })); + } } } @@ -234,6 +262,7 @@ impl MappableCommand { match &self { Self::Typable { name, .. } => name, Self::Static { name, .. } => name, + Self::Macro { name, .. } => name, } } @@ -241,6 +270,7 @@ impl MappableCommand { match &self { Self::Typable { doc, .. } => doc, Self::Static { doc, .. } => doc, + Self::Macro { name, .. } => name, } } @@ -269,6 +299,10 @@ impl MappableCommand { move_prev_long_word_start, "Move to start of previous long word", move_next_long_word_end, "Move to end of next long word", move_prev_long_word_end, "Move to end of previous long word", + move_next_sub_word_start, "Move to start of next sub word", + move_prev_sub_word_start, "Move to start of previous sub word", + move_next_sub_word_end, "Move to end of next sub word", + move_prev_sub_word_end, "Move to end of previous sub word", move_parent_node_end, "Move to end of the parent node", move_parent_node_start, "Move to beginning of the parent node", extend_next_word_start, "Extend to start of next word", @@ -279,6 +313,10 @@ impl MappableCommand { extend_prev_long_word_start, "Extend to start of previous long word", extend_next_long_word_end, "Extend to end of next long word", extend_prev_long_word_end, "Extend to end of prev long word", + extend_next_sub_word_start, "Extend to start of next sub word", + extend_prev_sub_word_start, "Extend to start of previous sub word", + extend_next_sub_word_end, "Extend to end of next sub word", + extend_prev_sub_word_end, "Extend to end of prev sub word", extend_parent_node_end, "Extend to end of the parent node", extend_parent_node_start, "Extend to beginning of the parent node", find_till_char, "Move till next occurrence of char", @@ -543,6 +581,11 @@ impl fmt::Debug for MappableCommand { .field(name) .field(args) .finish(), + MappableCommand::Macro { name, keys, .. } => f + .debug_tuple("MappableCommand") + .field(name) + .field(keys) + .finish(), } } } @@ -573,6 +616,11 @@ impl std::str::FromStr for MappableCommand { args, }) .ok_or_else(|| anyhow!("No TypableCommand named '{}'", s)) + } else if let Some(suffix) = s.strip_prefix('@') { + helix_view::input::parse_macro(suffix).map(|keys| Self::Macro { + name: s.to_string(), + keys, + }) } else { MappableCommand::STATIC_COMMAND_LIST .iter() @@ -1126,6 +1174,22 @@ fn move_next_long_word_end(cx: &mut Context) { move_word_impl(cx, movement::move_next_long_word_end) } +fn move_next_sub_word_start(cx: &mut Context) { + move_word_impl(cx, movement::move_next_sub_word_start) +} + +fn move_prev_sub_word_start(cx: &mut Context) { + move_word_impl(cx, movement::move_prev_sub_word_start) +} + +fn move_prev_sub_word_end(cx: &mut Context) { + move_word_impl(cx, movement::move_prev_sub_word_end) +} + +fn move_next_sub_word_end(cx: &mut Context) { + move_word_impl(cx, movement::move_next_sub_word_end) +} + fn goto_para_impl(cx: &mut Context, move_fn: F) where F: Fn(RopeSlice, Range, usize, Movement) -> Range + 'static, @@ -1362,6 +1426,22 @@ fn extend_next_long_word_end(cx: &mut Context) { extend_word_impl(cx, movement::move_next_long_word_end) } +fn extend_next_sub_word_start(cx: &mut Context) { + extend_word_impl(cx, movement::move_next_sub_word_start) +} + +fn extend_prev_sub_word_start(cx: &mut Context) { + extend_word_impl(cx, movement::move_prev_sub_word_start) +} + +fn extend_prev_sub_word_end(cx: &mut Context) { + extend_word_impl(cx, movement::move_prev_sub_word_end) +} + +fn extend_next_sub_word_end(cx: &mut Context) { + extend_word_impl(cx, movement::move_next_sub_word_end) +} + /// Separate branch to find_char designed only for `` char. // // This is necessary because the one document can have different line endings inside. And we @@ -3145,6 +3225,9 @@ pub fn command_palette(cx: &mut Context) { ui::PickerColumn::new("name", |item, _| match item { MappableCommand::Typable { name, .. } => format!(":{name}").into(), MappableCommand::Static { name, .. } => (*name).into(), + MappableCommand::Macro { .. } => { + unreachable!("macros aren't included in the command palette") + } }), ui::PickerColumn::new( "bindings", diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index cd40e053..7ad0369f 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -2300,7 +2300,7 @@ fn run_shell_command( move |editor: &mut Editor, compositor: &mut Compositor| { if !output.is_empty() { let contents = ui::Markdown::new( - format!("```sh\n{}\n```", output), + format!("```sh\n{}\n```", output.trim_end()), editor.syn_loader.clone(), ); let popup = Popup::new("shell", contents).position(Some( diff --git a/helix-term/src/keymap.rs b/helix-term/src/keymap.rs index 975274ed..020ecaf4 100644 --- a/helix-term/src/keymap.rs +++ b/helix-term/src/keymap.rs @@ -177,6 +177,19 @@ impl<'de> serde::de::Visitor<'de> for KeyTrieVisitor { .map_err(serde::de::Error::custom)?, ) } + + // Prevent macro keybindings from being used in command sequences. + // This is meant to be a temporary restriction pending a larger + // refactor of how command sequences are executed. + if commands + .iter() + .any(|cmd| matches!(cmd, MappableCommand::Macro { .. })) + { + return Err(serde::de::Error::custom( + "macro keybindings may not be used in command sequences", + )); + } + Ok(KeyTrie::Sequence(commands)) } @@ -199,6 +212,7 @@ impl KeyTrie { // recursively visit all nodes in keymap fn map_node(cmd_map: &mut ReverseKeymap, node: &KeyTrie, keys: &mut Vec) { match node { + KeyTrie::MappableCommand(MappableCommand::Macro { .. }) => {} KeyTrie::MappableCommand(cmd) => { let name = cmd.name(); if name != "no_op" { diff --git a/helix-term/src/ui/picker/query.rs b/helix-term/src/ui/picker/query.rs index e433a11f..005ddee4 100644 --- a/helix-term/src/ui/picker/query.rs +++ b/helix-term/src/ui/picker/query.rs @@ -58,11 +58,16 @@ impl PickerQuery { () => { let key = field.take().unwrap_or(primary_field); + // Trims one space from the end, enabling leading and trailing + // spaces in search patterns, while also retaining spaces as separators + // between column filters. + let pat = text.strip_suffix(' ').unwrap_or(&text); + if let Some(pattern) = fields.get_mut(key) { pattern.push(' '); - pattern.push_str(text.trim()); + pattern.push_str(pat); } else { - fields.insert(key.clone(), text.trim().to_string()); + fields.insert(key.clone(), pat.to_string()); } text.clear(); }; diff --git a/helix-tui/Cargo.toml b/helix-tui/Cargo.toml index 2bdb494f..ac56c724 100644 --- a/helix-tui/Cargo.toml +++ b/helix-tui/Cargo.toml @@ -21,7 +21,7 @@ helix-core = { path = "../helix-core" } bitflags = "2.6" cassowary = "0.3" unicode-segmentation = "1.11" -crossterm = { version = "0.27", optional = true } +crossterm = { version = "0.28", optional = true } termini = "1.0" serde = { version = "1", "optional" = true, features = ["derive"]} once_cell = "1.19" diff --git a/helix-tui/src/backend/crossterm.rs b/helix-tui/src/backend/crossterm.rs index 0e60e386..e8947ee0 100644 --- a/helix-tui/src/backend/crossterm.rs +++ b/helix-tui/src/backend/crossterm.rs @@ -8,8 +8,8 @@ use crossterm::{ }, execute, queue, style::{ - Attribute as CAttribute, Color as CColor, Print, SetAttribute, SetBackgroundColor, - SetForegroundColor, + Attribute as CAttribute, Color as CColor, Colors, Print, SetAttribute, SetBackgroundColor, + SetColors, SetForegroundColor, }, terminal::{self, Clear, ClearType}, Command, @@ -260,14 +260,12 @@ where diff.queue(&mut self.buffer)?; modifier = cell.modifier; } - if cell.fg != fg { - let color = CColor::from(cell.fg); - queue!(self.buffer, SetForegroundColor(color))?; + if cell.fg != fg || cell.bg != bg { + queue!( + self.buffer, + SetColors(Colors::new(cell.fg.into(), cell.bg.into())) + )?; fg = cell.fg; - } - if cell.bg != bg { - let color = CColor::from(cell.bg); - queue!(self.buffer, SetBackgroundColor(color))?; bg = cell.bg; } diff --git a/helix-vcs/Cargo.toml b/helix-vcs/Cargo.toml index 53d560c9..086d3701 100644 --- a/helix-vcs/Cargo.toml +++ b/helix-vcs/Cargo.toml @@ -29,4 +29,4 @@ log = "0.4" git = ["gix"] [dev-dependencies] -tempfile = "3.10" +tempfile = "3.11" diff --git a/helix-vcs/src/diff.rs b/helix-vcs/src/diff.rs index 634b179b..e49e171d 100644 --- a/helix-vcs/src/diff.rs +++ b/helix-vcs/src/diff.rs @@ -5,7 +5,7 @@ use std::sync::Arc; use helix_core::Rope; use helix_event::RenderLockGuard; use imara_diff::Algorithm; -use parking_lot::{Mutex, MutexGuard}; +use parking_lot::{RwLock, RwLockReadGuard}; use tokio::sync::mpsc::{unbounded_channel, UnboundedSender}; use tokio::task::JoinHandle; use tokio::time::Instant; @@ -37,7 +37,7 @@ struct DiffInner { #[derive(Clone, Debug)] pub struct DiffHandle { channel: UnboundedSender, - diff: Arc>, + diff: Arc>, inverted: bool, } @@ -48,7 +48,7 @@ impl DiffHandle { fn new_with_handle(diff_base: Rope, doc: Rope) -> (DiffHandle, JoinHandle<()>) { let (sender, receiver) = unbounded_channel(); - let diff: Arc> = Arc::default(); + let diff: Arc> = Arc::default(); let worker = DiffWorker { channel: receiver, diff: diff.clone(), @@ -70,7 +70,7 @@ impl DiffHandle { pub fn load(&self) -> Diff { Diff { - diff: self.diff.lock(), + diff: self.diff.read(), inverted: self.inverted, } } @@ -164,7 +164,7 @@ impl Hunk { /// non-overlapping order #[derive(Debug)] pub struct Diff<'a> { - diff: MutexGuard<'a, DiffInner>, + diff: RwLockReadGuard<'a, DiffInner>, inverted: bool, } diff --git a/helix-vcs/src/diff/worker.rs b/helix-vcs/src/diff/worker.rs index 3a9b6462..578d8b8e 100644 --- a/helix-vcs/src/diff/worker.rs +++ b/helix-vcs/src/diff/worker.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use helix_core::{Rope, RopeSlice}; use imara_diff::intern::InternedInput; -use parking_lot::Mutex; +use parking_lot::RwLock; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::Notify; use tokio::time::{timeout, timeout_at, Duration}; @@ -21,7 +21,7 @@ mod test; pub(super) struct DiffWorker { pub channel: UnboundedReceiver, - pub diff: Arc>, + pub diff: Arc>, pub new_hunks: Vec, pub diff_finished_notify: Arc, } @@ -73,7 +73,7 @@ impl DiffWorker { /// `self.new_hunks` is always empty after this function runs. /// To improve performance this function tries to reuse the allocation of the old diff previously stored in `self.line_diffs` fn apply_hunks(&mut self, diff_base: Rope, doc: Rope) { - let mut diff = self.diff.lock(); + let mut diff = self.diff.write(); diff.diff_base = diff_base; diff.doc = doc; swap(&mut diff.hunks, &mut self.new_hunks); diff --git a/helix-vcs/src/diff/worker/test.rs b/helix-vcs/src/diff/worker/test.rs index a6cc8900..ab410bd8 100644 --- a/helix-vcs/src/diff/worker/test.rs +++ b/helix-vcs/src/diff/worker/test.rs @@ -12,7 +12,7 @@ impl DiffHandle { // dropping the channel terminates the task drop(self.channel); handle.await.unwrap(); - let diff = diff.lock(); + let diff = diff.read(); Vec::clone(&diff.hunks) } } diff --git a/helix-view/Cargo.toml b/helix-view/Cargo.toml index 81729af0..a01f4295 100644 --- a/helix-view/Cargo.toml +++ b/helix-view/Cargo.toml @@ -26,9 +26,9 @@ helix-vcs = { path = "../helix-vcs" } bitflags = "2.6" anyhow = "1" -crossterm = { version = "0.27", optional = true } +crossterm = { version = "0.28", optional = true } -tempfile = "3.9" +tempfile = "3.11" # Conversion traits once_cell = "1.19" diff --git a/languages.toml b/languages.toml index 2bd57abb..12b0ad04 100644 --- a/languages.toml +++ b/languages.toml @@ -44,6 +44,7 @@ haskell-language-server = { command = "haskell-language-server-wrapper", args = idris2-lsp = { command = "idris2-lsp" } intelephense = { command = "intelephense", args = ["--stdio"] } jdtls = { command = "jdtls" } +jq-lsp = { command = "jq-lsp" } jsonnet-language-server = { command = "jsonnet-language-server", args= ["-t", "--lint"] } julia = { command = "julia", timeout = 60, args = [ "--startup-file=no", "--history-file=no", "--quiet", "-e", "using LanguageServer; runserver()", ] } koka = { command = "koka", args = ["--language-server", "--lsstdio"] } @@ -54,11 +55,13 @@ markdoc-ls = { command = "markdoc-ls", args = ["--stdio"] } markdown-oxide = { command = "markdown-oxide" } marksman = { command = "marksman", args = ["server"] } metals = { command = "metals", config = { "isHttpEnabled" = true, metals = { inlayHints = { typeParameters = {enable = true} , hintsInPatternMatch = {enable = true} } } } } +mesonlsp = { command = "mesonlsp", args = ["--lsp"] } mint = { command = "mint", args = ["ls"] } mojo-lsp = { command = "mojo-lsp-server" } nil = { command = "nil" } nimlangserver = { command = "nimlangserver" } nimlsp = { command = "nimlsp" } +nixd = { command = "nixd" } nls = { command = "nls" } nu-lsp = { command = "nu", args = [ "--lsp" ] } ocamllsp = { command = "ocamllsp" } @@ -93,6 +96,7 @@ taplo = { command = "taplo", args = ["lsp", "stdio"] } templ = { command = "templ", args = ["lsp"] } terraform-ls = { command = "terraform-ls", args = ["serve"] } texlab = { command = "texlab" } +typespec = { command = "tsp-server", args = ["--stdio"] } vala-language-server = { command = "vala-language-server" } vhdl_ls = { command = "vhdl_ls", args = [] } vlang-language-server = { command = "v-analyzer" } @@ -766,6 +770,23 @@ indent = { tab-width = 2, unit = " " } name = "typescript" source = { git = "https://github.com/tree-sitter/tree-sitter-typescript", rev = "b1bf4825d9eaa0f3bdeb1e52f099533328acfbdf", subpath = "typescript" } +[[language]] +name = "typespec" +scope = "source.typespec" +injection-regex = "(tsp|typespec)" +language-id = "typespec" +file-types = ["tsp"] +roots = ["tspconfig.yaml"] +auto-format = true +comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } +language-servers = ["typespec"] +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "typespec" +source = { git = "https://github.com/happenslol/tree-sitter-typespec", rev = "0ee05546d73d8eb64635ed8125de6f35c77759fe" } + [[language]] name = "tsx" scope = "source.tsx" @@ -866,7 +887,7 @@ injection-regex = "nix" file-types = ["nix"] shebangs = [] comment-token = "#" -language-servers = [ "nil" ] +language-servers = [ "nil", "nixd" ] indent = { tab-width = 2, unit = " " } [[grammar]] @@ -948,6 +969,8 @@ file-types = [ "tcshrc", "bashrc_Apple_Terminal", "zshrc_Apple_Terminal", + { glob = "i3/config" }, + { glob = "sway/config" }, { glob = "tmux.conf" }, { glob = ".bash_history" }, { glob = ".bash_login" }, @@ -1855,7 +1878,7 @@ auto-format = true [[grammar]] name = "gleam" -source = { git = "https://github.com/gleam-lang/tree-sitter-gleam", rev = "bcf9c45b56cbe46e9dac5eee0aee75df270000ac" } +source = { git = "https://github.com/gleam-lang/tree-sitter-gleam", rev = "426e67087fd62be5f4533581b5916b2cf010fb5b" } [[language]] name = "ron" @@ -2143,6 +2166,7 @@ injection-regex = "meson" file-types = [{ glob = "meson.build" }, { glob = "meson.options" }, { glob = "meson_options.txt" }] comment-token = "#" indent = { tab-width = 2, unit = " " } +language-servers = ["mesonlsp"] [[grammar]] name = "meson" @@ -3082,7 +3106,7 @@ indent = { tab-width = 4, unit = " " } [[grammar]] name = "just" -source = { git = "https://github.com/IndianBoy42/tree-sitter-just", rev = "379fbe36d1e441bc9414ea050ad0c85c9d6935ea" } +source = { git = "https://github.com/poliorcetics/tree-sitter-just", rev = "f58a8fd869035ac4653081401e6c2030251240ab" } [[language]] name = "gn" @@ -3139,7 +3163,7 @@ language-servers = ["fsharp-ls"] [[grammar]] name = "fsharp" -source = { git = "https://github.com/kaashyapan/tree-sitter-fsharp", rev = "18da392fd9bd5e79f357abcce13f61f3a15e3951" } +source = { git = "https://github.com/ionide/tree-sitter-fsharp", rev = "996ea9982bd4e490029f84682016b6793940113b" } [[language]] name = "t32" @@ -3216,6 +3240,19 @@ text-width = 72 name = "jjdescription" source = { git = "https://github.com/kareigu/tree-sitter-jjdescription", rev = "2ddec6cad07b366aee276a608e1daa2c29d3caf2" } +[[language]] +name = "jq" +scope = "source.jq" +injection-regex = "jq" +file-types = ["jq"] +comment-token = "#" +language-servers = ["jq-lsp"] +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "jq" +source = { git = "https://github.com/flurie/tree-sitter-jq", rev = "13990f530e8e6709b7978503da9bc8701d366791" } + [[grammar]] name = "wren" source = { git = "https://git.sr.ht/~jummit/tree-sitter-wren", rev = "6748694be32f11e7ec6b5faeb1b48ca6156d4e06" } @@ -3726,3 +3763,26 @@ grammar = "typescript" "{" = "}" "(" = ")" '"' = '"' + +[[language]] +name = "gherkin" +scope = "source.feature" +file-types = ["feature"] +comment-token = "#" +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "gherkin" +source = { git = "https://github.com/SamyAB/tree-sitter-gherkin", rev = "43873ee8de16476635b48d52c46f5b6407cb5c09" } + +[[language]] +name = "thrift" +scope = "source.thrift" +file-types = ["thrift"] +comment-token = "//" +block-comment-tokens = { start = "/*", end = "*/" } +indent = { tab-width = 2, unit = " " } + +[[grammar]] +name = "thrift" +source = { git = "https://github.com/tree-sitter-grammars/tree-sitter-thrift" , rev = "68fd0d80943a828d9e6f49c58a74be1e9ca142cf" } \ No newline at end of file diff --git a/runtime/queries/fsharp/highlights.scm b/runtime/queries/fsharp/highlights.scm index 68b70ba7..43905c88 100644 --- a/runtime/queries/fsharp/highlights.scm +++ b/runtime/queries/fsharp/highlights.scm @@ -1,16 +1,176 @@ ;; ---------------------------------------------------------------------------- ;; Literals and comments -[ - (line_comment) - (block_comment) - (block_comment_content) -] @comment +(line_comment) @comment.line +(block_comment) @comment.block + +(xml_doc) @comment.block.documentation + +(const + [ + (_) @constant + (unit) @constant.builtin + ]) + +(primary_constr_args (_) @variable.parameter) + +((identifier_pattern (long_identifier (identifier) @special)) + (#match? @special "^\_.*")) + +((long_identifier + (identifier)+ + . + (identifier) @variable.other.member)) ;; ---------------------------------------------------------------------------- ;; Punctuation +(wildcard_pattern) @string.special + +(type_name type_name: (_) @type) + +[ + (type) + (atomic_type) +] @type + +(member_signature + . + (identifier) @function.method + (curried_spec + (arguments_spec + "*"* @operator + (argument_spec + (argument_name_spec + "?"? @special + name: (_) @variable.parameter))))) + +(union_type_case) @constant + +(rules + (rule + pattern: (_) @constant + block: (_))) + +(identifier_pattern + . + (_) @constant + . + (_) @variable) + +(fsi_directive_decl . (string) @namespace) + +(import_decl . (_) @namespace) +(named_module + name: (_) @namespace) +(namespace + name: (_) @namespace) +(module_defn + . + (_) @namespace) + +(ce_expression + . + (_) @function.macro) + +(field_initializer + field: (_) @variable.other.member) + +(record_fields + (record_field + . + (identifier) @variable.other.member)) + +(dot_expression + base: (_) @namespace + field: (_) @variable.other.member) + +(value_declaration_left . (_) @variable) + +(function_declaration_left + . (_) @function + [ + (argument_patterns) + (argument_patterns (long_identifier (identifier))) + ] @variable.parameter) + +(member_defn + (method_or_prop_defn + [ + (property_or_ident) @function + (property_or_ident + instance: (identifier) @variable.builtin + method: (identifier) @function.method) + ] + args: (_)* @variable.parameter)) + +(application_expression + . + [ + (long_identifier_or_op [ + (long_identifier (identifier)* (identifier) @function) + (identifier) @function + ]) + (typed_expression . (long_identifier_or_op (long_identifier (identifier)* . (identifier) @function.call))) + (dot_expression base: (_) @variable.other.member field: (_) @function) + ] @function) + +((infix_expression + . + (_) + . + (infix_op) @operator + . + (_) @function + ) + (#eq? @operator "|>") + ) + +((infix_expression + . + (_) @function + . + (infix_op) @operator + . + (_) + ) + (#eq? @operator "<|") + ) + +[ + (xint) + (int) + (int16) + (uint16) + (int32) + (uint32) + (int64) + (uint64) + (nativeint) + (unativeint) +] @constant.numeric.integer + +[ + (ieee32) + (ieee64) + (float) + (decimal) +] @constant.numeric.float + +(bool) @constant.builtin.boolean + +([ + (string) + (triple_quoted_string) + (verbatim_string) + (char) +] @string) + +(compiler_directive_decl) @keyword.directive + +(attribute) @attribute + [ "(" ")" @@ -20,31 +180,40 @@ "]" "[|" "|]" + "{|" + "|}" "[<" ">]" ] @punctuation.bracket +(format_string_eval + [ + "{" + "}" + ] @punctuation.special) + [ - "," + "," ";" ] @punctuation.delimiter [ - "|" + "|" "=" ">" "<" "-" "~" + "->" + "<-" + "&&" + "||" + ":>" + ":?>" (infix_op) (prefix_op) - (symbolic_op) ] @operator - - -(attribute) @attribute - [ "if" "then" @@ -53,22 +222,29 @@ "when" "match" "match!" +] @keyword.control.conditional + +[ "and" "or" - "&&" - "||" - "then" -] @keyword.control.conditional + "not" + "upcast" + "downcast" +] @keyword.operator [ "return" "return!" + "yield" + "yield!" ] @keyword.control.return [ "for" "while" -] @keyword.control.return + "downto" + "to" +] @keyword.control.repeat [ @@ -82,115 +258,93 @@ "delegate" "static" "inline" - "internal" "mutable" "override" - "private" - "public" "rec" + "global" + (access_modifier) ] @keyword.storage.modifier [ - "enum" "let" "let!" + "use" + "use!" "member" - "module" - "namespace" +] @keyword.function + +[ + "enum" "type" -] @keyword.storage + "inherit" + "interface" +] @keyword.storage.type + +(try_expression + [ + "try" + "with" + "finally" + ] @keyword.control.exception) + +((identifier) @keyword.control.exception + (#any-of? @keyword.control.exception "failwith" "failwithf" "raise" "reraise")) [ "as" "assert" "begin" + "end" + "done" "default" + "in" "do" "do!" - "done" - "downcast" - "downto" - "end" "event" "field" - "finally" "fun" "function" "get" - "global" - "inherit" - "interface" + "set" "lazy" "new" - "not" - "null" "of" "param" "property" - "set" "struct" - "try" - "upcast" - "use" - "use!" "val" + "module" + "namespace" "with" - "yield" - "yield!" ] @keyword [ - "true" - "false" - "unit" - ] @constant.builtin + "null" +] @constant.builtin -[ - (type) - (const) -] @constant +(match_expression "with" @keyword.control.conditional) -[ - (union_type_case) - (rules (rule (identifier_pattern))) -] @type.enum +((type + (long_identifier (identifier) @type.builtin)) + (#any-of? @type.builtin "bool" "byte" "sbyte" "int16" "uint16" "int" "uint" "int64" "uint64" "nativeint" "unativeint" "decimal" "float" "double" "float32" "single" "char" "string" "unit")) -(fsi_directive_decl (string) @namespace) +(preproc_if + [ + "#if" @keyword.directive + "#endif" @keyword.directive + ] + condition: (_)? @keyword.directive) -[ - (import_decl (long_identifier)) - (named_module (long_identifier)) - (namespace (long_identifier)) - (named_module - name: (long_identifier) ) - (namespace - name: (long_identifier) ) -] @namespace +(preproc_else + "#else" @keyword.directive) +((long_identifier + (identifier)+ @namespace + . + (identifier))) -(dot_expression - base: (long_identifier_or_op) @variable.other.member - field: (long_identifier_or_op) @function) - -[ - ;;(value_declaration_left (identifier_pattern) ) - (function_declaration_left (identifier) ) - (call_expression (long_identifier_or_op (long_identifier))) - ;;(application_expression (long_identifier_or_op (long_identifier))) -] @function - -[ - (string) - (triple_quoted_string) -] @string - -[ - (int) - (int16) - (int32) - (int64) - (float) - (decimal) -] @constant.numeric - +(long_identifier_or_op + (op_identifier) @operator) +((identifier) @namespace + (#any-of? @namespace "Array" "Async" "Directory" "File" "List" "Option" "Path" "Map" "Set" "Lazy" "Seq" "Task" "String" "Result" )) diff --git a/runtime/queries/fsharp/injections.scm b/runtime/queries/fsharp/injections.scm new file mode 100644 index 00000000..54b89c5a --- /dev/null +++ b/runtime/queries/fsharp/injections.scm @@ -0,0 +1,8 @@ +([ + (line_comment) + (block_comment_content) +] @injection.content + (#set! injection.language "comment")) + +((xml_doc (xml_doc_content) @injection.content) + (#set! injection.language "xml")) diff --git a/runtime/queries/fsharp/locals.scm b/runtime/queries/fsharp/locals.scm index aa36755e..db2291f2 100644 --- a/runtime/queries/fsharp/locals.scm +++ b/runtime/queries/fsharp/locals.scm @@ -1,25 +1,32 @@ -; Scopes -;------- +(identifier) @local.reference [ - (ce_expression) - (module_defn) - (for_expression) - (do_expression) - (fun_expression) - (function_expression) - (try_expression) - (match_expression) - (elif_expression) - (if_expression) + (namespace) + (named_module) + (function_or_value_defn) ] @local.scope -; Definitions -;------------ +(value_declaration_left + . + [ + (_ (identifier) @local.definition) + (_ (_ (identifier) @local.definition)) + (_ (_ (_ (identifier) @local.definition))) + (_ (_ (_ (_ (identifier) @local.definition)))) + (_ (_ (_ (_ (_ (identifier) @local.definition))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition)))))) + ]) -(function_or_value_defn) @local.definition - -; References -;----------- - -(identifier) @local.reference +(function_declaration_left + . + ((_) @local.definition) + ((argument_patterns + [ + (_ (identifier) @local.definition) + (_ (_ (identifier) @local.definition)) + (_ (_ (_ (identifier) @local.definition))) + (_ (_ (_ (_ (identifier) @local.definition)))) + (_ (_ (_ (_ (_ (identifier) @local.definition))))) + (_ (_ (_ (_ (_ (_ (identifier) @local.definition)))))) + ]) + )) diff --git a/runtime/queries/gherkin/highlights.scm b/runtime/queries/gherkin/highlights.scm new file mode 100644 index 00000000..1a17e381 --- /dev/null +++ b/runtime/queries/gherkin/highlights.scm @@ -0,0 +1,17 @@ +[ + (feature_keyword) + (rule_keyword) + (background_keyword) + (scenario_keyword) + (given_keyword) + (when_keyword) + (then_keyword) + (and_keyword) + (but_keyword) + (asterisk_keyword) +] @keyword + +(tag) @function +(doc_string) @string +(data_table) @special +(comment) @comment diff --git a/runtime/queries/html/highlights.scm b/runtime/queries/html/highlights.scm index 99f39c95..5dd61b7c 100644 --- a/runtime/queries/html/highlights.scm +++ b/runtime/queries/html/highlights.scm @@ -1,13 +1,39 @@ (tag_name) @tag -(erroneous_end_tag_name) @tag.error +(erroneous_end_tag_name) @error (doctype) @constant (attribute_name) @attribute (comment) @comment -[ - "\"" - (attribute_value) -] @string +((attribute + (attribute_name) @_attr + (quoted_attribute_value (attribute_value) @markup.link.url)) + (#any-of? @_attr "href" "src")) + +((element + (start_tag + (tag_name) @_tag) + (text) @markup.link.label) + (#eq? @_tag "a")) + +(attribute [(attribute_value) (quoted_attribute_value)] @string) + +((element + (start_tag + (tag_name) @_tag) + (text) @markup.bold) + (#any-of? @_tag "strong" "b")) + +((element + (start_tag + (tag_name) @_tag) + (text) @markup.italic) + (#any-of? @_tag "em" "i")) + +((element + (start_tag + (tag_name) @_tag) + (text) @markup.strikethrough) + (#any-of? @_tag "s" "del")) [ "<" diff --git a/runtime/queries/jq/highlights.scm b/runtime/queries/jq/highlights.scm new file mode 100644 index 00000000..8cec2be9 --- /dev/null +++ b/runtime/queries/jq/highlights.scm @@ -0,0 +1,160 @@ +;; From nvim-treesitter, contributed by @ObserverOfTime et al. + +; Variables +(variable) @variable + +((variable) @constant.builtin + (#eq? @constant.builtin "$ENV")) + +((variable) @constant.builtin + (#eq? @constant.builtin "$__loc__")) + +; Properties +(index + (identifier) @variable.other.member) + +; Labels +(query + label: (variable) @label) + +(query + break_statement: (variable) @label) + +; Literals +(number) @constant.numeric + +(string) @string + +[ + "true" + "false" +] @constant.builtin.boolean + +"null" @type.builtin + +; Interpolation +[ + "\\(" + ")" +] @special + +; Format +(format) @attribute + +; Functions +(funcdef + (identifier) @function) + +(funcdefargs + (identifier) @variable.parameter) + +[ + "reduce" + "foreach" +] @function.builtin + +((funcname) @function + . + "(") + +; jq -n 'builtins | map(split("/")[0]) | unique | .[]' +((funcname) @function.builtin + (#any-of? @function.builtin + "IN" "INDEX" "JOIN" "abs" "acos" "acosh" "add" "all" "any" "arrays" "ascii_downcase" + "ascii_upcase" "asin" "asinh" "atan" "atan2" "atanh" "booleans" "bsearch" "builtins" "capture" + "cbrt" "ceil" "combinations" "contains" "copysign" "cos" "cosh" "debug" "del" "delpaths" "drem" + "empty" "endswith" "env" "erf" "erfc" "error" "exp" "exp10" "exp2" "explode" "expm1" "fabs" + "fdim" "finites" "first" "flatten" "floor" "fma" "fmax" "fmin" "fmod" "format" "frexp" + "from_entries" "fromdate" "fromdateiso8601" "fromjson" "fromstream" "gamma" "get_jq_origin" + "get_prog_origin" "get_search_list" "getpath" "gmtime" "group_by" "gsub" "halt" "halt_error" + "has" "hypot" "implode" "in" "index" "indices" "infinite" "input" "input_filename" + "input_line_number" "inputs" "inside" "isempty" "isfinite" "isinfinite" "isnan" "isnormal" + "iterables" "j0" "j1" "jn" "join" "keys" "keys_unsorted" "last" "ldexp" "length" "lgamma" + "lgamma_r" "limit" "localtime" "log" "log10" "log1p" "log2" "logb" "ltrimstr" "map" "map_values" + "match" "max" "max_by" "min" "min_by" "mktime" "modf" "modulemeta" "nan" "nearbyint" "nextafter" + "nexttoward" "normals" "not" "now" "nth" "nulls" "numbers" "objects" "path" "paths" "pick" "pow" + "pow10" "range" "recurse" "remainder" "repeat" "reverse" "rindex" "rint" "round" "rtrimstr" + "scalars" "scalb" "scalbln" "scan" "select" "setpath" "significand" "sin" "sinh" "sort" + "sort_by" "split" "splits" "sqrt" "startswith" "stderr" "strflocaltime" "strftime" "strings" + "strptime" "sub" "tan" "tanh" "test" "tgamma" "to_entries" "todate" "todateiso8601" "tojson" + "tonumber" "tostream" "tostring" "transpose" "trunc" "truncate_stream" "type" "unique" + "unique_by" "until" "utf8bytelength" "values" "walk" "while" "with_entries" "y0" "y1" "yn")) + +; Keywords +[ + "def" + "as" + "label" + "module" + "break" +] @keyword + +[ + "import" + "include" +] @keyword.control.import + +[ + "if" + "then" + "elif" + "else" + "end" +] @keyword.control.conditional + +[ + "try" + "catch" +] @keyword.control.exception + +[ + "or" + "and" +] @keyword.operator + +; Operators +[ + "." + "==" + "!=" + ">" + ">=" + "<=" + "<" + "=" + "+" + "-" + "*" + "/" + "%" + "+=" + "-=" + "*=" + "/=" + "%=" + "//=" + "|" + "?" + "//" + "?//" + (recurse) ; ".." +] @operator + +; Punctuation +[ + ";" + "," + ":" +] @punctuation.delimiter + +[ + "[" + "]" + "{" + "}" + "(" + ")" +] @punctuation.bracket + +; Comments +(comment) @comment.line diff --git a/runtime/queries/jq/injections.scm b/runtime/queries/jq/injections.scm new file mode 100644 index 00000000..ddfe53c3 --- /dev/null +++ b/runtime/queries/jq/injections.scm @@ -0,0 +1,25 @@ +;; From nvim-treesitter, contributed by @ObserverOfTime et al. + +((comment) @injection.content + (#set! injection.language "comment")) + +; test(val) +(query + ((funcname) @_function + (#any-of? @_function "test" "match" "capture" "scan" "split" "splits" "sub" "gsub")) + (args + . + (query + (string) @injection.content + (#set! injection.language "regex")))) + +; test(regex; flags) +(query + ((funcname) @_function + (#any-of? @_function "test" "match" "capture" "scan" "split" "splits" "sub" "gsub")) + (args + . + (args + (query + (string) @injection.content + (#set! injection.language "regex"))))) diff --git a/runtime/queries/jq/locals.scm b/runtime/queries/jq/locals.scm new file mode 100644 index 00000000..40946e7c --- /dev/null +++ b/runtime/queries/jq/locals.scm @@ -0,0 +1,12 @@ +;; From nvim-treesitter, contributed by @ObserverOfTime et al. + +(funcdef + (identifier) @local.definition) + +(funcdefargs + (identifier) @local.definition) + +(funcname) @local.reference + +(index + (identifier) @local.reference) diff --git a/runtime/queries/jq/textobjects.scm b/runtime/queries/jq/textobjects.scm new file mode 100644 index 00000000..ff078cd1 --- /dev/null +++ b/runtime/queries/jq/textobjects.scm @@ -0,0 +1,8 @@ +(comment) @comment.inside +(comment)+ @comment.around + +(funcdef + (query) @function.inside) @function.around + +(objectkeyval + (_) @entry.inside) @entry.around diff --git a/runtime/queries/just/folds.scm b/runtime/queries/just/folds.scm index 77079fd4..2640f4c4 100644 --- a/runtime/queries/just/folds.scm +++ b/runtime/queries/just/folds.scm @@ -1,5 +1,3 @@ -; From - ; Define collapse points ([ diff --git a/runtime/queries/just/highlights.scm b/runtime/queries/just/highlights.scm index d5e5cc19..258fadb9 100644 --- a/runtime/queries/just/highlights.scm +++ b/runtime/queries/just/highlights.scm @@ -1,5 +1,3 @@ -; From - ; This file specifies how matched syntax patterns should be highlighted [ @@ -26,35 +24,57 @@ (identifier) @variable) (alias - left: (identifier) @variable) + name: (identifier) @variable) (assignment - left: (identifier) @variable) + name: (identifier) @variable) + +(shell_variable_name) @variable ; Functions -(recipe_header +(recipe name: (identifier) @function) -(dependency - name: (identifier) @function) - -(dependency_expression - name: (identifier) @function) +(recipe_dependency + name: (identifier) @function.call) (function_call - name: (identifier) @function) + name: (identifier) @function.builtin) ; Parameters -(parameter +(recipe_parameter name: (identifier) @variable.parameter) ; Namespaces -(module +(mod name: (identifier) @namespace) +; Paths + +(mod + (path) @string.special.path) + +(import + (path) @string.special.path) + +; Shebangs + +(shebang_line) @keyword.directive +(shebang_line + (shebang_shell) @string.special) + + +(shell_expanded_string + [ + (expansion_short_start) + (expansion_long_start) + (expansion_long_middle) + (expansion_long_end) + ] @punctuation.special) + ; Operators [ @@ -95,55 +115,31 @@ ; Literals -(boolean) @constant.builtin.boolean +; Booleans are not allowed anywhere except in settings +(setting + (boolean) @constant.builtin.boolean) [ (string) (external_command) ] @string -(escape_sequence) @constant.character.escape +[ + (escape_sequence) + (escape_variable_end) +] @constant.character.escape ; Comments (comment) @comment.line -(shebang) @keyword.directive - -; highlight known settings (filtering does not always work) +; highlight known settings (setting - left: (identifier) @keyword - (#any-of? @keyword - "allow-duplicate-recipes" - "dotenv-filename" - "dotenv-load" - "dotenv-path" - "export" - "fallback" - "ignore-comments" - "positional-arguments" - "shell" - "tempdi" - "windows-powershell" - "windows-shell")) + name: (_) @keyword.function) -; highlight known attributes (filtering does not always work) +; highlight known attributes (attribute - (identifier) @attribute - (#any-of? @attribute - "private" - "allow-duplicate-recipes" - "dotenv-filename" - "dotenv-load" - "dotenv-path" - "export" - "fallback" - "ignore-comments" - "positional-arguments" - "shell" - "tempdi" - "windows-powershell" - "windows-shell")) + name: (identifier) @attribute) ; Numbers are part of the syntax tree, even if disallowed (numeric_error) @error diff --git a/runtime/queries/just/indents.scm b/runtime/queries/just/indents.scm index 7cfca3d7..c66dda4c 100644 --- a/runtime/queries/just/indents.scm +++ b/runtime/queries/just/indents.scm @@ -1,5 +1,3 @@ -; From -; ; This query specifies how to auto-indent logical blocks. ; ; Better documentation with diagrams is in https://docs.helix-editor.com/guides/indent.html diff --git a/runtime/queries/just/injections.scm b/runtime/queries/just/injections.scm index 54393059..39877be4 100644 --- a/runtime/queries/just/injections.scm +++ b/runtime/queries/just/injections.scm @@ -1,5 +1,3 @@ -; From -; ; Specify nested languages that live within a `justfile` ; ================ Always applicable ================ @@ -8,7 +6,7 @@ (#set! injection.language "comment")) ; Highlight the RHS of `=~` as regex -((regex_literal +((regex (_) @injection.content) (#set! injection.language "regex")) @@ -21,7 +19,7 @@ (#set! injection.include-children)) @injection.content (external_command - (command_body) @injection.content + (content) @injection.content (#set! injection.language "bash")) ; ================ Global language specified ================ @@ -43,7 +41,7 @@ ; they default to bash. Limitations... ; See https://github.com/tree-sitter/tree-sitter/issues/880 for more on that. -(source_file +(file (setting "shell" ":=" "[" (string) @_langstr (#match? @_langstr ".*(powershell|pwsh|cmd).*") (#set! injection.language "powershell")) @@ -57,10 +55,10 @@ (expression (value (external_command - (command_body) @injection.content)))) + (content) @injection.content)))) ]) -(source_file +(file (setting "shell" ":=" "[" (string) @injection.language (#not-match? @injection.language ".*(powershell|pwsh|cmd).*")) [ @@ -73,12 +71,12 @@ (expression (value (external_command - (command_body) @injection.content)))) + (content) @injection.content)))) ]) ; ================ Recipe language specified - Helix only ================ ; Set highlighting for recipes that specify a language using builtin shebang matching (recipe_body - (shebang) @injection.shebang + (shebang_line) @injection.shebang (#set! injection.include-children)) @injection.content diff --git a/runtime/queries/just/locals.scm b/runtime/queries/just/locals.scm index 827148a1..d612f5da 100644 --- a/runtime/queries/just/locals.scm +++ b/runtime/queries/just/locals.scm @@ -1,5 +1,3 @@ -; From -; ; This file tells us about the scope of variables so e.g. local ; variables override global functions with the same name @@ -10,32 +8,29 @@ ; Definitions (alias - left: (identifier) @local.definition) + name: (identifier) @local.definition) (assignment - left: (identifier) @local.definition) - -(module name: (identifier) @local.definition) -(parameter +(mod name: (identifier) @local.definition) -(recipe_header +(recipe_parameter + name: (identifier) @local.definition) + +(recipe name: (identifier) @local.definition) ; References (alias - right: (identifier) @local.reference) + name: (identifier) @local.reference) (function_call name: (identifier) @local.reference) -(dependency - name: (identifier) @local.reference) - -(dependency_expression +(recipe_dependency name: (identifier) @local.reference) (value diff --git a/runtime/queries/just/textobjects.scm b/runtime/queries/just/textobjects.scm index bb604178..b60b11e4 100644 --- a/runtime/queries/just/textobjects.scm +++ b/runtime/queries/just/textobjects.scm @@ -1,18 +1,19 @@ -; From -; ; Specify how to navigate around logical blocks in code +(assert_parameters + ((_) @parameter.inside . ","? @parameter.around)) @parameter.around + (recipe (recipe_body) @function.inside) @function.around -(parameters +(recipe_parameters ((_) @parameter.inside . ","? @parameter.around)) @parameter.around -(dependency_expression +(recipe_dependency (_) @parameter.inside) @parameter.around (function_call - arguments: (sequence - (expression) @parameter.inside) @parameter.around) @function.around + (function_parameters + ((_) @parameter.inside . ","? @parameter.around)) @parameter.around) @function.around (comment) @comment.around diff --git a/runtime/queries/thrift/folds.scm b/runtime/queries/thrift/folds.scm new file mode 100644 index 00000000..1361be1f --- /dev/null +++ b/runtime/queries/thrift/folds.scm @@ -0,0 +1,12 @@ +[ + (annotation_definition) + (enum_definition) + (exception_definition) + (function_definition) + (senum_definition) + (service_definition) + (struct_definition) + (union_definition) + + (comment) +] @fold diff --git a/runtime/queries/thrift/highlights.scm b/runtime/queries/thrift/highlights.scm new file mode 100644 index 00000000..567c3f9d --- /dev/null +++ b/runtime/queries/thrift/highlights.scm @@ -0,0 +1,211 @@ +; Variables + +((identifier) @variable) + +; Includes + +[ + "include" + "cpp_include" +] @keyword + +; Function + +(function_definition + (identifier) @function) + +; Fields + +(field (identifier) @variable.other.member) + +; Parameters + +(function_definition + (parameters + (parameter (identifier) @variable.parameter))) + +(throws + (parameters + (parameter (identifier) @keyword.control.exception))) + +; Types + +(typedef_identifier) @type +(struct_definition + "struct" (identifier) @type) + +(union_definition + "union" (identifier) @type) + +(exception_definition + "exception" (identifier) @type) + +(service_definition + "service" (identifier) @type) + +(interaction_definition + "interaction" (identifier) @type) + +(type + type: (identifier) @type) + +(definition_type + type: (identifier) @type) + +; Constants + +(const_definition (identifier) @constant) + +(enum_definition "enum" + . (identifier) @type + "{" (identifier) @constant "}") + +; Builtin Types + +(primitive) @type.builtin + +[ + "list" + "map" + "set" + "sink" + "stream" + "void" +] @type.builtin + +; Namespace + +(namespace_declaration + (namespace_scope) @tag + [(namespace) @namespace (_ (identifier) @namespace)]) + +; Attributes + +(annotation_definition + (annotation_identifier (identifier) @attribute)) +(fb_annotation_definition + "@" @attribute (annotation_identifier (identifier) @attribute) + (identifier)? @attribute) +(namespace_uri (string) @attribute) + +; Operators + +[ + "=" + "&" +] @operator + +; Exceptions + +[ + "throws" +] @keyword.control.exception + +; Keywords + +[ + "enum" + "exception" + "extends" + "interaction" + "namespace" + "senum" + "service" + "struct" + "typedef" + "union" + "uri" +] @keyword + +; Deprecated Keywords + +[ + "cocoa_prefix" + "cpp_namespace" + "csharp_namespace" + "delphi_namespace" + "java_package" + "perl_package" + "php_namespace" + "py_module" + "ruby_namespace" + "smalltalk_category" + "smalltalk_prefix" + "xsd_all" + "xsd_attrs" + "xsd_namespace" + "xsd_nillable" + "xsd_optional" +] @keyword + +; Extended Kewords +[ + "package" + "performs" +] @keyword + +[ + "async" + "oneway" +] @keyword + +; Qualifiers + +[ + "client" + "const" + "idempotent" + "optional" + "permanent" + "readonly" + "required" + "safe" + "server" + "stateful" + "transient" +] @type.directive + +; Literals + +(string) @string + +(escape_sequence) @constant.character.escape + +(namespace_uri + (string) @string.special) + +(number) @constant.numeric.integer + +(double) @constant.numeric.float + +(boolean) @constant.builtin.boolean + +; Typedefs + +(typedef_identifier) @type.definition + +; Punctuation + +[ + "*" +] @punctuation.special + +["{" "}"] @punctuation.bracket + +["(" ")"] @punctuation.bracket + +["[" "]"] @punctuation.bracket + +["<" ">"] @punctuation.bracket + +[ + "." + "," + ";" + ":" +] @punctuation.delimiter + +; Comments + +(comment) @comment + diff --git a/runtime/queries/thrift/injections.scm b/runtime/queries/thrift/injections.scm new file mode 100644 index 00000000..321c90ad --- /dev/null +++ b/runtime/queries/thrift/injections.scm @@ -0,0 +1,2 @@ +((comment) @injection.content + (#set! injection.language "comment")) diff --git a/runtime/queries/thrift/locals.scm b/runtime/queries/thrift/locals.scm new file mode 100644 index 00000000..538b4996 --- /dev/null +++ b/runtime/queries/thrift/locals.scm @@ -0,0 +1,51 @@ +; Scopes + +[ + (document) + (definition) +] @local.scope + +; References + +(identifier) @local.reference + +; Definitions + +(annotation_identifier) @local.definition + +; (const_definition (identifier) @definition.constant) + +; (enum_definition "enum" +; . (identifier) @definition.enum +; "{" (identifier) @definition.constant "}") + +; (senum_definition "senum" +; . (identifier) @definition.enum) + +; (field (identifier) @definition.field) + +; (function_definition (identifier) @definition.function) + +; (namespace_declaration +; "namespace" (namespace_scope) +; . (_) @definition.namespace +; (namespace_uri)?) + +; (parameter (identifier) @definition.parameter) + +; (struct_definition +; "struct" . (identifier) @definition.type) + +; (union_definition +; "union" . (identifier) @definition.type) + +; (exception_definition +; "exception" . (identifier) @definition.type) + +; (service_definition +; "service" . (identifier) @definition.type) + +; (interaction_definition +; "interaction" . (identifier) @definition.type) + +; (typedef_identifier) @definition.type diff --git a/runtime/queries/typespec/highlights.scm b/runtime/queries/typespec/highlights.scm new file mode 100644 index 00000000..8b8aa4c3 --- /dev/null +++ b/runtime/queries/typespec/highlights.scm @@ -0,0 +1,177 @@ +; Keywords + +[ + "is" + "extends" + "valueof" +] @keyword.operator + +[ + "namespace" + "scalar" + "interface" + "alias" +] @keyword + +[ + "model" + "enum" + "union" +] @keyword.storage.type + +[ + "op" + "fn" + "dec" +] @keyword.function + +"extern" @keyword.storage.modifier + +[ + "import" + "using" +] @keyword.control.import + +[ + "(" + ")" + "{" + "}" + "<" + ">" + "[" + "]" +] @punctuation.bracket + +[ + "," + ";" + "." + ":" +] @punctuation.delimiter + +[ + "|" + "&" + "=" + "..." +] @operator + +"?" @punctuation.special + +; Imports + +(import_statement + (quoted_string_literal) @string.special.path) + +; Namespaces + +(using_statement + module: (identifier_or_member_expression) @namespace) + +(namespace_statement + name: (identifier_or_member_expression) @namespace) + +; Comments + +[ + (single_line_comment) +] @comment.line + +[ + (multi_line_comment) +] @comment.block + +; Decorators + +(decorator + "@" @attribute + name: (identifier_or_member_expression) @attribute) + +(augment_decorator_statement + name: (identifier_or_member_expression) @attribute) + +(decorator + (decorator_arguments) @variable.parameter) + +; Scalars + +(scalar_statement + name: (identifier) @type) + +; Models + +(model_statement + name: (identifier) @type) + +(model_property + name: (identifier) @variable.other.member) + +; Operations + +(operation_statement + name: (identifier) @function.method) + +(operation_arguments + (model_property + name: (identifier) @variable.parameter)) + +(template_parameter + name: (identifier) @type.parameter) + +(function_parameter + name: (identifier) @variable.parameter) + +; Interfaces + +(interface_statement + name: (identifier) @type) + +(interface_statement + (interface_body + (interface_member + (identifier) @function.method))) + +; Enums + +(enum_statement + name: (identifier) @type.enum) + +(enum_member + name: (identifier) @constant) + +; Unions + +(union_statement + name: (identifier) @type) + +(union_variant + name: (identifier) @type.enum.variant) + +; Aliases + +(alias_statement + name: (identifier) @type) + +; Built-in types + +[ + (quoted_string_literal) + (triple_quoted_string_literal) +] @string + +(escape_sequence) @constant.character.escape + +(boolean_literal) @constant.builtin.boolean + +[ + (decimal_literal) + (hex_integer_literal) + (binary_integer_literal) +] @constant.numeric.integer + +(builtin_type) @type.builtin + +; Identifiers + +(identifier_or_member_expression) @type diff --git a/runtime/queries/typespec/indents.scm b/runtime/queries/typespec/indents.scm new file mode 100644 index 00000000..aee01f35 --- /dev/null +++ b/runtime/queries/typespec/indents.scm @@ -0,0 +1,18 @@ +[ + (model_expression) + (tuple_expression) + (namespace_body) + (interface_body) + (union_body) + (enum_body) + (template_arguments) + (template_parameters) + (operation_arguments) +] @indent.begin + +[ + "}" + ")" + ">" + "]" +] @indent.end diff --git a/runtime/queries/typespec/injections.scm b/runtime/queries/typespec/injections.scm new file mode 100644 index 00000000..81d7734c --- /dev/null +++ b/runtime/queries/typespec/injections.scm @@ -0,0 +1,5 @@ +([ + (single_line_comment) + (multi_line_comment) +] @injection.content + (#set! injection.language "comment")) diff --git a/runtime/queries/typespec/textobjects.scm b/runtime/queries/typespec/textobjects.scm new file mode 100644 index 00000000..7ee1251c --- /dev/null +++ b/runtime/queries/typespec/textobjects.scm @@ -0,0 +1,51 @@ +; Classes + +(enum_statement + (enum_body) @class.inside) @class.around + +(model_statement + (model_expression) @class.inside) @class.around + +(union_statement + (union_body) @class.inside) @class.around + +; Interfaces + +(interface_statement + (interface_body + (interface_member) @function.around) @class.inside) @class.around + +; Comments + +[ + (single_line_comment) + (multi_line_comment) +] @comment.inside + +[ + (single_line_comment) + (multi_line_comment) +]+ @comment.around + +; Functions + +[ + (decorator) + (decorator_declaration_statement) + (function_declaration_statement) + (operation_statement) +] @function.around + +(function_parameter_list + (function_parameter)? @parameter.inside)* @function.inside + +(decorator_arguments + (expression_list + (_) @parameter.inside)*) @function.inside + +(operation_arguments + (model_property)? @parameter.inside)* @function.inside + +(template_parameters + (template_parameter_list + (template_parameter) @parameter.inside)) @function.inside diff --git a/runtime/queries/verilog/textobjects.scm b/runtime/queries/verilog/textobjects.scm index f940832a..0a2034dc 100644 --- a/runtime/queries/verilog/textobjects.scm +++ b/runtime/queries/verilog/textobjects.scm @@ -3,4 +3,8 @@ (function_body_declaration (function_identifier (function_identifier - (simple_identifier) @function.inside)))) @function.around \ No newline at end of file + (simple_identifier) @function.inside)))) @function.around + +(comment) @comment.inside + +(comment)+ @comment.around diff --git a/runtime/themes/dark_plus.toml b/runtime/themes/dark_plus.toml index 77b9b3e7..813eebf3 100644 --- a/runtime/themes/dark_plus.toml +++ b/runtime/themes/dark_plus.toml @@ -1,7 +1,7 @@ # Author: David Else <12832280+David-Else@users.noreply.github.com> # SYNTAX -"attribute" = "fn_declaration" +"attribute" = "variable" "comment" = "dark_green" "constant" = "constant" "constant.builtin" = "blue2" @@ -39,10 +39,10 @@ # MARKUP "markup.heading" = { fg = "blue2", modifiers = ["bold"] } "markup.list" = "blue3" -"markup.bold" = { fg = "blue2", modifiers = ["bold"] } +"markup.bold" = { modifiers = ["bold"] } "markup.italic" = { modifiers = ["italic"] } "markup.strikethrough" = { modifiers = ["crossed_out"] } -"markup.link.url" = { modifiers = ["underlined"] } +"markup.link.url" = { underline.style= "line" } "markup.link.text" = "orange" "markup.quote" = "dark_green" "markup.raw" = "orange" @@ -57,7 +57,7 @@ # TODO: Alternate bg colour for `ui.cursor.match` and `ui.selection`. "ui.cursor" = { fg = "cursor", modifiers = ["reversed"] } "ui.cursor.primary" = { fg = "cursor", modifiers = ["reversed"] } -"ui.cursor.match" = { bg = "#3a3d41", modifiers = ["underlined"] } +"ui.cursor.match" = { bg = "#3a3d41", underline.style = "line" } "ui.selection" = { bg = "#3a3d41" } "ui.selection.primary" = { bg = "dark_blue" } "ui.linenr" = { fg = "dark_gray" } @@ -80,6 +80,8 @@ "ui.highlight.frameline" = { bg = "#4b4b18" } "ui.debug.active" = { fg = "#ffcc00" } "ui.debug.breakpoint" = { fg = "#e51400" } +"ui.picker.header.column" = { underline.style = "line" } +"ui.picker.header.column.active" = { fg ="white", underline.style = "line" } "warning" = { fg = "gold2" } "error" = { fg = "red" } "info" = { fg = "light_blue" } diff --git a/runtime/themes/iceberg-dark.toml b/runtime/themes/iceberg-dark.toml new file mode 100644 index 00000000..b6e0cf52 --- /dev/null +++ b/runtime/themes/iceberg-dark.toml @@ -0,0 +1,128 @@ +# Author : Chromo-residuum-opec + +"attribute" = { fg = "green" } +"boolean" = { fg = "purple" } +"character" = { fg = "purple" } +"comment" = { fg = "comment_fg" } +"conditional" = { fg = "blue" } +"constant" = { fg = "purple" } +"constructor" = { fg = "blue" } +"diagnostic.deprecated" = { modifiers = ["crossed_out"] } +"diagnostic.error" = { underline = { style = "curl", color = "red" } } +"diagnostic.hint" = { underline = { style = "curl", color = "comment_fg" } } +"diagnostic.info" = { underline = { style = "curl", color = "cyan" } } +"diagnostic.unnecessary" = { modifiers = ["dim"] } +"diagnostic.warning" = { underline = { style = "curl", color = "orange" } } +"diff.delta" = { fg = "blue" } +"diff.delta.gutter" = { fg = "cyan", bg = "linenr_bg" } +"diff.minus" = { fg = "red" } +"diff.minus.gutter" = { fg = "red", bg = "linenr_bg" } +"diff.plus" = { fg = "green" } +"diff.plus.gutter" = { fg = "green", bg = "linenr_bg" } +"error" = { fg = "red" } +"exception" = { fg = "blue" } +"field" = { fg = "background_fg" } +"float" = { fg = "purple" } +"function" = { fg = "pale" } +"function.macro" = { fg = "green" } +"hint" = { fg = "comment_fg" } +"identifier" = { fg = "blue" } +"info" = { fg = "cyan" } +"keyword" = { fg = "blue" } +"keyword.directive" = { fg = "green" } +"keyword.import" = { fg = "pale" } +"label" = { fg = "green" } +"markup.bold" = { modifiers = ["bold"] } +"markup.heading" = { fg = "blue", modifiers = ["bold"] } +"markup.italic" = { modifiers = ["italic"] } +"markup.link" = { fg = "blue", underline = { style = "line" } } +"markup.link.label" = { fg = "cyan" } +"markup.link.text" = { fg = "cyan" } +"markup.link.url" = { underline = { style = "line" } } +"markup.list" = { fg = "orange", modifiers = ["bold"] } +"markup.raw" = { fg = "cyan" } +"markup.raw.inline" = { bg = "black", fg = "blue" } +"markup.strikethrough" = { modifiers = ["crossed_out"] } +"method" = { fg = "pale" } +"namespace" = { fg = "blue" } +"number" = { fg = "purple" } +"operator" = { fg = "blue" } +"parameter" = { fg = "background_fg" } +"property" = { fg = "background_fg" } +"punctuation.bracket" = { fg = "background_fg" } +"punctuation.delimiter" = { fg = "background_fg" } +"punctuation.special" = { fg = "green" } +"repeat" = { fg = "blue" } +"special" = { fg = "green" } +"string" = { fg = "cyan" } +"string.escape" = { fg = "green" } +"string.special" = { fg = "green" } +"tag" = { fg = "blue" } +"tag.attribute" = { fg = "purple" } +"text" = { fg = "background_fg" } +"type" = { fg = "blue" } +"ui.background" = { fg = "background_fg", bg = "background_bg" } +"ui.background.separator" = { fg = "comment_fg" } +"ui.bufferline.active" = { fg = "pale" } +"ui.cursor.match" = { fg = "background_fg", bg = "matchparen_bg" } +"ui.cursor.normal" = { bg = "gray" } +"ui.cursor.primary" = { modifiers = ["reversed"] } +"ui.cursor.select" = { bg = "gray" } +"ui.gutter" = { fg = "linenr_fg", bg = "linenr_bg" } +"ui.help" = { fg = "background_fg", bg = "cursorlinenr_bg" } +"ui.linenr" = { fg = "linenr_fg", bg = "linenr_bg" } +"ui.menu" = { fg = "background_fg", bg = "cursorlinenr_bg" } +"ui.menu.border" = { fg = "comment_fg" } +"ui.menu.selected" = { fg = "menusel_fg", bg = "menusel_bg" } +"ui.popup" = { fg = "background_fg", bg = "cursorlinenr_bg" } +"ui.popup.info" = { fg = "blue" } +"ui.selection" = { bg = "sel_bg" } +"ui.statusline" = { bg = "statusline_bg", fg = "statusline_fg" } +"ui.statusline.insert" = { fg = "black", bg = "blue" } +"ui.statusline.select" = { fg = "black", bg = "green" } +"ui.text.focus" = { fg = "orange" } +"ui.virtual" = { fg = "linenr_fg" } +"ui.virtual.indent-guide" = { fg = "linenr_fg" } +"ui.virtual.jump-label" = { fg = "orange", modifiers = ["bold"] } +"ui.virtual.ruler" = { bg = "linenr_bg" } +"ui.virtual.whitespace" = { fg = "sel_bg" } +"ui.window" = { fg = "comment_fg", modifiers = ["bold"] } +"variable" = { fg = "background_fg" } +"variable.builtin" = { fg = "blue" } +"warning" = { fg = "orange" } + +[palette] + +orange = "#e2a578" +pale = "#a4aecc" +purple = "#a093c8" + +black = "#1e2132" +gray = "#6b7089" +red = "#e27878" +light-red = "#e98989" +green = "#b5bf82" +light-green = "#c0ca8e" +yellow = "#e2a478" +light-yellow = "#e9b189" +blue = "#85a0c7" +light-blue = "#91acd1" +magenta = "#a093c7" +light-magenta = "#ada0d3" +cyan = "#89b9c2" +light-cyan = "#95c4ce" +white = "#c6c8d1" +light-gray = "#d2d4de" + +background_bg = "#161822" +background_fg = "#c7c9d1" +comment_fg = "#6c7189" +cursorlinenr_bg = "#3d425c" +linenr_bg = "#1f2233" +linenr_fg = "#454d73" +matchparen_bg = "#3f455f" +menusel_bg = "#5c638a" +menusel_fg = "#f0f1f5" +sel_bg = "#282d43" +statusline_bg = "#0f1117" +statusline_fg = "#828597" diff --git a/runtime/themes/iceberg-light.toml b/runtime/themes/iceberg-light.toml new file mode 100644 index 00000000..2c7ca527 --- /dev/null +++ b/runtime/themes/iceberg-light.toml @@ -0,0 +1,39 @@ +# Author : Chromo-residuum-opec + +inherits = "iceberg-dark" +"ui.menu.selected" = { fg = "background_fg", bg = "menusel_bg" } + +[palette] + +orange = "#c67439" +pale = "#505695" +purple = "#785ab5" + +black = "#dcdfe7" +gray = "#8389a3" +red = "#cd517a" +light-red = "#cc3768" +green = "#668f3d" +light-green = "#598030" +yellow = "#c57339" +light-yellow = "#b6662d" +blue = "#2e539e" +light-blue = "#22478e" +magenta = "#7759b4" +light-magenta = "#6845ad" +cyan = "#3f84a6" +light-cyan = "#327698" +white = "#33374c" +light-gray = "#262a3f" + +background_bg = "#e9e9ed" +background_fg = "#33374d" +comment_fg = "#8489a4" +cursorlinenr_bg = "#cccfe0" +linenr_bg = "#dddfe9" +linenr_fg = "#a0a5c0" +matchparen_bg = "#bec0ca" +menusel_bg = "#a9afd1" +sel_bg = "#cacdd8" +statusline_bg = "#cad0de" +statusline_fg = "#757da3" diff --git a/xtask/src/docgen.rs b/xtask/src/docgen.rs index 034d9918..18c145d5 100644 --- a/xtask/src/docgen.rs +++ b/xtask/src/docgen.rs @@ -1,9 +1,9 @@ use crate::helpers; use crate::path; use crate::DynError; - use helix_term::commands::TYPABLE_COMMAND_LIST; use helix_term::health::TsFeature; +use std::collections::HashSet; use std::fs; pub const TYPABLE_COMMANDS_MD_OUTPUT: &str = "typable-cmd.md"; @@ -95,14 +95,25 @@ pub fn lang_features() -> Result { .to_owned(), ); } - row.push( - lc.language_servers - .iter() - .filter_map(|ls| config.language_server.get(&ls.name)) - .map(|s| md_mono(&s.command.clone())) - .collect::>() - .join(", "), - ); + let mut seen_commands = HashSet::new(); + let mut commands = String::new(); + for ls_config in lc + .language_servers + .iter() + .filter_map(|ls| config.language_server.get(&ls.name)) + { + let command = &ls_config.command; + if !seen_commands.insert(command) { + continue; + } + + if !commands.is_empty() { + commands.push_str(", "); + } + + commands.push_str(&md_mono(command)); + } + row.push(commands); md.push_str(&md_table_row(&row)); row.clear();