Merge branch 'helix-editor:master' into nullspace

pull/11409/head
Stephen Broadley 2 months ago committed by GitHub
commit 8c3e9b1a03
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -16,6 +16,7 @@ jobs:
steps:
- name: Checkout sources
uses: actions/checkout@v4
- name: Install stable toolchain
uses: dtolnay/rust-toolchain@1.70
@ -107,6 +108,9 @@ jobs:
- name: Validate queries
run: cargo xtask query-check
- name: Validate themes
run: cargo xtask theme-check
- name: Generate docs
run: cargo xtask docgen

@ -14,7 +14,7 @@ jobs:
uses: actions/checkout@v4
- name: Install nix
uses: cachix/install-nix-action@V27
uses: cachix/install-nix-action@V28
- name: Authenticate with Cachix
uses: cachix/cachix-action@v15

1089
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -37,8 +37,8 @@ All shortcuts/keymaps can be found [in the documentation on the website](https:/
- Built-in language server support
- Smart, incremental syntax highlighting and code editing via tree-sitter
It's a terminal-based editor first, but I'd like to explore a custom renderer
(similar to Emacs) in wgpu or skulpin.
Although it's primarily a terminal-based editor, I am interested in exploring
a custom renderer (similar to Emacs) using wgpu or skulpin.
Note: Only certain languages have indentation definitions at the moment. Check
`runtime/queries/<lang>/` for `indents.scm`.

@ -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 | ✓ | | | |
@ -19,6 +19,7 @@
| cairo | ✓ | ✓ | ✓ | `cairo-language-server` |
| capnp | ✓ | | ✓ | |
| cel | ✓ | | | |
| circom | ✓ | | | `circom-lsp` |
| clojure | ✓ | | | `clojure-lsp` |
| cmake | ✓ | ✓ | ✓ | `cmake-language-server` |
| comment | ✓ | | | |
@ -34,8 +35,8 @@
| devicetree | ✓ | | | |
| dhall | ✓ | ✓ | | `dhall-lsp-server` |
| diff | ✓ | | | |
| docker-compose | ✓ | | ✓ | `docker-compose-langserver`, `yaml-language-server` |
| dockerfile | ✓ | | | `docker-langserver` |
| docker-compose | ✓ | | ✓ | `docker-compose-langserver`, `yaml-language-server` |
| dockerfile | ✓ | | | `docker-langserver` |
| dot | ✓ | | | `dot-language-server` |
| dtd | ✓ | | | |
| earthfile | ✓ | ✓ | ✓ | `earthlyls` |
@ -46,9 +47,9 @@
| elixir | ✓ | ✓ | ✓ | `elixir-ls` |
| elm | ✓ | ✓ | | `elm-language-server` |
| elvish | ✓ | | | `elvish` |
| env | ✓ | | | |
| env | ✓ | | | |
| erb | ✓ | | | |
| erlang | ✓ | ✓ | | `erlang_ls` |
| erlang | ✓ | ✓ | | `erlang_ls`, `elp` |
| esdl | ✓ | | | |
| fidl | ✓ | | | |
| fish | ✓ | ✓ | ✓ | |
@ -58,9 +59,10 @@
| gas | ✓ | ✓ | | |
| gdscript | ✓ | ✓ | ✓ | |
| gemini | ✓ | | | |
| gherkin | ✓ | | | |
| git-attributes | ✓ | | | |
| git-commit | ✓ | ✓ | | |
| git-config | ✓ | | | |
| git-config | ✓ | | | |
| git-ignore | ✓ | | | |
| git-rebase | ✓ | | | |
| gjs | ✓ | ✓ | ✓ | `typescript-language-server`, `vscode-eslint-language-server`, `ember-language-server` |
@ -82,10 +84,10 @@
| hcl | ✓ | ✓ | ✓ | `terraform-ls` |
| heex | ✓ | ✓ | | `elixir-ls` |
| helm | ✓ | | | `helm_ls` |
| hocon | ✓ | | ✓ | |
| hocon | ✓ | | ✓ | |
| hoon | ✓ | | | |
| hosts | ✓ | | | |
| html | ✓ | | | `vscode-html-language-server` |
| html | ✓ | | | `vscode-html-language-server`, `superhtml` |
| hurl | ✓ | ✓ | ✓ | |
| hyprlang | ✓ | | ✓ | |
| idris | | | | `idris2-lsp` |
@ -97,6 +99,7 @@
| javascript | ✓ | ✓ | ✓ | `typescript-language-server` |
| jinja | ✓ | | | |
| jjdescription | ✓ | | | |
| jq | ✓ | ✓ | | `jq-lsp` |
| jsdoc | ✓ | | | |
| json | ✓ | ✓ | ✓ | `vscode-json-language-server` |
| json5 | ✓ | | | |
@ -125,7 +128,7 @@
| markdown.inline | ✓ | | | |
| matlab | ✓ | ✓ | ✓ | |
| mermaid | ✓ | | | |
| meson | ✓ | | ✓ | |
| meson | ✓ | | ✓ | `mesonlsp` |
| mint | | | | `mint` |
| mojo | ✓ | ✓ | ✓ | `mojo-lsp-server` |
| move | ✓ | | | |
@ -133,7 +136,7 @@
| nasm | ✓ | ✓ | | |
| nickel | ✓ | | ✓ | `nls` |
| nim | ✓ | ✓ | ✓ | `nimlangserver` |
| nix | ✓ | ✓ | | `nil` |
| nix | ✓ | ✓ | | `nil`, `nixd` |
| nu | ✓ | | | `nu` |
| nunjucks | ✓ | | | |
| ocaml | ✓ | | ✓ | `ocamllsp` |
@ -156,12 +159,12 @@
| pod | ✓ | | | |
| ponylang | ✓ | ✓ | ✓ | |
| powershell | ✓ | | | |
| prisma | ✓ | | | `prisma-language-server` |
| prisma | ✓ | | | `prisma-language-server` |
| prolog | | | | `swipl` |
| protobuf | ✓ | ✓ | ✓ | `bufls`, `pb` |
| prql | ✓ | | | |
| purescript | ✓ | ✓ | | `purescript-language-server` |
| python | ✓ | ✓ | ✓ | `pylsp` |
| python | ✓ | ✓ | ✓ | `ruff`, `jedi-language-server`, `pylsp` |
| qml | ✓ | | ✓ | `qmlls` |
| r | ✓ | | | `R` |
| racket | ✓ | | ✓ | `racket` |
@ -184,7 +187,7 @@
| sml | ✓ | | | |
| solidity | ✓ | ✓ | | `solc` |
| spicedb | ✓ | | | |
| sql | ✓ | | | |
| sql | ✓ | | | |
| sshclientconfig | ✓ | | | |
| starlark | ✓ | ✓ | | |
| strace | ✓ | | | |
@ -199,12 +202,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 | ✓ | | ✓ | |
@ -224,6 +229,6 @@
| xit | ✓ | | | |
| xml | ✓ | | ✓ | |
| xtc | ✓ | | | |
| yaml | ✓ | | ✓ | `yaml-language-server`, `ansible-language-server` |
| yaml | ✓ | | ✓ | `yaml-language-server`, `ansible-language-server` |
| yuck | ✓ | | | |
| zig | ✓ | ✓ | ✓ | `zls` |

@ -72,7 +72,7 @@
| `:sort` | Sort ranges in selection. |
| `:rsort` | Sort ranges in selection in reverse order. |
| `:reflow` | Hard-wrap the current selection of lines to a given width. |
| `:tree-sitter-subtree`, `:ts-subtree` | Display tree sitter subtree under cursor, primarily for debugging queries. |
| `:tree-sitter-subtree`, `:ts-subtree` | Display the smallest tree-sitter subtree that spans the primary selection, primarily for debugging queries. |
| `:config-reload` | Refresh user config. |
| `:config-open` | Open the user config.toml file. |
| `:config-open-workspace` | Open the workspace config.toml file. |

@ -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.

@ -145,6 +145,9 @@ Normal mode is the default mode when you launch helix. You can return to it from
| `Alt-i`, `Alt-down` | Shrink syntax tree object selection (**TS**) | `shrink_selection` |
| `Alt-p`, `Alt-left` | Select previous sibling node in syntax tree (**TS**) | `select_prev_sibling` |
| `Alt-n`, `Alt-right` | Select next sibling node in syntax tree (**TS**) | `select_next_sibling` |
| `Alt-a` | Select all sibling nodes in syntax tree (**TS**) | `select_all_siblings` |
| `Alt-e` | Move to end of parent node in syntax tree (**TS**) | `move_parent_node_end` |
| `Alt-b` | Move to start of parent node in syntax tree (**TS**) | `move_parent_node_start` |
### Search
@ -320,10 +323,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

@ -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.

@ -7,3 +7,27 @@ can be accessed via the command `hx --tutor` or `:tutor`.
> 💡 Currently, not all functionality is fully documented, please refer to the
> [key mappings](./keymap.md) list.
## Modes
Helix is a modal editor, meaning it has different modes for different tasks. The main modes are:
* [Normal mode](./keymap.md#normal-mode): For navigation and editing commands. This is the default mode.
* [Insert mode](./keymap.md#insert-mode): For typing text directly into the document. Access by typing `i` in normal mode.
* [Select/extend mode](./keymap.md#select--extend-mode): For making selections and performing operations on them. Access by typing `v` in normal mode.
## Buffers
Buffers are in-memory representations of files. You can have multiple buffers open at once. Use [pickers](./pickers.md) or commands like `:buffer-next` and `:buffer-previous` to open buffers or switch between them.
## Selection-first editing
Inspired by [Kakoune](http://kakoune.org/), Helix follows the `selection → action` model. This means that whatever you are going to act on (a word, a paragraph, a line, etc.) is selected first and the action itself (delete, change, yank, etc.) comes second. A cursor is simply a single width selection.
## Multiple selections
Also inspired by Kakoune, multiple selections are a core mode of interaction in Helix. For example, the standard way of replacing multiple instance of a word is to first select all instances (so there is one selection per instance) and then use the change action (`c`) to edit them all at the same time.
## Motions
Motions are commands that move the cursor or modify selections. They're used for navigation and text manipulation. Examples include `w` to move to the next word, or `f` to find a character. See the [Movement](./keymap.md#movement) section of the keymap for more motions.

@ -126,6 +126,7 @@
# disable fetching and building of tree-sitter grammars in the helix-term build.rs
HELIX_DISABLE_AUTO_GRAMMAR_BUILD = "1";
buildInputs = [stdenv.cc.cc.lib];
nativeBuildInputs = [pkgs.installShellFiles];
# disable tests
doCheck = false;
meta.mainProgram = "hx";
@ -141,6 +142,7 @@
cp contrib/Helix.desktop $out/share/applications
cp logo.svg $out/share/icons/hicolor/scalable/apps/helix.svg
cp contrib/helix.png $out/share/icons/hicolor/256x256/apps
installShellCompletion contrib/completion/hx.{bash,fish,zsh}
'';
});
helix = makeOverridableHelix self.packages.${system}.helix-unwrapped {};

@ -22,7 +22,7 @@ helix-loader = { path = "../helix-loader" }
ropey = { version = "1.6.1", default-features = false, features = ["simd"] }
smallvec = "1.13"
smartstring = "1.0.1"
unicode-segmentation = "1.11"
unicode-segmentation = "1.12"
# unicode-width is changing width definitions
# that both break our logic and disagree with common
# width definitions in terminals, we need to replace it.
@ -57,7 +57,7 @@ textwrap = "0.16.1"
nucleo.workspace = true
parking_lot = "0.12"
globset = "0.4.14"
globset = "0.4.15"
[dev-dependencies]
quickcheck = { version = "1", default-features = false }

@ -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 = [

@ -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),
],
}
});

