mirror of
https://github.com/helix-editor/helix.git
synced 2024-11-22 01:16:18 +04:00
Merge branch 'rewrite-and-refactor-all-documentation' of github.com:David-Else/helix into rewrite-and-refactor-all-documentation
This commit is contained in:
commit
7c8404248f
77
Cargo.lock
generated
77
Cargo.lock
generated
@ -728,7 +728,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c52b625ad8cc360a0b7f426266f21fb07bd49b8f4ccf1b3ca7bc89424db1dec4"
|
||||
dependencies = [
|
||||
"git-hash",
|
||||
"hashbrown 0.13.1",
|
||||
"hashbrown 0.13.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1104,9 +1104,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.13.1"
|
||||
version = "0.13.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ff8ae62cd3a9102e5637afc8452c55acf3844001bd5374e0b0bd7b6616c038"
|
||||
checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
|
||||
dependencies = [
|
||||
"ahash 0.8.2",
|
||||
]
|
||||
@ -1121,7 +1121,7 @@ dependencies = [
|
||||
"chrono",
|
||||
"encoding_rs",
|
||||
"etcetera",
|
||||
"hashbrown 0.13.1",
|
||||
"hashbrown 0.13.2",
|
||||
"helix-loader",
|
||||
"imara-diff",
|
||||
"log",
|
||||
@ -1379,6 +1379,16 @@ dependencies = [
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown 0.12.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "1.0.8"
|
||||
@ -1427,9 +1437,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.137"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7fcc620a3bff7cdd7a365be3376c97191aeaccc2a603e600951e452615bf89"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
@ -1546,6 +1556,15 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom8"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
@ -1867,6 +1886,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c68e921cef53841b8925c2abadd27c9b891d9613bdc43d6b823062866df38e8"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
@ -2122,9 +2150,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.24.1"
|
||||
version = "1.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae"
|
||||
checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
@ -2164,11 +2192,36 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.5.10"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"
|
||||
checksum = "4fb9d890e4dc9298b70f740f615f2e05b9db37dce531f6b24fb77ac993f9f217"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"nom8",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2336,9 +2389,9 @@ checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.3.0"
|
||||
version = "4.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
|
||||
checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
|
||||
dependencies = [
|
||||
"either",
|
||||
"libc",
|
||||
|
@ -1,6 +1,5 @@
|
||||
# Commands
|
||||
|
||||
Command mode, similar to Vim, can be activated by pressing `:`. The built-in
|
||||
commands are:
|
||||
Command mode, similar to Vim, can be activated by pressing `:`. The built-in commands are:
|
||||
|
||||
{{#include ./generated/typable-cmd.md}}
|
||||
|
@ -6,8 +6,7 @@ # Configuration
|
||||
- Linux and Mac: `~/.config/helix/config.toml`
|
||||
- Windows: `%AppData%\helix\config.toml`
|
||||
|
||||
> 💡 You can easily open the config file by typing `:config-open` within Helix
|
||||
> normal mode.
|
||||
> 💡 You can easily open the config file by typing `:config-open` within Helix normal mode.
|
||||
|
||||
Example config:
|
||||
|
||||
@ -28,36 +27,35 @@ # Configuration
|
||||
```
|
||||
|
||||
You can use a custom configuration file by specifying it with the `-c` or
|
||||
`--config` command line argument, for example
|
||||
`hx -c path/to/custom-config.toml`. Additionally, you can reload the
|
||||
configuration file by sending the USR1 signal to the Helix process on Unix
|
||||
operating systems, such as by using the command `pkill -USR1 hx`.
|
||||
`--config` command line argument, for example `hx -c path/to/custom-config.toml`.
|
||||
Additionally, you can reload the configuration file by sending the USR1
|
||||
signal to the Helix process on Unix operating systems, such as by using the command `pkill -USR1 hx`.
|
||||
|
||||
## Editor
|
||||
|
||||
### `[editor]` Section
|
||||
|
||||
| Key | Description | Default |
|
||||
| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
||||
| `scrolloff` | Number of lines of padding around the edge of the screen when scrolling. | `5` |
|
||||
| `mouse` | Enable mouse mode. | `true` |
|
||||
| `middle-click-paste` | Middle click paste support. | `true` |
|
||||
| `scroll-lines` | Number of lines to scroll per scroll wheel step. | `3` |
|
||||
| `shell` | Shell to use when running external commands. | Unix: `["sh", "-c"]`<br/>Windows: `["cmd", "/C"]` |
|
||||
| `line-number` | Line number display: `absolute` simply shows each line's number, while `relative` shows the distance from the current line. When unfocused or in insert mode, `relative` will still show absolute line numbers. | `absolute` |
|
||||
| `cursorline` | Highlight all lines with a cursor. | `false` |
|
||||
| `cursorcolumn` | Highlight all columns with a cursor. | `false` |
|
||||
| `gutters` | Gutters to display: Available are `diagnostics` and `diff` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "spacer", "line-numbers", "spacer", "diff"]` |
|
||||
| `auto-completion` | Enable automatic pop up of auto-completion. | `true` |
|
||||
| `auto-format` | Enable automatic formatting on save. | `true` |
|
||||
| `auto-save` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal. | `false` |
|
||||
| `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. | `400` |
|
||||
| `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` |
|
||||
| `auto-info` | Whether to display infoboxes | `true` |
|
||||
| `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative. | `false` |
|
||||
| `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file. | `[]` |
|
||||
| `bufferline` | Renders a line at the top of the editor displaying open buffers. Can be `always`, `never` or `multiple` (only shown if more than one buffer is in use) | `never` |
|
||||
| `color-modes` | Whether to color the mode indicator with different colors depending on the mode itself | `false` |
|
||||
| Key | Description | Default |
|
||||
|--|--|---------|
|
||||
| `scrolloff` | Number of lines of padding around the edge of the screen when scrolling. | `5` |
|
||||
| `mouse` | Enable mouse mode. | `true` |
|
||||
| `middle-click-paste` | Middle click paste support. | `true` |
|
||||
| `scroll-lines` | Number of lines to scroll per scroll wheel step. | `3` |
|
||||
| `shell` | Shell to use when running external commands. | Unix: `["sh", "-c"]`<br/>Windows: `["cmd", "/C"]` |
|
||||
| `line-number` | Line number display: `absolute` simply shows each line's number, while `relative` shows the distance from the current line. When unfocused or in insert mode, `relative` will still show absolute line numbers. | `absolute` |
|
||||
| `cursorline` | Highlight all lines with a cursor. | `false` |
|
||||
| `cursorcolumn` | Highlight all columns with a cursor. | `false` |
|
||||
| `gutters` | Gutters to display: Available are `diagnostics` and `diff` and `line-numbers` and `spacer`, note that `diagnostics` also includes other features like breakpoints, 1-width padding will be inserted if gutters is non-empty | `["diagnostics", "spacer", "line-numbers", "spacer", "diff"]` |
|
||||
| `auto-completion` | Enable automatic pop up of auto-completion. | `true` |
|
||||
| `auto-format` | Enable automatic formatting on save. | `true` |
|
||||
| `auto-save` | Enable automatic saving on the focus moving away from Helix. Requires [focus event support](https://github.com/helix-editor/helix/wiki/Terminal-Support) from your terminal. | `false` |
|
||||
| `idle-timeout` | Time in milliseconds since last keypress before idle timers trigger. Used for autocompletion, set to 0 for instant. | `400` |
|
||||
| `completion-trigger-len` | The min-length of word under cursor to trigger autocompletion | `2` |
|
||||
| `auto-info` | Whether to display infoboxes | `true` |
|
||||
| `true-color` | Set to `true` to override automatic detection of terminal truecolor support in the event of a false negative. | `false` |
|
||||
| `rulers` | List of column positions at which to display the rulers. Can be overridden by language specific `rulers` in `languages.toml` file. | `[]` |
|
||||
| `bufferline` | Renders a line at the top of the editor displaying open buffers. Can be `always`, `never` or `multiple` (only shown if more than one buffer is in use) | `never` |
|
||||
| `color-modes` | Whether to color the mode indicator with different colors depending on the mode itself | `false` |
|
||||
|
||||
### `[editor.statusline]` Section
|
||||
|
||||
@ -127,10 +125,10 @@ ### `[editor.lsp]` Section
|
||||
|
||||
### `[editor.cursor-shape]` Section
|
||||
|
||||
Defines the shape of cursor in each mode. Valid values for these options are
|
||||
`block`, `bar`, `underline`,or `hidden`.
|
||||
Defines the shape of cursor in each mode.
|
||||
Valid values for these options are `block`, `bar`, `underline`, or `hidden`.
|
||||
|
||||
> 💡Due to limitations of the terminal environment, only the primary cursor can
|
||||
> 💡 Due to limitations of the terminal environment, only the primary cursor can
|
||||
> change shape.
|
||||
|
||||
| Key | Description | Default |
|
||||
@ -145,20 +143,20 @@ ### `[editor.cursor-shape]` Section
|
||||
|
||||
### `[editor.file-picker]` Section
|
||||
|
||||
Set options for file picker and global search. Ignoring a file means it is not
|
||||
visible in the Helix file picker and global search.
|
||||
Sets options for file picker and global search. Ignoring a file means it is
|
||||
not visible in the Helix file picker and global search.
|
||||
|
||||
All git related options are only enabled in a git repository.
|
||||
|
||||
| Key | Description | Default |
|
||||
| ------------- | -------------------------------------------------------------------------------------------------------- | ------------------- |
|
||||
| `hidden` | Enables ignoring hidden files. | true |
|
||||
| `parents` | Enables reading ignore files from parent directories. | true |
|
||||
| `ignore` | Enables reading `.ignore` files. | true |
|
||||
| `git-ignore` | Enables reading `.gitignore` files. | true |
|
||||
| `git-global` | Enables reading global `.gitignore`, whose path is specified in git's config: `core.excludefile` option. | true |
|
||||
| `git-exclude` | Enables reading `.git/info/exclude` files. | true |
|
||||
| `max-depth` | Set with an integer value for maximum depth to recurse. | Defaults to `None`. |
|
||||
| Key | Description | Default |
|
||||
|--|--|---------|
|
||||
|`hidden` | Enables ignoring hidden files. | true
|
||||
|`parents` | Enables reading ignore files from parent directories. | true
|
||||
|`ignore` | Enables reading `.ignore` files. | true
|
||||
|`git-ignore` | Enables reading `.gitignore` files. | true
|
||||
|`git-global` | Enables reading global `.gitignore`, whose path is specified in git's config: `core.excludefile` option. | true
|
||||
|`git-exclude` | Enables reading `.git/info/exclude` files. | true
|
||||
|`max-depth` | Set with an integer value for maximum depth to recurse. | Defaults to `None`.
|
||||
|
||||
### `[editor.auto-pairs]` Section
|
||||
|
||||
@ -209,10 +207,10 @@ ### `[editor.search]` Section
|
||||
|
||||
Search specific options.
|
||||
|
||||
| Key | Description | Default |
|
||||
| ------------- | -------------------------------------------------------------------------------------------------- | ------- |
|
||||
| `smart-case` | Enable smart case regex searching (case-insensitive unless pattern contains upper case characters) | `true` |
|
||||
| `wrap-around` | Whether the search should wrap after depleting the matches | `true` |
|
||||
| Key | Description | Default |
|
||||
|--|--|---------|
|
||||
| `smart-case` | Enable smart case regex searching (case-insensitive unless pattern contains upper case characters) | `true` |
|
||||
| `wrap-around`| Whether the search should wrap after depleting the matches | `true` |
|
||||
|
||||
### `[editor.whitespace]` Section
|
||||
|
||||
@ -262,3 +260,54 @@ ### `[editor.indent-guides]` Section
|
||||
character = "╎" # Some characters that work well: "▏", "┆", "┊", "⸽"
|
||||
skip-levels = 1
|
||||
```
|
||||
|
||||
### `[editor.gutters]` Section
|
||||
|
||||
For simplicity, `editor.gutters` accepts an array of gutter types, which will
|
||||
use default settings for all gutter components.
|
||||
|
||||
```toml
|
||||
[editor]
|
||||
gutters = ["diff", "diagnostics", "line-numbers", "spacer"]
|
||||
```
|
||||
|
||||
To customize the behavior of gutters, the `[editor.gutters]` section must
|
||||
be used. This section contains top level settings, as well as settings for
|
||||
specific gutter components as sub-sections.
|
||||
|
||||
| Key | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| `layout` | A vector of gutters to display | `["diagnostics", "spacer", "line-numbers", "spacer", "diff"]` |
|
||||
|
||||
Example:
|
||||
|
||||
```toml
|
||||
[editor.gutters]
|
||||
layout = ["diff", "diagnostics", "line-numbers", "spacer"]
|
||||
```
|
||||
|
||||
#### `[editor.gutters.line-numbers]` Section
|
||||
|
||||
Options for the line number gutter
|
||||
|
||||
| Key | Description | Default |
|
||||
| --- | --- | --- |
|
||||
| `min-width` | The minimum number of characters to use | `3` |
|
||||
|
||||
Example:
|
||||
|
||||
```toml
|
||||
[editor.gutters.line-numbers]
|
||||
min-width = 1
|
||||
```
|
||||
|
||||
#### `[editor.gutters.diagnotics]` Section
|
||||
|
||||
Currently unused
|
||||
|
||||
#### `[editor.gutters.diff]` Section
|
||||
|
||||
Currently unused
|
||||
|
||||
#### `[editor.gutters.spacer]` Section
|
||||
|
||||
|
@ -109,6 +109,7 @@
|
||||
| ron | ✓ | | ✓ | |
|
||||
| ruby | ✓ | ✓ | ✓ | `solargraph` |
|
||||
| rust | ✓ | ✓ | ✓ | `rust-analyzer` |
|
||||
| sage | ✓ | ✓ | | |
|
||||
| scala | ✓ | | ✓ | `metals` |
|
||||
| scheme | ✓ | | | |
|
||||
| scss | ✓ | | | `vscode-css-language-server` |
|
||||
@ -129,7 +130,7 @@
|
||||
| twig | ✓ | | | |
|
||||
| typescript | ✓ | ✓ | ✓ | `typescript-language-server` |
|
||||
| ungrammar | ✓ | | | |
|
||||
| v | ✓ | | | `vls` |
|
||||
| v | ✓ | | | `v` |
|
||||
| vala | ✓ | | | `vala-language-server` |
|
||||
| verilog | ✓ | ✓ | | `svlangserver` |
|
||||
| vhs | ✓ | | | |
|
||||
|
@ -50,10 +50,3 @@ ## Common issues
|
||||
grammars.
|
||||
- If a parser is causing a segfault or you want to remove it, make sure to
|
||||
remove the compiled parser located at `runtime/grammar/<name>.so`.
|
||||
|
||||
[language configuration section]: ../languages.md
|
||||
[neovim-query-precedence]:
|
||||
https://github.com/helix-editor/helix/pull/1170#issuecomment-997294090
|
||||
[install-lsp-wiki]:
|
||||
https://github.com/helix-editor/helix/wiki/How-to-install-the-default-language-servers
|
||||
[lang-support]: ../lang-support.md
|
||||
|
@ -1,15 +1,15 @@
|
||||
# Adding Textobject Queries
|
||||
|
||||
Helix supports textobjects that are language specific, such as functions,
|
||||
classes, etc. These textobjects require an accompanying tree-sitter grammar and
|
||||
a `textobjects.scm` query file to work properly. Tree-sitter allows us to query
|
||||
the source code syntax tree and capture specific parts of it. The queries are
|
||||
written in a lisp dialect. More information on how to write queries can be found
|
||||
in the [official tree-sitter documentation][tree-sitter-queries].
|
||||
Helix supports textobjects that are language specific, such as functions, classes, etc.
|
||||
These textobjects require an accompanying tree-sitter grammar and a `textobjects.scm` query file
|
||||
to work properly. Tree-sitter allows us to query the source code syntax tree
|
||||
and capture specific parts of it. The queries are written in a lisp dialect.
|
||||
More information on how to write queries can be found in the [official tree-sitter
|
||||
documentation][tree-sitter-queries].
|
||||
|
||||
Query files should be placed in `runtime/queries/{language}/textobjects.scm`
|
||||
when contributing to Helix. Note that to test the query files locally you should
|
||||
put them under your local runtime directory (`~/.config/helix/runtime` on Linux
|
||||
when contributing to Helix. Note that to test the query files locally you should put
|
||||
them under your local runtime directory (`~/.config/helix/runtime` on Linux
|
||||
for example).
|
||||
|
||||
The following [captures][tree-sitter-captures] are recognized:
|
||||
@ -31,23 +31,18 @@ # Adding Textobject Queries
|
||||
|
||||
## Queries for Textobject Based Navigation
|
||||
|
||||
Tree-sitter based navigation in Helix is done using captures in the following
|
||||
order:
|
||||
Tree-sitter based navigation in Helix is done using captures in the
|
||||
following order:
|
||||
|
||||
- `object.movement`
|
||||
- `object.around`
|
||||
- `object.inside`
|
||||
|
||||
For example if a `function.around` capture has been already defined for a
|
||||
language in its `textobjects.scm` file, function navigation should also work
|
||||
automatically. `function.movement` should be defined only if the node captured
|
||||
by `function.around` doesn't make sense in a navigation context.
|
||||
For example if a `function.around` capture has been already defined for a language
|
||||
in its `textobjects.scm` file, function navigation should also work automatically.
|
||||
`function.movement` should be defined only if the node captured by `function.around`
|
||||
doesn't make sense in a navigation context.
|
||||
|
||||
[textobjects]: ../usage.md#textobjects
|
||||
[textobjects-nav]: ../usage.md#tree-sitter-textobject-based-navigation
|
||||
[tree-sitter-queries]:
|
||||
https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax
|
||||
[tree-sitter-captures]:
|
||||
https://tree-sitter.github.io/tree-sitter/using-parsers#capturing-nodes
|
||||
[textobject-examples]:
|
||||
https://github.com/search?q=repo%3Ahelix-editor%2Fhelix+filename%3Atextobjects.scm&type=Code&ref=advsearch&l=&l=
|
||||
[tree-sitter-queries]: https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax
|
||||
[tree-sitter-captures]: https://tree-sitter.github.io/tree-sitter/using-parsers#capturing-nodes
|
||||
[textobject-examples]: https://github.com/search?q=repo%3Ahelix-editor%2Fhelix+filename%3Atextobjects.scm&type=Code&ref=advsearch&l=&l=
|
||||
|
@ -1,7 +1,6 @@
|
||||
# Installing Helix
|
||||
|
||||
<!--toc:start-->
|
||||
|
||||
- [Installing Helix](#installing-helix)
|
||||
- [Using the Pre-built Binaries](#using-the-pre-built-binaries)
|
||||
- [Installing Helix on Linux through the Official Package Manager](#installing-helix-on-linux-through-the-official-package-manager)
|
||||
@ -33,7 +32,7 @@ ## Using the Pre-built Binaries
|
||||
## Installing Helix on Linux through the Official Package Manager
|
||||
|
||||
If your Linux distribution has Helix available through its official package
|
||||
manager, install it through that. The following list shows availability
|
||||
manager, install it through that. The following shows availability
|
||||
throughout the Linux ecosystem:
|
||||
|
||||
[![Packaging status](https://repology.org/badge/vertical-allrepos/helix.svg)](https://repology.org/project/helix/versions)
|
||||
@ -51,8 +50,7 @@ ### Ubuntu
|
||||
- 22.04 LTS (Jammy Jellyfish)
|
||||
- 22.10 (Kinetic Kudu)
|
||||
|
||||
Via
|
||||
[Maveonair's PPA](https://launchpad.net/~maveonair/+archive/ubuntu/helix-editor):
|
||||
Via [Maveonair's PPA](https://launchpad.net/~maveonair/+archive/ubuntu/helix-editor)
|
||||
|
||||
```sh
|
||||
sudo add-apt-repository ppa:maveonair/helix-editor
|
||||
@ -97,8 +95,8 @@ ## Installing Helix on macOS via Homebrew
|
||||
|
||||
## Installing Helix on Windows
|
||||
|
||||
Install on Windows using [Scoop](https://scoop.sh/),
|
||||
[Chocolatey](https://chocolatey.org/) or [MSYS2](https://msys2.org/).
|
||||
Install on Windows using [Scoop](https://scoop.sh/), [Chocolatey](https://chocolatey.org/)
|
||||
or [MSYS2](https://msys2.org/).
|
||||
|
||||
**Scoop:**
|
||||
|
||||
@ -120,7 +118,7 @@ ## Installing Helix on Windows
|
||||
pacman -S mingw-w64-ucrt-x86_64-helix
|
||||
```
|
||||
|
||||
## Building from Source
|
||||
2. Compile Helix:
|
||||
|
||||
1. Clone the repository:
|
||||
|
||||
@ -139,7 +137,14 @@ ## Building from Source
|
||||
grammars in the `runtime` folder, or in the folder specified in `HELIX_RUNTIME`
|
||||
(as described below).
|
||||
|
||||
3. Configure Helix's runtime files
|
||||
> 💡 If you are using the musl-libc instead of glibc the following environment variable must be set during the build
|
||||
> to ensure tree sitter grammars can be loaded correctly:
|
||||
>
|
||||
> ```sh
|
||||
> RUSTFLAGS="-C target-feature=-crt-static"
|
||||
> ```
|
||||
|
||||
3. Configure Helix's runtime files
|
||||
|
||||
**IMPORTANT**: The runtime files must be accessible to the newly created binary.
|
||||
They are currently located in the source code `runtime` directory. To make them
|
||||
@ -162,7 +167,7 @@ ## Building from Source
|
||||
|
||||
Or,
|
||||
|
||||
2. Create a symlink in `~/.config/helix/` that links to the source code
|
||||
2. Create a symlink in `~/.config/helix` that links to the source code
|
||||
directory.
|
||||
|
||||
```sh
|
||||
@ -174,7 +179,7 @@ ## Building from Source
|
||||
3. Configure the Desktop Shortcut
|
||||
|
||||
If your desktop environment supports the
|
||||
[XDG desktop menu](https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html),
|
||||
[XDG desktop menu](https://specifications.freedesktop.org/menu-spec/menu-spec-latest.html)
|
||||
you can configure Helix to show up in the application menu by copying the
|
||||
provided `.desktop` and icon files to their correct folders:
|
||||
|
||||
@ -198,7 +203,7 @@ ## Building from Source
|
||||
1. Set the `HELIX_RUNTIME` environment variable on your system to tell Helix
|
||||
where to find the runtime files.
|
||||
|
||||
You can either do this using the Windows settings (search for
|
||||
You can either do this using the Windows setting (search for
|
||||
`Edit environment variables for your account`) or use the `setx` command in
|
||||
Cmd:
|
||||
|
||||
|
@ -27,35 +27,35 @@ ## Normal mode
|
||||
|
||||
### Movement
|
||||
|
||||
> 💡 Unlike Vim, `f`, `F`, `t` and `T` are not confined to the current line.
|
||||
> NOTE: Unlike Vim, `f`, `F`, `t` and `T` are not confined to the current line.
|
||||
|
||||
| Key | Description | Command |
|
||||
| -------------------- | ------------------------------------------- | --------------------------- |
|
||||
| `h`, `Left` | Move left | `move_char_left` |
|
||||
| `j`, `Down` | Move down | `move_line_down` |
|
||||
| `k`, `Up` | Move up | `move_line_up` |
|
||||
| `l`, `Right` | Move right | `move_char_right` |
|
||||
| `w` | Move next word start | `move_next_word_start` |
|
||||
| `b` | Move previous word start | `move_prev_word_start` |
|
||||
| `e` | Move next word end | `move_next_word_end` |
|
||||
| `W` | Move next WORD start | `move_next_long_word_start` |
|
||||
| `B` | Move previous WORD start | `move_prev_long_word_start` |
|
||||
| `E` | Move next WORD end | `move_next_long_word_end` |
|
||||
| `t` | Find 'till next char | `find_till_char` |
|
||||
| `f` | Find next char | `find_next_char` |
|
||||
| `T` | Find 'till previous char | `till_prev_char` |
|
||||
| `F` | Find previous char | `find_prev_char` |
|
||||
| `G` | Go to line number `<n>` | `goto_line` |
|
||||
| `Alt-.` | Repeat last motion (`f`, `t` or `m`) | `repeat_last_motion` |
|
||||
| `Home` | Move to the start of the line | `goto_line_start` |
|
||||
| `End` | Move to the end of the line | `goto_line_end` |
|
||||
| `Ctrl-b`, `PageUp` | Move page up | `page_up` |
|
||||
| `Ctrl-f`, `PageDown` | Move page down | `page_down` |
|
||||
| `Ctrl-u` | Move half page up | `half_page_up` |
|
||||
| `Ctrl-d` | Move half page down | `half_page_down` |
|
||||
| `Ctrl-i` | Jump forward on the jump list | `jump_forward` |
|
||||
| `Ctrl-o` | Jump backward on the jump list | `jump_backward` |
|
||||
| `Ctrl-s` | Save the current selection to the jump list | `save_selection` |
|
||||
| Key | Description | Command |
|
||||
| ----- | ----------- | ------- |
|
||||
| `h`, `Left` | Move left | `move_char_left` |
|
||||
| `j`, `Down` | Move down | `move_line_down` |
|
||||
| `k`, `Up` | Move up | `move_line_up` |
|
||||
| `l`, `Right` | Move right | `move_char_right` |
|
||||
| `w` | Move next word start | `move_next_word_start` |
|
||||
| `b` | Move previous word start | `move_prev_word_start` |
|
||||
| `e` | Move next word end | `move_next_word_end` |
|
||||
| `W` | Move next WORD start | `move_next_long_word_start` |
|
||||
| `B` | Move previous WORD start | `move_prev_long_word_start` |
|
||||
| `E` | Move next WORD end | `move_next_long_word_end` |
|
||||
| `t` | Find 'till next char | `find_till_char` |
|
||||
| `f` | Find next char | `find_next_char` |
|
||||
| `T` | Find 'till previous char | `till_prev_char` |
|
||||
| `F` | Find previous char | `find_prev_char` |
|
||||
| `G` | Go to line number `<n>` | `goto_line` |
|
||||
| `Alt-.` | Repeat last motion (`f`, `t` or `m`) | `repeat_last_motion` |
|
||||
| `Home` | Move to the start of the line | `goto_line_start` |
|
||||
| `End` | Move to the end of the line | `goto_line_end` |
|
||||
| `Ctrl-b`, `PageUp` | Move page up | `page_up` |
|
||||
| `Ctrl-f`, `PageDown` | Move page down | `page_down` |
|
||||
| `Ctrl-u` | Move half page up | `half_page_up` |
|
||||
| `Ctrl-d` | Move half page down | `half_page_down` |
|
||||
| `Ctrl-i` | Jump forward on the jump list | `jump_forward` |
|
||||
| `Ctrl-o` | Jump backward on the jump list | `jump_backward` |
|
||||
| `Ctrl-s` | Save the current selection to the jump list | `save_selection` |
|
||||
|
||||
### Changes
|
||||
|
||||
@ -105,43 +105,42 @@ #### Shell
|
||||
|
||||
### Selection manipulation
|
||||
|
||||
| Key | Description | Command |
|
||||
| -------------------- | ------------------------------------------------------------- | ------------------------------------ |
|
||||
| `s` | Select all regex matches inside selections | `select_regex` |
|
||||
| `S` | Split selection into sub selections on regex matches | `split_selection` |
|
||||
| `Alt-s` | Split selection on newlines | `split_selection_on_newline` |
|
||||
| `Alt-_ ` | Merge consecutive selections | `merge_consecutive_selections` |
|
||||
| `&` | Align selection in columns | `align_selections` |
|
||||
| `_` | Trim whitespace from the selection | `trim_selections` |
|
||||
| `;` | Collapse selection onto a single cursor | `collapse_selection` |
|
||||
| `Alt-;` | Flip selection cursor and anchor | `flip_selections` |
|
||||
| `Alt-:` | Ensures the selection is in forward direction | `ensure_selections_forward` |
|
||||
| `,` | Keep only the primary selection | `keep_primary_selection` |
|
||||
| `Alt-,` | Remove the primary selection | `remove_primary_selection` |
|
||||
| `C` | Copy selection onto the next line (Add cursor below) | `copy_selection_on_next_line` |
|
||||
| `Alt-C` | Copy selection onto the previous line (Add cursor above) | `copy_selection_on_prev_line` |
|
||||
| `(` | Rotate main selection backward | `rotate_selections_backward` |
|
||||
| `)` | Rotate main selection forward | `rotate_selections_forward` |
|
||||
| `Alt-(` | Rotate selection contents backward | `rotate_selection_contents_backward` |
|
||||
| `Alt-)` | Rotate selection contents forward | `rotate_selection_contents_forward` |
|
||||
| `%` | Select entire file | `select_all` |
|
||||
| `x` | Select current line, if already selected, extend to next line | `extend_line_below` |
|
||||
| `X` | Extend selection to line bounds (line-wise selection) | `extend_to_line_bounds` |
|
||||
| `Alt-x` | Shrink selection to line bounds (line-wise selection) | `shrink_to_line_bounds` |
|
||||
| `J` | Join lines inside selection | `join_selections` |
|
||||
| `Alt-J` | Join lines inside selection and select space | `join_selections_space` |
|
||||
| `K` | Keep selections matching the regex | `keep_selections` |
|
||||
| `Alt-K` | Remove selections matching the regex | `remove_selections` |
|
||||
| `Ctrl-c` | Comment/uncomment the selections | `toggle_comments` |
|
||||
| `Alt-o`, `Alt-up` | Expand selection to parent syntax node (**TS**) | `expand_selection` |
|
||||
| `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` |
|
||||
| Key | Description | Command |
|
||||
| ----- | ----------- | ------- |
|
||||
| `s` | Select all regex matches inside selections | `select_regex` |
|
||||
| `S` | Split selection into sub selections on regex matches | `split_selection` |
|
||||
| `Alt-s` | Split selection on newlines | `split_selection_on_newline` |
|
||||
| `Alt-_ ` | Merge consecutive selections | `merge_consecutive_selections` |
|
||||
| `&` | Align selection in columns | `align_selections` |
|
||||
| `_` | Trim whitespace from the selection | `trim_selections` |
|
||||
| `;` | Collapse selection onto a single cursor | `collapse_selection` |
|
||||
| `Alt-;` | Flip selection cursor and anchor | `flip_selections` |
|
||||
| `Alt-:` | Ensures the selection is in forward direction | `ensure_selections_forward` |
|
||||
| `,` | Keep only the primary selection | `keep_primary_selection` |
|
||||
| `Alt-,` | Remove the primary selection | `remove_primary_selection` |
|
||||
| `C` | Copy selection onto the next line (Add cursor below) | `copy_selection_on_next_line` |
|
||||
| `Alt-C` | Copy selection onto the previous line (Add cursor above) | `copy_selection_on_prev_line` |
|
||||
| `(` | Rotate main selection backward | `rotate_selections_backward` |
|
||||
| `)` | Rotate main selection forward | `rotate_selections_forward` |
|
||||
| `Alt-(` | Rotate selection contents backward | `rotate_selection_contents_backward` |
|
||||
| `Alt-)` | Rotate selection contents forward | `rotate_selection_contents_forward` |
|
||||
| `%` | Select entire file | `select_all` |
|
||||
| `x` | Select current line, if already selected, extend to next line | `extend_line_below` |
|
||||
| `X` | Extend selection to line bounds (line-wise selection) | `extend_to_line_bounds` |
|
||||
| `Alt-x` | Shrink selection to line bounds (line-wise selection) | `shrink_to_line_bounds` |
|
||||
| `J` | Join lines inside selection | `join_selections` |
|
||||
| `Alt-J` | Join lines inside selection and select space | `join_selections_space` |
|
||||
| `K` | Keep selections matching the regex | `keep_selections` |
|
||||
| `Alt-K` | Remove selections matching the regex | `remove_selections` |
|
||||
| `Ctrl-c` | Comment/uncomment the selections | `toggle_comments` |
|
||||
| `Alt-o`, `Alt-up` | Expand selection to parent syntax node (**TS**) | `expand_selection` |
|
||||
| `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` |
|
||||
|
||||
### Search
|
||||
|
||||
Search commands operate on the `/` register by default. To use a different
|
||||
register, use `"<char>.`
|
||||
Search commands all operate on the `/` register by default. To use a different register, use `"<char>`.
|
||||
|
||||
| Key | Description | Command |
|
||||
| --- | ------------------------------------------- | ------------------ |
|
||||
@ -153,8 +152,7 @@ ### Search
|
||||
|
||||
### Minor modes
|
||||
|
||||
Minor modes are accessible from normal mode and typically switch back to normal
|
||||
mode after a command.
|
||||
Minor modes are accessible from normal mode and typically switch back to normal mode after a command.
|
||||
|
||||
| Key | Description | Command |
|
||||
| -------- | -------------------------------------------------- | -------------- |
|
||||
@ -167,13 +165,17 @@ ### Minor modes
|
||||
| `Ctrl-w` | Enter [window mode](#window-mode) | N/A |
|
||||
| `Space` | Enter [space mode](#space-mode) | N/A |
|
||||
|
||||
These modes (except command mode) can be configured by
|
||||
[remapping keys](https://docs.helix-editor.com/remapping.html#minor-modes).
|
||||
|
||||
#### View mode
|
||||
|
||||
View mode is accessed by typing `z` in [normal mode](#normal-mode) and is
|
||||
intended for scrolling and manipulating the view without changing the selection.
|
||||
The "sticky" variant of this mode (accessed by typing `Z` in normal mode) is
|
||||
persistent and can be exited using the escape key. This is useful when you're
|
||||
simply looking over text and not actively editing it.
|
||||
View mode is access by typing `z` in [normal mode](#normal-mode)
|
||||
and is intended for scrolling and manipulating the view without changing
|
||||
the selection. The "sticky" variant of this mode (accessed by typing `Z` in
|
||||
normal mode) is persistent and can be exited using the escape key. This is
|
||||
useful when you're simply looking over text and not actively editing it.
|
||||
|
||||
|
||||
| Key | Description | Command |
|
||||
| -------------------- | --------------------------------------------------------- | ------------------- |
|
||||
@ -193,26 +195,26 @@ #### Goto mode
|
||||
Goto mode is accessed by typing `g` in [normal mode](#normal-mode), it jumps to
|
||||
various locations.
|
||||
|
||||
| Key | Description | Command |
|
||||
| --- | ------------------------------------------------ | -------------------------- |
|
||||
| `g` | Go to line number `<n>` else start of file | `goto_file_start` |
|
||||
| `e` | Go to the end of the file | `goto_last_line` |
|
||||
| `f` | Go to files in the selection | `goto_file` |
|
||||
| `h` | Go to the start of the line | `goto_line_start` |
|
||||
| `l` | Go to the end of the line | `goto_line_end` |
|
||||
| `s` | Go to first non-whitespace character of the line | `goto_first_nonwhitespace` |
|
||||
| `t` | Go to the top of the screen | `goto_window_top` |
|
||||
| `c` | Go to the middle of the screen | `goto_window_center` |
|
||||
| `b` | Go to the bottom of the screen | `goto_window_bottom` |
|
||||
| `d` | Go to definition (**LSP**) | `goto_definition` |
|
||||
| `y` | Go to type definition (**LSP**) | `goto_type_definition` |
|
||||
| `r` | Go to references (**LSP**) | `goto_reference` |
|
||||
| `i` | Go to implementation (**LSP**) | `goto_implementation` |
|
||||
| `a` | Go to the last accessed/alternate file | `goto_last_accessed_file` |
|
||||
| `m` | Go to the last modified/alternate file | `goto_last_modified_file` |
|
||||
| `n` | Go to next buffer | `goto_next_buffer` |
|
||||
| `p` | Go to previous buffer | `goto_previous_buffer` |
|
||||
| `.` | Go to last modification in current file | `goto_last_modification` |
|
||||
| Key | Description | Command |
|
||||
| ----- | ----------- | ------- |
|
||||
| `g` | Go to line number `<n>` else start of file | `goto_file_start` |
|
||||
| `e` | Go to the end of the file | `goto_last_line` |
|
||||
| `f` | Go to files in the selection | `goto_file` |
|
||||
| `h` | Go to the start of the line | `goto_line_start` |
|
||||
| `l` | Go to the end of the line | `goto_line_end` |
|
||||
| `s` | Go to first non-whitespace character of the line | `goto_first_nonwhitespace` |
|
||||
| `t` | Go to the top of the screen | `goto_window_top` |
|
||||
| `c` | Go to the middle of the screen | `goto_window_center` |
|
||||
| `b` | Go to the bottom of the screen | `goto_window_bottom` |
|
||||
| `d` | Go to definition (**LSP**) | `goto_definition` |
|
||||
| `y` | Go to type definition (**LSP**) | `goto_type_definition` |
|
||||
| `r` | Go to references (**LSP**) | `goto_reference` |
|
||||
| `i` | Go to implementation (**LSP**) | `goto_implementation` |
|
||||
| `a` | Go to the last accessed/alternate file | `goto_last_accessed_file` |
|
||||
| `m` | Go to the last modified/alternate file | `goto_last_modified_file` |
|
||||
| `n` | Go to next buffer | `goto_next_buffer` |
|
||||
| `p` | Go to previous buffer | `goto_previous_buffer` |
|
||||
| `.` | Go to last modification in current file | `goto_last_modification` |
|
||||
|
||||
#### Match mode
|
||||
|
||||
@ -234,8 +236,8 @@ #### Match mode
|
||||
|
||||
#### Window mode
|
||||
|
||||
Window mode is accessed by typing `Ctrl-w` in [normal mode](#normal-mode), this
|
||||
layer is similar to Vim keybindings as Kakoune does not support window.
|
||||
Window mode is accessed by typing `Ctrl-w` in [normal mode](#normal-mode),
|
||||
this layer is similar to Vim keybindings as Kakoune does not support windows.
|
||||
|
||||
| Key | Description | Command |
|
||||
| ---------------------- | ---------------------------------------------------- | ----------------- |
|
||||
@ -261,31 +263,30 @@ #### Space mode
|
||||
|
||||
This layer is a kludge of mappings, mostly pickers.
|
||||
|
||||
| Key | Description | Command |
|
||||
| --- | ----------------------------------------------------------------------- | ----------------------------------- |
|
||||
| `f` | Open file picker | `file_picker` |
|
||||
| `F` | Open file picker at current working directory | `file_picker_in_current_directory` |
|
||||
| `b` | Open buffer picker | `buffer_picker` |
|
||||
| `j` | Open jump list picker | `jumplist_picker` |
|
||||
| `k` | Show documentation for item under cursor in a [popup](#popup) (**LSP**) | `hover` |
|
||||
| `s` | Open document symbol picker (**LSP**) | `symbol_picker` |
|
||||
| `S` | Open workspace symbol picker (**LSP**) | `workspace_symbol_picker` |
|
||||
| `d` | Open document diagnostics picker (**LSP**) | `diagnostics_picker` |
|
||||
| `D` | Open workspace diagnostics picker (**LSP**) | `workspace_diagnostics_picker` |
|
||||
| `r` | Rename symbol (**LSP**) | `rename_symbol` |
|
||||
| `a` | Apply code action (**LSP**) | `code_action` |
|
||||
| `'` | Open last fuzzy picker | `last_picker` |
|
||||
| `w` | Enter [window mode](#window-mode) | N/A |
|
||||
| `p` | Paste system clipboard after selections | `paste_clipboard_after` |
|
||||
| `P` | Paste system clipboard before selections | `paste_clipboard_before` |
|
||||
| `y` | Join and yank selections to clipboard | `yank_joined_to_clipboard` |
|
||||
| `Y` | Yank main selection to clipboard | `yank_main_selection_to_clipboard` |
|
||||
| `R` | Replace selections by clipboard contents | `replace_selections_with_clipboard` |
|
||||
| `/` | Global search in workspace folder | `global_search` |
|
||||
| `?` | Open command palette | `command_palette` |
|
||||
| Key | Description | Command |
|
||||
| ----- | ----------- | ------- |
|
||||
| `f` | Open file picker | `file_picker` |
|
||||
| `F` | Open file picker at current working directory | `file_picker_in_current_directory` |
|
||||
| `b` | Open buffer picker | `buffer_picker` |
|
||||
| `j` | Open jump list picker | `jump list_picker` |
|
||||
| `k` | Show documentation for item under cursor in a [popup](#popup) (**LSP**) | `hover` |
|
||||
| `s` | Open document symbol picker (**LSP**) | `symbol_picker` |
|
||||
| `S` | Open workspace symbol picker (**LSP**) | `workspace_symbol_picker` |
|
||||
| `d` | Open document diagnostics picker (**LSP**) | `diagnostics_picker` |
|
||||
| `D` | Open workspace diagnostics picker (**LSP**) | `workspace_diagnostics_picker` |
|
||||
| `r` | Rename symbol (**LSP**) | `rename_symbol` |
|
||||
| `a` | Apply code action (**LSP**) | `code_action` |
|
||||
| `'` | Open last fuzzy picker | `last_picker` |
|
||||
| `w` | Enter [window mode](#window-mode) | N/A |
|
||||
| `p` | Paste system clipboard after selections | `paste_clipboard_after` |
|
||||
| `P` | Paste system clipboard before selections | `paste_clipboard_before` |
|
||||
| `y` | Join and yank selections to clipboard | `yank_joined_to_clipboard` |
|
||||
| `Y` | Yank main selection to clipboard | `yank_main_selection_to_clipboard` |
|
||||
| `R` | Replace selections by clipboard contents | `replace_selections_with_clipboard` |
|
||||
| `/` | Global search in workspace folder | `global_search` |
|
||||
| `?` | Open command palette | `command_palette` |
|
||||
|
||||
> 💡 Global search displays results in a fuzzy picker, use `Space + '` to bring
|
||||
> it back up after opening a file.
|
||||
> TIP: Global search displays results in a fuzzy picker, use `Space + '` to bring it back up after opening a file.
|
||||
|
||||
##### Popup
|
||||
|
||||
@ -299,15 +300,14 @@ ##### Popup
|
||||
|
||||
#### Unimpaired
|
||||
|
||||
These mappings are in the style of
|
||||
[vim-unimpaired](https://github.com/tpope/vim-unimpaired).
|
||||
These mappings are in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaired).
|
||||
|
||||
| Key | Description | Command |
|
||||
| -------- | -------------------------------------------- | --------------------- |
|
||||
| `[d` | Go to previous diagnostic (**LSP**) | `goto_prev_diag` |
|
||||
| ----- | ----------- | ------- |
|
||||
| `]d` | Go to next diagnostic (**LSP**) | `goto_next_diag` |
|
||||
| `[D` | Go to first diagnostic in document (**LSP**) | `goto_first_diag` |
|
||||
| `[d` | Go to previous diagnostic (**LSP**) | `goto_prev_diag` |
|
||||
| `]D` | Go to last diagnostic in document (**LSP**) | `goto_last_diag` |
|
||||
| `[D` | Go to first diagnostic in document (**LSP**) | `goto_first_diag` |
|
||||
| `]f` | Go to next function (**TS**) | `goto_next_function` |
|
||||
| `[f` | Go to previous function (**TS**) | `goto_prev_function` |
|
||||
| `]t` | Go to next type definition (**TS**) | `goto_next_class` |
|
||||
@ -322,34 +322,34 @@ #### Unimpaired
|
||||
| `[p` | Go to previous paragraph | `goto_prev_paragraph` |
|
||||
| `]g` | Go to next change | `goto_next_change` |
|
||||
| `[g` | Go to previous change | `goto_prev_change` |
|
||||
| `[G` | Go to first change | `goto_first_change` |
|
||||
| `]G` | Go to last change | `goto_last_change` |
|
||||
| `[Space` | Add newline above | `add_newline_above` |
|
||||
| `[G` | Go to first change | `goto_first_change` |
|
||||
| `]Space` | Add newline below | `add_newline_below` |
|
||||
| `[Space` | Add newline above | `add_newline_above` |
|
||||
|
||||
## Insert mode
|
||||
|
||||
Insert mode bindings are minimal by default. Helix is designed to be a modal
|
||||
editor, and this is reflected in the user experience and internal mechanics.
|
||||
Changes to the text are only saved for undos when escaping from insert mode to
|
||||
normal mode.
|
||||
Insert mode bindings are minimal by default. Helix is designed to
|
||||
be a modal editor, and this is reflected in the user experience and internal
|
||||
mechanics. Changes to the text are only saved for undos when
|
||||
escaping from insert mode to normal mode.
|
||||
|
||||
> 💡 New users are strongly encouraged to learn the modal editing paradigm to
|
||||
> get the smoothest experience.
|
||||
> 💡 New users are strongly encouraged to learn the modal editing paradigm
|
||||
> to get the smoothest experience.
|
||||
|
||||
| Key | Description | Command |
|
||||
| ------------------------- | ------------------------- | ------------------------ |
|
||||
| `Escape` | Switch to normal mode | `normal_mode` |
|
||||
| `Ctrl-s` | Commit undo checkpoint | `commit_undo_checkpoint` |
|
||||
| `Ctrl-x` | Autocomplete | `completion` |
|
||||
| `Ctrl-r` | Insert a register content | `insert_register` |
|
||||
| `Ctrl-w`, `Alt-Backspace` | Delete previous word | `delete_word_backward` |
|
||||
| `Alt-d`, `Alt-Delete` | Delete next word | `delete_word_forward` |
|
||||
| `Ctrl-u` | Delete to start of line | `kill_to_line_start` |
|
||||
| `Ctrl-k` | Delete to end of line | `kill_to_line_end` |
|
||||
| `Ctrl-h`, `Backspace` | Delete previous char | `delete_char_backward` |
|
||||
| `Ctrl-d`, `Delete` | Delete next char | `delete_char_forward` |
|
||||
| `Ctrl-j`, `Enter` | Insert new line | `insert_newline` |
|
||||
| Key | Description | Command |
|
||||
| ----- | ----------- | ------- |
|
||||
| `Escape` | Switch to normal mode | `normal_mode` |
|
||||
| `Ctrl-s` | Commit undo checkpoint | `commit_undo_checkpoint` |
|
||||
| `Ctrl-x` | Autocomplete | `completion` |
|
||||
| `Ctrl-r` | Insert a register content | `insert_register` |
|
||||
| `Ctrl-w`, `Alt-Backspace` | Delete previous word | `delete_word_backward` |
|
||||
| `Alt-d`, `Alt-Delete` | Delete next word | `delete_word_forward` |
|
||||
| `Ctrl-u` | Delete to start of line | `kill_to_line_start` |
|
||||
| `Ctrl-k` | Delete to end of line | `kill_to_line_end` |
|
||||
| `Ctrl-h`, `Backspace` | Delete previous char | `delete_char_backward` |
|
||||
| `Ctrl-d`, `Delete` | Delete next char | `delete_char_forward` |
|
||||
| `Ctrl-j`, `Enter` | Insert new line | `insert_newline` |
|
||||
|
||||
These keys are not recommended, but are included for new users less familiar
|
||||
with modal editors.
|
||||
@ -382,9 +382,10 @@ ## Insert mode
|
||||
|
||||
## Select / extend mode
|
||||
|
||||
Select mode echoes Normal mode, but changes any movements to extend selections
|
||||
rather than replace them. Goto motions are also changed to extend, so that `vgl`
|
||||
for example extends the selection to the end of the line.
|
||||
Select mode echoes Normal mode, but changes any movements to extend
|
||||
selections rather than replace them. Goto motions are also changed to
|
||||
extend, so that `vgl` for example extends the selection to the end of
|
||||
the line.
|
||||
|
||||
Search is also affected. By default, `n` and `N` will remove the current
|
||||
selection and select the next instance of the search term. Toggling this mode
|
||||
|
@ -1,11 +1,10 @@
|
||||
# Language Support
|
||||
|
||||
The following languages and Language Servers are supported. To use Language
|
||||
Server features, you must first [install][lsp-install-wiki] the appropriate
|
||||
Language Server.
|
||||
The following languages and Language Servers are supported. To use
|
||||
Language Server features, you must first [install][lsp-install-wiki] the
|
||||
appropriate Language Server.
|
||||
|
||||
You can check the language support in your installed Helix version with
|
||||
`hx --health`.
|
||||
You can check the language support in your installed helix version with `hx --health`.
|
||||
|
||||
Also see the [Language Configuration][lang-config] docs and the [Adding
|
||||
Languages][adding-languages] guide for more language configuration information.
|
||||
|
@ -8,7 +8,7 @@ ## `languages.toml` files
|
||||
There are three possible locations for a `languages.toml` file:
|
||||
|
||||
1. In the Helix source code, this lives in the
|
||||
[Helix repository](https://github.com/helix-editor/helix/blob/master/languages.toml).
|
||||
[Helix repository](https://github.com/helix-editor/helix/blob/master/languages.toml)
|
||||
It provides the default configurations for languages and language servers.
|
||||
|
||||
2. In your [configuration directory](./configuration.md). This overrides values
|
||||
@ -67,8 +67,8 @@ ## Language configuration
|
||||
|
||||
### File-type detection and the `file-types` key
|
||||
|
||||
Helix determines which language configuration to use based on the `file-types`
|
||||
key from the above section. `file-types` is a list of strings or tables, for
|
||||
Helix determines which language configuration to use based on the `file-types` key
|
||||
from the above section. `file-types` is a list of strings or tables, for
|
||||
example:
|
||||
|
||||
```toml
|
||||
|
@ -1,10 +1,10 @@
|
||||
# Key Remapping
|
||||
|
||||
Helix currently supports one-way key remapping through a simple TOML
|
||||
configuration file. (More powerful solutions such as rebinding via commands will
|
||||
be available in the future).
|
||||
Helix currently supports one-way key remapping through a simple TOML configuration
|
||||
file. (More powerful solutions such as rebinding via commands will be
|
||||
available in the future).
|
||||
|
||||
To remap keys, create a `config.toml` file in your `Helix` configuration
|
||||
To remap keys, create a `config.toml` file in your `helix` configuration
|
||||
directory (default `~/.config/helix` on Linux systems) with a structure like
|
||||
this:
|
||||
|
||||
@ -30,8 +30,31 @@ # At most one section each of 'keys.normal', 'keys.insert' and 'keys.select'
|
||||
|
||||
```
|
||||
|
||||
> NOTE: Bindings can be nested, to create (or edit) minor modes:
|
||||
> `g = { a = "code_action"}` adds a new entry to the `goto` mode.
|
||||
## Minor modes
|
||||
|
||||
Minor modes are accessed by pressing a key (usually from normal mode), giving access to dedicated bindings. Bindings
|
||||
can be modified or added by nesting definitions.
|
||||
|
||||
```toml
|
||||
[keys.insert.j]
|
||||
k = "normal_mode" # Maps `jk` to exit insert mode
|
||||
|
||||
[keys.normal.g]
|
||||
a = "code_action" # Maps `ga` to show possible code actions
|
||||
|
||||
# invert `j` and `k` in view mode
|
||||
[keys.normal.z]
|
||||
j = "scroll_up"
|
||||
k = "scroll_down"
|
||||
|
||||
# create a new minor mode bound to `+`
|
||||
[keys.normal."+"]
|
||||
m = ":run-shell-command make"
|
||||
c = ":run-shell-command cargo build"
|
||||
t = ":run-shell-command cargo test"
|
||||
```
|
||||
|
||||
## Special keys and modifiers
|
||||
|
||||
Ctrl, Shift and Alt modifiers are encoded respectively with the prefixes `C-`,
|
||||
`S-` and `A-`. Special keys are encoded as follows:
|
||||
@ -58,9 +81,6 @@ # At most one section each of 'keys.normal', 'keys.insert' and 'keys.select'
|
||||
|
||||
Keys can be disabled by binding them to the `no_op` command.
|
||||
|
||||
You can find a list of available commands at
|
||||
[Keymap](https://docs.helix-editor.com/keymap.html)
|
||||
You can find a list of available commands in the [Keymap](https://docs.helix-editor.com/keymap.html) documentation.
|
||||
|
||||
> Commands can also be found 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 can also be found 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`.
|
||||
|
@ -1,8 +1,6 @@
|
||||
# Themes
|
||||
|
||||
To use a theme, add `theme = "<name>"` to the top of your
|
||||
[`config.toml`](./configuration.md) file, or select it during runtime using
|
||||
`:theme <name>`.
|
||||
To use a theme add `theme = "<name>"` to the top of your [`config.toml`](./configuration.md) file, or select it during runtime using `:theme <name>`.
|
||||
|
||||
## Creating a Theme
|
||||
|
||||
@ -11,13 +9,18 @@ ### Creating Your Theme File
|
||||
To create a theme file:
|
||||
|
||||
1. Create a 'themes' folder in your user configuration folder (e.g.
|
||||
`~/.config/helix/themes`)
|
||||
`~/.config/helix/themes`).
|
||||
2. Create a file with the name of your theme as the file name (e.g.
|
||||
`mytheme.toml`) and place it in your `themes` folder.
|
||||
|
||||
> 💡 The names "default" and "base16_default" are reserved for built-in themes
|
||||
> and cannot be overridden by user-defined themes.
|
||||
|
||||
### An overview of the Theme File Format
|
||||
|
||||
> 💡 The names "default" and "base16_default" are reserved for built-in themes
|
||||
> and cannot be overridden by user-defined themes.
|
||||
|
||||
### An Overview of the Theme File Format
|
||||
|
||||
Each line in the theme file is specified as follows:
|
||||
@ -26,10 +29,7 @@ ### An Overview of the Theme File Format
|
||||
key = { fg = "#ffffff", bg = "#000000", underline = { color = "#ff0000", style = "curl"}, modifiers = ["bold", "italic"] }
|
||||
```
|
||||
|
||||
Where `key` represents what you want to style, `fg` specifies the foreground
|
||||
color, `bg` the background color, `underline` the underline `style`/`color`, and
|
||||
`modifiers` is a list of style modifiers. `bg`, `underline` and `modifiers` can
|
||||
be omitted to defer to the defaults.
|
||||
Where `key` represents what you want to style, `fg` specifies the foreground color, `bg` the background color, `underline` the underline `style`/`color`, and `modifiers` is a list of style modifiers. `bg`, `underline` and `modifiers` can be omitted to defer to the defaults.
|
||||
|
||||
To specify only the foreground color:
|
||||
|
||||
@ -37,8 +37,7 @@ ### An Overview of the Theme File Format
|
||||
key = "#ffffff"
|
||||
```
|
||||
|
||||
If the key contains a dot `'.'`, it must be quoted to prevent it being parsed as
|
||||
a [dotted key](https://toml.io/en/v1.0.0#keys).
|
||||
If the key contains a dot `'.'`, it must be quoted to prevent it being parsed as a [dotted key](https://toml.io/en/v1.0.0#keys).
|
||||
|
||||
```toml
|
||||
"key.key" = "#ffffff"
|
||||
@ -55,16 +54,16 @@ ### Using the Linter
|
||||
use the supplied linting tool to ensure compliance with the specifications:
|
||||
|
||||
```sh
|
||||
cargo xtask themelint onedark # replace onedark with <name>
|
||||
cargo xtask themelint onedark # replace onedark with <name>
|
||||
```
|
||||
|
||||
## The Details of Theme Creation
|
||||
|
||||
### Color palettes
|
||||
|
||||
It's recommended to define a palette of named colors and refer to them in the
|
||||
configuration values in your theme. To do this, add a table called `palette` to
|
||||
your theme file:
|
||||
It's recommended to define a palette of named colors, and refer to them in the
|
||||
configuration values in your theme. To do this, add a table called
|
||||
`palette` to your theme file:
|
||||
|
||||
```toml
|
||||
"ui.background" = "white"
|
||||
@ -75,8 +74,8 @@ ### Color palettes
|
||||
black = "#000000"
|
||||
```
|
||||
|
||||
Keep in mind that the [palette] table includes all keys after its header, so it
|
||||
should be defined after the normal theme options.
|
||||
Keep in mind that the `[palette]` table includes all keys after its header,
|
||||
so it should be defined after the normal theme options.
|
||||
|
||||
The default palette uses the terminal's default 16 colors, and the colors names
|
||||
are listed below. The `[palette]` section in the config file takes precedence
|
||||
@ -103,29 +102,37 @@ ### Color palettes
|
||||
|
||||
### Modifiers
|
||||
|
||||
The following values can be used as modifiers, providing they are supported by
|
||||
The following values may be used as modifier, provided they are supported by
|
||||
your terminal emulator.
|
||||
|
||||
| Modifier |
|
||||
| ------------- |
|
||||
| `bold` |
|
||||
| `dim` |
|
||||
| `italic` |
|
||||
| `underlined` |
|
||||
| `slow_blink` |
|
||||
| `rapid_blink` |
|
||||
| `reversed` |
|
||||
| `hidden` |
|
||||
| `crossed_out` |
|
||||
| Modifier |
|
||||
| --- |
|
||||
| `bold` |
|
||||
| `dim` |
|
||||
| `italic` |
|
||||
| `underlined` |
|
||||
| `slow_blink` |
|
||||
| `rapid_blink` |
|
||||
| `reversed` |
|
||||
| `hidden` |
|
||||
| `crossed_out` |
|
||||
|
||||
> 💡 The `underlined` modifier is deprecated and only available for backwards
|
||||
> compatibility. Its behavior is equivalent to setting `underline.style="line"`.
|
||||
> 💡 The `underlined` modifier is deprecated and only available for backwards compatibility.
|
||||
> Its behavior is equivalent to setting `underline.style="line"`.
|
||||
|
||||
### Underline Style
|
||||
|
||||
One of the following values can be used for `underline.style`, providing it is
|
||||
One of the following values may be used as a value for `underline.style`, providing it is
|
||||
supported by your terminal emulator.
|
||||
|
||||
| Modifier |
|
||||
| --- |
|
||||
| `line` |
|
||||
| `curl` |
|
||||
| `dashed` |
|
||||
| `dotted` |
|
||||
| `double_line` |
|
||||
|
||||
| Modifier |
|
||||
| ------------- |
|
||||
| `line` |
|
||||
@ -136,7 +143,7 @@ ### Underline Style
|
||||
|
||||
### Inheritance
|
||||
|
||||
Extends other themes by setting the `inherits` property to an existing theme.
|
||||
Extend other themes by setting the `inherits` property to an existing theme.
|
||||
|
||||
```toml
|
||||
inherits = "boo_berry"
|
||||
@ -158,9 +165,7 @@ #### Syntax highlighting
|
||||
These keys match
|
||||
[tree-sitter scopes](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#theme).
|
||||
|
||||
When determining styling for a highlight, the longest matching theme key will be
|
||||
used. For example, if the highlight is `function.builtin.static,` the key
|
||||
`function.builtin` will be used instead of function.
|
||||
When determining styling for a highlight, the longest matching theme key will be used. For example, if the highlight is `function.builtin.static`, the key `function.builtin` will be used instead of `function`.
|
||||
|
||||
We use a similar set of scopes as
|
||||
[Sublime Text](https://www.sublimetext.com/docs/scope_naming.html). See also
|
||||
@ -174,10 +179,8 @@ #### Syntax highlighting
|
||||
- `variant`
|
||||
- `constructor`
|
||||
|
||||
- `constant` (TODO: constant.other.placeholder for `%v)`
|
||||
|
||||
- `builtin` Special constants provided by the language (`true`, `false`, `nil`
|
||||
etc.)
|
||||
- `constant` (TODO: constant.other.placeholder for `%v`)
|
||||
- `builtin` Special constants provided by the language (`true`, `false`, `nil` etc)
|
||||
- `boolean`
|
||||
- `character`
|
||||
- `escape`
|
||||
@ -285,53 +288,58 @@ #### Interface
|
||||
- `completion` - for completion doc popup UI
|
||||
- `hover` - for hover popup UI
|
||||
|
||||
| Key | Notes |
|
||||
| --------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| `ui.background` | |
|
||||
| `ui.background.separator` | Picker separator below input line |
|
||||
| `ui.cursor` | |
|
||||
| `ui.cursor.insert` | |
|
||||
| `ui.cursor.select` | |
|
||||
| `ui.cursor.match` | Matching bracket etc. |
|
||||
| `ui.cursor.primary` | Cursor with primary selection |
|
||||
| `ui.gutter` | Gutter |
|
||||
| `ui.gutter.selected` | Gutter for the line the cursor is on |
|
||||
| `ui.linenr` | Line numbers |
|
||||
| `ui.linenr.selected` | Line number for the line the cursor is on |
|
||||
| `ui.statusline` | `statusline` |
|
||||
| `ui.statusline.inactive` | `statusline` (unfocused document) |
|
||||
| `ui.statusline.normal` | `statusline` mode during normal mode ([only if `editor.color-modes` is enabled][editor-section]) |
|
||||
| `ui.statusline.insert` | `statusline` mode during insert mode ([only if `editor.color-modes` is enabled][editor-section]) |
|
||||
| `ui.statusline.select` | `statusline` mode during select mode ([only if `editor.color-modes` is enabled][editor-section]) |
|
||||
| `ui.statusline.separator` | Separator character in `statusline` |
|
||||
| `ui.popup` | Documentation popups (e.g. Space + k) |
|
||||
| `ui.popup.info` | Prompt for multiple key options |
|
||||
| `ui.window` | Borderlines separating splits |
|
||||
| `ui.help` | Description box for commands |
|
||||
| `ui.text` | Command prompts, popup text, etc. |
|
||||
| `ui.text.focus` | |
|
||||
| `ui.text.inactive` | Same as `ui.text` but when the text is inactive (e.g. suggestions) |
|
||||
| `ui.text.info` | The key: command text in `ui.popup.info` boxes |
|
||||
| `ui.virtual.ruler` | Ruler columns (see the [`editor.rulers` config][editor-section]) |
|
||||
| `ui.virtual.whitespace` | Visible whitespace characters |
|
||||
| `ui.virtual.indent-guide` | Vertical indent width guides |
|
||||
| `ui.menu` | Code and command completion menus |
|
||||
| `ui.menu.selected` | Selected autocomplete item |
|
||||
| `ui.menu.scroll` | `fg` sets thumb color, `bg` sets track color of scrollbar |
|
||||
| `ui.selection` | For selections in the editing area |
|
||||
| `ui.selection.primary` | |
|
||||
| `ui.cursorline.primary` | The line of the primary cursor ([if `cursorline` is enabled][editor-section]) |
|
||||
| `ui.cursorline.secondary` | The lines of any other cursors ([if `cursorline` is enabled][editor-section]) |
|
||||
| `ui.cursorcolumn.primary` | The column of the primary cursor ([if `cursorcolumn` is enabled][editor-section]) |
|
||||
| `ui.cursorcolumn.secondary` | The columns of any other cursors ([if `cursorcolumn` is enabled][editor-section]) |
|
||||
| `warning` | Diagnostics warning (gutter) |
|
||||
| `error` | Diagnostics error (gutter) |
|
||||
| `info` | Diagnostics info (gutter) |
|
||||
| `hint` | Diagnostics hint (gutter) |
|
||||
| `diagnostic` | Diagnostics fallback style (editing area) |
|
||||
| `diagnostic.hint` | Diagnostics hint (editing area) |
|
||||
| `diagnostic.info` | Diagnostics info (editing area) |
|
||||
| `diagnostic.warning` | Diagnostics warning (editing area) |
|
||||
| `diagnostic.error` | Diagnostics error (editing area) |
|
||||
|
||||
| Key | Notes |
|
||||
| --- | --- |
|
||||
| `ui.background` | |
|
||||
| `ui.background.separator` | Picker separator below input line |
|
||||
| `ui.cursor` | |
|
||||
| `ui.cursor.normal` | |
|
||||
| `ui.cursor.insert` | |
|
||||
| `ui.cursor.select` | |
|
||||
| `ui.cursor.match` | Matching bracket etc. |
|
||||
| `ui.cursor.primary` | Cursor with primary selection |
|
||||
| `ui.cursor.primary.normal` | |
|
||||
| `ui.cursor.primary.insert` | |
|
||||
| `ui.cursor.primary.select` | |
|
||||
| `ui.gutter` | Gutter |
|
||||
| `ui.gutter.selected` | Gutter for the line the cursor is on |
|
||||
| `ui.linenr` | Line numbers |
|
||||
| `ui.linenr.selected` | Line number for the line the cursor is on |
|
||||
| `ui.statusline` | Statusline |
|
||||
| `ui.statusline.inactive` | Statusline (unfocused document) |
|
||||
| `ui.statusline.normal` | Statusline mode during normal mode ([only if `editor.color-modes` is enabled][editor-section]) |
|
||||
| `ui.statusline.insert` | Statusline mode during insert mode ([only if `editor.color-modes` is enabled][editor-section]) |
|
||||
| `ui.statusline.select` | Statusline mode during select mode ([only if `editor.color-modes` is enabled][editor-section]) |
|
||||
| `ui.statusline.separator` | Separator character in statusline |
|
||||
| `ui.popup` | Documentation popups (e.g Space + k) |
|
||||
| `ui.popup.info` | Prompt for multiple key options |
|
||||
| `ui.window` | Border lines separating splits |
|
||||
| `ui.help` | Description box for commands |
|
||||
| `ui.text` | Command prompts, popup text, etc. |
|
||||
| `ui.text.focus` | |
|
||||
| `ui.text.inactive` | Same as `ui.text` but when the text is inactive (e.g. suggestions) |
|
||||
| `ui.text.info` | The key: command text in `ui.popup.info` boxes |
|
||||
| `ui.virtual.ruler` | Ruler columns (see the [`editor.rulers` config][editor-section]) |
|
||||
| `ui.virtual.whitespace` | Visible whitespace characters |
|
||||
| `ui.virtual.indent-guide` | Vertical indent width guides |
|
||||
| `ui.menu` | Code and command completion menus |
|
||||
| `ui.menu.selected` | Selected autocomplete item |
|
||||
| `ui.menu.scroll` | `fg` sets thumb color, `bg` sets track color of scrollbar |
|
||||
| `ui.selection` | For selections in the editing area |
|
||||
| `ui.selection.primary` | |
|
||||
| `ui.cursorline.primary` | The line of the primary cursor ([if cursorline is enabled][editor-section]) |
|
||||
| `ui.cursorline.secondary` | The lines of any other cursors ([if cursorline is enabled][editor-section]) |
|
||||
| `ui.cursorcolumn.primary` | The column of the primary cursor ([if cursorcolumn is enabled][editor-section]) |
|
||||
| `ui.cursorcolumn.secondary` | The columns of any other cursors ([if cursorcolumn is enabled][editor-section]) |
|
||||
| `warning` | Diagnostics warning (gutter) |
|
||||
| `error` | Diagnostics error (gutter) |
|
||||
| `info` | Diagnostics info (gutter) |
|
||||
| `hint` | Diagnostics hint (gutter) |
|
||||
| `diagnostic` | Diagnostics fallback style (editing area) |
|
||||
| `diagnostic.hint` | Diagnostics hint (editing area) |
|
||||
| `diagnostic.info` | Diagnostics info (editing area) |
|
||||
| `diagnostic.warning` | Diagnostics warning (editing area) |
|
||||
| `diagnostic.error` | Diagnostics error (editing area) |
|
||||
|
||||
[editor-section]: ./configuration.md#editor-section
|
||||
|
@ -1,7 +1,6 @@
|
||||
# Using Helix
|
||||
|
||||
<!--toc:start-->
|
||||
|
||||
- [Using Helix](#using-helix)
|
||||
- [Registers](#registers)
|
||||
- [User-defined Registers](#user-defined-registers)
|
||||
@ -34,8 +33,7 @@ ### User-defined Registers
|
||||
- `"ay` - Yank the current selection to register `a`.
|
||||
- `"op` - Paste the text in register `o` after the selection.
|
||||
|
||||
If a register is selected before invoking a change or delete command, the
|
||||
selection will be stored in the register and the action will be carried out:
|
||||
If a register is selected before invoking a change or delete command, the selection will be stored in the register and the action will be carried out:
|
||||
|
||||
- `"hc` - Store the selection in register `h` and then change it (delete and
|
||||
enter insert mode).
|
||||
@ -50,19 +48,15 @@ ### Built-in Registers
|
||||
| `"` | Last yanked text |
|
||||
| `_` | Black hole |
|
||||
|
||||
The system clipboard is not directly supported by a built-in register. Instead,
|
||||
special commands and keybindings are provided. Refer to the
|
||||
The system clipboard is not directly supported by a built-in register. Instead, special commands and keybindings are provided. Refer to the
|
||||
[key map](keymap.md#space-mode) for more details.
|
||||
|
||||
The black hole register is a no-op register, meaning that no data will be read
|
||||
or written to it.
|
||||
The black hole register is a no-op register, meaning that no data will be read or written to it.
|
||||
|
||||
## Surround
|
||||
|
||||
Helix includes built-in functionality similar to
|
||||
[vim-surround](https://github.com/tpope/vim-surround). The key mappings for this
|
||||
functionality have been inspired by
|
||||
[vim-sandwich](https://github.com/machakann/vim-sandwich).
|
||||
Helix includes built-in functionality similar to [vim-surround](https://github.com/tpope/vim-surround).
|
||||
The keymappings have been inspired from [vim-sandwich](https://github.com/machakann/vim-sandwich):
|
||||
|
||||
![Surround demo](https://user-images.githubusercontent.com/23398472/122865801-97073180-d344-11eb-8142-8f43809982c6.gif)
|
||||
|
||||
@ -74,8 +68,7 @@ ## Surround
|
||||
|
||||
You can use counts to act on outer pairs.
|
||||
|
||||
Surround can also act on multiple selections. For example, to change every
|
||||
occurrence of `(use)` to `[use]`:
|
||||
Surround can also act on multiple selections. For example, to change every occurrence of `(use)` to `[use]`:
|
||||
|
||||
1. `%` to select the whole file
|
||||
2. `s` to split the selections on a search term
|
||||
@ -86,9 +79,9 @@ ## Surround
|
||||
|
||||
## Moving the Primary Selection with Syntax-tree Motions
|
||||
|
||||
`Alt-p`, `Alt-o`, `Alt-i`, and `Alt-n` (or `Alt` and arrow keys) allow you to
|
||||
move the primary selection according to its location in the syntax tree. For
|
||||
example, many languages have the following syntax for function calls:
|
||||
`Alt-p`, `Alt-o`, `Alt-i`, and `Alt-n` (or `Alt` and arrow keys) allow you to move the primary
|
||||
selection according to its location in the syntax tree. For example, many languages have the
|
||||
following syntax for function calls:
|
||||
|
||||
```js
|
||||
func(arg1, arg2, arg3);
|
||||
@ -129,7 +122,10 @@ ## Moving the Primary Selection with Syntax-tree Motions
|
||||
Alt-n, it will select the next sibling in the syntax tree: `arg2`.
|
||||
|
||||
```js
|
||||
func([arg1], arg2, arg3) > func(arg1, [arg2], arg3);
|
||||
// before
|
||||
func([arg1], arg2, arg3)
|
||||
// after
|
||||
func(arg1, [arg2], arg3);
|
||||
```
|
||||
|
||||
Similarly, Alt-o will expand the selection to the parent node, in this case, the
|
||||
@ -173,24 +169,25 @@ ## Selecting and Manipulating Text with Textobjects
|
||||
| `t` | Test |
|
||||
| `g` | Change |
|
||||
|
||||
> 💡`f`, `c`, etc. need a tree-sitter grammar active for the current document
|
||||
> and a special tree-sitter query file to work properly. [Only some
|
||||
> grammars][lang-support] currently have the query file implemented.
|
||||
> Contributions are welcome!
|
||||
> 💡 `f`, `c`, etc need a tree-sitter grammar active for the current
|
||||
document and a special tree-sitter query file to work properly. [Only
|
||||
some grammars][lang-support] currently have the query file implemented.
|
||||
Contributions are welcome!
|
||||
|
||||
## Navigating Using Tree-sitter Textobjects
|
||||
## Navigating Using Tree-sitter Textobject
|
||||
|
||||
Navigating between functions, classes, parameters, and other elements is
|
||||
possible using tree-sitter and Textobject queries. For example, to move to the
|
||||
next function use `]f`, to move to previous class use `[c`, and so on.
|
||||
possible using tree-sitter and Textobject queries. For
|
||||
example to move to the next function use `]f`, to move to previous
|
||||
class use `[c`, and so on.
|
||||
|
||||
![tree-sitter-nav-demo][tree-sitter-nav-demo]
|
||||
|
||||
For the full reference see the [unimpaired][unimpaired-keybinds] section of the
|
||||
key bind documentation.
|
||||
For the full reference see the [unimpaired][unimpaired-keybinds] section of the key bind
|
||||
documentation.
|
||||
|
||||
> 💡 This feature relies on tree-sitter Textobjects and requires the
|
||||
> corresponding query file to work properly.
|
||||
> 💡 This feature relies on tree-sitter Textobjects
|
||||
> and requires the corresponding query file to work properly.
|
||||
|
||||
[lang-support]: ./lang-support.md
|
||||
[unimpaired-keybinds]: ./keymap.md#unimpaired
|
||||
|
@ -48,6 +48,18 @@
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #252932;
|
||||
--search-mark-bg: #e3b171;
|
||||
--hljs-background: #191f26;
|
||||
--hljs-color: #e6e1cf;
|
||||
--hljs-quote: #5c6773;
|
||||
--hljs-variable: #ff7733;
|
||||
--hljs-type: #ffee99;
|
||||
--hljs-title: #b8cc52;
|
||||
--hljs-symbol: #ffb454;
|
||||
--hljs-selector-tag: #ff7733;
|
||||
--hljs-selector-tag: #36a3d9;
|
||||
--hljs-selector-tag: #00568d;
|
||||
--hljs-selector-tag: #91b362;
|
||||
--hljs-selector-tag: #d96c75;
|
||||
}
|
||||
|
||||
.coal {
|
||||
@ -88,6 +100,18 @@
|
||||
--searchresults-border-color: #98a3ad;
|
||||
--searchresults-li-bg: #2b2b2f;
|
||||
--search-mark-bg: #355c7d;
|
||||
--hljs-background: #969896;
|
||||
--hljs-color: #cc6666;
|
||||
--hljs-quote: #de935f;
|
||||
--hljs-variable: #f0c674;
|
||||
--hljs-type: #b5bd68;
|
||||
--hljs-title: #8abeb7;
|
||||
--hljs-symbol: #81a2be;
|
||||
--hljs-selector-tag: #b294bb;
|
||||
--hljs-selector-tag: #1d1f21;
|
||||
--hljs-selector-tag: #c5c8c6;
|
||||
--hljs-selector-tag: #718c00;
|
||||
--hljs-selector-tag: #c82829;
|
||||
}
|
||||
|
||||
.light {
|
||||
@ -128,6 +152,14 @@
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #e4f2fe;
|
||||
--search-mark-bg: #a2cff5;
|
||||
--hljs-background: #f6f7f6;
|
||||
--hljs-color: #000;
|
||||
--hljs-quote: #575757;
|
||||
--hljs-variable: #d70025;
|
||||
--hljs-type: #b21e00;
|
||||
--hljs-title: #0030f2;
|
||||
--hljs-symbol: #008200;
|
||||
--hljs-selector-tag: #9d00ec;
|
||||
}
|
||||
|
||||
.navy {
|
||||
@ -168,6 +200,19 @@
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #a2cff5;
|
||||
|
||||
--hljs-background: #969896;
|
||||
--hljs-color: #cc6666;
|
||||
--hljs-quote: #de935f;
|
||||
--hljs-variable: #f0c674;
|
||||
--hljs-type: #b5bd68;
|
||||
--hljs-title: #8abeb7;
|
||||
--hljs-symbol: #81a2be;
|
||||
--hljs-selector-tag: #b294bb;
|
||||
--hljs-selector-tag: #1d1f21;
|
||||
--hljs-selector-tag: #c5c8c6;
|
||||
--hljs-selector-tag: #718c00;
|
||||
--hljs-selector-tag: #c82829;
|
||||
}
|
||||
|
||||
.rust {
|
||||
@ -208,6 +253,14 @@
|
||||
--searchresults-border-color: #888;
|
||||
--searchresults-li-bg: #dec2a2;
|
||||
--search-mark-bg: #e69f67;
|
||||
--hljs-background: #f6f7f6;
|
||||
--hljs-color: #000;
|
||||
--hljs-quote: #575757;
|
||||
--hljs-variable: #d70025;
|
||||
--hljs-type: #b21e00;
|
||||
--hljs-title: #0030f2;
|
||||
--hljs-symbol: #008200;
|
||||
--hljs-selector-tag: #9d00ec;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
@ -292,7 +345,15 @@
|
||||
--searchresults-header-fg: #5f5f71;
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #a2cff5;
|
||||
--search-mark-bg: #acff5;
|
||||
--hljs-background: #2f1e2e;
|
||||
--hljs-color: #a39e9b;
|
||||
--hljs-quote: #8d8687;
|
||||
--hljs-variable: #ef6155;
|
||||
--hljs-type: #f99b15;
|
||||
--hljs-title: #fec418;
|
||||
--hljs-symbol: #48b685;
|
||||
--hljs-selector-tag: #815ba4;
|
||||
}
|
||||
|
||||
.colibri {
|
||||
@ -338,5 +399,13 @@
|
||||
--searchresults-border-color: #5c5c68;
|
||||
--searchresults-li-bg: #242430;
|
||||
--search-mark-bg: #a2cff5;
|
||||
--hljs-background: #TODO;
|
||||
--hljs-color: #TODO;
|
||||
--hljs-quote: #TODO;
|
||||
--hljs-variable: #TODO;
|
||||
--hljs-type: #TODO;
|
||||
--hljs-title: #TODO;
|
||||
--hljs-symbol: #TODO;
|
||||
--hljs-selector-tag: #TODO;
|
||||
*/
|
||||
}
|
||||
|
@ -7,12 +7,12 @@ code.hljs {
|
||||
padding:3px 5px
|
||||
}
|
||||
.hljs {
|
||||
background:#2f1e2e;
|
||||
color:#a39e9b
|
||||
background: var(--hljs-background);
|
||||
color: var(--hljs-color);
|
||||
}
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color:#8d8687
|
||||
color: var(--hljs-quote)
|
||||
}
|
||||
.hljs-link,
|
||||
.hljs-meta,
|
||||
@ -23,7 +23,7 @@ code.hljs {
|
||||
.hljs-tag,
|
||||
.hljs-template-variable,
|
||||
.hljs-variable {
|
||||
color:#ef6155
|
||||
color: var(--hljs-variable)
|
||||
}
|
||||
.hljs-built_in,
|
||||
.hljs-deletion,
|
||||
@ -31,22 +31,22 @@ code.hljs {
|
||||
.hljs-number,
|
||||
.hljs-params,
|
||||
.hljs-type {
|
||||
color:#f99b15
|
||||
color: var(--hljs-type)
|
||||
}
|
||||
.hljs-attribute,
|
||||
.hljs-section,
|
||||
.hljs-title {
|
||||
color:#fec418
|
||||
color: var(--hljs-title)
|
||||
}
|
||||
.hljs-addition,
|
||||
.hljs-bullet,
|
||||
.hljs-string,
|
||||
.hljs-symbol {
|
||||
color:#48b685
|
||||
color: var(--hljs-symbol)
|
||||
}
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag {
|
||||
color:#815ba4
|
||||
color: var(--hljs-selector-tag)
|
||||
}
|
||||
.hljs-emphasis {
|
||||
font-style:italic
|
||||
|
87
contrib/Helix.appdata.xml
Normal file
87
contrib/Helix.appdata.xml
Normal file
@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>com.helix_editor.Helix</id>
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>MPL-2.0</project_license>
|
||||
<name>Helix</name>
|
||||
<summary>A post-modern text editor</summary>
|
||||
|
||||
<description>
|
||||
<p>
|
||||
Helix is a terminal-based text editor inspired by Kakoune / Neovim and written in Rust.
|
||||
</p>
|
||||
<ul>
|
||||
<li>Vim-like modal editing</li>
|
||||
<li>Multiple selections</li>
|
||||
<li>Built-in language server support</li>
|
||||
<li>Smart, incremental syntax highlighting and code editing via tree-sitter</li>
|
||||
</ul>
|
||||
</description>
|
||||
|
||||
<launchable type="desktop-id">Helix.desktop</launchable>
|
||||
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<caption>Helix with default theme</caption>
|
||||
<image>https://github.com/helix-editor/helix/raw/d4565b4404cabc522bd60822abd374755581d751/screenshot.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
|
||||
<url type="homepage">https://helix-editor.com/</url>
|
||||
<url type="donation">https://opencollective.com/helix-editor</url>
|
||||
<url type="help">https://docs.helix-editor.com/</url>
|
||||
<url type="vcs-browser">https://github.com/helix-editor/helix</url>
|
||||
<url type="bugtracker">https://github.com/helix-editor/helix/issues</url>
|
||||
|
||||
<content_rating type="oars-1.1" />
|
||||
|
||||
<releases>
|
||||
<release version="22.12" date="2022-12-6">
|
||||
<url>https://helix-editor.com/news/release-22-12-highlights/</url>
|
||||
</release>
|
||||
<release version="22.08" date="2022-8-31">
|
||||
<url>https://helix-editor.com/news/release-22-08-highlights/</url>
|
||||
</release>
|
||||
<release version="22.05" date="2022-5-28">
|
||||
<url>https://helix-editor.com/news/release-22-05-highlights/</url>
|
||||
</release>
|
||||
<release version="22.03" date="2022-3-28">
|
||||
<url>https://helix-editor.com/news/release-22-03-highlights/</url>
|
||||
</release>
|
||||
</releases>
|
||||
|
||||
<requires>
|
||||
<control>keyboard</control>
|
||||
</requires>
|
||||
|
||||
<categories>
|
||||
<category>Utility</category>
|
||||
<category>TextEditor</category>
|
||||
</categories>
|
||||
|
||||
<keywords>
|
||||
<keyword>text</keyword>
|
||||
<keyword>editor</keyword>
|
||||
<keyword>development</keyword>
|
||||
<keyword>programming</keyword>
|
||||
</keywords>
|
||||
|
||||
<provides>
|
||||
<binary>hx</binary>
|
||||
<mediatype>text/english</mediatype>
|
||||
<mediatype>text/plain</mediatype>
|
||||
<mediatype>text/x-makefile</mediatype>
|
||||
<mediatype>text/x-c++hdr</mediatype>
|
||||
<mediatype>text/x-c++src</mediatype>
|
||||
<mediatype>text/x-chdr</mediatype>
|
||||
<mediatype>text/x-csrc</mediatype>
|
||||
<mediatype>text/x-java</mediatype>
|
||||
<mediatype>text/x-moc</mediatype>
|
||||
<mediatype>text/x-pascal</mediatype>
|
||||
<mediatype>text/x-tcl</mediatype>
|
||||
<mediatype>text/x-tex</mediatype>
|
||||
<mediatype>application/x-shellscript</mediatype>
|
||||
<mediatype>text/x-c</mediatype>
|
||||
<mediatype>text/x-c++</mediatype>
|
||||
</provides>
|
||||
</component>
|
@ -5,6 +5,7 @@ ## Checklist
|
||||
we'll use `<tag>` as a placeholder for the tag being published.
|
||||
|
||||
* Merge the changelog PR
|
||||
* Add new `<release>` entry in `contrib/Helix.appdata.xml` with release information according to the [AppStream spec](https://www.freedesktop.org/software/appstream/docs/sect-Metadata-Releases.html)
|
||||
* Tag and push
|
||||
* `git tag -s -m "<tag>" -a <tag> && git push`
|
||||
* Make sure to switch to master and pull first
|
||||
|
@ -31,12 +31,12 @@ arc-swap = "1"
|
||||
regex = "1"
|
||||
bitflags = "1.3"
|
||||
ahash = "0.8.2"
|
||||
hashbrown = { version = "0.13.1", features = ["raw"] }
|
||||
hashbrown = { version = "0.13.2", features = ["raw"] }
|
||||
|
||||
log = "0.4"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
toml = "0.5"
|
||||
toml = "0.6"
|
||||
|
||||
imara-diff = "0.1.0"
|
||||
|
||||
|
@ -28,8 +28,8 @@ fn test_treesitter_indent(file_name: &str, lang_scope: &str) {
|
||||
|
||||
let mut config_file = test_dir;
|
||||
config_file.push("languages.toml");
|
||||
let config = std::fs::read(config_file).unwrap();
|
||||
let config = toml::from_slice(&config).unwrap();
|
||||
let config = std::fs::read_to_string(config_file).unwrap();
|
||||
let config = toml::from_str(&config).unwrap();
|
||||
let loader = Loader::new(config);
|
||||
|
||||
// set runtime path so we can find the queries
|
||||
|
@ -19,7 +19,7 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "net", "sync"] }
|
||||
which = "4.2"
|
||||
which = "4.4"
|
||||
|
||||
[dev-dependencies]
|
||||
fern = "0.6"
|
||||
|
@ -16,7 +16,7 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
toml = "0.5"
|
||||
toml = "0.6"
|
||||
etcetera = "0.4"
|
||||
tree-sitter = "0.20"
|
||||
once_cell = "1.17"
|
||||
|
@ -1,6 +1,9 @@
|
||||
use std::str::from_utf8;
|
||||
|
||||
/// Default built-in languages.toml.
|
||||
pub fn default_lang_config() -> toml::Value {
|
||||
toml::from_slice(include_bytes!("../../languages.toml"))
|
||||
let default_config = include_bytes!("../../languages.toml");
|
||||
toml::from_str(from_utf8(default_config).unwrap())
|
||||
.expect("Could not parse built-in languages.toml to valid toml")
|
||||
}
|
||||
|
||||
@ -11,8 +14,8 @@ pub fn user_lang_config() -> Result<toml::Value, toml::de::Error> {
|
||||
.chain([crate::config_dir()].into_iter())
|
||||
.map(|path| path.join("languages.toml"))
|
||||
.filter_map(|file| {
|
||||
std::fs::read(&file)
|
||||
.map(|config| toml::from_slice(&config))
|
||||
std::fs::read_to_string(file)
|
||||
.map(|config| toml::from_str(&config))
|
||||
.ok()
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
|
@ -515,5 +515,5 @@ pub fn load_runtime_file(language: &str, filename: &str) -> Result<String, std::
|
||||
.join("queries")
|
||||
.join(language)
|
||||
.join(filename);
|
||||
std::fs::read_to_string(&path)
|
||||
std::fs::read_to_string(path)
|
||||
}
|
||||
|
@ -179,6 +179,8 @@ fn get_name(v: &Value) -> Option<&str> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod merge_toml_tests {
|
||||
use std::str;
|
||||
|
||||
use super::merge_toml_values;
|
||||
use toml::Value;
|
||||
|
||||
@ -191,8 +193,9 @@ fn language_toml_map_merges() {
|
||||
indent = { tab-width = 4, unit = " ", test = "aaa" }
|
||||
"#;
|
||||
|
||||
let base: Value = toml::from_slice(include_bytes!("../../languages.toml"))
|
||||
.expect("Couldn't parse built-in languages config");
|
||||
let base = include_bytes!("../../languages.toml");
|
||||
let base = str::from_utf8(base).expect("Couldn't parse built-in languages config");
|
||||
let base: Value = toml::from_str(base).expect("Couldn't parse built-in languages config");
|
||||
let user: Value = toml::from_str(USER).unwrap();
|
||||
|
||||
let merged = merge_toml_values(base, user, 3);
|
||||
@ -224,8 +227,9 @@ fn language_toml_nested_array_merges() {
|
||||
language-server = { command = "deno", args = ["lsp"] }
|
||||
"#;
|
||||
|
||||
let base: Value = toml::from_slice(include_bytes!("../../languages.toml"))
|
||||
.expect("Couldn't parse built-in languages config");
|
||||
let base = include_bytes!("../../languages.toml");
|
||||
let base = str::from_utf8(base).expect("Couldn't parse built-in languages config");
|
||||
let base: Value = toml::from_str(base).expect("Couldn't parse built-in languages config");
|
||||
let user: Value = toml::from_str(USER).unwrap();
|
||||
|
||||
let merged = merge_toml_values(base, user, 3);
|
||||
|
@ -25,4 +25,4 @@ serde_json = "1.0"
|
||||
thiserror = "1.0"
|
||||
tokio = { version = "1.24", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot", "sync"] }
|
||||
tokio-stream = "0.1.11"
|
||||
which = "4.2"
|
||||
which = "4.4"
|
||||
|
@ -37,7 +37,7 @@ helix-loader = { version = "0.6", path = "../helix-loader" }
|
||||
anyhow = "1"
|
||||
once_cell = "1.17"
|
||||
|
||||
which = "4.2"
|
||||
which = "4.4"
|
||||
|
||||
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"] }
|
||||
@ -61,7 +61,7 @@ pulldown-cmark = { version = "0.9", default-features = false }
|
||||
content_inspector = "0.2.4"
|
||||
|
||||
# config
|
||||
toml = "0.5"
|
||||
toml = "0.6"
|
||||
|
||||
serde_json = "1.0"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
@ -5,7 +5,7 @@
|
||||
pub use dap::*;
|
||||
use helix_vcs::Hunk;
|
||||
pub use lsp::*;
|
||||
use tui::text::Spans;
|
||||
use tui::widgets::Row;
|
||||
pub use typed::*;
|
||||
|
||||
use helix_core::{
|
||||
@ -26,7 +26,6 @@
|
||||
SmallVec, Tendril, Transaction,
|
||||
};
|
||||
use helix_view::{
|
||||
apply_transaction,
|
||||
clipboard::ClipboardType,
|
||||
document::{FormatterError, Mode, SCRATCH_BUFFER_NAME},
|
||||
editor::{Action, Motion},
|
||||
@ -384,6 +383,7 @@ pub fn doc(&self) -> &str {
|
||||
swap_view_down, "Swap with split below",
|
||||
transpose_view, "Transpose splits",
|
||||
rotate_view, "Goto next window",
|
||||
rotate_view_reverse, "Goto previous window",
|
||||
hsplit, "Horizontal bottom split",
|
||||
hsplit_new, "Horizontal bottom split scratch buffer",
|
||||
vsplit, "Vertical right split",
|
||||
@ -448,9 +448,16 @@ pub fn doc(&self) -> &str {
|
||||
|
||||
impl fmt::Debug for MappableCommand {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("MappableCommand")
|
||||
.field(&self.name())
|
||||
.finish()
|
||||
match self {
|
||||
MappableCommand::Static { name, .. } => {
|
||||
f.debug_tuple("MappableCommand").field(name).finish()
|
||||
}
|
||||
MappableCommand::Typable { name, args, .. } => f
|
||||
.debug_tuple("MappableCommand")
|
||||
.field(name)
|
||||
.field(args)
|
||||
.finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -505,12 +512,16 @@ fn eq(&self, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(
|
||||
MappableCommand::Typable {
|
||||
name: first_name, ..
|
||||
name: first_name,
|
||||
args: first_args,
|
||||
..
|
||||
},
|
||||
MappableCommand::Typable {
|
||||
name: second_name, ..
|
||||
name: second_name,
|
||||
args: second_args,
|
||||
..
|
||||
},
|
||||
) => first_name == second_name,
|
||||
) => first_name == second_name && first_args == second_args,
|
||||
(
|
||||
MappableCommand::Static {
|
||||
name: first_name, ..
|
||||
@ -863,7 +874,7 @@ fn align_selections(cx: &mut Context) {
|
||||
changes.sort_unstable_by_key(|(from, _, _)| *from);
|
||||
|
||||
let transaction = Transaction::change(doc.text(), changes.into_iter());
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
fn goto_window(cx: &mut Context, align: Align) {
|
||||
@ -1315,7 +1326,7 @@ fn replace(cx: &mut Context) {
|
||||
}
|
||||
});
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
exit_select_mode(cx);
|
||||
}
|
||||
})
|
||||
@ -1333,7 +1344,7 @@ fn switch_case_impl<F>(cx: &mut Context, change_fn: F)
|
||||
(range.from(), range.to(), Some(text))
|
||||
});
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
fn switch_case(cx: &mut Context) {
|
||||
@ -1863,7 +1874,7 @@ fn new(path: &Path, line_num: usize) -> Self {
|
||||
impl ui::menu::Item for FileResult {
|
||||
type Data = Option<PathBuf>;
|
||||
|
||||
fn label(&self, current_path: &Self::Data) -> Spans {
|
||||
fn format(&self, current_path: &Self::Data) -> Row {
|
||||
let relative_path = helix_core::path::get_relative_path(&self.path)
|
||||
.to_string_lossy()
|
||||
.into_owned();
|
||||
@ -2003,6 +2014,10 @@ fn label(&self, current_path: &Self::Data) -> Spans {
|
||||
let line_num = *line_num;
|
||||
let (view, doc) = current!(cx.editor);
|
||||
let text = doc.text();
|
||||
if line_num >= text.len_lines() {
|
||||
cx.editor.set_error("The line you jumped to does not exist anymore because the file has changed.");
|
||||
return;
|
||||
}
|
||||
let start = text.line_to_char(line_num);
|
||||
let end = text.line_to_char((line_num + 1).min(text.len_lines()));
|
||||
|
||||
@ -2158,7 +2173,7 @@ fn delete_selection_impl(cx: &mut Context, op: Operation) {
|
||||
let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
|
||||
(range.from(), range.to(), None)
|
||||
});
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
|
||||
match op {
|
||||
Operation::Delete => {
|
||||
@ -2176,7 +2191,7 @@ fn delete_selection_insert_mode(doc: &mut Document, view: &mut View, selection:
|
||||
let transaction = Transaction::change_by_selection(doc.text(), selection, |range| {
|
||||
(range.from(), range.to(), None)
|
||||
});
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
fn delete_selection(cx: &mut Context) {
|
||||
@ -2272,7 +2287,7 @@ fn append_mode(cx: &mut Context) {
|
||||
doc.text(),
|
||||
[(end, end, Some(doc.line_ending.as_str().into()))].into_iter(),
|
||||
);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
let selection = doc.selection(view.id).clone().transform(|range| {
|
||||
@ -2311,7 +2326,7 @@ struct BufferMeta {
|
||||
impl ui::menu::Item for BufferMeta {
|
||||
type Data = ();
|
||||
|
||||
fn label(&self, _data: &Self::Data) -> Spans {
|
||||
fn format(&self, _data: &Self::Data) -> Row {
|
||||
let path = self
|
||||
.path
|
||||
.as_deref()
|
||||
@ -2380,7 +2395,7 @@ struct JumpMeta {
|
||||
impl ui::menu::Item for JumpMeta {
|
||||
type Data = ();
|
||||
|
||||
fn label(&self, _data: &Self::Data) -> Spans {
|
||||
fn format(&self, _data: &Self::Data) -> Row {
|
||||
let path = self
|
||||
.path
|
||||
.as_deref()
|
||||
@ -2453,7 +2468,7 @@ fn label(&self, _data: &Self::Data) -> Spans {
|
||||
impl ui::menu::Item for MappableCommand {
|
||||
type Data = ReverseKeymap;
|
||||
|
||||
fn label(&self, keymap: &Self::Data) -> Spans {
|
||||
fn format(&self, keymap: &Self::Data) -> Row {
|
||||
let fmt_binding = |bindings: &Vec<Vec<KeyEvent>>| -> String {
|
||||
bindings.iter().fold(String::new(), |mut acc, bind| {
|
||||
if !acc.is_empty() {
|
||||
@ -2582,7 +2597,7 @@ async fn make_format_callback(
|
||||
|
||||
if let Ok(format) = format {
|
||||
if doc.version() == doc_version {
|
||||
apply_transaction(&format, doc, view);
|
||||
doc.apply(&format, view.id);
|
||||
doc.append_changes_to_history(view);
|
||||
doc.detect_indent_and_line_ending();
|
||||
view.ensure_cursor_in_view(doc, scrolloff);
|
||||
@ -2675,7 +2690,7 @@ fn open(cx: &mut Context, open: Open) {
|
||||
|
||||
transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
// o inserts a new line after each line with a selection
|
||||
@ -3103,7 +3118,7 @@ pub fn insert_char(cx: &mut Context, c: char) {
|
||||
|
||||
let (view, doc) = current!(cx.editor);
|
||||
if let Some(t) = transaction {
|
||||
apply_transaction(&t, doc, view);
|
||||
doc.apply(&t, view.id);
|
||||
}
|
||||
|
||||
// TODO: need a post insert hook too for certain triggers (autocomplete, signature help, etc)
|
||||
@ -3125,7 +3140,7 @@ pub fn insert_tab(cx: &mut Context) {
|
||||
&doc.selection(view.id).clone().cursors(doc.text().slice(..)),
|
||||
indent,
|
||||
);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
pub fn insert_newline(cx: &mut Context) {
|
||||
@ -3230,7 +3245,7 @@ pub fn insert_newline(cx: &mut Context) {
|
||||
transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
|
||||
|
||||
let (view, doc) = current!(cx.editor);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
pub fn delete_char_backward(cx: &mut Context) {
|
||||
@ -3325,7 +3340,7 @@ pub fn delete_char_backward(cx: &mut Context) {
|
||||
}
|
||||
});
|
||||
let (view, doc) = current!(cx.editor);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
|
||||
lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic);
|
||||
}
|
||||
@ -3343,7 +3358,7 @@ pub fn delete_char_forward(cx: &mut Context) {
|
||||
None,
|
||||
)
|
||||
});
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
|
||||
lsp::signature_help_impl(cx, SignatureHelpInvoked::Automatic);
|
||||
}
|
||||
@ -3624,7 +3639,7 @@ fn paste_impl(
|
||||
transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
|
||||
}
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
pub(crate) fn paste_bracketed_value(cx: &mut Context, contents: String) {
|
||||
@ -3716,7 +3731,7 @@ fn replace_with_yanked(cx: &mut Context) {
|
||||
}
|
||||
});
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
exit_select_mode(cx);
|
||||
}
|
||||
}
|
||||
@ -3740,7 +3755,7 @@ fn replace_selections_with_clipboard_impl(
|
||||
)
|
||||
});
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
doc.append_changes_to_history(view);
|
||||
}
|
||||
Err(e) => return Err(e.context("Couldn't get system clipboard contents")),
|
||||
@ -3812,7 +3827,7 @@ fn indent(cx: &mut Context) {
|
||||
Some((pos, pos, Some(indent.clone())))
|
||||
}),
|
||||
);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
fn unindent(cx: &mut Context) {
|
||||
@ -3851,7 +3866,7 @@ fn unindent(cx: &mut Context) {
|
||||
|
||||
let transaction = Transaction::change(doc.text(), changes.into_iter());
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
fn format_selections(cx: &mut Context) {
|
||||
@ -3906,7 +3921,7 @@ fn format_selections(cx: &mut Context) {
|
||||
language_server.offset_encoding(),
|
||||
);
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
fn join_selections_impl(cx: &mut Context, select_space: bool) {
|
||||
@ -3938,6 +3953,11 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// nothing to do, bail out early to avoid crashes later
|
||||
if changes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
changes.sort_unstable_by_key(|(from, _to, _text)| *from);
|
||||
changes.dedup();
|
||||
|
||||
@ -3960,7 +3980,7 @@ fn join_selections_impl(cx: &mut Context, select_space: bool) {
|
||||
Transaction::change(doc.text(), changes.into_iter())
|
||||
};
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
fn keep_or_remove_selections_impl(cx: &mut Context, remove: bool) {
|
||||
@ -4103,7 +4123,7 @@ fn toggle_comments(cx: &mut Context) {
|
||||
.map(|tc| tc.as_ref());
|
||||
let transaction = comment::toggle_line_comments(doc.text(), doc.selection(view.id), token);
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
exit_select_mode(cx);
|
||||
}
|
||||
|
||||
@ -4159,7 +4179,7 @@ fn rotate_selection_contents(cx: &mut Context, direction: Direction) {
|
||||
.map(|(range, fragment)| (range.from(), range.to(), Some(fragment))),
|
||||
);
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
fn rotate_selection_contents_forward(cx: &mut Context) {
|
||||
@ -4317,6 +4337,10 @@ fn rotate_view(cx: &mut Context) {
|
||||
cx.editor.focus_next()
|
||||
}
|
||||
|
||||
fn rotate_view_reverse(cx: &mut Context) {
|
||||
cx.editor.focus_prev()
|
||||
}
|
||||
|
||||
fn jump_view_right(cx: &mut Context) {
|
||||
cx.editor.focus_direction(tree::Direction::Right)
|
||||
}
|
||||
@ -4688,7 +4712,7 @@ fn surround_add(cx: &mut Context) {
|
||||
|
||||
let transaction = Transaction::change(doc.text(), changes.into_iter())
|
||||
.with_selection(Selection::new(ranges, selection.primary_index()));
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
exit_select_mode(cx);
|
||||
})
|
||||
}
|
||||
@ -4728,7 +4752,7 @@ fn surround_replace(cx: &mut Context) {
|
||||
(pos, pos + 1, Some(t))
|
||||
}),
|
||||
);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
exit_select_mode(cx);
|
||||
});
|
||||
})
|
||||
@ -4756,7 +4780,7 @@ fn surround_delete(cx: &mut Context) {
|
||||
|
||||
let transaction =
|
||||
Transaction::change(doc.text(), change_pos.into_iter().map(|p| (p, p + 1, None)));
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
exit_select_mode(cx);
|
||||
})
|
||||
}
|
||||
@ -4971,7 +4995,7 @@ fn shell(cx: &mut compositor::Context, cmd: &str, behavior: &ShellBehavior) {
|
||||
if behavior != &ShellBehavior::Ignore {
|
||||
let transaction = Transaction::change(doc.text(), changes.into_iter())
|
||||
.with_selection(Selection::new(ranges, selection.primary_index()));
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
doc.append_changes_to_history(view);
|
||||
}
|
||||
|
||||
@ -5034,7 +5058,7 @@ fn add_newline_impl(cx: &mut Context, open: Open) {
|
||||
});
|
||||
|
||||
let transaction = Transaction::change(text, changes);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
|
||||
enum IncrementDirection {
|
||||
@ -5101,7 +5125,7 @@ fn increment_impl(cx: &mut Context, increment_direction: IncrementDirection) {
|
||||
let new_selection = Selection::new(new_selection_ranges, selection.primary_index());
|
||||
let transaction = Transaction::change(doc.text(), changes.into_iter());
|
||||
let transaction = transaction.with_selection(new_selection);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
use serde_json::{to_value, Value};
|
||||
use tokio_stream::wrappers::UnboundedReceiverStream;
|
||||
use tui::text::Spans;
|
||||
use tui::{text::Spans, widgets::Row};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
@ -25,7 +25,7 @@
|
||||
impl ui::menu::Item for StackFrame {
|
||||
type Data = ();
|
||||
|
||||
fn label(&self, _data: &Self::Data) -> Spans {
|
||||
fn format(&self, _data: &Self::Data) -> Row {
|
||||
self.name.as_str().into() // TODO: include thread_states in the label
|
||||
}
|
||||
}
|
||||
@ -33,7 +33,7 @@ fn label(&self, _data: &Self::Data) -> Spans {
|
||||
impl ui::menu::Item for DebugTemplate {
|
||||
type Data = ();
|
||||
|
||||
fn label(&self, _data: &Self::Data) -> Spans {
|
||||
fn format(&self, _data: &Self::Data) -> Row {
|
||||
self.name.as_str().into()
|
||||
}
|
||||
}
|
||||
@ -41,7 +41,7 @@ fn label(&self, _data: &Self::Data) -> Spans {
|
||||
impl ui::menu::Item for Thread {
|
||||
type Data = ThreadStates;
|
||||
|
||||
fn label(&self, thread_states: &Self::Data) -> Spans {
|
||||
fn format(&self, thread_states: &Self::Data) -> Row {
|
||||
format!(
|
||||
"{} ({})",
|
||||
self.name,
|
||||
|
@ -5,12 +5,15 @@
|
||||
util::{diagnostic_to_lsp_diagnostic, lsp_pos_to_pos, lsp_range_to_range, range_to_lsp_range},
|
||||
OffsetEncoding,
|
||||
};
|
||||
use tui::text::{Span, Spans};
|
||||
use tui::{
|
||||
text::{Span, Spans},
|
||||
widgets::Row,
|
||||
};
|
||||
|
||||
use super::{align_view, push_jump, Align, Context, Editor, Open};
|
||||
|
||||
use helix_core::{path, Selection};
|
||||
use helix_view::{apply_transaction, document::Mode, editor::Action, theme::Style};
|
||||
use helix_view::{document::Mode, editor::Action, theme::Style};
|
||||
|
||||
use crate::{
|
||||
compositor::{self, Compositor},
|
||||
@ -46,7 +49,7 @@ impl ui::menu::Item for lsp::Location {
|
||||
/// Current working directory.
|
||||
type Data = PathBuf;
|
||||
|
||||
fn label(&self, cwdir: &Self::Data) -> Spans {
|
||||
fn format(&self, cwdir: &Self::Data) -> Row {
|
||||
// The preallocation here will overallocate a few characters since it will account for the
|
||||
// URL's scheme, which is not used most of the time since that scheme will be "file://".
|
||||
// Those extra chars will be used to avoid allocating when writing the line number (in the
|
||||
@ -80,7 +83,7 @@ impl ui::menu::Item for lsp::SymbolInformation {
|
||||
/// Path to currently focussed document
|
||||
type Data = Option<lsp::Url>;
|
||||
|
||||
fn label(&self, current_doc_path: &Self::Data) -> Spans {
|
||||
fn format(&self, current_doc_path: &Self::Data) -> Row {
|
||||
if current_doc_path.as_ref() == Some(&self.location.uri) {
|
||||
self.name.as_str().into()
|
||||
} else {
|
||||
@ -110,7 +113,7 @@ struct PickerDiagnostic {
|
||||
impl ui::menu::Item for PickerDiagnostic {
|
||||
type Data = (DiagnosticStyles, DiagnosticsFormat);
|
||||
|
||||
fn label(&self, (styles, format): &Self::Data) -> Spans {
|
||||
fn format(&self, (styles, format): &Self::Data) -> Row {
|
||||
let mut style = self
|
||||
.diag
|
||||
.severity
|
||||
@ -149,6 +152,7 @@ fn label(&self, (styles, format): &Self::Data) -> Spans {
|
||||
Span::styled(&self.diag.message, style),
|
||||
Span::styled(code, style),
|
||||
])
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
@ -467,7 +471,7 @@ pub fn workspace_diagnostics_picker(cx: &mut Context) {
|
||||
|
||||
impl ui::menu::Item for lsp::CodeActionOrCommand {
|
||||
type Data = ();
|
||||
fn label(&self, _data: &Self::Data) -> Spans {
|
||||
fn format(&self, _data: &Self::Data) -> Row {
|
||||
match self {
|
||||
lsp::CodeActionOrCommand::CodeAction(action) => action.title.as_str().into(),
|
||||
lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(),
|
||||
@ -662,7 +666,7 @@ pub fn code_action(cx: &mut Context) {
|
||||
|
||||
impl ui::menu::Item for lsp::Command {
|
||||
type Data = ();
|
||||
fn label(&self, _data: &Self::Data) -> Spans {
|
||||
fn format(&self, _data: &Self::Data) -> Row {
|
||||
self.title.as_str().into()
|
||||
}
|
||||
}
|
||||
@ -796,7 +800,7 @@ pub fn apply_workspace_edit(
|
||||
offset_encoding,
|
||||
);
|
||||
let view = view_mut!(editor, view_id);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
doc.append_changes_to_history(view);
|
||||
};
|
||||
|
||||
|
@ -4,10 +4,7 @@
|
||||
|
||||
use super::*;
|
||||
|
||||
use helix_view::{
|
||||
apply_transaction,
|
||||
editor::{Action, CloseError, ConfigEvent},
|
||||
};
|
||||
use helix_view::editor::{Action, CloseError, ConfigEvent};
|
||||
use ui::completers::{self, Completer};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -480,7 +477,7 @@ fn set_line_ending(
|
||||
}
|
||||
}),
|
||||
);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
doc.append_changes_to_history(view);
|
||||
|
||||
Ok(())
|
||||
@ -925,7 +922,7 @@ fn replace_selections_with_clipboard_impl(
|
||||
(range.from(), range.to(), Some(contents.as_str().into()))
|
||||
});
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
doc.append_changes_to_history(view);
|
||||
Ok(())
|
||||
}
|
||||
@ -1596,7 +1593,7 @@ fn sort_impl(
|
||||
.map(|(s, fragment)| (s.from(), s.to(), Some(fragment))),
|
||||
);
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
doc.append_changes_to_history(view);
|
||||
|
||||
Ok(())
|
||||
@ -1640,7 +1637,7 @@ fn reflow(
|
||||
(range.from(), range.to(), Some(reflowed_text))
|
||||
});
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
doc.append_changes_to_history(view);
|
||||
view.ensure_cursor_in_view(doc, scrolloff);
|
||||
|
||||
|
@ -184,7 +184,7 @@ fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
|
||||
S: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut commands = Vec::new();
|
||||
while let Some(command) = seq.next_element::<&str>()? {
|
||||
while let Some(command) = seq.next_element::<String>()? {
|
||||
commands.push(
|
||||
command
|
||||
.parse::<MappableCommand>()
|
||||
@ -600,4 +600,43 @@ fn reverse_map() {
|
||||
"Mismatch"
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn escaped_keymap() {
|
||||
use crate::commands::MappableCommand;
|
||||
use helix_view::input::{KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
let keys = r#"
|
||||
"+" = [
|
||||
"select_all",
|
||||
":pipe sed -E 's/\\s+$//g'",
|
||||
]
|
||||
"#;
|
||||
|
||||
let key = KeyEvent {
|
||||
code: KeyCode::Char('+'),
|
||||
modifiers: KeyModifiers::NONE,
|
||||
};
|
||||
|
||||
let expectation = Keymap::new(KeyTrie::Node(KeyTrieNode::new(
|
||||
"",
|
||||
hashmap! {
|
||||
key => KeyTrie::Sequence(vec!{
|
||||
MappableCommand::select_all,
|
||||
MappableCommand::Typable {
|
||||
name: "pipe".to_string(),
|
||||
args: vec!{
|
||||
"sed".to_string(),
|
||||
"-E".to_string(),
|
||||
"'s/\\s+$//g'".to_string()
|
||||
},
|
||||
doc: "".to_string(),
|
||||
},
|
||||
})
|
||||
},
|
||||
vec![key],
|
||||
)));
|
||||
|
||||
assert_eq!(toml::from_str(keys), Ok(expectation));
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::compositor::{Component, Context, Event, EventResult};
|
||||
use helix_view::{apply_transaction, editor::CompleteAction, ViewId};
|
||||
use helix_view::{editor::CompleteAction, ViewId};
|
||||
use tui::buffer::Buffer as Surface;
|
||||
use tui::text::Spans;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
@ -33,11 +32,7 @@ fn filter_text(&self, _data: &Self::Data) -> Cow<str> {
|
||||
.into()
|
||||
}
|
||||
|
||||
fn label(&self, _data: &Self::Data) -> Spans {
|
||||
self.label.as_str().into()
|
||||
}
|
||||
|
||||
fn row(&self, _data: &Self::Data) -> menu::Row {
|
||||
fn format(&self, _data: &Self::Data) -> menu::Row {
|
||||
menu::Row::new(vec![
|
||||
menu::Cell::from(self.label.as_str()),
|
||||
menu::Cell::from(match self.kind {
|
||||
@ -188,7 +183,7 @@ fn completion_changes(transaction: &Transaction, trigger_offset: usize) -> Vec<C
|
||||
|
||||
// initialize a savepoint
|
||||
doc.savepoint();
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
|
||||
editor.last_completion = Some(CompleteAction {
|
||||
trigger_offset,
|
||||
@ -208,7 +203,7 @@ fn completion_changes(transaction: &Transaction, trigger_offset: usize) -> Vec<C
|
||||
trigger_offset,
|
||||
);
|
||||
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
|
||||
editor.last_completion = Some(CompleteAction {
|
||||
trigger_offset,
|
||||
@ -238,7 +233,7 @@ fn completion_changes(transaction: &Transaction, trigger_offset: usize) -> Vec<C
|
||||
additional_edits.clone(),
|
||||
offset_encoding, // TODO: should probably transcode in Client
|
||||
);
|
||||
apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@
|
||||
visual_coords_at_pos, LineEnding, Position, Range, Selection, Transaction,
|
||||
};
|
||||
use helix_view::{
|
||||
apply_transaction,
|
||||
document::{Mode, SCRATCH_BUFFER_NAME},
|
||||
editor::{CompleteAction, CursorShapeConfig},
|
||||
graphics::{Color, CursorKind, Modifier, Rect, Style},
|
||||
@ -342,23 +341,29 @@ pub fn doc_selection_highlights(
|
||||
let selection_scope = theme
|
||||
.find_scope_index("ui.selection")
|
||||
.expect("could not find `ui.selection` scope in the theme!");
|
||||
let primary_selection_scope = theme
|
||||
.find_scope_index("ui.selection.primary")
|
||||
.unwrap_or(selection_scope);
|
||||
let base_cursor_scope = theme
|
||||
.find_scope_index("ui.cursor")
|
||||
.unwrap_or(selection_scope);
|
||||
let base_primary_cursor_scope = theme
|
||||
.find_scope_index("ui.cursor.primary")
|
||||
.unwrap_or(base_cursor_scope);
|
||||
|
||||
let cursor_scope = match mode {
|
||||
Mode::Insert => theme.find_scope_index("ui.cursor.insert"),
|
||||
Mode::Select => theme.find_scope_index("ui.cursor.select"),
|
||||
Mode::Normal => Some(base_cursor_scope),
|
||||
Mode::Normal => theme.find_scope_index("ui.cursor.normal"),
|
||||
}
|
||||
.unwrap_or(base_cursor_scope);
|
||||
|
||||
let primary_cursor_scope = theme
|
||||
.find_scope_index("ui.cursor.primary")
|
||||
.unwrap_or(cursor_scope);
|
||||
let primary_selection_scope = theme
|
||||
.find_scope_index("ui.selection.primary")
|
||||
.unwrap_or(selection_scope);
|
||||
let primary_cursor_scope = match mode {
|
||||
Mode::Insert => theme.find_scope_index("ui.cursor.primary.insert"),
|
||||
Mode::Select => theme.find_scope_index("ui.cursor.primary.select"),
|
||||
Mode::Normal => theme.find_scope_index("ui.cursor.primary.normal"),
|
||||
}
|
||||
.unwrap_or(base_primary_cursor_scope);
|
||||
|
||||
let mut spans: Vec<(usize, std::ops::Range<usize>)> = Vec::new();
|
||||
for (i, range) in selection.iter().enumerate() {
|
||||
@ -386,7 +391,14 @@ pub fn doc_selection_highlights(
|
||||
if range.head > range.anchor {
|
||||
// Standard case.
|
||||
let cursor_start = prev_grapheme_boundary(text, range.head);
|
||||
spans.push((selection_scope, range.anchor..cursor_start));
|
||||
// non block cursors look like they exclude the cursor
|
||||
let selection_end =
|
||||
if selection_is_primary && !cursor_is_block && mode != Mode::Insert {
|
||||
range.head
|
||||
} else {
|
||||
cursor_start
|
||||
};
|
||||
spans.push((selection_scope, range.anchor..selection_end));
|
||||
if !selection_is_primary || cursor_is_block {
|
||||
spans.push((cursor_scope, cursor_start..range.head));
|
||||
}
|
||||
@ -396,7 +408,16 @@ pub fn doc_selection_highlights(
|
||||
if !selection_is_primary || cursor_is_block {
|
||||
spans.push((cursor_scope, range.head..cursor_end));
|
||||
}
|
||||
spans.push((selection_scope, cursor_end..range.anchor));
|
||||
// non block cursors look like they exclude the cursor
|
||||
let selection_start = if selection_is_primary
|
||||
&& !cursor_is_block
|
||||
&& !(mode == Mode::Insert && cursor_end == range.anchor)
|
||||
{
|
||||
range.head
|
||||
} else {
|
||||
cursor_end
|
||||
};
|
||||
spans.push((selection_scope, selection_start..range.anchor));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1026,7 +1047,7 @@ fn command_mode(&mut self, mode: Mode, cxt: &mut commands::Context, event: KeyEv
|
||||
(shift_position(start), shift_position(end), t)
|
||||
}),
|
||||
);
|
||||
apply_transaction(&tx, doc, view);
|
||||
doc.apply(&tx, view.id);
|
||||
}
|
||||
InsertEvent::TriggerCompletion => {
|
||||
let (_, doc) = current!(cxt.editor);
|
||||
|
@ -4,7 +4,7 @@
|
||||
compositor::{Callback, Component, Compositor, Context, Event, EventResult},
|
||||
ctrl, key, shift,
|
||||
};
|
||||
use tui::{buffer::Buffer as Surface, text::Spans, widgets::Table};
|
||||
use tui::{buffer::Buffer as Surface, widgets::Table};
|
||||
|
||||
pub use tui::widgets::{Cell, Row};
|
||||
|
||||
@ -18,28 +18,24 @@ pub trait Item {
|
||||
/// Additional editor state that is used for label calculation.
|
||||
type Data;
|
||||
|
||||
fn label(&self, data: &Self::Data) -> Spans;
|
||||
fn format(&self, data: &Self::Data) -> Row;
|
||||
|
||||
fn sort_text(&self, data: &Self::Data) -> Cow<str> {
|
||||
let label: String = self.label(data).into();
|
||||
let label: String = self.format(data).cell_text().collect();
|
||||
label.into()
|
||||
}
|
||||
|
||||
fn filter_text(&self, data: &Self::Data) -> Cow<str> {
|
||||
let label: String = self.label(data).into();
|
||||
let label: String = self.format(data).cell_text().collect();
|
||||
label.into()
|
||||
}
|
||||
|
||||
fn row(&self, data: &Self::Data) -> Row {
|
||||
Row::new(vec![Cell::from(self.label(data))])
|
||||
}
|
||||
}
|
||||
|
||||
impl Item for PathBuf {
|
||||
/// Root prefix to strip.
|
||||
type Data = PathBuf;
|
||||
|
||||
fn label(&self, root_path: &Self::Data) -> Spans {
|
||||
fn format(&self, root_path: &Self::Data) -> Row {
|
||||
self.strip_prefix(root_path)
|
||||
.unwrap_or(self)
|
||||
.to_string_lossy()
|
||||
@ -81,7 +77,7 @@ pub fn new(
|
||||
Self {
|
||||
options,
|
||||
editor_data,
|
||||
matcher: Box::new(Matcher::default()),
|
||||
matcher: Box::default(),
|
||||
matches,
|
||||
cursor: None,
|
||||
widths: Vec::new(),
|
||||
@ -144,10 +140,10 @@ fn recalculate_size(&mut self, viewport: (u16, u16)) {
|
||||
let n = self
|
||||
.options
|
||||
.first()
|
||||
.map(|option| option.row(&self.editor_data).cells.len())
|
||||
.map(|option| option.format(&self.editor_data).cells.len())
|
||||
.unwrap_or_default();
|
||||
let max_lens = self.options.iter().fold(vec![0; n], |mut acc, option| {
|
||||
let row = option.row(&self.editor_data);
|
||||
let row = option.format(&self.editor_data);
|
||||
// maintain max for each column
|
||||
for (acc, cell) in acc.iter_mut().zip(row.cells.iter()) {
|
||||
let width = cell.content.width();
|
||||
@ -331,7 +327,9 @@ const fn div_ceil(a: usize, b: usize) -> usize {
|
||||
(a + b - 1) / b
|
||||
}
|
||||
|
||||
let rows = options.iter().map(|option| option.row(&self.editor_data));
|
||||
let rows = options
|
||||
.iter()
|
||||
.map(|option| option.format(&self.editor_data));
|
||||
let table = Table::new(rows)
|
||||
.style(style)
|
||||
.highlight_style(selected)
|
||||
|
@ -7,23 +7,23 @@
|
||||
use futures_util::future::BoxFuture;
|
||||
use tui::{
|
||||
buffer::Buffer as Surface,
|
||||
widgets::{Block, BorderType, Borders},
|
||||
layout::Constraint,
|
||||
text::{Span, Spans},
|
||||
widgets::{Block, BorderType, Borders, Cell, Table},
|
||||
};
|
||||
|
||||
use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
|
||||
use tui::widgets::Widget;
|
||||
|
||||
use std::{
|
||||
cmp::{self, Ordering},
|
||||
time::Instant,
|
||||
};
|
||||
use std::cmp::{self, Ordering};
|
||||
use std::{collections::HashMap, io::Read, path::PathBuf};
|
||||
|
||||
use crate::ui::{Prompt, PromptEvent};
|
||||
use helix_core::{movement::Direction, Position};
|
||||
use helix_core::{movement::Direction, unicode::segmentation::UnicodeSegmentation, Position};
|
||||
use helix_view::{
|
||||
editor::Action,
|
||||
graphics::{CursorKind, Margin, Modifier, Rect},
|
||||
theme::Style,
|
||||
Document, DocumentId, Editor,
|
||||
};
|
||||
|
||||
@ -389,6 +389,8 @@ pub struct Picker<T: Item> {
|
||||
pub truncate_start: bool,
|
||||
/// Whether to show the preview panel (default true)
|
||||
show_preview: bool,
|
||||
/// Constraints for tabular formatting
|
||||
widths: Vec<Constraint>,
|
||||
|
||||
callback_fn: Box<dyn Fn(&mut Context, &T, Action)>,
|
||||
}
|
||||
@ -406,10 +408,30 @@ pub fn new(
|
||||
|_editor: &mut Context, _pattern: &str, _event: PromptEvent| {},
|
||||
);
|
||||
|
||||
let n = options
|
||||
.first()
|
||||
.map(|option| option.format(&editor_data).cells.len())
|
||||
.unwrap_or_default();
|
||||
let max_lens = options.iter().fold(vec![0; n], |mut acc, option| {
|
||||
let row = option.format(&editor_data);
|
||||
// maintain max for each column
|
||||
for (acc, cell) in acc.iter_mut().zip(row.cells.iter()) {
|
||||
let width = cell.content.width();
|
||||
if width > *acc {
|
||||
*acc = width;
|
||||
}
|
||||
}
|
||||
acc
|
||||
});
|
||||
let widths = max_lens
|
||||
.into_iter()
|
||||
.map(|len| Constraint::Length(len as u16))
|
||||
.collect();
|
||||
|
||||
let mut picker = Self {
|
||||
options,
|
||||
editor_data,
|
||||
matcher: Box::new(Matcher::default()),
|
||||
matcher: Box::default(),
|
||||
matches: Vec::new(),
|
||||
cursor: 0,
|
||||
prompt,
|
||||
@ -418,6 +440,7 @@ pub fn new(
|
||||
show_preview: true,
|
||||
callback_fn: Box::new(callback_fn),
|
||||
completion_height: 0,
|
||||
widths,
|
||||
};
|
||||
|
||||
// scoring on empty input:
|
||||
@ -437,8 +460,6 @@ pub fn new(
|
||||
}
|
||||
|
||||
pub fn score(&mut self) {
|
||||
let now = Instant::now();
|
||||
|
||||
let pattern = self.prompt.line();
|
||||
|
||||
if pattern == &self.previous_pattern {
|
||||
@ -480,8 +501,6 @@ pub fn score(&mut self) {
|
||||
self.force_score();
|
||||
}
|
||||
|
||||
log::debug!("picker score {:?}", Instant::now().duration_since(now));
|
||||
|
||||
// reset cursor position
|
||||
self.cursor = 0;
|
||||
let pattern = self.prompt.line();
|
||||
@ -657,7 +676,7 @@ fn handle_event(&mut self, event: &Event, cx: &mut Context) -> EventResult {
|
||||
fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
||||
let text_style = cx.editor.theme.get("ui.text");
|
||||
let selected = cx.editor.theme.get("ui.text.focus");
|
||||
let highlighted = cx.editor.theme.get("special").add_modifier(Modifier::BOLD);
|
||||
let highlight_style = cx.editor.theme.get("special").add_modifier(Modifier::BOLD);
|
||||
|
||||
// -- Render the frame:
|
||||
// clear area
|
||||
@ -697,61 +716,123 @@ fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
|
||||
}
|
||||
|
||||
// -- Render the contents:
|
||||
// subtract area of prompt from top and current item marker " > " from left
|
||||
let inner = inner.clip_top(2).clip_left(3);
|
||||
// subtract area of prompt from top
|
||||
let inner = inner.clip_top(2);
|
||||
|
||||
let rows = inner.height;
|
||||
let offset = self.cursor - (self.cursor % std::cmp::max(1, rows as usize));
|
||||
let cursor = self.cursor.saturating_sub(offset);
|
||||
|
||||
let files = self
|
||||
let options = self
|
||||
.matches
|
||||
.iter()
|
||||
.skip(offset)
|
||||
.map(|pmatch| (pmatch.index, self.options.get(pmatch.index).unwrap()));
|
||||
.take(rows as usize)
|
||||
.map(|pmatch| &self.options[pmatch.index])
|
||||
.map(|option| option.format(&self.editor_data))
|
||||
.map(|mut row| {
|
||||
const TEMP_CELL_SEP: &str = " ";
|
||||
|
||||
for (i, (_index, option)) in files.take(rows as usize).enumerate() {
|
||||
let is_active = i == (self.cursor - offset);
|
||||
if is_active {
|
||||
surface.set_string(
|
||||
inner.x.saturating_sub(3),
|
||||
inner.y + i as u16,
|
||||
" > ",
|
||||
selected,
|
||||
);
|
||||
surface.set_style(
|
||||
Rect::new(inner.x, inner.y + i as u16, inner.width, 1),
|
||||
selected,
|
||||
);
|
||||
}
|
||||
let line = row.cell_text().fold(String::new(), |mut s, frag| {
|
||||
s.push_str(&frag);
|
||||
s.push_str(TEMP_CELL_SEP);
|
||||
s
|
||||
});
|
||||
|
||||
let spans = option.label(&self.editor_data);
|
||||
let (_score, highlights) = FuzzyQuery::new(self.prompt.line())
|
||||
.fuzzy_indicies(&String::from(&spans), &self.matcher)
|
||||
.unwrap_or_default();
|
||||
// Items are filtered by using the text returned by menu::Item::filter_text
|
||||
// but we do highlighting here using the text in Row and therefore there
|
||||
// might be inconsistencies. This is the best we can do since only the
|
||||
// text in Row is displayed to the end user.
|
||||
let (_score, highlights) = FuzzyQuery::new(self.prompt.line())
|
||||
.fuzzy_indicies(&line, &self.matcher)
|
||||
.unwrap_or_default();
|
||||
|
||||
spans.0.into_iter().fold(inner, |pos, span| {
|
||||
let new_x = surface
|
||||
.set_string_truncated(
|
||||
pos.x,
|
||||
pos.y + i as u16,
|
||||
&span.content,
|
||||
pos.width as usize,
|
||||
|idx| {
|
||||
if highlights.contains(&idx) {
|
||||
highlighted.patch(span.style)
|
||||
} else if is_active {
|
||||
selected.patch(span.style)
|
||||
let highlight_byte_ranges: Vec<_> = line
|
||||
.char_indices()
|
||||
.enumerate()
|
||||
.filter_map(|(char_idx, (byte_offset, ch))| {
|
||||
highlights
|
||||
.contains(&char_idx)
|
||||
.then(|| byte_offset..byte_offset + ch.len_utf8())
|
||||
})
|
||||
.collect();
|
||||
|
||||
// The starting byte index of the current (iterating) cell
|
||||
let mut cell_start_byte_offset = 0;
|
||||
for cell in row.cells.iter_mut() {
|
||||
let spans = match cell.content.lines.get(0) {
|
||||
Some(s) => s,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let mut cell_len = 0;
|
||||
|
||||
let graphemes_with_style: Vec<_> = spans
|
||||
.0
|
||||
.iter()
|
||||
.flat_map(|span| {
|
||||
span.content
|
||||
.grapheme_indices(true)
|
||||
.zip(std::iter::repeat(span.style))
|
||||
})
|
||||
.map(|((grapheme_byte_offset, grapheme), style)| {
|
||||
cell_len += grapheme.len();
|
||||
let start = cell_start_byte_offset;
|
||||
|
||||
let grapheme_byte_range =
|
||||
grapheme_byte_offset..grapheme_byte_offset + grapheme.len();
|
||||
|
||||
if highlight_byte_ranges.iter().any(|hl_rng| {
|
||||
hl_rng.start >= start + grapheme_byte_range.start
|
||||
&& hl_rng.end <= start + grapheme_byte_range.end
|
||||
}) {
|
||||
(grapheme, style.patch(highlight_style))
|
||||
} else {
|
||||
text_style.patch(span.style)
|
||||
(grapheme, style)
|
||||
}
|
||||
},
|
||||
true,
|
||||
self.truncate_start,
|
||||
)
|
||||
.0;
|
||||
pos.clip_left(new_x - pos.x)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let mut span_list: Vec<(String, Style)> = Vec::new();
|
||||
for (grapheme, style) in graphemes_with_style {
|
||||
if span_list.last().map(|(_, sty)| sty) == Some(&style) {
|
||||
let (string, _) = span_list.last_mut().unwrap();
|
||||
string.push_str(grapheme);
|
||||
} else {
|
||||
span_list.push((String::from(grapheme), style))
|
||||
}
|
||||
}
|
||||
|
||||
let spans: Vec<Span> = span_list
|
||||
.into_iter()
|
||||
.map(|(string, style)| Span::styled(string, style))
|
||||
.collect();
|
||||
let spans: Spans = spans.into();
|
||||
*cell = Cell::from(spans);
|
||||
|
||||
cell_start_byte_offset += cell_len + TEMP_CELL_SEP.len();
|
||||
}
|
||||
|
||||
row
|
||||
});
|
||||
}
|
||||
|
||||
let table = Table::new(options)
|
||||
.style(text_style)
|
||||
.highlight_style(selected)
|
||||
.highlight_symbol(" > ")
|
||||
.column_spacing(1)
|
||||
.widths(&self.widths);
|
||||
|
||||
use tui::widgets::TableState;
|
||||
|
||||
table.render_table(
|
||||
inner,
|
||||
surface,
|
||||
&mut TableState {
|
||||
offset: 0,
|
||||
selected: Some(cursor),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn cursor(&self, area: Rect, editor: &Editor) -> (Option<Position>, CursorKind) {
|
||||
|
@ -130,7 +130,7 @@ pub async fn test_key_sequence_with_input_text<T: Into<TestCase>>(
|
||||
})
|
||||
.with_selection(test_case.in_selection.clone());
|
||||
|
||||
helix_view::apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
|
||||
test_key_sequence(
|
||||
&mut app,
|
||||
@ -315,7 +315,7 @@ pub fn build(self) -> anyhow::Result<Application> {
|
||||
.with_selection(selection);
|
||||
|
||||
// replace the initial text with the input text
|
||||
helix_view::apply_transaction(&trans, doc, view);
|
||||
doc.apply(&trans, view.id);
|
||||
}
|
||||
|
||||
Ok(app)
|
||||
|
@ -433,7 +433,7 @@ pub fn set_string_truncated_at_end(
|
||||
(x_offset as u16, y)
|
||||
}
|
||||
|
||||
pub fn set_spans<'a>(&mut self, x: u16, y: u16, spans: &Spans<'a>, width: u16) -> (u16, u16) {
|
||||
pub fn set_spans(&mut self, x: u16, y: u16, spans: &Spans, width: u16) -> (u16, u16) {
|
||||
let mut remaining_width = width;
|
||||
let mut x = x;
|
||||
for span in &spans.0 {
|
||||
@ -454,7 +454,7 @@ pub fn set_spans<'a>(&mut self, x: u16, y: u16, spans: &Spans<'a>, width: u16) -
|
||||
(x, y)
|
||||
}
|
||||
|
||||
pub fn set_span<'a>(&mut self, x: u16, y: u16, span: &Span<'a>, width: u16) -> (u16, u16) {
|
||||
pub fn set_span(&mut self, x: u16, y: u16, span: &Span, width: u16) -> (u16, u16) {
|
||||
self.set_stringn(x, y, span.content.as_ref(), width as usize, span.style)
|
||||
}
|
||||
|
||||
@ -521,10 +521,10 @@ pub fn clear_with(&mut self, area: Rect, style: Style) {
|
||||
pub fn merge(&mut self, other: &Buffer) {
|
||||
let area = self.area.union(other.area);
|
||||
let cell: Cell = Default::default();
|
||||
self.content.resize(area.area() as usize, cell.clone());
|
||||
self.content.resize(area.area(), cell.clone());
|
||||
|
||||
// Move original content to the appropriate space
|
||||
let size = self.area.area() as usize;
|
||||
let size = self.area.area();
|
||||
for i in (0..size).rev() {
|
||||
let (x, y) = self.pos_of(i);
|
||||
// New index in content
|
||||
@ -537,7 +537,7 @@ pub fn merge(&mut self, other: &Buffer) {
|
||||
|
||||
// Push content of the other buffer into this one (may erase previous
|
||||
// data)
|
||||
let size = other.area.area() as usize;
|
||||
let size = other.area.area();
|
||||
for i in 0..size {
|
||||
let (x, y) = other.pos_of(i);
|
||||
// New index in content
|
||||
|
@ -436,6 +436,32 @@ fn from(lines: Vec<Spans<'a>>) -> Text<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Text<'a>> for String {
|
||||
fn from(text: Text<'a>) -> String {
|
||||
String::from(&text)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&Text<'a>> for String {
|
||||
fn from(text: &Text<'a>) -> String {
|
||||
let size = text
|
||||
.lines
|
||||
.iter()
|
||||
.flat_map(|spans| spans.0.iter().map(|span| span.content.len()))
|
||||
.sum::<usize>()
|
||||
+ text.lines.len().saturating_sub(1); // for newline after each line
|
||||
let mut output = String::with_capacity(size);
|
||||
|
||||
for spans in &text.lines {
|
||||
for span in &spans.0 {
|
||||
output.push_str(&span.content);
|
||||
}
|
||||
output.push('\n');
|
||||
}
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for Text<'a> {
|
||||
type Item = Spans<'a>;
|
||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||
|
@ -4,14 +4,8 @@
|
||||
text::Text,
|
||||
widgets::{Block, Widget},
|
||||
};
|
||||
use cassowary::{
|
||||
strength::{MEDIUM, REQUIRED, WEAK},
|
||||
WeightedRelation::*,
|
||||
{Expression, Solver},
|
||||
};
|
||||
use helix_core::unicode::width::UnicodeWidthStr;
|
||||
use helix_view::graphics::{Rect, Style};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// A [`Cell`] contains the [`Text`] to be displayed in a [`Row`] of a [`Table`].
|
||||
///
|
||||
@ -126,6 +120,17 @@ pub fn bottom_margin(mut self, margin: u16) -> Self {
|
||||
fn total_height(&self) -> u16 {
|
||||
self.height.saturating_add(self.bottom_margin)
|
||||
}
|
||||
|
||||
/// Returns the contents of cells as plain text, without styles and colors.
|
||||
pub fn cell_text(&self) -> impl Iterator<Item = String> + '_ {
|
||||
self.cells.iter().map(|cell| String::from(&cell.content))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Into<Cell<'a>>> From<T> for Row<'a> {
|
||||
fn from(cell: T) -> Self {
|
||||
Row::new(vec![cell.into()])
|
||||
}
|
||||
}
|
||||
|
||||
/// A widget to display data in formatted columns.
|
||||
@ -260,69 +265,32 @@ pub fn column_spacing(mut self, spacing: u16) -> Self {
|
||||
}
|
||||
|
||||
fn get_columns_widths(&self, max_width: u16, has_selection: bool) -> Vec<u16> {
|
||||
let mut solver = Solver::new();
|
||||
let mut var_indices = HashMap::new();
|
||||
let mut ccs = Vec::new();
|
||||
let mut variables = Vec::new();
|
||||
for i in 0..self.widths.len() {
|
||||
let var = cassowary::Variable::new();
|
||||
variables.push(var);
|
||||
var_indices.insert(var, i);
|
||||
}
|
||||
let spacing_width = (variables.len() as u16).saturating_sub(1) * self.column_spacing;
|
||||
let mut available_width = max_width.saturating_sub(spacing_width);
|
||||
let mut constraints = Vec::with_capacity(self.widths.len() * 2 + 1);
|
||||
if has_selection {
|
||||
let highlight_symbol_width =
|
||||
self.highlight_symbol.map(|s| s.width() as u16).unwrap_or(0);
|
||||
available_width = available_width.saturating_sub(highlight_symbol_width);
|
||||
constraints.push(Constraint::Length(highlight_symbol_width));
|
||||
}
|
||||
for (i, constraint) in self.widths.iter().enumerate() {
|
||||
ccs.push(variables[i] | GE(WEAK) | 0.);
|
||||
ccs.push(match *constraint {
|
||||
Constraint::Length(v) => variables[i] | EQ(MEDIUM) | f64::from(v),
|
||||
Constraint::Percentage(v) => {
|
||||
variables[i] | EQ(WEAK) | (f64::from(v * available_width) / 100.0)
|
||||
}
|
||||
Constraint::Ratio(n, d) => {
|
||||
variables[i]
|
||||
| EQ(WEAK)
|
||||
| (f64::from(available_width) * f64::from(n) / f64::from(d))
|
||||
}
|
||||
Constraint::Min(v) => variables[i] | GE(WEAK) | f64::from(v),
|
||||
Constraint::Max(v) => variables[i] | LE(WEAK) | f64::from(v),
|
||||
})
|
||||
for constraint in self.widths {
|
||||
constraints.push(*constraint);
|
||||
constraints.push(Constraint::Length(self.column_spacing));
|
||||
}
|
||||
solver
|
||||
.add_constraint(
|
||||
variables
|
||||
.iter()
|
||||
.fold(Expression::from_constant(0.), |acc, v| acc + *v)
|
||||
| LE(REQUIRED)
|
||||
| f64::from(available_width),
|
||||
)
|
||||
.unwrap();
|
||||
solver.add_constraints(&ccs).unwrap();
|
||||
let mut widths = vec![0; variables.len()];
|
||||
for &(var, value) in solver.fetch_changes() {
|
||||
let index = var_indices[&var];
|
||||
let value = if value.is_sign_negative() {
|
||||
0
|
||||
} else {
|
||||
value.round() as u16
|
||||
};
|
||||
widths[index] = value;
|
||||
if !self.widths.is_empty() {
|
||||
constraints.pop();
|
||||
}
|
||||
// Cassowary could still return columns widths greater than the max width when there are
|
||||
// fixed length constraints that cannot be satisfied. Therefore, we clamp the widths from
|
||||
// left to right.
|
||||
let mut available_width = max_width;
|
||||
for w in &mut widths {
|
||||
*w = available_width.min(*w);
|
||||
available_width = available_width
|
||||
.saturating_sub(*w)
|
||||
.saturating_sub(self.column_spacing);
|
||||
let mut chunks = crate::layout::Layout::default()
|
||||
.direction(crate::layout::Direction::Horizontal)
|
||||
.constraints(constraints)
|
||||
.split(Rect {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: max_width,
|
||||
height: 1,
|
||||
});
|
||||
if has_selection {
|
||||
chunks.remove(0);
|
||||
}
|
||||
widths
|
||||
chunks.iter().step_by(2).map(|c| c.width).collect()
|
||||
}
|
||||
|
||||
fn get_row_bounds(
|
||||
@ -477,6 +445,9 @@ pub fn render_table(mut self, area: Rect, buf: &mut Buffer, state: &mut TableSta
|
||||
};
|
||||
let mut col = table_row_start_col;
|
||||
for (width, cell) in columns_widths.iter().zip(table_row.cells.iter()) {
|
||||
if is_selected {
|
||||
buf.set_style(table_row_area, self.highlight_style);
|
||||
}
|
||||
render_cell(
|
||||
buf,
|
||||
cell,
|
||||
@ -489,9 +460,6 @@ pub fn render_table(mut self, area: Rect, buf: &mut Buffer, state: &mut TableSta
|
||||
);
|
||||
col += *width + self.column_spacing;
|
||||
}
|
||||
if is_selected {
|
||||
buf.set_style(table_row_area, self.highlight_style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,10 +39,10 @@ chardetng = "0.1"
|
||||
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
toml = "0.5"
|
||||
toml = "0.6"
|
||||
log = "~0.4"
|
||||
|
||||
which = "4.2"
|
||||
which = "4.4"
|
||||
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
|
@ -27,7 +27,7 @@
|
||||
};
|
||||
|
||||
use crate::editor::RedrawHandle;
|
||||
use crate::{apply_transaction, DocumentId, Editor, View, ViewId};
|
||||
use crate::{DocumentId, Editor, View, ViewId};
|
||||
|
||||
/// 8kB of buffer space for encoding and decoding `Rope`s.
|
||||
const BUF_SIZE: usize = 8192;
|
||||
@ -650,7 +650,7 @@ pub fn reload(
|
||||
// This is not considered a modification of the contents of the file regardless
|
||||
// of the encoding.
|
||||
let transaction = helix_core::diff::compare_ropes(self.text(), &rope);
|
||||
apply_transaction(&transaction, self, view);
|
||||
self.apply(&transaction, view.id);
|
||||
self.append_changes_to_history(view);
|
||||
self.reset_modified();
|
||||
|
||||
@ -852,9 +852,6 @@ fn apply_impl(&mut self, transaction: &Transaction, view_id: ViewId) -> bool {
|
||||
}
|
||||
|
||||
/// Apply a [`Transaction`] to the [`Document`] to change its text.
|
||||
/// Instead of calling this function directly, use [crate::apply_transaction]
|
||||
/// to ensure that the transaction is applied to the appropriate [`View`] as
|
||||
/// well.
|
||||
pub fn apply(&mut self, transaction: &Transaction, view_id: ViewId) -> bool {
|
||||
// store the state just before any changes are made. This allows us to undo to the
|
||||
// state just before a transaction was applied.
|
||||
@ -911,7 +908,7 @@ pub fn savepoint(&mut self) {
|
||||
|
||||
pub fn restore(&mut self, view: &mut View) {
|
||||
if let Some(revert) = self.savepoint.take() {
|
||||
apply_transaction(&revert, self, view);
|
||||
self.apply(&revert, view.id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,6 +71,96 @@ fn serialize_duration_millis<S>(duration: &Duration, serializer: S) -> Result<S:
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
||||
pub struct GutterConfig {
|
||||
/// Gutter Layout
|
||||
pub layout: Vec<GutterType>,
|
||||
/// Options specific to the "line-numbers" gutter
|
||||
pub line_numbers: GutterLineNumbersConfig,
|
||||
}
|
||||
|
||||
impl Default for GutterConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
layout: vec![
|
||||
GutterType::Diagnostics,
|
||||
GutterType::Spacer,
|
||||
GutterType::LineNumbers,
|
||||
GutterType::Spacer,
|
||||
GutterType::Diff,
|
||||
],
|
||||
line_numbers: GutterLineNumbersConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<GutterType>> for GutterConfig {
|
||||
fn from(x: Vec<GutterType>) -> Self {
|
||||
GutterConfig {
|
||||
layout: x,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_gutter_seq_or_struct<'de, D>(deserializer: D) -> Result<GutterConfig, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct GutterVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for GutterVisitor {
|
||||
type Value = GutterConfig;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
formatter,
|
||||
"an array of gutter names or a detailed gutter configuration"
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
|
||||
where
|
||||
S: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let mut gutters = Vec::new();
|
||||
while let Some(gutter) = seq.next_element::<String>()? {
|
||||
gutters.push(
|
||||
gutter
|
||||
.parse::<GutterType>()
|
||||
.map_err(serde::de::Error::custom)?,
|
||||
)
|
||||
}
|
||||
|
||||
Ok(gutters.into())
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error>
|
||||
where
|
||||
M: serde::de::MapAccess<'de>,
|
||||
{
|
||||
let deserializer = serde::de::value::MapAccessDeserializer::new(map);
|
||||
Deserialize::deserialize(deserializer)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(GutterVisitor)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
||||
pub struct GutterLineNumbersConfig {
|
||||
/// Minimum number of characters to use for line number gutter. Defaults to 3.
|
||||
pub min_width: usize,
|
||||
}
|
||||
|
||||
impl Default for GutterLineNumbersConfig {
|
||||
fn default() -> Self {
|
||||
Self { min_width: 3 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
|
||||
pub struct FilePickerConfig {
|
||||
@ -132,8 +222,8 @@ pub struct Config {
|
||||
pub cursorline: bool,
|
||||
/// Highlight the columns cursors are currently on. Defaults to false.
|
||||
pub cursorcolumn: bool,
|
||||
/// Gutters. Default ["diagnostics", "line-numbers"]
|
||||
pub gutters: Vec<GutterType>,
|
||||
#[serde(deserialize_with = "deserialize_gutter_seq_or_struct")]
|
||||
pub gutters: GutterConfig,
|
||||
/// Middle click paste support. Defaults to true.
|
||||
pub middle_click_paste: bool,
|
||||
/// Automatic insertion of pairs to parentheses, brackets,
|
||||
@ -206,10 +296,10 @@ pub fn get_terminal_provider() -> Option<TerminalConfig> {
|
||||
});
|
||||
}
|
||||
|
||||
return Some(TerminalConfig {
|
||||
Some(TerminalConfig {
|
||||
command: "conhost".to_string(),
|
||||
args: vec!["cmd".to_string(), "/C".to_string()],
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(any(windows, target_os = "wasm32")))]
|
||||
@ -606,13 +696,7 @@ fn default() -> Self {
|
||||
line_number: LineNumber::Absolute,
|
||||
cursorline: false,
|
||||
cursorcolumn: false,
|
||||
gutters: vec![
|
||||
GutterType::Diagnostics,
|
||||
GutterType::Spacer,
|
||||
GutterType::LineNumbers,
|
||||
GutterType::Spacer,
|
||||
GutterType::Diff,
|
||||
],
|
||||
gutters: GutterConfig::default(),
|
||||
middle_click_paste: true,
|
||||
auto_pairs: AutoPairConfig::default(),
|
||||
auto_completion: true,
|
||||
@ -844,6 +928,7 @@ pub fn refresh_config(&mut self) {
|
||||
let config = self.config();
|
||||
self.auto_pairs = (&config.auto_pairs).into();
|
||||
self.reset_idle_timer();
|
||||
self._refresh();
|
||||
}
|
||||
|
||||
pub fn clear_idle_timer(&mut self) {
|
||||
@ -984,6 +1069,7 @@ fn _refresh(&mut self) {
|
||||
for (view, _) in self.tree.views_mut() {
|
||||
let doc = doc_mut!(self, &view.doc);
|
||||
view.sync_changes(doc);
|
||||
view.gutters = config.gutters.clone();
|
||||
view.ensure_cursor_in_view(doc, config.scrolloff)
|
||||
}
|
||||
}
|
||||
@ -1285,6 +1371,10 @@ pub fn focus_next(&mut self) {
|
||||
self.focus(self.tree.next());
|
||||
}
|
||||
|
||||
pub fn focus_prev(&mut self) {
|
||||
self.focus(self.tree.prev());
|
||||
}
|
||||
|
||||
pub fn focus_direction(&mut self, direction: tree::Direction) {
|
||||
let current_view = self.tree.focus;
|
||||
if let Some(id) = self.tree.find_split_in_direction(current_view, direction) {
|
||||
@ -1498,6 +1588,6 @@ fn inserted_a_new_blank_line(changes: &[Operation], pos: usize, line_end_pos: us
|
||||
let line_start_pos = text.line_to_char(range.cursor_line(text));
|
||||
(line_start_pos, pos, None)
|
||||
});
|
||||
crate::apply_transaction(&transaction, doc, view);
|
||||
doc.apply(&transaction, view.id);
|
||||
}
|
||||
}
|
||||
|
@ -251,7 +251,6 @@ pub fn intersects(self, other: Rect) -> bool {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub enum Color {
|
||||
Reset,
|
||||
Black,
|
||||
@ -353,7 +352,6 @@ fn from(style: UnderlineStyle) -> Self {
|
||||
///
|
||||
/// let m = Modifier::BOLD | Modifier::ITALIC;
|
||||
/// ```
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Modifier: u16 {
|
||||
const BOLD = 0b0000_0000_0001;
|
||||
const DIM = 0b0000_0000_0010;
|
||||
@ -450,7 +448,6 @@ fn from_str(modifier: &str) -> Result<Self, Self::Err> {
|
||||
/// );
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
pub struct Style {
|
||||
pub fg: Option<Color>,
|
||||
pub bg: Option<Color>,
|
||||
|
@ -35,10 +35,10 @@ pub fn style<'doc>(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(self, _view: &View, doc: &Document) -> usize {
|
||||
pub fn width(self, view: &View, doc: &Document) -> usize {
|
||||
match self {
|
||||
GutterType::Diagnostics => 1,
|
||||
GutterType::LineNumbers => line_numbers_width(_view, doc),
|
||||
GutterType::LineNumbers => line_numbers_width(view, doc),
|
||||
GutterType::Spacer => 1,
|
||||
GutterType::Diff => 1,
|
||||
}
|
||||
@ -140,12 +140,13 @@ pub fn line_numbers<'doc>(
|
||||
is_focused: bool,
|
||||
) -> GutterFn<'doc> {
|
||||
let text = doc.text().slice(..);
|
||||
let last_line = view.last_line(doc);
|
||||
let width = GutterType::LineNumbers.width(view, doc);
|
||||
let width = line_numbers_width(view, doc);
|
||||
|
||||
let last_line_in_view = view.last_line(doc);
|
||||
|
||||
// Whether to draw the line number for the last line of the
|
||||
// document or not. We only draw it if it's not an empty line.
|
||||
let draw_last = text.line_to_byte(last_line) < text.len_bytes();
|
||||
let draw_last = text.line_to_byte(last_line_in_view) < text.len_bytes();
|
||||
|
||||
let linenr = theme.get("ui.linenr");
|
||||
let linenr_select = theme.get("ui.linenr.selected");
|
||||
@ -158,7 +159,7 @@ pub fn line_numbers<'doc>(
|
||||
let mode = editor.mode;
|
||||
|
||||
Box::new(move |line: usize, selected: bool, out: &mut String| {
|
||||
if line == last_line && !draw_last {
|
||||
if line == last_line_in_view && !draw_last {
|
||||
write!(out, "{:>1$}", '~', width).unwrap();
|
||||
Some(linenr)
|
||||
} else {
|
||||
@ -187,14 +188,19 @@ pub fn line_numbers<'doc>(
|
||||
})
|
||||
}
|
||||
|
||||
pub fn line_numbers_width(_view: &View, doc: &Document) -> usize {
|
||||
/// The width of a "line-numbers" gutter
|
||||
///
|
||||
/// The width of the gutter depends on the number of lines in the document,
|
||||
/// whether there is content on the last line (the `~` line), and the
|
||||
/// `editor.gutters.line-numbers.min-width` settings.
|
||||
fn line_numbers_width(view: &View, doc: &Document) -> usize {
|
||||
let text = doc.text();
|
||||
let last_line = text.len_lines().saturating_sub(1);
|
||||
let draw_last = text.line_to_byte(last_line) < text.len_bytes();
|
||||
let last_drawn = if draw_last { last_line + 1 } else { last_line };
|
||||
|
||||
// set a lower bound to 2-chars to minimize ambiguous relative line numbers
|
||||
std::cmp::max(count_digits(last_drawn), 2)
|
||||
let digits = count_digits(last_drawn);
|
||||
let n_min = view.gutters.line_numbers.min_width;
|
||||
digits.max(n_min)
|
||||
}
|
||||
|
||||
pub fn padding<'doc>(
|
||||
@ -282,3 +288,82 @@ pub fn diagnostics_or_breakpoints<'doc>(
|
||||
breakpoints(line, selected, out).or_else(|| diagnostics(line, selected, out))
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::document::Document;
|
||||
use crate::editor::{GutterConfig, GutterLineNumbersConfig};
|
||||
use crate::graphics::Rect;
|
||||
use crate::DocumentId;
|
||||
use helix_core::Rope;
|
||||
|
||||
#[test]
|
||||
fn test_default_gutter_widths() {
|
||||
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
|
||||
let rope = Rope::from_str("abc\n\tdef");
|
||||
let doc = Document::from(rope, None);
|
||||
|
||||
assert_eq!(view.gutters.layout.len(), 5);
|
||||
assert_eq!(view.gutters.layout[0].width(&view, &doc), 1);
|
||||
assert_eq!(view.gutters.layout[1].width(&view, &doc), 1);
|
||||
assert_eq!(view.gutters.layout[2].width(&view, &doc), 3);
|
||||
assert_eq!(view.gutters.layout[3].width(&view, &doc), 1);
|
||||
assert_eq!(view.gutters.layout[4].width(&view, &doc), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_configured_gutter_widths() {
|
||||
let gutters = GutterConfig {
|
||||
layout: vec![GutterType::Diagnostics],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut view = View::new(DocumentId::default(), gutters);
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
|
||||
let rope = Rope::from_str("abc\n\tdef");
|
||||
let doc = Document::from(rope, None);
|
||||
|
||||
assert_eq!(view.gutters.layout.len(), 1);
|
||||
assert_eq!(view.gutters.layout[0].width(&view, &doc), 1);
|
||||
|
||||
let gutters = GutterConfig {
|
||||
layout: vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
line_numbers: GutterLineNumbersConfig { min_width: 10 },
|
||||
};
|
||||
|
||||
let mut view = View::new(DocumentId::default(), gutters);
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
|
||||
let rope = Rope::from_str("abc\n\tdef");
|
||||
let doc = Document::from(rope, None);
|
||||
|
||||
assert_eq!(view.gutters.layout.len(), 2);
|
||||
assert_eq!(view.gutters.layout[0].width(&view, &doc), 1);
|
||||
assert_eq!(view.gutters.layout[1].width(&view, &doc), 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_numbers_gutter_width_resizes() {
|
||||
let gutters = GutterConfig {
|
||||
layout: vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
line_numbers: GutterLineNumbersConfig { min_width: 1 },
|
||||
};
|
||||
|
||||
let mut view = View::new(DocumentId::default(), gutters);
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
|
||||
let rope = Rope::from_str("a\nb");
|
||||
let doc_short = Document::from(rope, None);
|
||||
|
||||
let rope = Rope::from_str("a\nb\nc\nd\ne\nf\ng\nh\ni\nj\nk\nl\nm\nn\no\np");
|
||||
let doc_long = Document::from(rope, None);
|
||||
|
||||
assert_eq!(view.gutters.layout.len(), 2);
|
||||
assert_eq!(view.gutters.layout[1].width(&view, &doc_short), 1);
|
||||
assert_eq!(view.gutters.layout[1].width(&view, &doc_long), 2);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
bitflags! {
|
||||
/// Represents key modifiers (shift, control, alt).
|
||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||
pub struct KeyModifiers: u8 {
|
||||
const SHIFT = 0b0000_0001;
|
||||
const CONTROL = 0b0000_0010;
|
||||
|
@ -66,17 +66,6 @@ pub fn align_view(doc: &Document, view: &mut View, align: Align) {
|
||||
view.offset.row = line.saturating_sub(relative);
|
||||
}
|
||||
|
||||
/// Applies a [`helix_core::Transaction`] to the given [`Document`]
|
||||
/// and [`View`].
|
||||
pub fn apply_transaction(
|
||||
transaction: &helix_core::Transaction,
|
||||
doc: &mut Document,
|
||||
view: &View,
|
||||
) -> bool {
|
||||
// TODO remove this helper function. Just call Document::apply everywhere directly.
|
||||
doc.apply(transaction, view.id)
|
||||
}
|
||||
|
||||
pub use document::Document;
|
||||
pub use editor::Editor;
|
||||
pub use theme::Theme;
|
||||
|
@ -1,9 +1,10 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
str,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use anyhow::{anyhow, Result};
|
||||
use helix_core::hashmap;
|
||||
use helix_loader::merge_toml_values;
|
||||
use log::warn;
|
||||
@ -15,12 +16,13 @@
|
||||
pub use crate::graphics::{Color, Modifier, Style};
|
||||
|
||||
pub static DEFAULT_THEME_DATA: Lazy<Value> = Lazy::new(|| {
|
||||
toml::from_slice(include_bytes!("../../theme.toml")).expect("Failed to parse default theme")
|
||||
let bytes = include_bytes!("../../theme.toml");
|
||||
toml::from_str(str::from_utf8(bytes).unwrap()).expect("Failed to parse base default theme")
|
||||
});
|
||||
|
||||
pub static BASE16_DEFAULT_THEME_DATA: Lazy<Value> = Lazy::new(|| {
|
||||
toml::from_slice(include_bytes!("../../base16_theme.toml"))
|
||||
.expect("Failed to parse base 16 default theme")
|
||||
let bytes = include_bytes!("../../base16_theme.toml");
|
||||
toml::from_str(str::from_utf8(bytes).unwrap()).expect("Failed to parse base 16 default theme")
|
||||
});
|
||||
|
||||
pub static DEFAULT_THEME: Lazy<Theme> = Lazy::new(|| Theme {
|
||||
@ -70,7 +72,7 @@ pub fn load(&self, name: &str) -> Result<Theme> {
|
||||
fn load_theme(
|
||||
&self,
|
||||
name: &str,
|
||||
base_them_name: &str,
|
||||
base_theme_name: &str,
|
||||
only_default_dir: bool,
|
||||
) -> Result<Value> {
|
||||
let path = self.path(name, only_default_dir);
|
||||
@ -92,8 +94,8 @@ fn load_theme(
|
||||
"base16_default" => BASE16_DEFAULT_THEME_DATA.clone(),
|
||||
_ => self.load_theme(
|
||||
parent_theme_name,
|
||||
base_them_name,
|
||||
base_them_name == parent_theme_name,
|
||||
base_theme_name,
|
||||
base_theme_name == parent_theme_name,
|
||||
)?,
|
||||
};
|
||||
|
||||
@ -148,8 +150,8 @@ fn merge_themes(&self, parent_theme_toml: Value, theme_toml: Value) -> Value {
|
||||
|
||||
// Loads the theme data as `toml::Value` first from the user_dir then in default_dir
|
||||
fn load_toml(&self, path: PathBuf) -> Result<Value> {
|
||||
let data = std::fs::read(&path)?;
|
||||
let value = toml::from_slice(data.as_slice())?;
|
||||
let data = std::fs::read_to_string(path)?;
|
||||
let value = toml::from_str(&data)?;
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
@ -207,16 +209,18 @@ pub struct Theme {
|
||||
|
||||
impl From<Value> for Theme {
|
||||
fn from(value: Value) -> Self {
|
||||
let values: Result<HashMap<String, Value>> =
|
||||
toml::from_str(&value.to_string()).context("Failed to load theme");
|
||||
if let Value::Table(table) = value {
|
||||
let (styles, scopes, highlights) = build_theme_values(table);
|
||||
|
||||
let (styles, scopes, highlights) = build_theme_values(values);
|
||||
|
||||
Self {
|
||||
styles,
|
||||
scopes,
|
||||
highlights,
|
||||
..Default::default()
|
||||
Self {
|
||||
styles,
|
||||
scopes,
|
||||
highlights,
|
||||
..Default::default()
|
||||
}
|
||||
} else {
|
||||
warn!("Expected theme TOML value to be a table, found {:?}", value);
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -226,9 +230,9 @@ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let values = HashMap::<String, Value>::deserialize(deserializer)?;
|
||||
let values = Map::<String, Value>::deserialize(deserializer)?;
|
||||
|
||||
let (styles, scopes, highlights) = build_theme_values(Ok(values));
|
||||
let (styles, scopes, highlights) = build_theme_values(values);
|
||||
|
||||
Ok(Self {
|
||||
styles,
|
||||
@ -240,39 +244,37 @@ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
}
|
||||
|
||||
fn build_theme_values(
|
||||
values: Result<HashMap<String, Value>>,
|
||||
mut values: Map<String, Value>,
|
||||
) -> (HashMap<String, Style>, Vec<String>, Vec<Style>) {
|
||||
let mut styles = HashMap::new();
|
||||
let mut scopes = Vec::new();
|
||||
let mut highlights = Vec::new();
|
||||
|
||||
if let Ok(mut colors) = values {
|
||||
// TODO: alert user of parsing failures in editor
|
||||
let palette = colors
|
||||
.remove("palette")
|
||||
.map(|value| {
|
||||
ThemePalette::try_from(value).unwrap_or_else(|err| {
|
||||
warn!("{}", err);
|
||||
ThemePalette::default()
|
||||
})
|
||||
})
|
||||
.unwrap_or_default();
|
||||
// remove inherits from value to prevent errors
|
||||
let _ = colors.remove("inherits");
|
||||
styles.reserve(colors.len());
|
||||
scopes.reserve(colors.len());
|
||||
highlights.reserve(colors.len());
|
||||
for (name, style_value) in colors {
|
||||
let mut style = Style::default();
|
||||
if let Err(err) = palette.parse_style(&mut style, style_value) {
|
||||
// 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);
|
||||
}
|
||||
|
||||
// these are used both as UI and as highlights
|
||||
styles.insert(name.clone(), style);
|
||||
scopes.push(name);
|
||||
highlights.push(style);
|
||||
ThemePalette::default()
|
||||
})
|
||||
})
|
||||
.unwrap_or_default();
|
||||
// remove inherits from value to prevent errors
|
||||
let _ = values.remove("inherits");
|
||||
styles.reserve(values.len());
|
||||
scopes.reserve(values.len());
|
||||
highlights.reserve(values.len());
|
||||
for (name, style_value) in values {
|
||||
let mut style = Style::default();
|
||||
if let Err(err) = palette.parse_style(&mut style, style_value) {
|
||||
warn!("{}", err);
|
||||
}
|
||||
|
||||
// these are used both as UI and as highlights
|
||||
styles.insert(name.clone(), style);
|
||||
scopes.push(name);
|
||||
highlights.push(style);
|
||||
}
|
||||
|
||||
(styles, scopes, highlights)
|
||||
@ -515,10 +517,8 @@ fn test_parse_style_table() {
|
||||
|
||||
let mut style = Style::default();
|
||||
let palette = ThemePalette::default();
|
||||
if let Value::Table(entries) = table {
|
||||
for (_name, value) in entries {
|
||||
palette.parse_style(&mut style, value).unwrap();
|
||||
}
|
||||
for (_name, value) in table {
|
||||
palette.parse_style(&mut style, value).unwrap();
|
||||
}
|
||||
|
||||
assert_eq!(
|
||||
|
@ -701,7 +701,7 @@ fn next_back(&mut self) -> Option<Self::Item> {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::editor::GutterType;
|
||||
use crate::editor::GutterConfig;
|
||||
use crate::DocumentId;
|
||||
|
||||
#[test]
|
||||
@ -712,34 +712,22 @@ fn find_split_in_direction() {
|
||||
width: 180,
|
||||
height: 80,
|
||||
});
|
||||
let mut view = View::new(
|
||||
DocumentId::default(),
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
||||
view.area = Rect::new(0, 0, 180, 80);
|
||||
tree.insert(view);
|
||||
|
||||
let l0 = tree.focus;
|
||||
let view = View::new(
|
||||
DocumentId::default(),
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let view = View::new(DocumentId::default(), GutterConfig::default());
|
||||
tree.split(view, Layout::Vertical);
|
||||
let r0 = tree.focus;
|
||||
|
||||
tree.focus = l0;
|
||||
let view = View::new(
|
||||
DocumentId::default(),
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let view = View::new(DocumentId::default(), GutterConfig::default());
|
||||
tree.split(view, Layout::Horizontal);
|
||||
let l1 = tree.focus;
|
||||
|
||||
tree.focus = l0;
|
||||
let view = View::new(
|
||||
DocumentId::default(),
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let view = View::new(DocumentId::default(), GutterConfig::default());
|
||||
tree.split(view, Layout::Vertical);
|
||||
let l2 = tree.focus;
|
||||
|
||||
@ -781,40 +769,28 @@ fn swap_split_in_direction() {
|
||||
});
|
||||
|
||||
let doc_l0 = DocumentId::default();
|
||||
let mut view = View::new(
|
||||
doc_l0,
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let mut view = View::new(doc_l0, GutterConfig::default());
|
||||
view.area = Rect::new(0, 0, 180, 80);
|
||||
tree.insert(view);
|
||||
|
||||
let l0 = tree.focus;
|
||||
|
||||
let doc_r0 = DocumentId::default();
|
||||
let view = View::new(
|
||||
doc_r0,
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let view = View::new(doc_r0, GutterConfig::default());
|
||||
tree.split(view, Layout::Vertical);
|
||||
let r0 = tree.focus;
|
||||
|
||||
tree.focus = l0;
|
||||
|
||||
let doc_l1 = DocumentId::default();
|
||||
let view = View::new(
|
||||
doc_l1,
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let view = View::new(doc_l1, GutterConfig::default());
|
||||
tree.split(view, Layout::Horizontal);
|
||||
let l1 = tree.focus;
|
||||
|
||||
tree.focus = l0;
|
||||
|
||||
let doc_l2 = DocumentId::default();
|
||||
let view = View::new(
|
||||
doc_l2,
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let view = View::new(doc_l2, GutterConfig::default());
|
||||
tree.split(view, Layout::Vertical);
|
||||
let l2 = tree.focus;
|
||||
|
||||
|
@ -1,4 +1,10 @@
|
||||
use crate::{align_view, editor::GutterType, graphics::Rect, Align, Document, DocumentId, ViewId};
|
||||
use crate::{
|
||||
align_view,
|
||||
editor::{GutterConfig, GutterType},
|
||||
graphics::Rect,
|
||||
Align, Document, DocumentId, ViewId,
|
||||
};
|
||||
|
||||
use helix_core::{
|
||||
pos_at_visual_coords, visual_coords_at_pos, Position, RopeSlice, Selection, Transaction,
|
||||
};
|
||||
@ -103,8 +109,8 @@ pub struct View {
|
||||
pub last_modified_docs: [Option<DocumentId>; 2],
|
||||
/// used to store previous selections of tree-sitter objects
|
||||
pub object_selections: Vec<Selection>,
|
||||
/// GutterTypes used to fetch Gutter (constructor) and width for rendering
|
||||
gutters: Vec<GutterType>,
|
||||
/// all gutter-related configuration settings, used primarily for gutter rendering
|
||||
pub gutters: GutterConfig,
|
||||
/// A mapping between documents and the last history revision the view was updated at.
|
||||
/// Changes between documents and views are synced lazily when switching windows. This
|
||||
/// mapping keeps track of the last applied history revision so that only new changes
|
||||
@ -123,7 +129,7 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
}
|
||||
|
||||
impl View {
|
||||
pub fn new(doc: DocumentId, gutter_types: Vec<crate::editor::GutterType>) -> Self {
|
||||
pub fn new(doc: DocumentId, gutters: GutterConfig) -> Self {
|
||||
Self {
|
||||
id: ViewId::default(),
|
||||
doc,
|
||||
@ -133,7 +139,7 @@ pub fn new(doc: DocumentId, gutter_types: Vec<crate::editor::GutterType>) -> Sel
|
||||
docs_access_history: Vec::new(),
|
||||
last_modified_docs: [None, None],
|
||||
object_selections: Vec::new(),
|
||||
gutters: gutter_types,
|
||||
gutters,
|
||||
doc_revisions: HashMap::new(),
|
||||
}
|
||||
}
|
||||
@ -154,11 +160,12 @@ pub fn inner_height(&self) -> usize {
|
||||
}
|
||||
|
||||
pub fn gutters(&self) -> &[GutterType] {
|
||||
&self.gutters
|
||||
&self.gutters.layout
|
||||
}
|
||||
|
||||
pub fn gutter_offset(&self, doc: &Document) -> u16 {
|
||||
self.gutters
|
||||
.layout
|
||||
.iter()
|
||||
.map(|gutter| gutter.width(self, doc) as u16)
|
||||
.sum()
|
||||
@ -380,8 +387,6 @@ pub fn remove_document(&mut self, doc_id: &DocumentId) {
|
||||
// }
|
||||
|
||||
/// Applies a [`Transaction`] to the view.
|
||||
/// Instead of calling this function directly, use [crate::apply_transaction]
|
||||
/// which applies a transaction to the [`Document`] and view together.
|
||||
pub fn apply(&mut self, transaction: &Transaction, doc: &mut Document) {
|
||||
self.jumps.apply(transaction, doc);
|
||||
self.doc_revisions
|
||||
@ -416,18 +421,19 @@ pub fn sync_changes(&mut self, doc: &mut Document) {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use helix_core::Rope;
|
||||
const OFFSET: u16 = 3; // 1 diagnostic + 2 linenr (< 100 lines)
|
||||
const OFFSET_WITHOUT_LINE_NUMBERS: u16 = 1; // 1 diagnostic
|
||||
// const OFFSET: u16 = GUTTERS.iter().map(|(_, width)| *width as u16).sum();
|
||||
|
||||
// 1 diagnostic + 1 spacer + 3 linenr (< 1000 lines) + 1 spacer + 1 diff
|
||||
const DEFAULT_GUTTER_OFFSET: u16 = 7;
|
||||
|
||||
// 1 diagnostics + 1 spacer + 1 gutter
|
||||
const DEFAULT_GUTTER_OFFSET_ONLY_DIAGNOSTICS: u16 = 3;
|
||||
|
||||
use crate::document::Document;
|
||||
use crate::editor::GutterType;
|
||||
use crate::editor::{GutterConfig, GutterLineNumbersConfig, GutterType};
|
||||
|
||||
#[test]
|
||||
fn test_text_pos_at_screen_coords() {
|
||||
let mut view = View::new(
|
||||
DocumentId::default(),
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
let rope = Rope::from_str("abc\n\tdef");
|
||||
let doc = Document::from(rope, None);
|
||||
@ -447,24 +453,24 @@ fn test_text_pos_at_screen_coords() {
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 78, 41, 4), None);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 3, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 3, 4),
|
||||
Some(3)
|
||||
);
|
||||
|
||||
assert_eq!(view.text_pos_at_screen_coords(&doc, 40, 80, 4), Some(3));
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 1, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + DEFAULT_GUTTER_OFFSET + 1, 4),
|
||||
Some(4)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 4, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + DEFAULT_GUTTER_OFFSET + 4, 4),
|
||||
Some(5)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET + 7, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + DEFAULT_GUTTER_OFFSET + 7, 4),
|
||||
Some(8)
|
||||
);
|
||||
|
||||
@ -473,19 +479,36 @@ fn test_text_pos_at_screen_coords() {
|
||||
|
||||
#[test]
|
||||
fn test_text_pos_at_screen_coords_without_line_numbers_gutter() {
|
||||
let mut view = View::new(DocumentId::default(), vec![GutterType::Diagnostics]);
|
||||
let mut view = View::new(
|
||||
DocumentId::default(),
|
||||
GutterConfig {
|
||||
layout: vec![GutterType::Diagnostics],
|
||||
line_numbers: GutterLineNumbersConfig::default(),
|
||||
},
|
||||
);
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
let rope = Rope::from_str("abc\n\tdef");
|
||||
let doc = Document::from(rope, None);
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 41, 40 + OFFSET_WITHOUT_LINE_NUMBERS + 1, 4),
|
||||
view.text_pos_at_screen_coords(
|
||||
&doc,
|
||||
41,
|
||||
40 + DEFAULT_GUTTER_OFFSET_ONLY_DIAGNOSTICS + 1,
|
||||
4
|
||||
),
|
||||
Some(4)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_text_pos_at_screen_coords_without_any_gutters() {
|
||||
let mut view = View::new(DocumentId::default(), vec![]);
|
||||
let mut view = View::new(
|
||||
DocumentId::default(),
|
||||
GutterConfig {
|
||||
layout: vec![],
|
||||
line_numbers: GutterLineNumbersConfig::default(),
|
||||
},
|
||||
);
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
let rope = Rope::from_str("abc\n\tdef");
|
||||
let doc = Document::from(rope, None);
|
||||
@ -494,76 +517,70 @@ fn test_text_pos_at_screen_coords_without_any_gutters() {
|
||||
|
||||
#[test]
|
||||
fn test_text_pos_at_screen_coords_cjk() {
|
||||
let mut view = View::new(
|
||||
DocumentId::default(),
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
let rope = Rope::from_str("Hi! こんにちは皆さん");
|
||||
let doc = Document::from(rope, None);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET, 4),
|
||||
Some(0)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 4, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 4, 4),
|
||||
Some(4)
|
||||
);
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 5, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 5, 4),
|
||||
Some(4)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 6, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 6, 4),
|
||||
Some(5)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 7, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 7, 4),
|
||||
Some(5)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 8, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 8, 4),
|
||||
Some(6)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_text_pos_at_screen_coords_graphemes() {
|
||||
let mut view = View::new(
|
||||
DocumentId::default(),
|
||||
vec![GutterType::Diagnostics, GutterType::LineNumbers],
|
||||
);
|
||||
let mut view = View::new(DocumentId::default(), GutterConfig::default());
|
||||
view.area = Rect::new(40, 40, 40, 40);
|
||||
let rope = Rope::from_str("Hèl̀l̀ò world!");
|
||||
let doc = Document::from(rope, None);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET, 4),
|
||||
Some(0)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 1, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 1, 4),
|
||||
Some(1)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 2, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 2, 4),
|
||||
Some(3)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 3, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 3, 4),
|
||||
Some(5)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + OFFSET + 4, 4),
|
||||
view.text_pos_at_screen_coords(&doc, 40, 40 + DEFAULT_GUTTER_OFFSET + 4, 4),
|
||||
Some(7)
|
||||
);
|
||||
}
|
||||
|
@ -473,7 +473,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-html", rev = "29f53
|
||||
name = "python"
|
||||
scope = "source.python"
|
||||
injection-regex = "python"
|
||||
file-types = ["py"]
|
||||
file-types = ["py","pyi","py3","pyw","ptl",".pythonstartup",".pythonrc","SConstruct"]
|
||||
shebangs = ["python"]
|
||||
roots = []
|
||||
comment-token = "#"
|
||||
@ -652,7 +652,7 @@ name = "java"
|
||||
scope = "source.java"
|
||||
injection-regex = "java"
|
||||
file-types = ["java"]
|
||||
roots = ["pom.xml"]
|
||||
roots = ["pom.xml", "build.gradle"]
|
||||
language-server = { command = "jdtls" }
|
||||
indent = { tab-width = 4, unit = " " }
|
||||
|
||||
@ -717,6 +717,7 @@ source = { git = "https://github.com/tree-sitter/tree-sitter-ocaml", rev = "23d4
|
||||
|
||||
[[language]]
|
||||
name = "lua"
|
||||
injection-regex = "lua"
|
||||
scope = "source.lua"
|
||||
file-types = ["lua"]
|
||||
shebangs = ["lua"]
|
||||
@ -877,7 +878,7 @@ source = { git = "https://github.com/uyha/tree-sitter-cmake", rev = "6e51463ef30
|
||||
[[language]]
|
||||
name = "make"
|
||||
scope = "source.make"
|
||||
file-types = ["Makefile", "makefile", "mk", "justfile", ".justfile"]
|
||||
file-types = ["Makefile", "makefile", "mk", "Justfile", "justfile", ".justfile"]
|
||||
injection-regex = "(make|makefile|Makefile|mk|just)"
|
||||
roots = []
|
||||
comment-token = "#"
|
||||
@ -1018,7 +1019,7 @@ source = { git = "https://github.com/Flakebi/tree-sitter-tablegen", rev = "568dd
|
||||
name = "markdown"
|
||||
scope = "source.md"
|
||||
injection-regex = "md|markdown"
|
||||
file-types = ["md", "markdown"]
|
||||
file-types = ["md", "markdown", "PULLREQ_EDITMSG"]
|
||||
roots = [".marksman.toml"]
|
||||
language-server = { command = "marksman", args=["server"] }
|
||||
indent = { tab-width = 2, unit = " " }
|
||||
@ -1065,7 +1066,7 @@ config = { "isHttpEnabled" = true }
|
||||
|
||||
[[grammar]]
|
||||
name = "scala"
|
||||
source = { git = "https://github.com/tree-sitter/tree-sitter-scala", rev = "db1c8c23d7996476a791db85a0d292084c19c232" }
|
||||
source = { git = "https://github.com/tree-sitter/tree-sitter-scala", rev = "f6bbf35de41653b409ca9a3537a154f2b095ef64" }
|
||||
|
||||
[[language]]
|
||||
name = "dockerfile"
|
||||
@ -1602,7 +1603,7 @@ scope = "source.v"
|
||||
file-types = ["v", "vv"]
|
||||
shebangs = ["v run"]
|
||||
roots = ["v.mod"]
|
||||
language-server = { command = "vls", args = [] }
|
||||
language-server = { command = "v", args = ["ls"] }
|
||||
auto-format = true
|
||||
comment-token = "//"
|
||||
indent = { tab-width = 4, unit = "\t" }
|
||||
@ -1777,6 +1778,7 @@ auto-format = true
|
||||
comment-token = "//"
|
||||
language-server = { command = "cuelsp" }
|
||||
indent = { tab-width = 4, unit = "\t" }
|
||||
formatter = { command = "cue", args = ["fmt", "-"] }
|
||||
|
||||
[[grammar]]
|
||||
name = "cue"
|
||||
@ -1979,7 +1981,7 @@ roots = []
|
||||
|
||||
[[grammar]]
|
||||
name = "xml"
|
||||
source = { git = "https://github.com/RenjiSann/tree-sitter-xml", rev = "422528a43630db6dcc1e222d1c5ee3babd559473" }
|
||||
source = { git = "https://github.com/RenjiSann/tree-sitter-xml", rev = "48a7c2b6fb9d515577e115e6788937e837815651" }
|
||||
|
||||
[[language]]
|
||||
name = "wit"
|
||||
@ -2108,3 +2110,13 @@ formatter = { command = "dhall" , args = ["format"] }
|
||||
[[grammar]]
|
||||
name = "dhall"
|
||||
source = { git = "https://github.com/jbellerb/tree-sitter-dhall", rev = "affb6ee38d629c9296749767ab832d69bb0d9ea8" }
|
||||
|
||||
[[language]]
|
||||
name = "sage"
|
||||
scope = "source.sage"
|
||||
file-types = ["sage"]
|
||||
injection-regex = "sage"
|
||||
roots = []
|
||||
comment-token = "#"
|
||||
indent = { tab-width = 4, unit = " " }
|
||||
grammar = "python"
|
||||
|
@ -6,6 +6,8 @@
|
||||
(indented_string_expression (string_fragment) @injection.content))
|
||||
(#set! injection.combined))
|
||||
|
||||
; Common attribute keys corresponding to scripts,
|
||||
; such as those of stdenv.mkDerivation.
|
||||
((binding
|
||||
attrpath: (attrpath (identifier) @_path)
|
||||
expression: (indented_string_expression
|
||||
@ -14,6 +16,25 @@
|
||||
(#set! injection.language "bash")
|
||||
(#set! injection.combined))
|
||||
|
||||
; builtins.{match,split} regex str
|
||||
; Example: nix/tests/lang/eval-okay-regex-{match,split}.nix
|
||||
((apply_expression
|
||||
function: (_) @_func
|
||||
argument: (indented_string_expression (string_fragment) @injection.content))
|
||||
(#match? @_func "(^|\\.)match|split$")
|
||||
(#set! injection.language "regex")
|
||||
(#set! injection.combined))
|
||||
|
||||
; builtins.fromJSON json
|
||||
; Example: nix/tests/lang/eval-okay-fromjson.nix
|
||||
((apply_expression
|
||||
function: (_) @_func
|
||||
argument: (indented_string_expression (string_fragment) @injection.content))
|
||||
(#match? @_func "(^|\\.)fromJSON$")
|
||||
(#set! injection.language "json")
|
||||
(#set! injection.combined))
|
||||
|
||||
; trivial-builders.nix pkgs.writeShellScript[Bin] name content
|
||||
((apply_expression
|
||||
function: (apply_expression function: (_) @_func)
|
||||
argument: (indented_string_expression (string_fragment) @injection.content))
|
||||
@ -21,6 +42,8 @@
|
||||
(#set! injection.language "bash")
|
||||
(#set! injection.combined))
|
||||
|
||||
; trivial-builders.nix, aliases.nix
|
||||
; pkgs.runCommand[[No]CC][Local] name attrs content
|
||||
(apply_expression
|
||||
(apply_expression
|
||||
function: (apply_expression
|
||||
@ -30,6 +53,7 @@
|
||||
(#set! injection.language "bash")
|
||||
(#set! injection.combined))
|
||||
|
||||
; trivial-builders.nix pkgs.writeShellApplication { text = content; }
|
||||
(apply_expression
|
||||
function: ((_) @_func)
|
||||
argument: (_ (_)* (_ (_)* (binding
|
||||
@ -40,3 +64,89 @@
|
||||
(#match? @_path "^text$")
|
||||
(#set! injection.language "bash")
|
||||
(#set! injection.combined))
|
||||
|
||||
; trivial-builders.nix pkgs.writeCBin name content
|
||||
((apply_expression
|
||||
function: (apply_expression function: (_) @_func)
|
||||
argument: (indented_string_expression (string_fragment) @injection.content))
|
||||
(#match? @_func "(^|\\.)writeC(Bin)?$")
|
||||
(#set! injection.language "c")
|
||||
(#set! injection.combined))
|
||||
|
||||
; pkgs.writers.* usage examples: nixpkgs/pkgs/build-support/writers/test.nix
|
||||
|
||||
; pkgs.writers.write{Bash,Dash}[Bin] name content
|
||||
((apply_expression
|
||||
function: (apply_expression function: (_) @_func)
|
||||
argument: (indented_string_expression (string_fragment) @injection.content))
|
||||
(#match? @_func "(^|\\.)write[BD]ash(Bin)?$")
|
||||
(#set! injection.language "bash")
|
||||
(#set! injection.combined))
|
||||
|
||||
; pkgs.writers.writeFish[Bin] name content
|
||||
((apply_expression
|
||||
function: (apply_expression function: (_) @_func)
|
||||
argument: (indented_string_expression (string_fragment) @injection.content))
|
||||
(#match? @_func "(^|\\.)writeFish(Bin)?$")
|
||||
(#set! injection.language "fish")
|
||||
(#set! injection.combined))
|
||||
|
||||
; pkgs.writers.writeRust[Bin] name attrs content
|
||||
(apply_expression
|
||||
(apply_expression
|
||||
function: (apply_expression
|
||||
function: ((_) @_func)))
|
||||
argument: (indented_string_expression (string_fragment) @injection.content)
|
||||
(#match? @_func "(^|\\.)writeRust(Bin)?$")
|
||||
(#set! injection.language "rust")
|
||||
(#set! injection.combined))
|
||||
|
||||
; pkgs.writers.writeHaskell[Bin] name attrs content
|
||||
(apply_expression
|
||||
(apply_expression
|
||||
function: (apply_expression
|
||||
function: ((_) @_func)))
|
||||
argument: (indented_string_expression (string_fragment) @injection.content)
|
||||
(#match? @_func "(^|\\.)writeHaskell(Bin)?$")
|
||||
(#set! injection.language "haskell")
|
||||
(#set! injection.combined))
|
||||
|
||||
; pkgs.writers.writeJS[Bin] name attrs content
|
||||
(apply_expression
|
||||
(apply_expression
|
||||
function: (apply_expression
|
||||
function: ((_) @_func)))
|
||||
argument: (indented_string_expression (string_fragment) @injection.content)
|
||||
(#match? @_func "(^|\\.)writeJS(Bin)?$")
|
||||
(#set! injection.language "javascript")
|
||||
(#set! injection.combined))
|
||||
|
||||
; pkgs.writers.writePerl[Bin] name attrs content
|
||||
(apply_expression
|
||||
(apply_expression
|
||||
function: (apply_expression
|
||||
function: ((_) @_func)))
|
||||
argument: (indented_string_expression (string_fragment) @injection.content)
|
||||
(#match? @_func "(^|\\.)writePerl(Bin)?$")
|
||||
(#set! injection.language "perl")
|
||||
(#set! injection.combined))
|
||||
|
||||
; pkgs.writers.write{Python,PyPy}{2,3}[Bin] name attrs content
|
||||
(apply_expression
|
||||
(apply_expression
|
||||
function: (apply_expression
|
||||
function: ((_) @_func)))
|
||||
argument: (indented_string_expression (string_fragment) @injection.content)
|
||||
(#match? @_func "(^|\\.)write(Python|PyPy)[23](Bin)?$")
|
||||
(#set! injection.language "python")
|
||||
(#set! injection.combined))
|
||||
|
||||
; pkgs.writers.writeFSharp[Bin] name content
|
||||
; No query available for f-sharp as of the time of writing
|
||||
; See: https://github.com/helix-editor/helix/issues/4943
|
||||
; ((apply_expression
|
||||
; function: (apply_expression function: (_) @_func)
|
||||
; argument: (indented_string_expression (string_fragment) @injection.content))
|
||||
; (#match? @_func "(^|\\.)writeFSharp(Bin)?$")
|
||||
; (#set! injection.language "f-sharp")
|
||||
; (#set! injection.combined))
|
||||
|
1
runtime/queries/sage/highlights.scm
Normal file
1
runtime/queries/sage/highlights.scm
Normal file
@ -0,0 +1 @@
|
||||
; inherits: python
|
2
runtime/queries/sage/injections.scm
Normal file
2
runtime/queries/sage/injections.scm
Normal file
@ -0,0 +1,2 @@
|
||||
((comment) @injection.content
|
||||
(#set! injection.language "comment"))
|
1
runtime/queries/sage/textobjects.scm
Normal file
1
runtime/queries/sage/textobjects.scm
Normal file
@ -0,0 +1 @@
|
||||
; inherits: python
|
@ -112,6 +112,9 @@
|
||||
(generic_function
|
||||
function: (identifier) @function)
|
||||
|
||||
(interpolated_string_expression
|
||||
interpolator: (identifier) @function)
|
||||
|
||||
(
|
||||
(identifier) @function.builtin
|
||||
(#match? @function.builtin "^super$")
|
||||
|
@ -51,7 +51,8 @@
|
||||
"markup.heading" = "my_yellow1"
|
||||
"markup.list" = "my_white2"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = "my_turquoise2"
|
||||
"markup.link.text" = "my_white2"
|
||||
"markup.quote" = "my_brown"
|
||||
|
@ -37,6 +37,7 @@
|
||||
"markup.list" = "base08"
|
||||
"markup.bold" = { fg = "base0A", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "base0E", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "base09", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "base08"
|
||||
"markup.quote" = "base0C"
|
||||
|
@ -37,6 +37,7 @@
|
||||
"markup.list" = "base08"
|
||||
"markup.bold" = { fg = "base0A", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "base0E", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "base09", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "base08"
|
||||
"markup.quote" = "base0C"
|
||||
|
@ -34,6 +34,7 @@
|
||||
"markup.list" = "light-red"
|
||||
"markup.bold" = { fg = "light-yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "light-magenta", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "light-red"
|
||||
"markup.quote" = "light-cyan"
|
||||
|
@ -45,6 +45,7 @@
|
||||
"markup.list" = "light-red"
|
||||
"markup.bold" = { fg = "light-yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "light-magenta", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "yellow", underline = { color = "yellow", style = "line"} }
|
||||
"markup.link.text" = "light-red"
|
||||
"markup.quote" = "light-cyan"
|
||||
|
@ -31,6 +31,7 @@
|
||||
"markup.list" = "bogster-red"
|
||||
"markup.bold" = { fg = "bogster-yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "bogster-purp", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "bogster-yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "bogster-red"
|
||||
"markup.quote" = "bogster-teal"
|
||||
|
@ -31,6 +31,7 @@
|
||||
"markup.list" = "bogster-red"
|
||||
"markup.bold" = { fg = "bogster-yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "bogster-yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "bogster-red"
|
||||
"markup.quote" = "bogster-lblue"
|
||||
|
@ -22,6 +22,7 @@
|
||||
"markup.list" = { fg = "bubblegum" }
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "violet", modifiers = ["underlined"] }
|
||||
"markup.link.text" = { fg = "violet" }
|
||||
"markup.quote" = { fg = "berry_desaturated" }
|
||||
|
@ -50,6 +50,7 @@
|
||||
"markup.list" = "mauve"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "rosewater", modifiers = ["italic", "underlined"] }
|
||||
"markup.link.text" = "blue"
|
||||
"markup.raw" = "flamingo"
|
||||
|
@ -56,6 +56,7 @@
|
||||
"markup.list" = "white"
|
||||
"markup.bold" = { fg = "white", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "white", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { fg = "white", modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "lightblue", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "white"
|
||||
"markup.quote" = "darkgreen"
|
||||
|
@ -84,6 +84,7 @@
|
||||
"markup.list" = "pink"
|
||||
"markup.bold" = { fg = "emerald_green", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "blue", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "blue", underline = { color = "blue", style = "line" } }
|
||||
"markup.link.text" = "pink"
|
||||
"markup.quote" = "yellow"
|
||||
|
@ -44,6 +44,7 @@
|
||||
"markup.list" = "blue3"
|
||||
"markup.bold" = { fg = "blue2", modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { modifiers = ["underlined"] }
|
||||
"markup.link.text" = "orange"
|
||||
"markup.quote" = "dark_green"
|
||||
|
@ -29,6 +29,7 @@
|
||||
|
||||
'markup.bold' = { fg = 'orange', modifiers = ['bold'] }
|
||||
'markup.italic' = { fg = 'magenta', modifiers = ['italic'] }
|
||||
'markup.strikethrough' = { modifiers = ['crossed_out'] }
|
||||
'markup.heading' = { fg = 'red' }
|
||||
'markup.link' = { fg = 'orange' }
|
||||
'markup.link.url' = { fg = 'magenta' }
|
||||
|
@ -40,7 +40,7 @@
|
||||
"ui.text" = { fg = "foreground" }
|
||||
"ui.text.focus" = { fg = "cyan" }
|
||||
"ui.window" = { fg = "foreground" }
|
||||
"ui.virtual.whitespace" = { fg = "comment" }
|
||||
"ui.virtual.whitespace" = { fg = "subtle" }
|
||||
"ui.virtual.ruler" = { bg = "background_dark"}
|
||||
|
||||
"error" = { fg = "red" }
|
||||
@ -50,6 +50,7 @@
|
||||
"markup.list" = "cyan"
|
||||
"markup.bold" = { fg = "orange", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "yellow", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = "cyan"
|
||||
"markup.link.text" = "pink"
|
||||
"markup.quote" = { fg = "yellow", modifiers = ["italic"] }
|
||||
@ -63,6 +64,7 @@ background = "#282a36"
|
||||
background_dark = "#21222c"
|
||||
primary_highlight = "#800049"
|
||||
secondary_highlight = "#4d4f66"
|
||||
subtle = "#424450"
|
||||
foreground = "#f8f8f2"
|
||||
comment = "#6272a4"
|
||||
red = "#ff5555"
|
||||
@ -72,3 +74,4 @@ green = "#50fa7b"
|
||||
purple = "#bd93f9"
|
||||
cyan = "#8be9fd"
|
||||
pink = "#ff79c6"
|
||||
|
||||
|
@ -50,6 +50,7 @@
|
||||
"markup.list" = "cyan"
|
||||
"markup.bold" = { fg = "orange", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "yellow", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = "cyan"
|
||||
"markup.link.text" = "pink"
|
||||
"markup.quote" = { fg = "yellow", modifiers = ["italic"] }
|
||||
|
@ -37,6 +37,7 @@
|
||||
"markup.list" = { fg = "black" }
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "royalblue3", modifiers = ["underlined"] }
|
||||
"markup.link.text" = { fg = "royalblue3" }
|
||||
"markup.quote" = { fg = "gray60" }
|
||||
|
@ -46,6 +46,7 @@
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "blue", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "purple"
|
||||
"markup.quote" = "grey2"
|
||||
|
@ -46,6 +46,7 @@
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "blue", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "purple"
|
||||
"markup.quote" = "grey2"
|
||||
|
@ -37,14 +37,15 @@
|
||||
"markup.raw" = { fg = "orange_text", bg = "orange_bg" }
|
||||
"markup.raw.inline" = { fg = "orange_text", bg = "orange_bg" }
|
||||
"markup.raw.block" = { fg = "orange_text", bg = "orange_bg" }
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "blue_text", bg = "blue_bg", modifiers = [
|
||||
"underlined",
|
||||
] }
|
||||
"markup.link.label" = { fg = "blue_text", bg = "blue_bg" }
|
||||
"markup.link.text" = { fg = "blue_text", bg = "blue_bg" }
|
||||
"markup.quote" = { fg = "teal_text", bg = "teal_bg" }
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.list" = { fg = "purple_text", bg = "purple_bg" }
|
||||
|
||||
"ui.background" = { fg = "base1", bg = "base7" }
|
||||
|
@ -46,6 +46,7 @@
|
||||
# "markup.normal" = {} # .completion / .hover
|
||||
"markup.bold" = { fg = "lightest", modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.heading" = { fg = "cyan", modifiers = ["bold"] } # .marker / .1 / .2 / .3 / .4 / .5 / .6
|
||||
"markup.list" = "pink" # .unnumbered / .numbered
|
||||
"markup.list.numbered" = "cyan"
|
||||
|
@ -9,9 +9,9 @@ keyword = "scale.red.3"
|
||||
namespace = "scale.orange.2"
|
||||
punctuation = "fg.default"
|
||||
"punctuation.delimiter" = "fg.default"
|
||||
operator = "scale.red.3"
|
||||
operator = "scale.blue.1"
|
||||
special = "scale.blue.1"
|
||||
"variable.other.member" = "fg.default"
|
||||
"variable.other.member" = "scale.blue.1"
|
||||
variable = "fg.default"
|
||||
"variable.parameter" = "scale.orange.2"
|
||||
"variable.builtin" = "scale.red.3"
|
||||
@ -33,6 +33,7 @@ label = "scale.red.3"
|
||||
"markup.heading" = "scale.blue.2"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { modifiers = ["underlined"] }
|
||||
"markup.link.text" = { fg = "scale.blue.1", modifiers = ["underlined"] }
|
||||
"markup.raw" = "scale.blue.2"
|
||||
|
@ -9,9 +9,9 @@ keyword = "scale.red.5"
|
||||
namespace = "scale.orange.6"
|
||||
punctuation = "fg.default"
|
||||
"punctuation.delimiter" = "fg.default"
|
||||
operator = "scale.red.5"
|
||||
operator = "scale.blue.8"
|
||||
special = "scale.blue.8"
|
||||
"variable.other.member" = "fg.default"
|
||||
"variable.other.member" = "scale.blue.8"
|
||||
variable = "fg.default"
|
||||
"variable.parameter" = "scale.orange.6"
|
||||
"variable.builtin" = "scale.red.5"
|
||||
@ -33,6 +33,7 @@ label = "scale.red.5"
|
||||
"markup.heading" = "scale.blue.6"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { modifiers = ["underlined"] }
|
||||
"markup.link.text" = { fg = "scale.blue.8", modifiers = ["underlined"] }
|
||||
"markup.raw" = "scale.blue.6"
|
||||
|
@ -33,10 +33,10 @@
|
||||
"diff.delta" = "orange1"
|
||||
"diff.minus" = "red1"
|
||||
|
||||
"warning" = { fg = "orange1", bg = "bg1" }
|
||||
"error" = { fg = "red1", bg = "bg1" }
|
||||
"info" = { fg = "aqua1", bg = "bg1" }
|
||||
"hint" = { fg = "blue1", bg = "bg1" }
|
||||
"warning" = "orange1"
|
||||
"error" = "red1"
|
||||
"info" = "aqua1"
|
||||
"hint" = "blue1"
|
||||
|
||||
"ui.background" = { bg = "bg0" }
|
||||
"ui.linenr" = { fg = "bg4" }
|
||||
@ -69,6 +69,7 @@
|
||||
"markup.heading" = "aqua1"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "green1", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red1"
|
||||
"markup.raw" = "red1"
|
||||
|
@ -34,10 +34,10 @@
|
||||
"diff.delta" = "orange1"
|
||||
"diff.minus" = "red1"
|
||||
|
||||
"warning" = { fg = "orange1", bg = "bg1" }
|
||||
"error" = { fg = "red1", bg = "bg1" }
|
||||
"info" = { fg = "aqua1", bg = "bg1" }
|
||||
"hint" = { fg = "blue1", bg = "bg1" }
|
||||
"warning" = "orange1"
|
||||
"error" = "red1"
|
||||
"info" = "aqua1"
|
||||
"hint" = "blue1"
|
||||
|
||||
"diagnostic.error" = { underline = { style = "curl", color = "red0" } }
|
||||
"diagnostic.warning" = { underline = { style = "curl", color = "orange1" } }
|
||||
@ -70,6 +70,7 @@
|
||||
"markup.heading" = "aqua1"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "green1", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red1"
|
||||
"markup.raw" = "red1"
|
||||
|
@ -34,10 +34,10 @@
|
||||
"diff.delta" = "orange1"
|
||||
"diff.minus" = "red1"
|
||||
|
||||
"warning" = { fg = "orange1", bg = "bg1" }
|
||||
"error" = { fg = "red1", bg = "bg1" }
|
||||
"info" = { fg = "aqua1", bg = "bg1" }
|
||||
"hint" = { fg = "blue1", bg = "bg1" }
|
||||
"warning" = "orange1"
|
||||
"error" = "red1"
|
||||
"info" = "aqua1"
|
||||
"hint" = "blue1"
|
||||
|
||||
"ui.background" = { bg = "bg0" }
|
||||
"ui.linenr" = { fg = "bg4" }
|
||||
@ -70,6 +70,7 @@
|
||||
"markup.heading" = "aqua1"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "green1", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red1"
|
||||
"markup.raw" = "red1"
|
||||
|
@ -75,6 +75,7 @@
|
||||
"markup.list" = { fg = "t4" }
|
||||
"markup.bold" = { fg = "t4" }
|
||||
"markup.italic" = { fg = "t4" }
|
||||
"markup.strikethrough" = { fg = "t4", modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "t4", modifiers = ["underlined"] }
|
||||
"markup.link.text" = { fg = "t4" }
|
||||
"markup.quote" = { fg = "t4" }
|
||||
|
@ -33,6 +33,7 @@
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red"
|
||||
"markup.quote" = "cyan"
|
||||
|
@ -14,30 +14,31 @@
|
||||
"ui.linenr" = { fg = "sumiInk4" }
|
||||
"ui.linenr.selected" = { fg = "roninYellow" }
|
||||
|
||||
"ui.virtual.ruler" = { bg = "sumiInk2" }
|
||||
"ui.virtual.whitespace" = "waveBlue1"
|
||||
"ui.virtual.indent-guide" = "sumiInk4"
|
||||
|
||||
"ui.statusline" = { fg = "oldWhite", bg = "sumiInk0" }
|
||||
"ui.statusline.inactive" = { fg = "fujiGray", bg = "sumiInk0" }
|
||||
"ui.statusline.normal" = { fg = "sumiInk0", bg = "crystalBlue", modifiers = ["bold"] }
|
||||
"ui.statusline.insert" = { fg = "sumiInk0", bg = "autumnGreen" }
|
||||
"ui.statusline.select" = { fg = "sumiInk0", bg = "oniViolet" }
|
||||
"ui.statusline.insert" = { fg = "sumiInk0", bg = "autumnGreen", modifiers = ["bold"] }
|
||||
"ui.statusline.select" = { fg = "sumiInk0", bg = "oniViolet", modifiers = ["bold"] }
|
||||
|
||||
"ui.bufferline" = { fg = "oldWhite", bg = "sumiInk0" }
|
||||
"ui.bufferline.inactive" = { fg = "fujiGray", bg = "sumiInk0" }
|
||||
|
||||
"ui.popup" = { fg = "fujiWhite", bg = "sumiInk0" }
|
||||
"ui.window" = { fg = "fujiWhite" }
|
||||
"ui.help" = { fg = "fujiWhite", bg = "sumiInk1" }
|
||||
"ui.window" = { fg = "sumiInk0" }
|
||||
"ui.help" = { fg = "fujiWhite", bg = "sumiInk0" }
|
||||
"ui.text" = "fujiWhite"
|
||||
"ui.text.focus" = { fg = "fujiWhite", bg = "waveBlue1", modifiers = ["bold"] }
|
||||
"ui.virtual" = "waveBlue1"
|
||||
|
||||
"ui.cursor" = { fg = "waveBlue1", bg = "fujiWhite"}
|
||||
"ui.cursor.primary" = { fg = "waveBlue1", bg = "seaFoam" }
|
||||
"ui.cursor.match" = { fg = "seaFoam", modifiers = ["bold"] }
|
||||
"ui.highlight" = { fg = "fujiWhite", bg = "waveBlue2" }
|
||||
"ui.menu" = { fg = "fujiWhite", bg = "sumiInk1" }
|
||||
"ui.menu.selected" = { fg = "fujiWhite", bg = "waveBlue1" }
|
||||
"ui.menu" = { fg = "fujiWhite", bg = "sumiInk0" }
|
||||
"ui.menu.selected" = { fg = "fujiWhite", bg = "waveBlue1", modifiers = ["bold"] }
|
||||
|
||||
"ui.cursorline.primary" = { bg = "sumiInk3"}
|
||||
|
||||
@ -96,6 +97,7 @@ hint = "dragonBlue"
|
||||
"markup.list" = "oniViolet"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "springBlue", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "crystalBlue"
|
||||
"markup.quote" = "seaFoam"
|
||||
@ -107,7 +109,8 @@ fujiWhite = "#DCD7BA" # default foreground
|
||||
oldWhite = "#C8C093" # dark foreground, e.g. statuslines
|
||||
sumiInk0 = "#16161D" # dark background, e.g. statuslines, floating windows
|
||||
sumiInk1 = "#1F1F28" # default background
|
||||
sumiInk3 = "#363646" # lighter background, e.g. colorcolumns and folds
|
||||
sumiInk2 = "#2A2A37" # lighter background, e.g. colorcolumns, folds
|
||||
sumiInk3 = "#363646" # lighter background, e.g. cursorline
|
||||
sumiInk4 = "#54546D" # darker foreground, e.g. linenumbers, fold column
|
||||
waveBlue1 = "#223249" # popup background, visual selection background
|
||||
waveBlue2 = "#2D4F67" # popup selection background, search background
|
||||
|
@ -67,8 +67,9 @@
|
||||
|
||||
"markup.heading" = { fg = "orange" }
|
||||
"markup.list" = { fg = "blue" }
|
||||
"markup.bold" = { fg = "magenta" }
|
||||
"markup.italic" = { fg = "blue" }
|
||||
"markup.bold" = { fg = "magenta", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "blue", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "comment" , modifiers = ["underlined"] }
|
||||
"markup.link.text" = { fg = "comment" }
|
||||
"markup.quote" = { fg = "yellow" }
|
||||
|
@ -50,6 +50,7 @@
|
||||
"markup.list" = "gray06"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "green", modifiers = ["underlined"] }
|
||||
"markup.link.text" = { fg = "blue", modifiers = ["italic"] }
|
||||
"markup.raw" = "yellow"
|
||||
|
@ -46,6 +46,7 @@
|
||||
"markup.list" = "red"
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "magenta", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "yellow", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "red"
|
||||
"markup.quote" = "cyan"
|
||||
|
17
runtime/themes/monokai_aqua.toml
Normal file
17
runtime/themes/monokai_aqua.toml
Normal file
@ -0,0 +1,17 @@
|
||||
inherits = "monokai"
|
||||
|
||||
"keyword.control.import" = { fg = "cyan", modifiers = ["italic"] }
|
||||
"keyword.function" = { fg = "cyan", modifiers = ["italic"] }
|
||||
"keyword.storage.type" = { fg = "cyan", modifiers = ["italic"] }
|
||||
|
||||
"namespace" = { fg = "text" }
|
||||
|
||||
"type" = { fg = "type", modifiers = ["bold"] }
|
||||
|
||||
"ui.statusline.normal" = { fg = "light-black", bg = "cyan" }
|
||||
"ui.statusline.insert" = { fg = "light-black", bg = "green" }
|
||||
"ui.statusline.select" = { fg = "light-black", bg = "purple" }
|
||||
|
||||
[palette]
|
||||
cyan = "#66D9EF"
|
||||
type = "#66D9EF"
|
@ -99,6 +99,7 @@
|
||||
"markup.heading" = "green"
|
||||
"markup.bold" = { fg = "orange", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "orange", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "orange", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "yellow"
|
||||
"markup.quote" = "green"
|
||||
|
@ -96,6 +96,7 @@
|
||||
"markup.heading" = "green"
|
||||
"markup.bold" = { fg = "orange", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "orange", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "orange", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "yellow"
|
||||
"markup.quote" = "green"
|
||||
|
@ -99,6 +99,7 @@
|
||||
"markup.heading" = "green"
|
||||
"markup.bold" = { fg = "orange", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "orange", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "orange", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "yellow"
|
||||
"markup.quote" = "green"
|
||||
|
@ -96,6 +96,7 @@
|
||||
"markup.heading" = "green"
|
||||
"markup.bold" = { fg = "orange", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "orange", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "orange", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "yellow"
|
||||
"markup.quote" = "green"
|
||||
|
@ -65,7 +65,7 @@
|
||||
"variable.parameter" = "#f59762"
|
||||
|
||||
# error
|
||||
"error" = { bg = "magenta", fg = "yellow" }
|
||||
"error" = { fg = "red", modifiers = ["bold"] }
|
||||
|
||||
# annotations, decorators
|
||||
"special" = "#f59762"
|
||||
@ -88,7 +88,7 @@
|
||||
|
||||
# make diagnostic underlined, to distinguish with selection text.
|
||||
"diagnostic.warning" = { underline = { color = "orange", style = "curl" } }
|
||||
"diagnostic.error" = { underline = { color = "magenta", style = "curl" } } # maybe should be red?
|
||||
"diagnostic.error" = { underline = { color = "red", style = "curl" } }
|
||||
"diagnostic.info" = { underline = { color = "base8", style = "curl" } }
|
||||
"diagnostic.hint" = { underline = { color = "base8", style = "curl" } }
|
||||
|
||||
@ -96,6 +96,7 @@
|
||||
"markup.heading" = "green"
|
||||
"markup.bold" = { fg = "orange", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "orange", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.url" = { fg = "orange", modifiers = ["underlined"] }
|
||||
"markup.link.text" = "yellow"
|
||||
"markup.quote" = "green"
|
||||
|
@ -80,6 +80,7 @@
|
||||
'markup.list' = { fg = 'pink' }
|
||||
'markup.bold' = { fg = 'foreground', modifiers = ['bold'] }
|
||||
'markup.italic' = { fg = 'foreground', modifiers = ['italic'] }
|
||||
'markup.strikethrough' = { fg = 'foreground', modifiers = ['crossed_out'] }
|
||||
'markup.link' = { fg = 'pink', modifiers = ['underlined'] }
|
||||
'markup.link.url' = { fg = 'slate', modifiers = ['underlined'] }
|
||||
'markup.quote' = { fg = 'green', modifiers = ['italic'] }
|
||||
|
@ -55,6 +55,7 @@
|
||||
"markup.list" = { fg = "magenta", modifiers = ["bold"] }
|
||||
"markup.bold" = { fg = "orange", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "pink" }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link" = { fg = "yellow-bright", modifiers = ["bold"] }
|
||||
"markup.quote" = { fg = "blue" }
|
||||
|
||||
|
@ -150,6 +150,7 @@
|
||||
|
||||
'markup.bold' = { modifiers = ["bold"] } # Bold text.
|
||||
'markup.italic' = { modifiers = ["italic"] } # Italicised text.
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] } # Crossed out text.
|
||||
|
||||
'markup.link' = { fg = "light-blue", modifiers = ["underlined"] }
|
||||
'markup.link.url' = { } # Urls pointed to by links.
|
||||
|
@ -49,6 +49,7 @@
|
||||
"markup.list" = "base08"
|
||||
"markup.quote" = "base0C"
|
||||
"markup.raw" = "base0B"
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
|
||||
"diff.delta" = "base09"
|
||||
"diff.plus" = "base0B"
|
||||
|
@ -95,6 +95,7 @@
|
||||
"markup.list" = "nord9"
|
||||
"markup.bold" = { modifiers = ["bold"] }
|
||||
"markup.italic" = { modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.link.text" = "nord8"
|
||||
"markup.raw" = "nord7"
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
"markup.raw.inline" = { fg = "green" }
|
||||
"markup.bold" = { fg = "gold", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "purple", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.list" = { fg = "red" }
|
||||
"markup.quote" = { fg = "yellow" }
|
||||
"markup.link.url" = { fg = "cyan", modifiers = ["underlined"]}
|
||||
|
@ -31,6 +31,7 @@
|
||||
"markup.raw.block" = { fg = "white" }
|
||||
"markup.bold" = { fg = "gold", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "purple", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.list" = { fg = "red" }
|
||||
"markup.quote" = { fg = "yellow" }
|
||||
"markup.link.url" = { fg = "blue", modifiers = ["underlined"]}
|
||||
|
@ -48,6 +48,7 @@
|
||||
"markup.raw.inline" = { fg = "green", bg = "grey-200" }
|
||||
"markup.bold" = { fg = "yellow", modifiers = ["bold"] }
|
||||
"markup.italic" = { fg = "purple", modifiers = ["italic"] }
|
||||
"markup.strikethrough" = { modifiers = ["crossed_out"] }
|
||||
"markup.list" = { fg = "light-blue" }
|
||||
"markup.quote" = { fg = "gray" }
|
||||
"markup.link.url" = { fg = "cyan", modifiers = ["underlined"] }
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user