@ -2692,6 +2692,8 @@ fn pretty_print_tree_impl<W: fmt::Write>(
}
write!(fmt, "({}", node.kind())?;
} else {
write!(fmt, " \"{}\"", node.kind())?;
}
// Handle children.
@ -2950,7 +2952,7 @@ mod test {
#[test]
fn test_pretty_print() {
let source = r#"// Hello"#;
assert_pretty_print("rust", source, "(line_comment)", 0, source.len());
assert_pretty_print("rust", source, "(line_comment \"//\")", 0, source.len());
// A large tree should be indented with fields:
let source = r#"fn main() {
@ -2960,16 +2962,16 @@ mod test {
"rust",
source,
concat!(
"(function_item\n",
"(function_item \"fn\"\n",
" name: (identifier)\n",
" parameters: (parameters)\n",
" body: (block\n",
" parameters: (parameters \"(\" \")\")\n",
" body: (block \"{\"\n",
" (expression_statement\n",
" (macro_invocation\n",
" macro: (identifier)\n",
" (token_tree\n",
" (string_literal\n",
" (string_content)))))))",
" macro: (identifier) \"!\"\n",
" (token_tree \"(\"\n",
" (string_literal \"\"\"\n",
" (string_content) \"\"\") \")\")) \";\") \"}\"))",
),
0,
source.len(),
@ -2981,7 +2983,7 @@ mod test {
// Error nodes are printed as errors:
let source = r#"}{"#;
assert_pretty_print("rust", source, "(ERROR)", 0, source.len());
assert_pretty_print("rust", source, "(ERROR \"}\" \"{\")", 0, source.len());
// Fields broken under unnamed nodes are determined correctly.
// In the following source, `object` belongs to the `singleton_method`
@ -2996,11 +2998,11 @@ mod test {
"ruby",
source,
concat!(
"(singleton_method\n",
" object: (self)\n",
"(singleton_method \"def\"\n",
" object: (self) \".\"\n",
" name: (identifier)\n",
" body: (body_statement\n",
" (true)))"
" (true)) \"end\")"
),
0,
source.len(),

@ -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

@ -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.12.0"
dunce = "1.0.5"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
libloading = "0.8"

@ -225,13 +225,17 @@ 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`, `.jj` 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(".jj").exists()
|| ancestor.join(".helix").exists()
{
return (ancestor.to_owned(), false);
}
}

@ -21,9 +21,9 @@ keywords = ["language", "server", "lsp", "vscode", "lsif"]
license = "MIT"
[dependencies]
bitflags = "1.0.1"
serde = { version = "1.0.34", features = ["derive"] }
serde_json = "1.0.50"
bitflags = "2.6.0"
serde = { version = "1.0.209", features = ["derive"] }
serde_json = "1.0.127"
serde_repr = "0.1"
url = {version = "2.0.0", features = ["serde"]}

@ -132,6 +132,7 @@ pub struct InlineValueEvaluatableExpression {
/// - directly as a text value (class InlineValueText).
/// - as a name to use for a variable lookup (class InlineValueVariableLookup)
/// - as an evaluatable expression (class InlineValueEvaluatableExpression)
///
/// The InlineValue types combines all inline value types into one type.
///
/// @since 3.17.0

@ -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;

@ -22,11 +22,11 @@ helix-lsp-types = { path = "../helix-lsp-types" }
anyhow = "1.0"
futures-executor = "0.3"
futures-util = { version = "0.3", features = ["std", "async-await"], default-features = false }
globset = "0.4.14"
globset = "0.4.15"
log = "0.4"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.39", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] }
tokio = { version = "1.40", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] }
tokio-stream = "0.1.15"
parking_lot = "0.12.3"
arc-swap = "1"

@ -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.12"

@ -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<u64> {
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 {

@ -51,7 +51,7 @@ impl<'a> RopeSliceExt<'a> for RopeSlice<'a> {
if len < text.len() {
return false;
}
self.get_byte_slice(..len - text.len())
self.get_byte_slice(..text.len())
.map_or(false, |start| start == text)
}
@ -137,4 +137,14 @@ mod tests {
}
}
}
#[test]
fn starts_with() {
assert!(RopeSlice::from("asdf").starts_with("a"));
}
#[test]
fn ends_with() {
assert!(RopeSlice::from("asdf").ends_with("f"));
}
}

@ -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 }
@ -53,7 +53,7 @@ log = "0.4"
nucleo.workspace = true
ignore = "0.4"
# markdown doc rendering
pulldown-cmark = { version = "0.11", default-features = false }
pulldown-cmark = { version = "0.12", default-features = false }
# file type detection
content_inspector = "0.2.4"
thiserror = "1.0"
@ -69,15 +69,15 @@ serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
# ripgrep for global search
grep-regex = "0.1.12"
grep-searcher = "0.1.13"
grep-regex = "0.1.13"
grep-searcher = "0.1.14"
[target.'cfg(not(windows))'.dependencies] # https://github.com/vorner/signal-hook/issues/100
signal-hook-tokio = { version = "0.3", features = ["futures-v0_3"] }
libc = "0.2.155"
libc = "0.2.158"
[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.12.0"
same-file = "1.0.1"

@ -846,7 +846,15 @@ impl Application {
}
}
Notification::ShowMessage(params) => {
log::warn!("unhandled window/showMessage: {:?}", params);
if self.config.load().editor.lsp.display_messages {
match params.typ {
lsp::MessageType::ERROR => self.editor.set_error(params.message),
lsp::MessageType::WARNING => {
self.editor.set_warning(params.message)
}
_ => self.editor.set_status(params.message),
}
}
}
Notification::LogMessage(params) => {
log::info!("window/logMessage: {:?}", params);
@ -930,7 +938,7 @@ impl Application {
self.lsp_progress.update(server_id, token, work);
}
if self.config.load().editor.lsp.display_messages {
if self.config.load().editor.lsp.display_progress_messages {
self.editor.set_status(status);
}
}

@ -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<KeyEvent>,
},
}
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<F>(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 `<ret>` 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",
@ -4543,6 +4626,14 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) {
let text = doc.text();
let slice = text.slice(..);
let comment_tokens = doc
.language_config()
.and_then(|config| config.comment_tokens.as_deref())
.unwrap_or(&[]);
// Sort by length to handle Rust's /// vs //
let mut comment_tokens: Vec<&str> = comment_tokens.iter().map(|x| x.as_str()).collect();
comment_tokens.sort_unstable_by_key(|x| std::cmp::Reverse(x.len()));
let mut changes = Vec::new();
for selection in doc.selection(view.id) {
@ -4554,10 +4645,31 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) {
changes.reserve(lines.len());
let first_line_idx = slice.line_to_char(start);
let first_line_idx = skip_while(slice, first_line_idx, |ch| matches!(ch, ' ' | 't'))
.unwrap_or(first_line_idx);
let first_line = slice.slice(first_line_idx..);
let mut current_comment_token = comment_tokens
.iter()
.find(|token| first_line.starts_with(token));
for line in lines {
let start = line_end_char_index(&slice, line);
let mut end = text.line_to_char(line + 1);
end = skip_while(slice, end, |ch| matches!(ch, ' ' | '\t')).unwrap_or(end);
let slice_from_end = slice.slice(end..);
if let Some(token) = comment_tokens
.iter()
.find(|token| slice_from_end.starts_with(token))
{
if Some(token) == current_comment_token {
end += token.chars().count();
end = skip_while(slice, end, |ch| matches!(ch, ' ' | '\t')).unwrap_or(end);
} else {
// update current token, but don't delete this one.
current_comment_token = Some(token);
}
}
let separator = if end == line_end_char_index(&slice, line + 1) {
// the joining line contains only space-characters => don't include a whitespace when joining
@ -5055,6 +5167,8 @@ fn jump_forward(cx: &mut Context) {
}
doc.set_selection(view.id, selection);
// Document we switch to might not have been opened in the view before
doc.ensure_view_init(view.id);
view.ensure_cursor_in_view_center(doc, config.scrolloff);
};
}
@ -5075,6 +5189,8 @@ fn jump_backward(cx: &mut Context) {
}
doc.set_selection(view.id, selection);
// Document we switch to might not have been opened in the view before
doc.ensure_view_init(view.id);
view.ensure_cursor_in_view_center(doc, config.scrolloff);
};
}

@ -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(
@ -3032,7 +3032,7 @@ pub const TYPABLE_COMMAND_LIST: &[TypableCommand] = &[
TypableCommand {
name: "tree-sitter-subtree",
aliases: &["ts-subtree"],
doc: "Display tree sitter subtree under cursor, primarily for debugging queries.",
doc: "Display the smallest tree-sitter subtree that spans the primary selection, primarily for debugging queries.",
fun: tree_sitter_subtree,
signature: CommandSignature::none(),
},

@ -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<KeyEvent>) {
match node {
KeyTrie::MappableCommand(MappableCommand::Macro { .. }) => {}
KeyTrie::MappableCommand(cmd) => {
let name = cmd.name();
if name != "no_op" {

@ -433,7 +433,7 @@ impl<'a> TextRenderer<'a> {
Grapheme::Newline => &self.newline,
};
let in_bounds = self.column_in_bounds(position.col + width - 1);
let in_bounds = self.column_in_bounds(position.col, width);
if in_bounds {
self.surface.set_string(
@ -452,7 +452,6 @@ impl<'a> TextRenderer<'a> {
);
self.surface.set_style(rect, style);
}
if *is_in_indent_area && !is_whitespace {
*last_indent_level = position.col;
*is_in_indent_area = false;
@ -461,8 +460,8 @@ impl<'a> TextRenderer<'a> {
width
}
pub fn column_in_bounds(&self, colum: usize) -> bool {
self.offset.col <= colum && colum < self.viewport.width as usize + self.offset.col
pub fn column_in_bounds(&self, colum: usize, width: usize) -> bool {
self.offset.col <= colum && colum + width <= self.offset.col + self.viewport.width as usize
}
/// Overlay indentation guides ontop of a rendered line

@ -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();
};

@ -164,7 +164,7 @@ impl Decoration for Cursor<'_> {
renderer: &mut TextRenderer,
grapheme: &FormattedGrapheme,
) -> usize {
if renderer.column_in_bounds(grapheme.visual_pos.col)
if renderer.column_in_bounds(grapheme.visual_pos.col, grapheme.width())
&& renderer.offset.row < grapheme.visual_pos.row
{
let position = grapheme.visual_pos - renderer.offset;

@ -98,7 +98,7 @@ impl Renderer<'_, '_> {
fn draw_eol_diagnostic(&mut self, diag: &Diagnostic, row: u16, col: usize) -> u16 {
let style = self.styles.severity_style(diag.severity());
let width = self.renderer.viewport.width;
if !self.renderer.column_in_bounds(col + 1) {
if !self.renderer.column_in_bounds(col + 1, 1) {
return 0;
}
let col = (col - self.renderer.offset.col) as u16;

@ -632,6 +632,41 @@ async fn test_join_selections_space() -> anyhow::Result<()> {
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn test_join_selections_comment() -> anyhow::Result<()> {
test((
indoc! {"\
/// #[a|]#bc
/// def
"},
":lang rust<ret>J",
indoc! {"\
/// #[a|]#bc def
"},
))
.await?;
// Only join if the comment token matches the previous line.
test((
indoc! {"\
#[| // a
// b
/// c
/// d
e
/// f
// g]#
"},
":lang rust<ret>J",
indoc! {"\
#[| // a b /// c d e f // g]#
"},
))
.await?;
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
async fn test_read_file() -> anyhow::Result<()> {
let mut file = tempfile::NamedTempFile::new()?;

@ -20,8 +20,8 @@ helix-core = { path = "../helix-core" }
bitflags = "2.6"
cassowary = "0.3"
unicode-segmentation = "1.11"
crossterm = { version = "0.27", optional = true }
unicode-segmentation = "1.12"
crossterm = { version = "0.28", optional = true }
termini = "1.0"
serde = { version = "1", "optional" = true, features = ["derive"]}
once_cell = "1.19"

@ -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;
}

@ -19,7 +19,7 @@ tokio = { version = "1", features = ["rt", "rt-multi-thread", "time", "sync", "p
parking_lot = "0.12"
arc-swap = { version = "1.7.1" }
gix = { version = "0.64.0", features = ["attributes", "status"], default-features = false, optional = true }
gix = { version = "0.66.0", features = ["attributes", "status"], default-features = false, optional = true }
imara-diff = "0.1.7"
anyhow = "1"
@ -29,4 +29,4 @@ log = "0.4"
git = ["gix"]
[dev-dependencies]
tempfile = "3.10"
tempfile = "3.12"

@ -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<Event>,
diff: Arc<Mutex<DiffInner>>,
diff: Arc<RwLock<DiffInner>>,
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<Mutex<DiffInner>> = Arc::default();
let diff: Arc<RwLock<DiffInner>> = 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,
}

@ -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<Event>,
pub diff: Arc<Mutex<DiffInner>>,
pub diff: Arc<RwLock<DiffInner>>,
pub new_hunks: Vec<Hunk>,
pub diff_finished_notify: Arc<Notify>,
}
@ -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);

@ -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)
}
}

@ -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.12"
# Conversion traits
once_cell = "1.19"

@ -1920,12 +1920,15 @@ impl Document {
return None;
};
let severity = diagnostic.severity.map(|severity| match severity {
lsp::DiagnosticSeverity::ERROR => Error,
lsp::DiagnosticSeverity::WARNING => Warning,
lsp::DiagnosticSeverity::INFORMATION => Info,
lsp::DiagnosticSeverity::HINT => Hint,
severity => unreachable!("unrecognized diagnostic severity: {:?}", severity),
let severity = diagnostic.severity.and_then(|severity| match severity {
lsp::DiagnosticSeverity::ERROR => Some(Error),
lsp::DiagnosticSeverity::WARNING => Some(Warning),
lsp::DiagnosticSeverity::INFORMATION => Some(Info),
lsp::DiagnosticSeverity::HINT => Some(Hint),
severity => {
log::error!("unrecognized diagnostic severity: {:?}", severity);
None
}
});
if let Some(lang_conf) = language_config {

@ -423,7 +423,9 @@ pub fn get_terminal_provider() -> Option<TerminalConfig> {
pub struct LspConfig {
/// Enables LSP
pub enable: bool,
/// Display LSP progress messages below statusline
/// Display LSP messagess from $/progress below statusline
pub display_progress_messages: bool,
/// Display LSP messages from window/showMessage below statusline
pub display_messages: bool,
/// Enable automatic pop up of signature help (parameter hints)
pub auto_signature_help: bool,
@ -441,7 +443,8 @@ impl Default for LspConfig {
fn default() -> Self {
Self {
enable: true,
display_messages: false,
display_progress_messages: false,
display_messages: true,
auto_signature_help: true,
display_signature_help_docs: true,
display_inlay_hints: false,
@ -1290,6 +1293,13 @@ impl Editor {
self.status_msg = Some((error, Severity::Error));
}
#[inline]
pub fn set_warning<T: Into<Cow<'static, str>>>(&mut self, warning: T) {
let warning = warning.into();
log::warn!("editor warning: {}", warning);
self.status_msg = Some((warning, Severity::Warning));
}
#[inline]
pub fn get_status(&self) -> Option<(&Cow<'static, str>, &Severity)> {
self.status_msg.as_ref().map(|(status, sev)| (status, sev))

@ -53,20 +53,34 @@ impl Loader {
/// Loads a theme searching directories in priority order.
pub fn load(&self, name: &str) -> Result<Theme> {
let (theme, warnings) = self.load_with_warnings(name)?;
for warning in warnings {
warn!("Theme '{}': {}", name, warning);
}
Ok(theme)
}
/// Loads a theme searching directories in priority order, returning any warnings
pub fn load_with_warnings(&self, name: &str) -> Result<(Theme, Vec<String>)> {
if name == "default" {
return Ok(self.default());
return Ok((self.default(), Vec::new()));
}
if name == "base16_default" {
return Ok(self.base16_default());
return Ok((self.base16_default(), Vec::new()));
}
let mut visited_paths = HashSet::new();
let theme = self.load_theme(name, &mut visited_paths).map(Theme::from)?;
let (theme, warnings) = self
.load_theme(name, &mut visited_paths)
.map(Theme::from_toml)?;
Ok(Theme {
let theme = Theme {
name: name.into(),
..theme
})
};
Ok((theme, warnings))
}
/// Recursively load a theme, merging with any inherited parent themes.
@ -87,10 +101,7 @@ impl Loader {
let theme_toml = if let Some(parent_theme_name) = inherits {
let parent_theme_name = parent_theme_name.as_str().ok_or_else(|| {
anyhow!(
"Theme: expected 'inherits' to be a string: {}",
parent_theme_name
)
anyhow!("Expected 'inherits' to be a string: {}", parent_theme_name)
})?;
let parent_theme_toml = match parent_theme_name {
@ -181,9 +192,9 @@ impl Loader {
})
.ok_or_else(|| {
if cycle_found {
anyhow!("Theme: cycle found in inheriting: {}", name)
anyhow!("Cycle found in inheriting: {}", name)
} else {
anyhow!("Theme: file not found for: {}", name)
anyhow!("File not found for: {}", name)
}
})
}
@ -220,19 +231,11 @@ pub struct Theme {
impl From<Value> for Theme {
fn from(value: Value) -> Self {
if let Value::Table(table) = value {
let (styles, scopes, highlights) = build_theme_values(table);
Self {
styles,
scopes,
highlights,
..Default::default()
}
} else {
warn!("Expected theme TOML value to be a table, found {:?}", value);
Default::default()
let (theme, warnings) = Theme::from_toml(value);
for warning in warnings {
warn!("{}", warning);
}
theme
}
}
@ -242,31 +245,29 @@ impl<'de> Deserialize<'de> for Theme {
D: Deserializer<'de>,
{
let values = Map::<String, Value>::deserialize(deserializer)?;
let (styles, scopes, highlights) = build_theme_values(values);
Ok(Self {
styles,
scopes,
highlights,
..Default::default()
})
let (theme, warnings) = Theme::from_keys(values);
for warning in warnings {
warn!("{}", warning);
}
Ok(theme)
}
}
fn build_theme_values(
mut values: Map<String, Value>,
) -> (HashMap<String, Style>, Vec<String>, Vec<Style>) {
) -> (HashMap<String, Style>, Vec<String>, Vec<Style>, Vec<String>) {
let mut styles = HashMap::new();
let mut scopes = Vec::new();
let mut highlights = Vec::new();
let mut warnings = Vec::new();
// TODO: alert user of parsing failures in editor
let palette = values
.remove("palette")
.map(|value| {
ThemePalette::try_from(value).unwrap_or_else(|err| {
warn!("{}", err);
warnings.push(err);
ThemePalette::default()
})
})
@ -279,7 +280,7 @@ fn build_theme_values(
for (name, style_value) in values {
let mut style = Style::default();
if let Err(err) = palette.parse_style(&mut style, style_value) {
warn!("{}", err);
warnings.push(err);
}
// these are used both as UI and as highlights
@ -288,7 +289,7 @@ fn build_theme_values(
highlights.push(style);
}
(styles, scopes, highlights)
(styles, scopes, highlights, warnings)
}
impl Theme {
@ -354,6 +355,27 @@ impl Theme {
.all(|color| !matches!(color, Some(Color::Rgb(..))))
})
}
fn from_toml(value: Value) -> (Self, Vec<String>) {
if let Value::Table(table) = value {
Theme::from_keys(table)
} else {
warn!("Expected theme TOML value to be a table, found {:?}", value);
Default::default()
}
}
fn from_keys(toml_keys: Map<String, Value>) -> (Self, Vec<String>) {
let (styles, scopes, highlights, load_errors) = build_theme_values(toml_keys);
let theme = Self {
styles,
scopes,
highlights,
..Default::default()
};
(theme, load_errors)
}
}
struct ThemePalette {
@ -408,7 +430,7 @@ impl ThemePalette {
if let Ok(index) = s.parse::<u8>() {
return Ok(Color::Indexed(index));
}
Err(format!("Theme: malformed ANSI: {}", s))
Err(format!("Malformed ANSI: {}", s))
}
fn hex_string_to_rgb(s: &str) -> Result<Color, String> {
@ -422,13 +444,13 @@ impl ThemePalette {
}
}
Err(format!("Theme: malformed hexcode: {}", s))
Err(format!("Malformed hexcode: {}", s))
}
fn parse_value_as_str(value: &Value) -> Result<&str, String> {
value
.as_str()
.ok_or(format!("Theme: unrecognized value: {}", value))
.ok_or(format!("Unrecognized value: {}", value))
}
pub fn parse_color(&self, value: Value) -> Result<Color, String> {
@ -445,14 +467,14 @@ impl ThemePalette {
value
.as_str()
.and_then(|s| s.parse().ok())
.ok_or(format!("Theme: invalid modifier: {}", value))
.ok_or(format!("Invalid modifier: {}", value))
}
pub fn parse_underline_style(value: &Value) -> Result<UnderlineStyle, String> {
value
.as_str()
.and_then(|s| s.parse().ok())
.ok_or(format!("Theme: invalid underline style: {}", value))
.ok_or(format!("Invalid underline style: {}", value))
}
pub fn parse_style(&self, style: &mut Style, value: Value) -> Result<(), String> {
@ -462,9 +484,7 @@ impl ThemePalette {
"fg" => *style = style.fg(self.parse_color(value)?),
"bg" => *style = style.bg(self.parse_color(value)?),
"underline" => {
let table = value
.as_table_mut()
.ok_or("Theme: underline must be table")?;
let table = value.as_table_mut().ok_or("Underline must be table")?;
if let Some(value) = table.remove("color") {
*style = style.underline_color(self.parse_color(value)?);
}
@ -473,13 +493,11 @@ impl ThemePalette {
}
if let Some(attr) = table.keys().next() {
return Err(format!("Theme: invalid underline attribute: {attr}"));
return Err(format!("Invalid underline attribute: {attr}"));
}
}
"modifiers" => {
let modifiers = value
.as_array()
.ok_or("Theme: modifiers should be an array")?;
let modifiers = value.as_array().ok_or("Modifiers should be an array")?;
for modifier in modifiers {
if modifier
@ -492,7 +510,7 @@ impl ThemePalette {
}
}
}
_ => return Err(format!("Theme: invalid style attribute: {}", name)),
_ => return Err(format!("Invalid style attribute: {}", name)),
}
}
} else {

@ -16,6 +16,7 @@ bicep-langserver = { command = "bicep-langserver" }
bitbake-language-server = { command = "bitbake-language-server" }
bufls = { command = "bufls", args = ["serve"] }
cairo-language-server = { command = "cairo-language-server", args = [] }
circom-lsp = { command = "circom-lsp" }
cl-lsp = { command = "cl-lsp", args = [ "stdio" ] }
clangd = { command = "clangd" }
clojure-lsp = { command = "clojure-lsp" }
@ -32,6 +33,7 @@ dot-language-server = { command = "dot-language-server", args = ["--stdio"] }
earthlyls = { command = "earthlyls" }
elixir-ls = { command = "elixir-ls", config = { elixirLS.dialyzerEnabled = false } }
elm-language-server = { command = "elm-language-server" }
elp = { command = "elp", args = ["server"] }
elvish = { command = "elvish", args = ["-lsp"] }
erlang-ls = { command = "erlang_ls" }
forc = { command = "forc", args = ["lsp"] }
@ -44,21 +46,25 @@ haskell-language-server = { command = "haskell-language-server-wrapper", args =
idris2-lsp = { command = "idris2-lsp" }
intelephense = { command = "intelephense", args = ["--stdio"] }
jdtls = { command = "jdtls" }
jedi = { command = "jedi-language-server" }
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"] }
kotlin-language-server = { command = "kotlin-language-server" }
lean = { command = "lean", args = [ "--server" ] }
lean = { command = "lean", args = [ "--server", "--memory=1024" ] }
ltex-ls = { command = "ltex-ls" }
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" }
@ -81,6 +87,7 @@ racket = { command = "racket", args = ["-l", "racket-langserver"] }
regols = { command = "regols" }
rescript-language-server = { command = "rescript-language-server", args = ["--stdio"] }
robotframework_ls = { command = "robotframework_ls" }
ruff = { command = "ruff", args = ["server"] }
serve-d = { command = "serve-d" }
slint-lsp = { command = "slint-lsp", args = [] }
solargraph = { command = "solargraph", args = ["stdio"] }
@ -88,12 +95,15 @@ solc = { command = "solc", args = ["--lsp"] }
sourcekit-lsp = { command = "sourcekit-lsp" }
svlangserver = { command = "svlangserver", args = [] }
swipl = { command = "swipl", args = [ "-g", "use_module(library(lsp_server))", "-g", "lsp_server:main", "-t", "halt", "--", "stdio" ] }
superhtml = { command = "superhtml", args = ["lsp"]}
tailwindcss-ls = { command = "tailwindcss-language-server", args = ["--stdio"] }
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" }
vale-ls = { command = "vale-ls" }
vhdl_ls = { command = "vhdl_ls", args = [] }
vlang-language-server = { command = "v-analyzer" }
vscode-css-language-server = { command = "vscode-css-language-server", args = ["--stdio"], config = { provideFormatter = true, css = { validate = { enable = true } } } }
@ -439,6 +449,8 @@ file-types = [
{ glob = ".watchmanconfig" },
"avsc",
{ glob = ".prettierrc" },
"ldtk",
"ldtkl",
]
language-servers = [ "vscode-json-language-server" ]
auto-format = true
@ -766,6 +778,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"
@ -813,9 +842,9 @@ source = { git = "https://github.com/serenadeai/tree-sitter-scss", rev = "c478c6
name = "html"
scope = "text.html.basic"
injection-regex = "html"
file-types = ["html", "htm", "shtml", "xhtml", "xht", "jsp", "asp", "aspx", "jshtm", "volt", "rhtml"]
file-types = ["html", "htm", "shtml", "xhtml", "xht", "jsp", "asp", "aspx", "jshtm", "volt", "rhtml", "cshtml"]
block-comment-tokens = { start = "<!--", end = "-->" }
language-servers = [ "vscode-html-language-server" ]
language-servers = [ "vscode-html-language-server", "superhtml" ]
auto-format = true
indent = { tab-width = 2, unit = " " }
@ -831,7 +860,7 @@ file-types = ["py", "pyi", "py3", "pyw", "ptl", "rpy", "cpy", "ipy", "pyt", { gl
shebangs = ["python"]
roots = ["pyproject.toml", "setup.py", "poetry.lock", "pyrightconfig.json"]
comment-token = "#"
language-servers = [ "pylsp" ]
language-servers = ["ruff", "jedi", "pylsp"]
# TODO: pyls needs utf-8 offsets
indent = { tab-width = 4, unit = " " }
@ -857,7 +886,7 @@ indent = { tab-width = 2, unit = " " }
[[grammar]]
name = "nickel"
source = { git = "https://github.com/nickel-lang/tree-sitter-nickel", rev = "e1d9337864d209898a08c26b8cd4c2dd14c15148" }
source = { git = "https://github.com/nickel-lang/tree-sitter-nickel", rev = "88d836a24b3b11c8720874a1a9286b8ae838d30a" }
[[language]]
name = "nix"
@ -866,7 +895,7 @@ injection-regex = "nix"
file-types = ["nix"]
shebangs = []
comment-token = "#"
language-servers = [ "nil" ]
language-servers = [ "nil", "nixd" ]
indent = { tab-width = 2, unit = " " }
[[grammar]]
@ -888,6 +917,7 @@ file-types = [
"podspec",
"rjs",
"rbi",
"rbs",
{ glob = "rakefile" },
{ glob = "gemfile" },
{ glob = "Rakefile" },
@ -948,6 +978,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" },
@ -1769,7 +1801,7 @@ roots = ["rebar.config"]
shebangs = ["escript"]
comment-token = "%%"
indent = { tab-width = 4, unit = " " }
language-servers = [ "erlang-ls" ]
language-servers = [ "erlang-ls", "elp" ]
[[grammar]]
name = "erlang"
@ -1803,7 +1835,7 @@ auto-format = true
[[grammar]]
name = "hcl"
source = { git = "https://github.com/MichaHoffmann/tree-sitter-hcl", rev = "3cb7fc28247efbcb2973b97e71c78838ad98a583" }
source = { git = "https://github.com/tree-sitter-grammars/tree-sitter-hcl", rev = "9e3ec9848f28d26845ba300fd73c740459b83e9b" }
[[language]]
name = "tfvars"
@ -1855,7 +1887,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"
@ -2107,7 +2139,7 @@ language-servers = [ "ols" ]
comment-token = "//"
block-comment-tokens = { start = "/*", end = "*/" }
indent = { tab-width = 4, unit = "\t" }
formatter = { command = "odinfmt", args = [ "-stdin", "true" ] }
formatter = { command = "odinfmt", args = [ "-stdin" ] }
[language.debugger]
name = "lldb-dap"
@ -2143,6 +2175,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"
@ -2373,7 +2406,7 @@ language-servers = [ "slint-lsp" ]
[[grammar]]
name = "slint"
source = { git = "https://github.com/slint-ui/tree-sitter-slint", rev = "4a0558cc0fcd7a6110815b9bbd7cc12d7ab31e74" }
source = { git = "https://github.com/slint-ui/tree-sitter-slint", rev = "34ccfd58d3baee7636f62d9326f32092264e8407" }
[[language]]
name = "task"
@ -3082,7 +3115,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 +3172,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 +3249,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 +3772,41 @@ 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" }
[[language]]
name = "circom"
scope = "source.circom"
injection-regex = "circom"
file-types = ["circom"]
roots = ["package.json"]
comment-tokens = "//"
indent = { tab-width = 4, unit = " " }
auto-format = false
language-servers = ["circom-lsp"]
[[grammar]]
name = "circom"
source = { git = "https://github.com/Decurity/tree-sitter-circom", rev = "02150524228b1e6afef96949f2d6b7cc0aaf999e" }

@ -0,0 +1,142 @@
; identifiers
; -----------
(identifier) @variable
; Pragma
; -----------
(pragma_directive) @keyword.directive
; Include
; -----------
(include_directive) @keyword.directive
; Literals
; --------
(string) @string
(int_literal) @constant.numeric.integer
(comment) @comment
; Definitions
; -----------
(function_definition
name: (identifier) @keyword.function)
(template_definition
name: (identifier) @keyword.function)
; Use contructor coloring for special functions
(main_component_definition) @constructor
; Invocations
(call_expression . (identifier) @function)
; Function parameters
(parameter name: (identifier) @variable.parameter)
; Members
(member_expression property: (property_identifier) @variable.other.member)
; Tokens
; -------
; Keywords
[
"signal"
"var"
"component"
] @keyword.storage.type
[ "include" ] @keyword.control.import
[
"public"
"input"
"output"
] @keyword.storage.modifier
[
"for"
"while"
] @keyword.control.repeat
[
"if"
"else"
] @keyword.control.conditional
[
"return"
] @keyword.control.return
[
"function"
"template"
] @keyword.function
; Punctuation
[
"("
")"
"["
"]"
"{"
"}"
] @punctuation.bracket
[
"."
","
";"
] @punctuation.delimiter
; Operators
; https://docs.circom.io/circom-language/basic-operators
[
"="
"?"
"&&"
"||"
"!"
"<"
">"
"<="
">="
"=="
"!="
"+"
"-"
"*"
"**"
"/"
"\\"
"%"
"+="
"-="
"*="
"**="
"/="
"\\="
"%="
"++"
"--"
"&"
"|"
"~"
"^"
">>"
"<<"
"&="
"|="
; "\~=" ; bug, uncomment and circom will not highlight
"^="
">>="
"<<="
] @operator
[
"<=="
"==>"
"<--"
"-->"
"==="
] @operator

@ -0,0 +1,9 @@
(function_definition) @local.scope
(template_definition) @local.scope
(main_component_definition) @local.scope
(block_statement) @local.scope
(parameter name: (identifier) @local.definition) @local.definition
(identifier) @local.reference

@ -0,0 +1,4 @@
(comment) @comment.inside
(comment)+ @comment.around

@ -0,0 +1,6 @@
(comment) @comment.inside
(comment)+ @comment.around
(variable_assignment
(_) @entry.inside) @entry.around

@ -101,19 +101,19 @@
]
)
; non-builtin command names
(command name: (word) @function)
; derived from builtin -n (fish 3.2.2)
; derived from builtin -n (fish 3.7.1)
(command
name: [
(word) @function.builtin
(#match? @function.builtin "^(\.|:|_|alias|argparse|bg|bind|block|breakpoint|builtin|cd|command|commandline|complete|contains|count|disown|echo|emit|eval|exec|exit|fg|functions|history|isatty|jobs|math|printf|pwd|random|read|realpath|set|set_color|source|status|string|test|time|type|ulimit|wait)$")
]
(word) @function.builtin
(#any-of? @function.builtin "abbr" "alias" "and" "argparse" "begin" "bg" "bind" "block" "break" "breakpoint" "builtin" "case" "cd" "command" "commandline" "complete" "contains" "continue" "count" "disown" "echo" "else" "emit" "end" "eval" "exec" "exit" "false" "fg" "for" "function" "functions" "history" "if" "isatty" "jobs" "math" "not" "or" "path" "printf" "pwd" "random" "read" "realpath" "return" "set" "set_color" "source" "status" "string" "switch" "test" "time" "true" "type" "ulimit" "wait" "while")
]
)
(test_command "test" @function.builtin)
; non-builtin command names
(command name: (word) @function)
;; Functions
(function_definition ["function" "end"] @keyword.function)

@ -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,10 +180,18 @@
"]"
"[|"
"|]"
"{|"
"|}"
"[<"
">]"
] @punctuation.bracket
(format_string_eval
[
"{"
"}"
] @punctuation.special)
[
","
";"
@ -36,15 +204,16 @@
"<"
"-"
"~"
"->"
"<-"
"&&"
"||"
":>"
":?>"
(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
[
(type)
(const)
] @constant
[
(union_type_case)
(rules (rule (identifier_pattern)))
] @type.enum
(fsi_directive_decl (string) @namespace)
[
(import_decl (long_identifier))
(named_module (long_identifier))
(namespace (long_identifier))
(named_module
name: (long_identifier) )
(namespace
name: (long_identifier) )
] @namespace
"null"
] @constant.builtin
(match_expression "with" @keyword.control.conditional)
(dot_expression
base: (long_identifier_or_op) @variable.other.member
field: (long_identifier_or_op) @function)
((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"))
[
;;(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
(preproc_if
[
"#if" @keyword.directive
"#endif" @keyword.directive
]
condition: (_)? @keyword.directive)
[
(string)
(triple_quoted_string)
] @string
(preproc_else
"#else" @keyword.directive)
[
(int)
(int16)
(int32)
(int64)
(float)
(decimal)
] @constant.numeric
((long_identifier
(identifier)+ @namespace
.
(identifier)))
(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" ))

@ -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"))

@ -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
;------------
(function_or_value_defn) @local.definition
(value_declaration_left
.
[
(_ (identifier) @local.definition)
(_ (_ (identifier) @local.definition))
(_ (_ (_ (identifier) @local.definition)))
(_ (_ (_ (_ (identifier) @local.definition))))
(_ (_ (_ (_ (_ (identifier) @local.definition)))))
(_ (_ (_ (_ (_ (_ (identifier) @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))))))
])
))

@ -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

@ -0,0 +1,6 @@
(comment) @comment.inside
(comment)+ @comment.around
(variable
(_) @entry.inside) @entry.around

@ -4,3 +4,8 @@
(function_arguments
((_) @parameter.inside . ","? @parameter.around) @parameter.around)
(attribute
(_) @entry.inside) @entry.around
(tuple
(_) @entry.around)

@ -0,0 +1,10 @@
(comment) @comment.inside
(comment)+ @comment.around
(pair
(_) @entry.inside) @entry.around
(array
(_) @entry.around)

@ -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"))
[
"<"

@ -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

@ -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")))))

@ -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)

@ -0,0 +1,8 @@
(comment) @comment.inside
(comment)+ @comment.around
(funcdef
(query) @function.inside) @function.around
(objectkeyval
(_) @entry.inside) @entry.around

@ -1,5 +1,3 @@
; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/folds.scm>
; Define collapse points
([

@ -1,5 +1,3 @@
; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/highlights.scm>
; 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)
; Functions
(shell_variable_name) @variable
(recipe_header
name: (identifier) @function)
; Functions
(dependency
(recipe
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"))
; highlight known attributes (filtering does not always work)
name: (_) @keyword.function)
; 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

@ -1,5 +1,3 @@
; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/indents.scm>
;
; This query specifies how to auto-indent logical blocks.
;
; Better documentation with diagrams is in https://docs.helix-editor.com/guides/indent.html

@ -1,5 +1,3 @@
; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/injections.scm>
;
; 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

@ -1,5 +1,3 @@
; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/locals.scm>
;
; 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)
name: (identifier) @local.definition)
(module
(mod
name: (identifier) @local.definition)
(parameter
(recipe_parameter
name: (identifier) @local.definition)
(recipe_header
(recipe
name: (identifier) @local.definition)
; References
(alias
right: (identifier) @local.reference)
(function_call
name: (identifier) @local.reference)
(dependency
(function_call
name: (identifier) @local.reference)
(dependency_expression
(recipe_dependency
name: (identifier) @local.reference)
(value

@ -1,18 +1,19 @@
; From <https://github.com/IndianBoy42/tree-sitter-just/blob/6c2f018ab1d90946c0ce029bb2f7d57f56895dff/queries-flavored/helix/textobjects.scm>
;
; 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

@ -23,17 +23,19 @@
(let_in_block
"let" @keyword
"rec"? @keyword
"in" @keyword
)
(let_binding
pat: (pattern
(ident) @variable
)
"in" @keyword
)
(fun_expr
"fun" @keyword.function
pats:
(pattern
id: (ident) @variable.parameter
)+
(pattern_fun (ident) @variable.parameter)+
"=>" @operator
)
(record_field) @variable.other.member

@ -7,3 +7,6 @@
(function_expression
body: (_) @function.inside) @function.around
(binding
(_) @entry.inside) @entry.around

@ -0,0 +1,17 @@
(model_declaration
((statement_block) @class.inside)) @class.around
(call_expression
(arguments (_) @parameter.inside . ","? @parameter.around) @parameter.around)
(column_declaration) @entry.around
(array (_) @entry.around)
(assignment_expression
(_) @entry.inside) @entry.around
(developer_comment) @comment.inside
(developer_comment)+ @comment.around

@ -55,6 +55,55 @@
"'" @label
(identifier) @label)
; ---
; Prelude
; ---
((identifier) @type.enum.variant.builtin
(#any-of? @type.enum.variant.builtin "Some" "None" "Ok" "Err"))
((type_identifier) @type.builtin
(#any-of?
@type.builtin
"Send"
"Sized"
"Sync"
"Unpin"
"Drop"
"Fn"
"FnMut"
"FnOnce"
"AsMut"
"AsRef"
"From"
"Into"
"DoubleEndedIterator"
"ExactSizeIterator"
"Extend"
"IntoIterator"
"Iterator"
"Option"
"Result"
"Clone"
"Copy"
"Debug"
"Default"
"Eq"
"Hash"
"Ord"
"PartialEq"
"PartialOrd"
"ToOwned"
"Box"
"String"
"ToString"
"Vec"
"FromIterator"
"TryFrom"
"TryInto"))
; ---
; Punctuation
; ---

@ -0,0 +1,4 @@
(comment) @comment.inside
(comment)+ @comment.around

@ -0,0 +1,12 @@
[
(annotation_definition)
(enum_definition)
(exception_definition)
(function_definition)
(senum_definition)
(service_definition)
(struct_definition)
(union_definition)
(comment)
] @fold

@ -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

@ -0,0 +1,2 @@
((comment) @injection.content
(#set! injection.language "comment"))

@ -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

@ -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

@ -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

@ -0,0 +1,5 @@
([
(single_line_comment)
(multi_line_comment)
] @injection.content
(#set! injection.language "comment"))

@ -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

@ -4,3 +4,7 @@
(function_identifier
(function_identifier
(simple_identifier) @function.inside)))) @function.around
(comment) @comment.inside
(comment)+ @comment.around

@ -6,9 +6,13 @@
(attribute
(attribute_name) @attribute
(quoted_attribute_value
(attribute_value) @string)
)
[(attribute_value) (quoted_attribute_value)]? @string)
(directive_attribute
(directive_name) @attribute
(directive_argument)? @attribute
(directive_modifiers)? @attribute
[(attribute_value) (quoted_attribute_value)]? @string)
(comment) @comment
@ -18,4 +22,7 @@
"</"
"{{"
"}}"
"/>"
] @punctuation.bracket
"=" @punctuation.delimiter

@ -0,0 +1,7 @@
(comment) @comment.inside
(comment)+ @comment.around
(block_mapping_pair
(_) @entry.inside) @entry.around

@ -68,8 +68,10 @@
"ui.statusline.select" = { fg = "my_gray7", bg = "my_black", modifiers = ["bold"] }
"ui.text.focus" = "my_white1"
"ui.text" = "my_white1"
"ui.virtual.inlay-hint" = { fg = "my_gray4", bg="my_black", modifiers = ["normal"] }
"ui.virtual.inlay-hint.parameter" = { fg = "my_gray4", modifiers = ["normal"] }
# Invalid modifier: "normal". See 'https://github.com/helix-editor/helix/issues/5709'
"ui.virtual.inlay-hint" = { fg = "my_gray4", bg="my_black" } #, modifiers = ["normal"] }
# "ui.virtual.inlay-hint.parameter" = { fg = "my_gray4", modifiers = ["normal"] }
"ui.virtual.inlay-hint.parameter" = "my_gray4"
"ui.virtual.inlay-hint.type" = { fg = "my_gray4", modifiers = ["italic"] }
"ui.virtual.jump-label" = { fg = "my_yellow2", modifiers = ["bold"] }
"ui.virtual.ruler" = { bg = "my_gray1" }

@ -59,8 +59,10 @@
"ui.text.focus" = { fg = "bogster-fg1", modifiers= ["bold"] }
"ui.virtual.whitespace" = "bogster-base5"
"ui.virtual.ruler" = { bg = "bogster-base0" }
"ui.virtual.jump-label" = { fg = "bogster-base0", bg = "bogster-yellow", modifiers = [ "bold" ] }
"ui.selection" = { bg = "bogster-base3" }
"ui.selection" = { bg = "bogster-base2" }
"ui.selection.primary" = { bg = "bogster-base3" }
"ui.cursor.match" = { fg = "bogster-base3", bg = "bogster-orange" }
"ui.cursor" = { fg = "bogster-base5", modifiers = ["reversed"] }

@ -28,6 +28,8 @@
"ui.virtual.jump-label" = { fg = "lightblue", modifiers = ["italic", "bold"] }
"ui.bufferline" = { fg = "grey04", bg = "grey00" }
"ui.bufferline.active" = { fg = "grey07", bg = "grey02" }
"ui.picker.header.column" = { fg = "grey05", modifiers = ["italic", "bold"] }
"ui.picker.header.column.active" = { fg = "grey05", bg = "grey03", modifiers = ["italic", "bold"] }
"operator" = "grey05"
"variable" = "white"

@ -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" }

@ -68,7 +68,8 @@
"ui.menu.selected" = { fg = "dark_red", bg = "light_blue" }
"ui.selection" = { bg = "lightgoldenrod1" }
"ui.selection.primary" = { bg = "lightgoldenrod2" }
"ui.virtual.whitespace" = "highlight"
# Malformed ANSI: highlight. See 'https://github.com/helix-editor/helix/issues/5709'
# "ui.virtual.whitespace" = "highlight"
"ui.virtual.ruler" = { bg = "gray95" }
"ui.virtual.inlay-hint" = { fg = "gray75" }
"ui.cursorline.primary" = { bg = "darkseagreen2" }

@ -65,7 +65,7 @@
"diff.minus" = "red"
"ui.background" = { bg = "bg0" }
"ui.background.separator" = "bg_visual"
"ui.background.separator" = "grey0"
"ui.cursor" = { fg = "bg1", bg = "grey2" }
"ui.cursor.insert" = { fg = "bg0", bg = "grey1" }
"ui.cursor.select" = { fg = "bg0", bg = "blue" }
@ -90,6 +90,7 @@
"bold",
] }
"ui.popup" = { fg = "grey2", bg = "bg2" }
"ui.picker.header" = { modifiers = ["bold", "underlined"] }
"ui.window" = { fg = "bg4", bg = "bg_dim" }
"ui.help" = { fg = "fg", bg = "bg2" }
"ui.text" = "fg"

@ -64,7 +64,7 @@
"diff.minus" = "red"
"ui.background" = { bg = "bg0" }
"ui.background.separator" = "bg_visual"
"ui.background.separator" = "grey0"
"ui.cursor" = { fg = "bg1", bg = "grey2" }
"ui.cursor.insert" = { fg = "bg0", bg = "grey1" }
"ui.cursor.select" = { fg = "bg0", bg = "blue" }
@ -89,6 +89,7 @@
"bold",
] }
"ui.popup" = { fg = "grey2", bg = "bg2" }
"ui.picker.header" = { modifiers = ["bold", "underlined"] }
"ui.window" = { fg = "bg4", bg = "bg_dim" }
"ui.help" = { fg = "fg", bg = "bg2" }
"ui.text" = "fg"

@ -61,8 +61,11 @@
"ui.virtual" = { fg = "base5", bg = "base6" }
"ui.virtual.whitespace" = { fg = "base5" }
"ui.virtual.ruler" = { bg = "base6" }
"ui.virtual.inlay-hint" = { fg = "base4", modifiers = ["normal"] }
"ui.virtual.inlay-hint.parameter" = { fg = "base3", modifiers = ["normal"] }
# Invalid modifier: "normal". See 'https://github.com/helix-editor/helix/issues/5709'
# "ui.virtual.inlay-hint" = { fg = "base4", modifiers = ["normal"] }
# "ui.virtual.inlay-hint.parameter" = { fg = "base3", modifiers = ["normal"] }
"ui.virtual.inlay-hint" = "base4"
"ui.virtual.inlay-hint.parameter" = "base3"
"ui.virtual.inlay-hint.type" = { fg = "base3", modifiers = ["italic"] }
"ui.linenr" = { bg = "base6" }

@ -67,6 +67,7 @@
"ui.virtual.ruler" = { bg = "bg1" }
"ui.virtual.inlay-hint" = { fg = "bg7" }
"ui.virtual.wrap" = { fg = "bg2" }
"ui.virtual.jump-label" = { fg = "red3", modifiers = ["bold"] }
"diagnostic.warning" = { underline = { color = "orange1", style = "dashed" } }
"diagnostic.error" = { underline = { color = "red3", style = "dashed" } }

@ -94,6 +94,8 @@
"ui.menu" = { fg = "fg1", bg = "bg2" }
"ui.menu.selected" = { fg = "bg2", bg = "blue1", modifiers = ["bold"] }
"ui.popup" = { bg = "bg1" }
"ui.picker.header.column" = { underline.style = "line" }
"ui.picker.header.column.active" = { modifiers = ["bold"], underline.style = "line" }
"ui.selection" = { bg = "bg2" }
"ui.selection.primary" = { bg = "bg3" }

@ -33,7 +33,7 @@ tag = "red"
"ui.selection" = { bg = "selection" }
"ui.virtual.indent-guide" = { fg = "gray" }
"ui.virtual.whitespace" = { fg = "light-gray" }
"ui.virtual.ruler" = { bg ="dark-bg" }
"ui.virtual.ruler" = { bg = "dark-bg" }
"ui.statusline" = { bg = "dark-bg", fg = "light-gray" }
"ui.popup" = { bg = "dark-bg", fg = "orange" }
"ui.help" = { bg = "dark-bg", fg = "orange" }
@ -43,6 +43,8 @@ tag = "red"
"ui.bufferline" = { bg = "dark-bg", fg = "light-gray" }
"ui.bufferline.active" = { bg = "dark-bg", fg = "orange" }
"ui.virtual.jump-label" = { fg = "pink", modifiers = ["bold"] }
"ui.picker.header.column" = { fg = "orange", underline.style = "line" }
"ui.picker.header.column.active" = { fg = "purple", modifiers = ["bold"], underline.style = "line" }
# Diagnostics
"diagnostic" = { underline = { style = "curl" } }

@ -0,0 +1,128 @@
# Author : Chromo-residuum-opec <development.0extl@simplelogin.com>
"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"

@ -0,0 +1,39 @@
# Author : Chromo-residuum-opec <development.0extl@simplelogin.com>
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"

@ -25,7 +25,8 @@
"ui.statusline.normal" = { fg = "sumiInk0", bg = "crystalBlue", modifiers = ["bold"] }
"ui.statusline.insert" = { fg = "sumiInk0", bg = "autumnGreen", modifiers = ["bold"] }
"ui.statusline.select" = { fg = "sumiInk0", bg = "oniViolet", modifiers = ["bold"] }
"ui.statusline.separator" = { fg = "", bg = "" }
# Malformed ANSI: "". See 'https://github.com/helix-editor/helix/issues/5709'
# "ui.statusline.separator" = { fg = "", bg = "" }
"ui.bufferline" = { fg = "fujiGray", bg = "sumiInk0" }
"ui.bufferline.active" = { fg = "oldWhite", bg = "sumiInk0" }

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

Loading…
Cancel
Save