on master help sign in
Merge branch 'lsp-async-init'
commitfd36fbdebfa6508699979dc426725cbbc2dbe295 authorBlaž Hrastnik <blaz@mxxn.io> dateTue 7 Sep 2021, 13:05 (5 years ago) parentfde0a843cbdc05
38 files changed+778−483
book/book.toml+2−1 book/src/configuration.md+13 book/src/from-vim.md+2 book/src/install.md+3−1 book/src/keymap.md+52−48 book/src/themes.md+124−78 book/theme/css/general.css+13 book/theme/css/variables.css+14−10 book/theme/highlight.css+37−64 helix-core/src/indent.rs+8−3 helix-core/src/syntax.rs+6−2 helix-lsp/src/client.rs+49−41 helix-lsp/src/lib.rs+49−53 helix-lsp/src/transport.rs+115−22 helix-syntax/build.rs+1−2 helix-syntax/languages/tree-sitter-julia+1−1 helix-term/src/application.rs+53−54 helix-term/src/job.rs+2−2 helix-view/src/document.rs+33−16 helix-view/src/editor.rs+15−10 languages.toml+27−1 runtime/queries/c/highlights.scm+1−1 runtime/queries/go/highlights.scm+35−4 runtime/queries/go/locals.scm+30 runtime/queries/haskell/highlights.scm+8−6 runtime/queries/javascript/highlights.scm+1−1 runtime/queries/julia/highlights.scm+43−39 runtime/queries/latex/highlights.scm+2−2 runtime/queries/lua/highlights.scm+9−9 runtime/queries/ocaml/highlights.scm+4−4 runtime/queries/ruby/highlights.scm+1−1 runtime/queries/rust/highlights.scm+2−2 runtime/queries/rust/locals.scm+17 runtime/queries/tsx/highlights.scm+1 runtime/queries/yaml/highlights.scm+1−1 runtime/themes/dark_plus.toml+1 runtime/themes/monokai.toml+1 theme.toml+2−4
book/book.toml+2−1
@@ -3,8 +3,9 @@ authors = ["Blaž Hrastnik"]
33language = "en"
44multilingual = false
55src = "src"
6−theme = "colibri"
76edit-url-template = "https://github.com/helix-editor/helix/tree/master/book/{path}?mode=edit"
87
98[output.html]
109cname = "docs.helix-editor.com"
10+default-theme = "colibri"
11+preferred-dark-theme = "colibri"
book/src/configuration.md+13
@@ -5,6 +5,19 @@ To override global configuration parameters, create a `config.toml` file located
55* Linux and Mac: `~/.config/helix/config.toml`
66* Windows: `%AppData%\helix\config.toml`
77
8+## Editor
9+
10+`[editor]` section of the config.
11+
12+| Key | Description | Default |
13+|--|--|---------|
14+| `scrolloff` | Number of lines of padding around the edge of the screen when scrolling. | `3` |
15+| `mouse` | Enable mouse mode. | `true` |
16+| `middle-click-paste` | Middle click paste support. | `true` |
17+| `scroll-lines` | Number of lines to scroll per scroll wheel step. | `3` |
18+| `shell` | Shell to use when running external commands. | Unix: `["sh", "-c"]`<br/>Windows: `["cmd", "/C"]` |
19+| `line-number` | Line number display (`absolute`, `relative`) | `absolute` |
20+
821## LSP
922
1023To display all language server messages in the status line add the following to your `config.toml`:
book/src/from-vim.md+2
@@ -7,4 +7,6 @@ going to act on (a word, a paragraph, a line, etc) is selected first and the
77action itself (delete, change, yank, etc) comes second. A cursor is simply a
88single width selection.
99
10+See also Kakoune's [Migrating from Vim](https://github.com/mawww/kakoune/wiki/Migrating-from-Vim).
11+
1012> TODO: Mention texobjects, surround, registers
book/src/install.md+3−1
@@ -23,7 +23,9 @@ shell for working on Helix.
2323
2424### Arch Linux
2525
26−Binary packages are available on AUR:
26+Releases are available in the `community` repository.
27+
28+Packages are also available on AUR:
2729- [helix-bin](https://aur.archlinux.org/packages/helix-bin/) contains the pre-built release
2830- [helix-git](https://aur.archlinux.org/packages/helix-git/) builds the master branch
2931
book/src/keymap.md+52−48
@@ -4,7 +4,7 @@
44
55### Movement
66
7−> NOTE: `f`, `F`, `t` and `T` are not confined to the current line.
7+> NOTE: Unlike vim, `f`, `F`, `t` and `T` are not confined to the current line.
88
99| Key | Description | Command |
1010| ----- | ----------- | ------- |
@@ -28,14 +28,14 @@
2828| `PageDown` | Move page down | `page_down` |
2929| `Ctrl-u` | Move half page up | `half_page_up` |
3030| `Ctrl-d` | Move half page down | `half_page_down` |
31−| `Ctrl-i` | Jump forward on the jumplist TODO: conflicts tab | `jump_forward` |
31+| `Ctrl-i` | Jump forward on the jumplist | `jump_forward` |
3232| `Ctrl-o` | Jump backward on the jumplist | `jump_backward` |
3333| `v` | Enter [select (extend) mode](#select--extend-mode) | `select_mode` |
3434| `g` | Enter [goto mode](#goto-mode) | N/A |
3535| `m` | Enter [match mode](#match-mode) | N/A |
3636| `:` | Enter command mode | `command_mode` |
3737| `z` | Enter [view mode](#view-mode) | N/A |
38−| `Ctrl-w` | Enter [window mode](#window-mode) (maybe will be remove for spc w w later) | N/A |
38+| `Ctrl-w` | Enter [window mode](#window-mode) | N/A |
3939| `Space` | Enter [space mode](#space-mode) | N/A |
4040| `K` | Show documentation for the item under the cursor | `hover` |
4141
@@ -66,6 +66,16 @@
6666| `d` | Delete selection | `delete_selection` |
6767| `c` | Change selection (delete and enter insert mode) | `change_selection` |
6868
69+#### Shell
70+
71+| Key | Description | Command |
72+| ------ | ----------- | ------- |
73+| <code>&#124;</code> | Pipe each selection through shell command, replacing with output | `shell_pipe` |
74+| <code>A-&#124;</code> | Pipe each selection into shell command, ignoring output | `shell_pipe_to` |
75+| `!` | Run shell command, inserting output before each selection | `shell_insert_output` |
76+| `A-!` | Run shell command, appending output after each selection | `shell_append_output` |
77+
78+
6979### Selection manipulation
7080
7181| Key | Description | Command |
@@ -87,17 +97,10 @@
8797| | Expand selection to parent syntax node TODO: pick a key | `expand_selection` |
8898| `J` | Join lines inside selection | `join_selections` |
8999| `K` | Keep selections matching the regex TODO: overlapped by hover help | `keep_selections` |
100+| `$` | Pipe each selection into shell command, keep selections where command returned 0 | `shell_keep_pipe` |
90101| `Space` | Keep only the primary selection TODO: overlapped by space mode | `keep_primary_selection` |
91102| `Ctrl-c` | Comment/uncomment the selections | `toggle_comments` |
92103
93−### Insert Mode
94−
95−| Key | Description | Command |
96−| ----- | ----------- | ------- |
97−| `Escape` | Switch to normal mode | `normal_mode` |
98−| `Ctrl-x` | Autocomplete | `completion` |
99−| `Ctrl-w` | Delete previous word | `delete_word_backward` |
100−
101104### Search
102105
103106> TODO: The search implementation isn't ideal yet -- we don't support searching
@@ -110,38 +113,11 @@ in reverse, or searching via smartcase.
110113| `N` | Add next search match to selection | `extend_search_next` |
111114| `*` | Use current selection as the search pattern | `search_selection` |
112115
113−### Unimpaired
114−
115−Mappings in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaired)
116−
117−| Key | Description | Command |
118−| ----- | ----------- | ------- |
119−| `[d` | Go to previous diagnostic | `goto_prev_diag` |
120−| `]d` | Go to next diagnostic | `goto_next_diag` |
121−| `[D` | Go to first diagnostic in document | `goto_first_diag` |
122−| `]D` | Go to last diagnostic in document | `goto_last_diag` |
123−| `[space` | Add newline above | `add_newline_above` |
124−| `]space` | Add newline below | `add_newline_below` |
125−
126−### Shell
116+### Minor modes
127117
128−| Key | Description | Command |
129−| ------ | ----------- | ------- |
130−| `\|` | Pipe each selection through shell command, replacing with output | `shell_pipe` |
131−| `A-\|` | Pipe each selection into shell command, ignoring output | `shell_pipe_to` |
132−| `!` | Run shell command, inserting output before each selection | `shell_insert_output` |
133−| `A-!` | Run shell command, appending output after each selection | `shell_append_output` |
134−| `$` | Pipe each selection into shell command, keep selections where command returned 0 | `shell_keep_pipe` |
135−
136−## Select / extend mode
137−
138−I'm still pondering whether to keep this mode or not. It changes movement
139−commands to extend the existing selection instead of replacing it.
140−
141−> NOTE: It's a bit confusing at the moment because extend hasn't been
142−> implemented for all movement commands yet.
118+These sub-modes are accessible from normal mode and typically switch back to normal mode after a command.
143119
144−## View mode
120+#### View mode
145121
146122View mode is intended for scrolling and manipulating the view without changing
147123the selection.
@@ -155,7 +131,7 @@ the selection.
155131| `j` | Scroll the view downwards | `scroll_down` |
156132| `k` | Scroll the view upwards | `scroll_up` |
157133
158−## Goto mode
134+#### Goto mode
159135
160136Jumps to various locations.
161137
@@ -177,7 +153,7 @@ Jumps to various locations.
177153| `i` | Go to implementation | `goto_implementation` |
178154| `a` | Go to the last accessed/alternate file | `goto_last_accessed_file` |
179155
180−## Match mode
156+#### Match mode
181157
182158Enter this mode using `m` from normal mode. See the relavant section
183159in [Usage](./usage.md) for an explanation about [surround](./usage.md#surround)
@@ -192,11 +168,9 @@ and [textobject](./usage.md#textobject) usage.
192168| `a` `<object>` | Select around textobject | `select_textobject_around` |
193169| `i` `<object>` | Select inside textobject | `select_textobject_inner` |
194170
195−## Object mode
196−
197171TODO: Mappings for selecting syntax nodes (a superset of `[`).
198172
199−## Window mode
173+#### Window mode
200174
201175This layer is similar to vim keybindings as kakoune does not support window.
202176
@@ -207,9 +181,9 @@ This layer is similar to vim keybindings as kakoune does not support window.
207181| `h`, `Ctrl-h` | Horizontal bottom split | `hsplit` |
208182| `q`, `Ctrl-q` | Close current window | `wclose` |
209183
210−## Space mode
184+#### Space mode
211185
212−This layer is a kludge of mappings I had under leader key in neovim.
186+This layer is a kludge of mappings, mostly pickers.
213187
214188| Key | Description | Command |
215189| ----- | ----------- | ------- |
@@ -226,6 +200,36 @@ This layer is a kludge of mappings I had under leader key in neovim.
226200| `Y` | Yank main selection to clipboard | `yank_main_selection_to_clipboard` |
227201| `R` | Replace selections by clipboard contents | `replace_selections_with_clipboard` |
228202
203+
204+#### Unimpaired
205+
206+Mappings in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaired).
207+
208+| Key | Description | Command |
209+| ----- | ----------- | ------- |
210+| `[d` | Go to previous diagnostic | `goto_prev_diag` |
211+| `]d` | Go to next diagnostic | `goto_next_diag` |
212+| `[D` | Go to first diagnostic in document | `goto_first_diag` |
213+| `]D` | Go to last diagnostic in document | `goto_last_diag` |
214+| `[space` | Add newline above | `add_newline_above` |
215+| `]space` | Add newline below | `add_newline_below` |
216+
217+## Insert Mode
218+
219+| Key | Description | Command |
220+| ----- | ----------- | ------- |
221+| `Escape` | Switch to normal mode | `normal_mode` |
222+| `Ctrl-x` | Autocomplete | `completion` |
223+| `Ctrl-w` | Delete previous word | `delete_word_backward` |
224+
225+## Select / extend mode
226+
227+I'm still pondering whether to keep this mode or not. It changes movement
228+commands (including goto) to extend the existing selection instead of replacing it.
229+
230+> NOTE: It's a bit confusing at the moment because extend hasn't been
231+> implemented for all movement commands yet.
232+
229233# Picker
230234
231235Keys to use within picker. Remapping currently not supported.
book/src/themes.md+124−78
@@ -30,85 +30,9 @@ if the key contains a dot `'.'`, it must be quoted to prevent it being parsed as
3030"key.key" = "#ffffff"
3131```
3232
33−Possible modifiers:
33+### Color palettes
3434
35−| Modifier |
36−| --- |
37−| `bold` |
38−| `dim` |
39−| `italic` |
40−| `underlined` |
41−| `slow\_blink` |
42−| `rapid\_blink` |
43−| `reversed` |
44−| `hidden` |
45−| `crossed\_out` |
46−
47−Possible keys:
48−
49−| Key | Notes |
50−| --- | --- |
51−| `attribute` | |
52−| `keyword` | |
53−| `keyword.directive` | Preprocessor directives (\#if in C) |
54−| `keyword.control` | Control flow |
55−| `namespace` | |
56−| `punctuation` | |
57−| `punctuation.delimiter` | |
58−| `operator` | |
59−| `special` | |
60−| `property` | |
61−| `variable` | |
62−| `variable.parameter` | |
63−| `type` | |
64−| `type.builtin` | |
65−| `type.enum.variant` | Enum variants |
66−| `constructor` | |
67−| `function` | |
68−| `function.macro` | |
69−| `function.builtin` | |
70−| `comment` | |
71−| `variable.builtin` | |
72−| `constant` | |
73−| `constant.builtin` | |
74−| `string` | |
75−| `number` | |
76−| `escape` | Escaped characters |
77−| `label` | For lifetimes |
78−| `module` | |
79−| `ui.background` | |
80−| `ui.cursor` | |
81−| `ui.cursor.insert` | |
82−| `ui.cursor.select` | |
83−| `ui.cursor.match` | Matching bracket etc. |
84−| `ui.cursor.primary` | Cursor with primary selection |
85−| `ui.linenr` | |
86−| `ui.linenr.selected` | |
87−| `ui.statusline` | |
88−| `ui.statusline.inactive` | |
89−| `ui.popup` | |
90−| `ui.window` | |
91−| `ui.help` | |
92−| `ui.text` | |
93−| `ui.text.focus` | |
94−| `ui.info` | |
95−| `ui.info.text` | |
96−| `ui.menu` | |
97−| `ui.menu.selected` | |
98−| `ui.selection` | For selections in the editing area |
99−| `ui.selection.primary` | |
100−| `warning` | LSP warning |
101−| `error` | LSP error |
102−| `info` | LSP info |
103−| `hint` | LSP hint |
104−
105−These keys match [tree-sitter scopes](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#theme). We half-follow the common scopes from [macromates language grammars](https://macromates.com/manual/en/language_grammars) with some differences.
106−
107−For a given highlight produced, styling will be determined based on the longest matching theme key. So it's enough to provide function to highlight `function.macro` and `function.builtin` as well, but you can use more specific scopes to highlight specific cases differently.
108−
109−## Color palettes
110−
111−You can define a palette of named colors, and refer to them from the
35+It's recommended define a palette of named colors, and refer to them from the
11236configuration values in your theme. To do this, add a table called
11337`palette` to your theme file:
11438
@@ -146,3 +70,125 @@ over it and is merged into the default palette.
14670| `light-cyan` |
14771| `light-gray` |
14872| `white` |
73+
74+### Modifiers
75+
76+The following values may be used as modifiers.
77+
78+Less common modifiers might not be supported by your terminal emulator.
79+
80+| Modifier |
81+| --- |
82+| `bold` |
83+| `dim` |
84+| `italic` |
85+| `underlined` |
86+| `slow_blink` |
87+| `rapid_blink` |
88+| `reversed` |
89+| `hidden` |
90+| `crossed_out` |
91+
92+### Scopes
93+
94+The following is a list of scopes available to use for styling.
95+
96+#### Syntax highlighting
97+
98+These keys match [tree-sitter scopes](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#theme).
99+
100+For a given highlight produced, styling will be determined based on the longest matching theme key. For example, the highlight `function.builtin.static` would match the key `function.builtin` rather than `function`.
101+
102+We use a similar set of scopes as
103+[SublimeText](https://www.sublimetext.com/docs/scope_naming.html). See also
104+[TextMate](https://macromates.com/manual/en/language_grammars) scopes.
105+
106+- `escape` (TODO: rename to (constant).character.escape)
107+
108+- `type` - Types
109+ - `builtin` - Primitive types provided by the language (`int`, `usize`)
110+
111+- `constant` (TODO: constant.other.placeholder for %v)
112+ - `builtin` Special constants provided by the language (`true`, `false`, `nil` etc)
113+ - `boolean`
114+ - `character`
115+
116+- `number` (TODO: rename to constant.number/.numeric.{integer, float, complex})
117+- `string` (TODO: string.quoted.{single, double}, string.raw/.unquoted)?
118+ - `regexp` - Regular expressions
119+ - `special`
120+ - `path`
121+ - `url`
122+
123+- `comment` - Code comments
124+ - `line` - Single line comments (`//`)
125+ - `block` - Block comments (e.g. (`/* */`)
126+ - `documentation` - Documentation comments (e.g. `///` in Rust)
127+
128+- `variable` - Variables
129+ - `builtin` - Reserved language variables (`self`, `this`, `super`, etc)
130+ - `parameter` - Function parameters
131+ - `property`
132+ - `function` (TODO: ?)
133+
134+- `label`
135+
136+- `punctuation`
137+ - `delimiter` - Commas, colons
138+ - `bracket` - Parentheses, angle brackets, etc.
139+
140+- `keyword`
141+ - `control`
142+ - `conditional` - `if`, `else`
143+ - `repeat` - `for`, `while`, `loop`
144+ - `import` - `import`, `export`
145+ - (TODO: return?)
146+ - `directive` - Preprocessor directives (`#if` in C)
147+ - `function` - `fn`, `func`
148+
149+- `operator` - `||`, `+=`, `>`, `or`
150+
151+- `function`
152+ - `builtin`
153+ - `method`
154+ - `macro`
155+ - `special` (preprocesor in C)
156+
157+- `tag` - Tags (e.g. `<body>` in HTML)
158+
159+- `namespace`
160+
161+#### Interface
162+
163+These scopes are used for theming the editor interface.
164+
165+
166+| Key | Notes |
167+| --- | --- |
168+| `ui.background` | |
169+| `ui.cursor` | |
170+| `ui.cursor.insert` | |
171+| `ui.cursor.select` | |
172+| `ui.cursor.match` | Matching bracket etc. |
173+| `ui.cursor.primary` | Cursor with primary selection |
174+| `ui.linenr` | |
175+| `ui.linenr.selected` | |
176+| `ui.statusline` | Statusline |
177+| `ui.statusline.inactive` | Statusline (unfocused document) |
178+| `ui.popup` | |
179+| `ui.window` | |
180+| `ui.help` | |
181+| `ui.text` | |
182+| `ui.text.focus` | |
183+| `ui.info` | |
184+| `ui.info.text` | |
185+| `ui.menu` | |
186+| `ui.menu.selected` | |
187+| `ui.selection` | For selections in the editing area |
188+| `ui.selection.primary` | |
189+| `warning` | Diagnostics warning |
190+| `error` | Diagnostics error |
191+| `info` | Diagnostics info |
192+| `hint` | Diagnostics hint |
193+
194+
book/theme/css/general.css+13
@@ -114,6 +114,19 @@ h6:target::before {
114114 margin-bottom: .875em;
115115}
116116
117+.content ul li {
118+margin-bottom: .25rem;
119+}
120+.content ul {
121+ list-style-type: square;
122+}
123+.content ul ul, .content ol ul {
124+ margin-bottom: .5rem;
125+}
126+.content li p {
127+ margin-bottom: .5em;
128+}
129+
117130.content p { line-height: 1.45em; }
118131.content ol { line-height: 1.45em; }
119132.content ul { line-height: 1.45em; }
book/theme/css/variables.css+14−10
@@ -69,7 +69,7 @@
6969
7070 --links: #2b79a2;
7171
72− --inline-code-color: #c5c8c6;;
72+ --inline-code-color: #c5c8c6;
7373
7474 --theme-popup-bg: #141617;
7575 --theme-popup-border: #43484d;
@@ -110,7 +110,7 @@
110110
111111 --links: #20609f;
112112
113− --inline-code-color: #301900;
113+ --inline-code-color: #a39e9b;
114114
115115 --theme-popup-bg: #fafafa;
116116 --theme-popup-border: #cccccc;
@@ -151,7 +151,7 @@
151151
152152 --links: #2b79a2;
153153
154− --inline-code-color: #c5c8c6;;
154+ --inline-code-color: #c5c8c6;
155155
156156 --theme-popup-bg: #161923;
157157 --theme-popup-border: #737480;
@@ -192,7 +192,7 @@
192192
193193 --links: #2b79a2;
194194
195− --inline-code-color: #6e6b5e;
195+ --inline-code-color: #c5c8c6;
196196
197197 --theme-popup-bg: #e1e1db;
198198 --theme-popup-border: #b38f6b;
@@ -234,7 +234,7 @@
234234
235235 --links: #2b79a2;
236236
237− --inline-code-color: #c5c8c6;;
237+ --inline-code-color: #6e6b5e;
238238
239239 --theme-popup-bg: #141617;
240240 --theme-popup-border: #43484d;
@@ -261,6 +261,7 @@
261261.colibri {
262262 --bg: #3b224c;
263263 --fg: #bcbdd0;
264+ --heading-fg: #fff;
264265
265266 --sidebar-bg: #281733;
266267 --sidebar-fg: #c8c9db;
@@ -276,18 +277,19 @@
276277 /* --links: #a4a0e8; */
277278 --links: #ECCDBA;
278279
279− --inline-code-color: #c5c8c6;;
280+ --inline-code-color: hsl(48.7, 7.8%, 70%);
280281
281282 --theme-popup-bg: #161923;
282283 --theme-popup-border: #737480;
283284 --theme-hover: rgba(0,0,0, .2);
284285
285− --quote-bg: hsl(226, 15%, 17%);
286+ --quote-bg: #281733;
286287 --quote-border: hsl(226, 15%, 22%);
287288
288− --table-border-color: hsl(226, 23%, 16%);
289− --table-header-bg: hsl(226, 23%, 31%);
289+ --table-border-color: hsl(226, 23%, 76%);
290+ --table-header-bg: hsla(226, 23%, 31%, 0);
290291 --table-alternate-bg: hsl(226, 23%, 14%);
292+ --table-border-line: hsla(201deg, 20%, 92%, 0.2);
291293
292294 --searchbar-border-color: #aaa;
293295 --searchbar-bg: #aeaec6;
@@ -300,6 +302,7 @@
300302}
301303
302304.colibri {
305+/*
303306 --bg: #ffffff;
304307 --fg: #452859;
305308 --fg: #5a5977;
@@ -318,7 +321,7 @@
318321
319322 --links: #6F44F0;
320323
321− --inline-code-color: #697C81;
324+ --inline-code-color: #a39e9b;
322325
323326 --theme-popup-bg: #161923;
324327 --theme-popup-border: #737480;
@@ -341,4 +344,5 @@
341344 --searchresults-border-color: #5c5c68;
342345 --searchresults-li-bg: #242430;
343346 --search-mark-bg: #a2cff5;
347+*/
344348}
book/theme/highlight.css+37−64
@@ -1,83 +1,56 @@
1−/*
2− * An increased contrast highlighting scheme loosely based on the
3− * "Base16 Atelier Dune Light" theme by Bram de Haan
4− * (http://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune)
5− * Original Base16 color scheme by Chris Kempson
6− * (https://github.com/chriskempson/base16)
7− */
8−
9−/* Comment */
1+pre code.hljs {
2+ display:block;
3+ overflow-x:auto;
4+ padding:1em
5+}
6+code.hljs {
7+ padding:3px 5px
8+}
9+.hljs {
10+ background:#2f1e2e;
11+ color:#a39e9b
12+}
1013.hljs-comment,
1114.hljs-quote {
12− color: #575757;
15+ color:#8d8687
1316}
14−
15−/* Red */
16−.hljs-variable,
17−.hljs-template-variable,
18−.hljs-attribute,
19−.hljs-tag,
20−.hljs-name,
21−.hljs-regexp,
2217.hljs-link,
18+.hljs-meta,
2319.hljs-name,
20+.hljs-regexp,
21+.hljs-selector-class,
2422.hljs-selector-id,
25−.hljs-selector-class {
26− color: #d70025;
23+.hljs-tag,
24+.hljs-template-variable,
25+.hljs-variable {
26+ color:#ef6155
2727}
28−
29−/* Orange */
30−.hljs-number,
31−.hljs-meta,
3228.hljs-built_in,
33−.hljs-builtin-name,
29+.hljs-deletion,
3430.hljs-literal,
35−.hljs-type,
36−.hljs-params {
37− color: #b21e00;
31+.hljs-number,
32+.hljs-params,
33+.hljs-type {
34+ color:#f99b15
3835}
39−
40−/* Green */
41−.hljs-string,
42−.hljs-symbol,
43−.hljs-bullet {
44− color: #008200;
36+.hljs-attribute,
37+.hljs-section,
38+.hljs-title {
39+ color:#fec418
4540}
46−
47−/* Blue */
48−.hljs-title,
49−.hljs-section {
50− color: #0030f2;
41+.hljs-addition,
42+.hljs-bullet,
43+.hljs-string,
44+.hljs-symbol {
45+ color:#48b685
5146}
52−
53−/* Purple */
5447.hljs-keyword,
5548.hljs-selector-tag {
56− color: #9d00ec;
57−}
58−
59−.hljs {
60− display: block;
61− overflow-x: auto;
62− background: #f6f7f6;
63− color: #000;
64− padding: 0.5em;
49+ color:#815ba4
6550}
66−
6751.hljs-emphasis {
68− font-style: italic;
52+ font-style:italic
6953}
70−
7154.hljs-strong {
72− font-weight: bold;
73−}
74−
75−.hljs-addition {
76− color: #22863a;
77− background-color: #f0fff4;
78−}
79−
80−.hljs-deletion {
81− color: #b31d28;
82− background-color: #ffeef0;
55+ font-weight:700
8356}
helix-core/src/indent.rs+8−3
@@ -316,8 +316,12 @@ pub fn suggested_indent_for_pos(
316316pub fn get_scopes(syntax: Option<&Syntax>, text: RopeSlice, pos: usize) -> Vec<&'static str> {
317317 let mut scopes = Vec::new();
318318 if let Some(syntax) = syntax {
319− let byte_start = text.char_to_byte(pos);
320− let node = match get_highest_syntax_node_at_bytepos(syntax, byte_start) {
319+ let pos = text.char_to_byte(pos);
320+ let mut node = match syntax
321+ .tree()
322+ .root_node()
323+ .descendant_for_byte_range(pos, pos)
324+ {
321325 Some(node) => node,
322326 None => return scopes,
323327 };
@@ -325,7 +329,8 @@ pub fn get_scopes(syntax: Option<&Syntax>, text: RopeSlice, pos: usize) -> Vec<&
325329 scopes.push(node.kind());
326330
327331 while let Some(parent) = node.parent() {
328− scopes.push(parent.kind())
332+ scopes.push(parent.kind());
333+ node = parent;
329334 }
330335 }
331336
helix-core/src/syntax.rs+6−2
@@ -144,8 +144,12 @@ impl LanguageConfiguration {
144144 &highlights_query,
145145 &injections_query,
146146 &locals_query,
147− )
148− .unwrap(); // TODO: no unwrap
147+ );
148+
149+ let config = match config {
150+ Ok(config) => config,
151+ Err(err) => panic!("{}", err),
152+ }; // TODO: avoid panic
149153 config.configure(scopes);
150154 Some(Arc::new(config))
151155 }
helix-lsp/src/client.rs+49−41
@@ -9,11 +9,17 @@ use lsp_types as lsp;
99use serde_json::Value;
1010use std::future::Future;
1111use std::process::Stdio;
12−use std::sync::atomic::{AtomicU64, Ordering};
12+use std::sync::{
13+ atomic::{AtomicU64, Ordering},
14+ Arc,
15+};
1316use tokio::{
1417 io::{BufReader, BufWriter},
1518 process::{Child, Command},
16− sync::mpsc::{channel, UnboundedReceiver, UnboundedSender},
19+ sync::{
20+ mpsc::{channel, UnboundedReceiver, UnboundedSender},
21+ Notify, OnceCell,
22+ },
1723};
1824
1925#[derive(Debug)]
@@ -22,18 +28,19 @@ pub struct Client {
2228 _process: Child,
2329 server_tx: UnboundedSender<Payload>,
2430 request_counter: AtomicU64,
25− capabilities: Option<lsp::ServerCapabilities>,
31+ pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>,
2632 offset_encoding: OffsetEncoding,
2733 config: Option<Value>,
2834}
2935
3036impl Client {
37+ #[allow(clippy::type_complexity)]
3138 pub fn start(
3239 cmd: &str,
3340 args: &[String],
3441 config: Option<Value>,
3542 id: usize,
36− ) -> Result<(Self, UnboundedReceiver<(usize, Call)>)> {
43+ ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)> {
3744 let process = Command::new(cmd)
3845 .args(args)
3946 .stdin(Stdio::piped())
@@ -50,22 +57,20 @@ impl Client {
5057 let reader = BufReader::new(process.stdout.take().expect("Failed to open stdout"));
5158 let stderr = BufReader::new(process.stderr.take().expect("Failed to open stderr"));
5259
53− let (server_rx, server_tx) = Transport::start(reader, writer, stderr, id);
60+ let (server_rx, server_tx, initialize_notify) =
61+ Transport::start(reader, writer, stderr, id);
5462
5563 let client = Self {
5664 id,
5765 _process: process,
5866 server_tx,
5967 request_counter: AtomicU64::new(0),
60− capabilities: None,
68+ capabilities: OnceCell::new(),
6169 offset_encoding: OffsetEncoding::Utf8,
6270 config,
6371 };
6472
65− // TODO: async client.initialize()
66− // maybe use an arc<atomic> flag
67−
68− Ok((client, server_rx))
73+ Ok((client, server_rx, initialize_notify))
6974 }
7075
7176 pub fn id(&self) -> usize {
@@ -88,9 +93,13 @@ impl Client {
8893 }
8994 }
9095
96+ pub fn is_initialized(&self) -> bool {
97+ self.capabilities.get().is_some()
98+ }
99+
91100 pub fn capabilities(&self) -> &lsp::ServerCapabilities {
92101 self.capabilities
93− .as_ref()
102+ .get()
94103 .expect("language server not yet initialized!")
95104 }
96105
@@ -143,7 +152,8 @@ impl Client {
143152 })
144153 .map_err(|e| Error::Other(e.into()))?;
145154
146− timeout(Duration::from_secs(2), rx.recv())
155+ // TODO: specifiable timeout, delay other calls until initialize success
156+ timeout(Duration::from_secs(20), rx.recv())
147157 .await
148158 .map_err(|_| Error::Timeout)? // return Timeout
149159 .ok_or(Error::StreamClosed)?
@@ -151,7 +161,7 @@ impl Client {
151161 }
152162
153163 /// Send a RPC notification to the language server.
154− fn notify<R: lsp::notification::Notification>(
164+ pub fn notify<R: lsp::notification::Notification>(
155165 &self,
156166 params: R::Params,
157167 ) -> impl Future<Output = Result<()>>
@@ -213,7 +223,7 @@ impl Client {
213223 // General messages
214224 // -------------------------------------------------------------------------------------------
215225
216− pub(crate) async fn initialize(&mut self) -> Result<()> {
226+ pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> {
217227 // TODO: delay any requests that are triggered prior to initialize
218228 let root = find_root(None).and_then(|root| lsp::Url::from_file_path(root).ok());
219229
@@ -281,14 +291,7 @@ impl Client {
281291 locale: None, // TODO
282292 };
283293
284− let response = self.request::<lsp::request::Initialize>(params).await?;
285− self.capabilities = Some(response.capabilities);
286−
287− // next up, notify<initialized>
288− self.notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
289− .await?;
290−
291− Ok(())
294+ self.request::<lsp::request::Initialize>(params).await
292295 }
293296
294297 pub async fn shutdown(&self) -> Result<()> {
@@ -445,7 +448,7 @@ impl Client {
445448 ) -> Option<impl Future<Output = Result<()>>> {
446449 // figure out what kind of sync the server supports
447450
448− let capabilities = self.capabilities.as_ref().unwrap();
451+ let capabilities = self.capabilities.get().unwrap();
449452
450453 let sync_capabilities = match capabilities.text_document_sync {
451454 Some(lsp::TextDocumentSyncCapability::Kind(kind))
@@ -463,7 +466,7 @@ impl Client {
463466 // range = None -> whole document
464467 range: None, //Some(Range)
465468 range_length: None, // u64 apparently deprecated
466− text: "".to_string(),
469+ text: new_text.to_string(),
467470 }]
468471 }
469472 lsp::TextDocumentSyncKind::Incremental => {
@@ -491,12 +494,12 @@ impl Client {
491494
492495 // will_save / will_save_wait_until
493496
494− pub async fn text_document_did_save(
497+ pub fn text_document_did_save(
495498 &self,
496499 text_document: lsp::TextDocumentIdentifier,
497500 text: &Rope,
498− ) -> Result<()> {
499− let capabilities = self.capabilities.as_ref().unwrap();
501+ ) -> Option<impl Future<Output = Result<()>>> {
502+ let capabilities = self.capabilities.get().unwrap();
500503
501504 let include_text = match &capabilities.text_document_sync {
502505 Some(lsp::TextDocumentSyncCapability::Options(lsp::TextDocumentSyncOptions {
@@ -508,17 +511,18 @@ impl Client {
508511 include_text,
509512 }) => include_text.unwrap_or(false),
510513 // Supported(false)
511− _ => return Ok(()),
514+ _ => return None,
512515 },
513516 // unsupported
514− _ => return Ok(()),
517+ _ => return None,
515518 };
516519
517− self.notify::<lsp::notification::DidSaveTextDocument>(lsp::DidSaveTextDocumentParams {
518− text_document,
519− text: include_text.then(|| text.into()),
520− })
521− .await
520+ Some(self.notify::<lsp::notification::DidSaveTextDocument>(
521+ lsp::DidSaveTextDocumentParams {
522+ text_document,
523+ text: include_text.then(|| text.into()),
524+ },
525+ ))
522526 }
523527
524528 pub fn completion(
@@ -584,19 +588,19 @@ impl Client {
584588
585589 // formatting
586590
587− pub async fn text_document_formatting(
591+ pub fn text_document_formatting(
588592 &self,
589593 text_document: lsp::TextDocumentIdentifier,
590594 options: lsp::FormattingOptions,
591595 work_done_token: Option<lsp::ProgressToken>,
592− ) -> anyhow::Result<Vec<lsp::TextEdit>> {
593− let capabilities = self.capabilities.as_ref().unwrap();
596+ ) -> Option<impl Future<Output = Result<Vec<lsp::TextEdit>>>> {
597+ let capabilities = self.capabilities.get().unwrap();
594598
595599 // check if we're able to format
596600 match capabilities.document_formatting_provider {
597601 Some(lsp::OneOf::Left(true)) | Some(lsp::OneOf::Right(_)) => (),
598602 // None | Some(false)
599− _ => return Ok(Vec::new()),
603+ _ => return None,
600604 };
601605 // TODO: return err::unavailable so we can fall back to tree sitter formatting
602606
@@ -606,9 +610,13 @@ impl Client {
606610 work_done_progress_params: lsp::WorkDoneProgressParams { work_done_token },
607611 };
608612
609− let response = self.request::<lsp::request::Formatting>(params).await?;
613+ let request = self.call::<lsp::request::Formatting>(params);
610614
611− Ok(response.unwrap_or_default())
615+ Some(async move {
616+ let json = request.await?;
617+ let response: Vec<lsp::TextEdit> = serde_json::from_value(json)?;
618+ Ok(response)
619+ })
612620 }
613621
614622 pub async fn text_document_range_formatting(
@@ -618,7 +626,7 @@ impl Client {
618626 options: lsp::FormattingOptions,
619627 work_done_token: Option<lsp::ProgressToken>,
620628 ) -> anyhow::Result<Vec<lsp::TextEdit>> {
621− let capabilities = self.capabilities.as_ref().unwrap();
629+ let capabilities = self.capabilities.get().unwrap();
622630
623631 // check if we're able to format
624632 match capabilities.document_range_formatting_provider {
helix-lsp/src/lib.rs+49−53
@@ -226,6 +226,8 @@ impl MethodCall {
226226
227227#[derive(Debug, PartialEq, Clone)]
228228pub enum Notification {
229+ // we inject this notification to signal the LSP is ready
230+ Initialized,
229231 PublishDiagnostics(lsp::PublishDiagnosticsParams),
230232 ShowMessage(lsp::ShowMessageParams),
231233 LogMessage(lsp::LogMessageParams),
@@ -237,6 +239,7 @@ impl Notification {
237239 use lsp::notification::Notification as _;
238240
239241 let notification = match method {
242+ lsp::notification::Initialized::METHOD => Self::Initialized,
240243 lsp::notification::PublishDiagnostics::METHOD => {
241244 let params: lsp::PublishDiagnosticsParams = params
242245 .parse()
@@ -294,7 +297,7 @@ impl Registry {
294297 }
295298 }
296299
297− pub fn get_by_id(&mut self, id: usize) -> Option<&Client> {
300+ pub fn get_by_id(&self, id: usize) -> Option<&Client> {
298301 self.inner
299302 .values()
300303 .find(|(client_id, _)| client_id == &id)
@@ -302,33 +305,52 @@ impl Registry {
302305 }
303306
304307 pub fn get(&mut self, language_config: &LanguageConfiguration) -> Result<Arc<Client>> {
305− if let Some(config) = &language_config.language_server {
306− // avoid borrow issues
307− let inner = &mut self.inner;
308− let s_incoming = &mut self.incoming;
309−
310− match inner.entry(language_config.scope.clone()) {
311− Entry::Occupied(entry) => Ok(entry.get().1.clone()),
312− Entry::Vacant(entry) => {
313− // initialize a new client
314− let id = self.counter.fetch_add(1, Ordering::Relaxed);
315− let (mut client, incoming) = Client::start(
316− &config.command,
317− &config.args,
318− serde_json::from_str(language_config.config.as_deref().unwrap_or("")).ok(),
319− id,
320− )?;
321− // TODO: run this async without blocking
322− futures_executor::block_on(client.initialize())?;
323− s_incoming.push(UnboundedReceiverStream::new(incoming));
324− let client = Arc::new(client);
325−
326− entry.insert((id, client.clone()));
327− Ok(client)
328− }
308+ let config = match &language_config.language_server {
309+ Some(config) => config,
310+ None => return Err(Error::LspNotDefined),
311+ };
312+
313+ match self.inner.entry(language_config.scope.clone()) {
314+ Entry::Occupied(entry) => Ok(entry.get().1.clone()),
315+ Entry::Vacant(entry) => {
316+ // initialize a new client
317+ let id = self.counter.fetch_add(1, Ordering::Relaxed);
318+ let (client, incoming, initialize_notify) = Client::start(
319+ &config.command,
320+ &config.args,
321+ serde_json::from_str(language_config.config.as_deref().unwrap_or("")).ok(),
322+ id,
323+ )?;
324+ self.incoming.push(UnboundedReceiverStream::new(incoming));
325+ let client = Arc::new(client);
326+
327+ // Initialize the client asynchronously
328+ let _client = client.clone();
329+ tokio::spawn(async move {
330+ use futures_util::TryFutureExt;
331+ let value = _client
332+ .capabilities
333+ .get_or_try_init(|| {
334+ _client
335+ .initialize()
336+ .map_ok(|response| response.capabilities)
337+ })
338+ .await;
339+
340+ value.expect("failed to initialize capabilities");
341+
342+ // next up, notify<initialized>
343+ _client
344+ .notify::<lsp::notification::Initialized>(lsp::InitializedParams {})
345+ .await
346+ .unwrap();
347+
348+ initialize_notify.notify_one();
349+ });
350+
351+ entry.insert((id, client.clone()));
352+ Ok(client)
329353 }
330− } else {
331− Err(Error::LspNotDefined)
332354 }
333355 }
334356
@@ -415,32 +437,6 @@ impl LspProgressMap {
415437 }
416438}
417439
418−// REGISTRY = HashMap<LanguageId, Lazy/OnceCell<Arc<RwLock<Client>>>
419−// spawn one server per language type, need to spawn one per workspace if server doesn't support
420−// workspaces
421−//
422−// could also be a client per root dir
423−//
424−// storing a copy of Option<Arc<RwLock<Client>>> on Document would make the LSP client easily
425−// accessible during edit/save callbacks
426−//
427−// the event loop needs to process all incoming streams, maybe we can just have that be a separate
428−// task that's continually running and store the state on the client, then use read lock to
429−// retrieve data during render
430−// -> PROBLEM: how do you trigger an update on the editor side when data updates?
431−//
432−// -> The data updates should pull all events until we run out so we don't frequently re-render
433−//
434−//
435−// v2:
436−//
437−// there should be a registry of lsp clients, one per language type (or workspace).
438−// the clients should lazy init on first access
439−// the client.initialize() should be called async and we buffer any requests until that completes
440−// there needs to be a way to process incoming lsp messages from all clients.
441−// -> notifications need to be dispatched to wherever
442−// -> requests need to generate a reply and travel back to the same lsp!
443−
444440#[cfg(test)]
445441mod tests {
446442 use super::{lsp, util::*, OffsetEncoding};
helix-lsp/src/transport.rs+115−22
@@ -1,7 +1,7 @@
1−use crate::Result;
1+use crate::{Error, Result};
22use anyhow::Context;
33use jsonrpc_core as jsonrpc;
4−use log::{debug, error, info, warn};
4+use log::{error, info};
55use serde::{Deserialize, Serialize};
66use serde_json::Value;
77use std::collections::HashMap;
@@ -11,7 +11,7 @@ use tokio::{
1111 process::{ChildStderr, ChildStdin, ChildStdout},
1212 sync::{
1313 mpsc::{unbounded_channel, Sender, UnboundedReceiver, UnboundedSender},
14− Mutex,
14+ Mutex, Notify,
1515 },
1616};
1717
@@ -51,9 +51,11 @@ impl Transport {
5151 ) -> (
5252 UnboundedReceiver<(usize, jsonrpc::Call)>,
5353 UnboundedSender<Payload>,
54+ Arc<Notify>,
5455 ) {
5556 let (client_tx, rx) = unbounded_channel();
5657 let (tx, client_rx) = unbounded_channel();
58+ let notify = Arc::new(Notify::new());
5759
5860 let transport = Self {
5961 id,
@@ -62,11 +64,21 @@ impl Transport {
6264
6365 let transport = Arc::new(transport);
6466
65− tokio::spawn(Self::recv(transport.clone(), server_stdout, client_tx));
67+ tokio::spawn(Self::recv(
68+ transport.clone(),
69+ server_stdout,
70+ client_tx.clone(),
71+ ));
6672 tokio::spawn(Self::err(transport.clone(), server_stderr));
67− tokio::spawn(Self::send(transport, server_stdin, client_rx));
68−
69− (rx, tx)
73+ tokio::spawn(Self::send(
74+ transport,
75+ server_stdin,
76+ client_tx,
77+ client_rx,
78+ notify.clone(),
79+ ));
80+
81+ (rx, tx, notify)
7082 }
7183
7284 async fn recv_server_message(
@@ -76,14 +88,18 @@ impl Transport {
7688 let mut content_length = None;
7789 loop {
7890 buffer.truncate(0);
79− reader.read_line(buffer).await?;
80− let header = buffer.trim();
91+ if reader.read_line(buffer).await? == 0 {
92+ return Err(Error::StreamClosed);
93+ };
94+
95+ // debug!("<- header {:?}", buffer);
8196
82− if header.is_empty() {
97+ if buffer == "\r\n" {
98+ // look for an empty CRLF line
8399 break;
84100 }
85101
86− debug!("<- header {}", header);
102+ let header = buffer.trim();
87103
88104 let parts = header.split_once(": ");
89105
@@ -96,7 +112,8 @@ impl Transport {
96112 // Workaround: Some non-conformant language servers will output logging and other garbage
97113 // into the same stream as JSON-RPC messages. This can also happen from shell scripts that spawn
98114 // the server. Skip such lines and log a warning.
99− warn!("Failed to parse header: {:?}", header);
115+
116+ // warn!("Failed to parse header: {:?}", header);
100117 }
101118 }
102119 }
@@ -121,8 +138,10 @@ impl Transport {
121138 buffer: &mut String,
122139 ) -> Result<()> {
123140 buffer.truncate(0);
124− err.read_line(buffer).await?;
125− error!("err <- {}", buffer);
141+ if err.read_line(buffer).await? == 0 {
142+ return Err(Error::StreamClosed);
143+ };
144+ error!("err <- {:?}", buffer);
126145
127146 Ok(())
128147 }
@@ -255,16 +274,90 @@ impl Transport {
255274 async fn send(
256275 transport: Arc<Self>,
257276 mut server_stdin: BufWriter<ChildStdin>,
277+ client_tx: UnboundedSender<(usize, jsonrpc::Call)>,
258278 mut client_rx: UnboundedReceiver<Payload>,
279+ initialize_notify: Arc<Notify>,
259280 ) {
260− while let Some(msg) = client_rx.recv().await {
261− match transport
262− .send_payload_to_server(&mut server_stdin, msg)
263− .await
264− {
265− Ok(_) => {}
266− Err(err) => {
267− error!("err: <- {:?}", err);
281+ let mut pending_messages: Vec<Payload> = Vec::new();
282+ let mut is_pending = true;
283+
284+ // Determine if a message is allowed to be sent early
285+ fn is_initialize(payload: &Payload) -> bool {
286+ use lsp_types::{
287+ notification::{Initialized, Notification},
288+ request::{Initialize, Request},
289+ };
290+ match payload {
291+ Payload::Request {
292+ value: jsonrpc::MethodCall { method, .. },
293+ ..
294+ } if method == Initialize::METHOD => true,
295+ Payload::Notification(jsonrpc::Notification { method, .. })
296+ if method == Initialized::METHOD =>
297+ {
298+ true
299+ }
300+ _ => false,
301+ }
302+ }
303+
304+ // TODO: events that use capabilities need to do the right thing
305+
306+ loop {
307+ tokio::select! {
308+ biased;
309+ _ = initialize_notify.notified() => { // TODO: notified is technically not cancellation safe
310+ // server successfully initialized
311+ is_pending = false;
312+
313+ use lsp_types::notification::Notification;
314+ // Hack: inject an initialized notification so we trigger code that needs to happen after init
315+ let notification = ServerMessage::Call(jsonrpc::Call::Notification(jsonrpc::Notification {
316+ jsonrpc: None,
317+
318+ method: lsp_types::notification::Initialized::METHOD.to_string(),
319+ params: jsonrpc::Params::None,
320+ }));
321+ match transport.process_server_message(&client_tx, notification).await {
322+ Ok(_) => {}
323+ Err(err) => {
324+ error!("err: <- {:?}", err);
325+ }
326+ }
327+
328+ // drain the pending queue and send payloads to server
329+ for msg in pending_messages.drain(..) {
330+ log::info!("Draining pending message {:?}", msg);
331+ match transport.send_payload_to_server(&mut server_stdin, msg).await {
332+ Ok(_) => {}
333+ Err(err) => {
334+ error!("err: <- {:?}", err);
335+ }
336+ }
337+ }
338+ }
339+ msg = client_rx.recv() => {
340+ if let Some(msg) = msg {
341+ if is_pending && !is_initialize(&msg) {
342+ // ignore notifications
343+ if let Payload::Notification(_) = msg {
344+ continue;
345+ }
346+
347+ log::info!("Language server not initialized, delaying request");
348+ pending_messages.push(msg);
349+ } else {
350+ match transport.send_payload_to_server(&mut server_stdin, msg).await {
351+ Ok(_) => {}
352+ Err(err) => {
353+ error!("err: <- {:?}", err);
354+ }
355+ }
356+ }
357+ } else {
358+ // channel closed
359+ break;
360+ }
268361 }
269362 }
270363 }
helix-syntax/build.rs+1−2
@@ -158,10 +158,9 @@ fn build_dir(dir: &str, language: &str) {
158158 .is_none()
159159 {
160160 eprintln!(
161− "The directory {} is empty, did you use 'git clone --recursive'?",
161+ "The directory {} is empty, you probably need to use 'git submodule update --init --recursive'?",
162162 dir
163163 );
164− eprintln!("You can fix in using 'git submodule init && git submodule update --recursive'.");
165164 std::process::exit(1);
166165 }
167166
helix-syntax/languages/tree-sitter-julia+1−1

Submodule changed.

helix-term/src/application.rs+53−54
@@ -4,7 +4,7 @@ use helix_view::{theme, Editor};
44
55use crate::{args::Args, compositor::Compositor, config::Config, job::Jobs, ui};
66
7−use log::error;
7+use log::{error, warn};
88
99use std::{
1010 io::{stdout, Write},
@@ -275,16 +275,42 @@ impl Application {
275275 };
276276
277277 match notification {
278− Notification::PublishDiagnostics(params) => {
279− let path = Some(params.uri.to_file_path().unwrap());
278+ Notification::Initialized => {
279+ let language_server =
280+ match self.editor.language_servers.get_by_id(server_id) {
281+ Some(language_server) => language_server,
282+ None => {
283+ warn!("can't find language server with id `{}`", server_id);
284+ return;
285+ }
286+ };
287+
288+ let docs = self.editor.documents().filter(|doc| {
289+ doc.language_server().map(|server| server.id()) == Some(server_id)
290+ });
280291
281− let doc = self
282− .editor
283− .documents
284− .iter_mut()
285− .find(|(_, doc)| doc.path() == path.as_ref());
292+ // trigger textDocument/didOpen for docs that are already open
293+ for doc in docs {
294+ // TODO: extract and share with editor.open
295+ let language_id = doc
296+ .language()
297+ .and_then(|s| s.split('.').last()) // source.rust
298+ .map(ToOwned::to_owned)
299+ .unwrap_or_default();
300+
301+ tokio::spawn(language_server.text_document_did_open(
302+ doc.url().unwrap(),
303+ doc.version(),
304+ doc.text(),
305+ language_id,
306+ ));
307+ }
308+ }
309+ Notification::PublishDiagnostics(params) => {
310+ let path = params.uri.to_file_path().unwrap();
311+ let doc = self.editor.document_by_path_mut(&path);
286312
287− if let Some((_, doc)) = doc {
313+ if let Some(doc) = doc {
288314 let text = doc.text();
289315
290316 let diagnostics = params
@@ -429,10 +455,27 @@ impl Application {
429455 Call::MethodCall(helix_lsp::jsonrpc::MethodCall {
430456 method, params, id, ..
431457 }) => {
458+ let language_server = match self.editor.language_servers.get_by_id(server_id) {
459+ Some(language_server) => language_server,
460+ None => {
461+ warn!("can't find language server with id `{}`", server_id);
462+ return;
463+ }
464+ };
465+
432466 let call = match MethodCall::parse(&method, params) {
433467 Some(call) => call,
434468 None => {
435469 error!("Method not found {}", method);
470+ // language_server.reply(
471+ // call.id,
472+ // // TODO: make a Into trait that can cast to Err(jsonrpc::Error)
473+ // Err(helix_lsp::jsonrpc::Error {
474+ // code: helix_lsp::jsonrpc::ErrorCode::MethodNotFound,
475+ // message: "Method not found".to_string(),
476+ // data: None,
477+ // }),
478+ // );
436479 return;
437480 }
438481 };
@@ -445,53 +488,9 @@ impl Application {
445488 if spinner.is_stopped() {
446489 spinner.start();
447490 }
448−
449− let doc = self.editor.documents().find(|doc| {
450− doc.language_server()
451− .map(|server| server.id() == server_id)
452− .unwrap_or_default()
453− });
454− match doc {
455− Some(doc) => {
456− // it's ok to unwrap, we check for the language server before
457− let server = doc.language_server().unwrap();
458− tokio::spawn(server.reply(id, Ok(serde_json::Value::Null)));
459− }
460− None => {
461− if let Some(server) =
462− self.editor.language_servers.get_by_id(server_id)
463− {
464− log::warn!(
465− "missing document with language server id `{}`",
466− server_id
467− );
468− tokio::spawn(server.reply(
469− id,
470− Err(helix_lsp::jsonrpc::Error {
471− code: helix_lsp::jsonrpc::ErrorCode::InternalError,
472− message: "document missing".to_string(),
473− data: None,
474− }),
475− ));
476− } else {
477− log::warn!(
478− "can't find language server with id `{}`",
479− server_id
480− );
481− }
482− }
483− }
491+ tokio::spawn(language_server.reply(id, Ok(serde_json::Value::Null)));
484492 }
485493 }
486− // self.language_server.reply(
487− // call.id,
488− // // TODO: make a Into trait that can cast to Err(jsonrpc::Error)
489− // Err(helix_lsp::jsonrpc::Error {
490− // code: helix_lsp::jsonrpc::ErrorCode::MethodNotFound,
491− // message: "Method not found".to_string(),
492− // data: None,
493− // }),
494− // );
495494 }
496495 e => unreachable!("{:?}", e),
497496 }
helix-term/src/job.rs+2−2
@@ -61,7 +61,7 @@ impl Jobs {
6161 }
6262
6363 pub fn handle_callback(
64− &mut self,
64+ &self,
6565 editor: &mut Editor,
6666 compositor: &mut Compositor,
6767 call: anyhow::Result<Option<Callback>>,
@@ -84,7 +84,7 @@ impl Jobs {
8484 }
8585 }
8686
87− pub fn add(&mut self, j: Job) {
87+ pub fn add(&self, j: Job) {
8888 if j.wait {
8989 self.wait_futures.push(j.future);
9090 } else {
helix-view/src/document.rs+33−16
@@ -386,21 +386,24 @@ impl Document {
386386 /// If supported, returns the changes that should be applied to this document in order
387387 /// to format it nicely.
388388 pub fn format(&self) -> Option<impl Future<Output = LspFormatting> + 'static> {
389− if let Some(language_server) = self.language_server.clone() {
389+ if let Some(language_server) = self.language_server() {
390390 let text = self.text.clone();
391− let id = self.identifier();
391+ let offset_encoding = language_server.offset_encoding();
392+ let request = language_server.text_document_formatting(
393+ self.identifier(),
394+ lsp::FormattingOptions::default(),
395+ None,
396+ )?;
397+
392398 let fut = async move {
393− let edits = language_server
394− .text_document_formatting(id, lsp::FormattingOptions::default(), None)
395− .await
396− .unwrap_or_else(|e| {
397− log::warn!("LSP formatting failed: {}", e);
398− Default::default()
399− });
399+ let edits = request.await.unwrap_or_else(|e| {
400+ log::warn!("LSP formatting failed: {}", e);
401+ Default::default()
402+ });
400403 LspFormatting {
401404 doc: text,
402405 edits,
403− offset_encoding: language_server.offset_encoding(),
406+ offset_encoding,
404407 }
405408 };
406409 Some(fut)
@@ -469,9 +472,14 @@ impl Document {
469472 to_writer(&mut file, encoding, &text).await?;
470473
471474 if let Some(language_server) = language_server {
472− language_server
473− .text_document_did_save(identifier, &text)
474− .await?;
475+ if language_server.is_initialized() {
476+ return Ok(());
477+ }
478+ if let Some(notification) =
479+ language_server.text_document_did_save(identifier, &text)
480+ {
481+ notification.await?;
482+ }
475483 }
476484
477485 Ok(())
@@ -646,7 +654,7 @@ impl Document {
646654 // }
647655
648656 // emit lsp notification
649− if let Some(language_server) = &self.language_server {
657+ if let Some(language_server) = self.language_server() {
650658 let notify = language_server.text_document_did_change(
651659 self.versioned_identifier(),
652660 &old_doc,
@@ -795,9 +803,18 @@ impl Document {
795803 self.version
796804 }
797805
798− #[inline]
799806 pub fn language_server(&self) -> Option<&helix_lsp::Client> {
800− self.language_server.as_deref()
807+ let server = self.language_server.as_deref();
808+ let initialized = server
809+ .map(|server| server.is_initialized())
810+ .unwrap_or(false);
811+
812+ // only resolve language_server if it's initialized
813+ if initialized {
814+ server
815+ } else {
816+ None
817+ }
801818 }
802819
803820 #[inline]
helix-view/src/editor.rs+15−10
@@ -255,20 +255,21 @@ impl Editor {
255255 .and_then(|language| self.language_servers.get(language).ok());
256256
257257 if let Some(language_server) = language_server {
258− doc.set_language_server(Some(language_server.clone()));
259−
260258 let language_id = doc
261259 .language()
262260 .and_then(|s| s.split('.').last()) // source.rust
263261 .map(ToOwned::to_owned)
264262 .unwrap_or_default();
265263
264+ // TODO: this now races with on_init code if the init happens too quickly
266265 tokio::spawn(language_server.text_document_did_open(
267266 doc.url().unwrap(),
268267 doc.version(),
269268 doc.text(),
270269 language_id,
271270 ));
271+
272+ doc.set_language_server(Some(language_server));
272273 }
273274
274275 let id = self.documents.insert(doc);
@@ -287,14 +288,9 @@ impl Editor {
287288
288289 if close_buffer {
289290 // get around borrowck issues
290− let language_servers = &mut self.language_servers;
291291 let doc = &self.documents[view.doc];
292292
293− let language_server = doc
294− .language
295− .as_ref()
296− .and_then(|language| language_servers.get(language).ok());
297− if let Some(language_server) = language_server {
293+ if let Some(language_server) = doc.language_server() {
298294 tokio::spawn(language_server.text_document_did_close(doc.identifier()));
299295 }
300296 self.documents.remove(view.doc);
@@ -324,20 +320,24 @@ impl Editor {
324320 view.ensure_cursor_in_view(doc, self.config.scrolloff)
325321 }
326322
323+ #[inline]
327324 pub fn document(&self, id: DocumentId) -> Option<&Document> {
328325 self.documents.get(id)
329326 }
330327
328+ #[inline]
331329 pub fn document_mut(&mut self, id: DocumentId) -> Option<&mut Document> {
332330 self.documents.get_mut(id)
333331 }
334332
333+ #[inline]
335334 pub fn documents(&self) -> impl Iterator<Item = &Document> {
336− self.documents.iter().map(|(_id, doc)| doc)
335+ self.documents.values()
337336 }
338337
338+ #[inline]
339339 pub fn documents_mut(&mut self) -> impl Iterator<Item = &mut Document> {
340− self.documents.iter_mut().map(|(_id, doc)| doc)
340+ self.documents.values_mut()
341341 }
342342
343343 pub fn document_by_path<P: AsRef<Path>>(&self, path: P) -> Option<&Document> {
@@ -345,6 +345,11 @@ impl Editor {
345345 .find(|doc| doc.path().map(|p| p == path.as_ref()).unwrap_or(false))
346346 }
347347
348+ pub fn document_by_path_mut<P: AsRef<Path>>(&mut self, path: P) -> Option<&mut Document> {
349+ self.documents_mut()
350+ .find(|doc| doc.path().map(|p| p == path.as_ref()).unwrap_or(false))
351+ }
352+
348353 pub fn cursor(&self) -> (Option<Position>, CursorKind) {
349354 let view = view!(self);
350355 let doc = &self.documents[view.doc];
languages.toml+27−1
@@ -116,6 +116,17 @@ roots = []
116116language-server = { command = "typescript-language-server", args = ["--stdio"] }
117117indent = { tab-width = 2, unit = " " }
118118
119+[[language]]
120+name = "tsx"
121+scope = "source.tsx"
122+injection-regex = "^(tsx)$" # |typescript
123+file-types = ["tsx"]
124+roots = []
125+# TODO: highlights-jsx, highlights-params
126+
127+language-server = { command = "typescript-language-server", args = ["--stdio"] }
128+indent = { tab-width = 2, unit = " " }
129+
119130[[language]]
120131name = "css"
121132scope = "source.css"
@@ -204,7 +215,22 @@ injection-regex = "julia"
204215file-types = ["jl"]
205216roots = []
206217comment-token = "#"
207−language-server = { command = "julia", args = [ "--startup-file=no", "--history-file=no", "-e", "using LanguageServer;using Pkg;import StaticLint;import SymbolServer;env_path = dirname(Pkg.Types.Context().env.project_file);server = LanguageServer.LanguageServerInstance(stdin, stdout, env_path, \"\");server.runlinter = true;run(server);" ] }
218+language-server = { command = "julia", args = [
219+ "--startup-file=no",
220+ "--history-file=no",
221+ "--quiet",
222+ "-e",
223+ """
224+ using LanguageServer;
225+ using Pkg;
226+ import StaticLint;
227+ env_path = dirname(Pkg.Types.Context().env.project_file);
228+
229+ server = LanguageServer.LanguageServerInstance(stdin, stdout, env_path, "");
230+ server.runlinter = true;
231+ run(server);
232+ """,
233+ ] }
208234indent = { tab-width = 2, unit = " " }
209235
210236[[language]]
runtime/queries/c/highlights.scm+1−1
@@ -61,7 +61,7 @@
6161
6262(null) @constant
6363(number_literal) @number
64−(char_literal) @number
64+(char_literal) @string
6565
6666(call_expression
6767 function: (identifier) @function)
runtime/queries/go/highlights.scm+35−4
@@ -17,9 +17,18 @@
1717
1818; Identifiers
1919
20+((identifier) @constant (match? @constant "^[A-Z][A-Z\\d_]+$"))
21+(const_spec
22+ name: (identifier) @constant)
23+
24+(parameter_declaration (identifier) @variable.parameter)
25+(variadic_parameter_declaration (identifier) @variable.parameter)
26+
2027(type_identifier) @type
2128(field_identifier) @property
2229(identifier) @variable
30+(package_identifier) @variable
31+
2332
2433; Operators
2534
@@ -79,10 +88,8 @@
7988 "go"
8089 "goto"
8190 "if"
82− "import"
8391 "interface"
8492 "map"
85− "package"
8693 "range"
8794 "return"
8895 "select"
@@ -92,6 +99,29 @@
9299 "var"
93100] @keyword
94101
102+[
103+ "import"
104+ "package"
105+] @keyword.control.import
106+
107+; Delimiters
108+
109+[
110+ ":"
111+ "."
112+ ","
113+ ";"
114+] @punctuation.delimiter
115+
116+[
117+ "("
118+ ")"
119+ "["
120+ "]"
121+ "{"
122+ "}"
123+] @punctuation.bracket
124+
95125; Literals
96126
97127[
@@ -111,7 +141,8 @@
111141[
112142 (true)
113143 (false)
114− (nil)
115−] @constant.builtin
144+] @constant.builtin.boolean
145+
146+(nil) @constant.builtin
116147
117148(comment) @comment
runtime/queries/go/locals.scmnew file+30
@@ -0,0 +1,30 @@
1+; Scopes
2+
3+(block) @local.scope
4+
5+; Definitions
6+
7+(parameter_declaration (identifier) @local.definition)
8+(variadic_parameter_declaration (identifier) @local.definition)
9+
10+(short_var_declaration
11+ left: (expression_list
12+ (identifier) @local.definition))
13+
14+(var_spec
15+ name: (identifier) @local.definition)
16+
17+(for_statement
18+ (range_clause
19+ left: (expression_list
20+ (identifier) @local.definition)))
21+
22+(const_declaration
23+ (const_spec
24+ name: (identifier) @local.definition))
25+
26+; References
27+
28+(identifier) @local.reference
29+(field_identifier) @local.reference
30+
runtime/queries/haskell/highlights.scm+8−6
@@ -2,19 +2,19 @@
22(operator) @operator
33(exp_name (constructor) @constructor)
44(constructor_operator) @operator
5−(module) @module_name
5+(module) @namespace
66(type) @type
77(type) @class
88(constructor) @constructor
99(pragma) @pragma
1010(comment) @comment
1111(signature name: (variable) @fun_type_name)
12−(function name: (variable) @fun_name)
12+(function name: (variable) @function)
1313(constraint class: (class_name (type)) @class)
1414(class (class_head class: (class_name (type)) @class))
1515(instance (instance_head class: (class_name (type)) @class))
16−(integer) @literal
17−(exp_literal (float)) @literal
16+(integer) @number
17+(exp_literal (float)) @number
1818(char) @literal
1919(con_unit) @literal
2020(con_list) @literal
@@ -39,5 +39,7 @@
3939"do" @keyword
4040"mdo" @keyword
4141"rec" @keyword
42−"(" @paren
43−")" @paren
42+[
43+ "("
44+ ")"
45+] @punctuation.bracket
runtime/queries/javascript/highlights.scm+1−1
@@ -87,7 +87,7 @@
8787 (template_string)
8888] @string
8989
90−(regex) @string.special
90+(regex) @string.regexp
9191(number) @number
9292
9393; Tokens
runtime/queries/julia/highlights.scm+43−39
@@ -1,9 +1,3 @@
1−(identifier) @variable
2−;; In case you want type highlighting based on Julia naming conventions (this might collide with mathematical notation)
3−;((identifier) @type ; exception: mark `A_foo` sort of identifiers as variables
4− ;(match? @type "^[A-Z][^_]"))
5−((identifier) @constant
6− (match? @constant "^[A-Z][A-Z_]{2}[A-Z_]*$"))
71
82[
93 (triple_string)
@@ -28,43 +22,43 @@
2822(call_expression
2923 (identifier) @function)
3024(call_expression
31− (field_expression (identifier) @method .))
25+ (field_expression (identifier) @function.method .))
3226(broadcast_call_expression
3327 (identifier) @function)
3428(broadcast_call_expression
35− (field_expression (identifier) @method .))
29+ (field_expression (identifier) @function.method .))
3630(parameter_list
37− (identifier) @parameter)
31+ (identifier) @variable.parameter)
3832(parameter_list
3933 (optional_parameter .
40− (identifier) @parameter))
34+ (identifier) @variable.parameter))
4135(typed_parameter
42− (identifier) @parameter
36+ (identifier) @variable.parameter
4337 (identifier) @type)
4438(type_parameter_list
4539 (identifier) @type)
4640(typed_parameter
47− (identifier) @parameter
41+ (identifier) @variable.parameter
4842 (parameterized_identifier) @type)
4943(function_expression
50− . (identifier) @parameter)
51−(spread_parameter) @parameter
44+ . (identifier) @variable.parameter)
45+(spread_parameter) @variable.parameter
5246(spread_parameter
53− (identifier) @parameter)
47+ (identifier) @variable.parameter)
5448(named_argument
55− . (identifier) @parameter)
49+ . (identifier) @variable.parameter)
5650(argument_list
5751 (typed_expression
58− (identifier) @parameter
52+ (identifier) @variable.parameter
5953 (identifier) @type))
6054(argument_list
6155 (typed_expression
62− (identifier) @parameter
56+ (identifier) @variable.parameter
6357 (parameterized_identifier) @type))
6458
6559;; Symbol expressions (:my-wanna-be-lisp-keyword)
6660(quote_expression
67− (identifier)) @symbol
61+ (identifier)) @string.special.symbol
6862
6963;; Parsing error! foo (::Type) get's parsed as two quote expressions
7064(argument_list
@@ -76,7 +70,7 @@
7670 (identifier) @type)
7771(parameterized_identifier (_)) @type
7872(argument_list
79− (typed_expression . (identifier) @parameter))
73+ (typed_expression . (identifier) @variable.parameter))
8074
8175(typed_expression
8276 (identifier) @type .)
@@ -113,13 +107,13 @@
113107"end" @keyword
114108
115109(if_statement
116− ["if" "end"] @conditional)
110+ ["if" "end"] @keyword.control.conditional)
117111(elseif_clause
118− ["elseif"] @conditional)
112+ ["elseif"] @keyword.control.conditional)
119113(else_clause
120− ["else"] @conditional)
114+ ["else"] @keyword.control.conditional)
121115(ternary_expression
122− ["?" ":"] @conditional)
116+ ["?" ":"] @keyword.control.conditional)
123117
124118(function_definition ["function" "end"] @keyword.function)
125119
@@ -134,47 +128,57 @@
134128 "type"
135129] @keyword
136130
137−((identifier) @keyword (#any-of? @keyword "global" "local"))
131+((identifier) @keyword (match? @keyword "global|local"))
138132
139133(compound_expression
140134 ["begin" "end"] @keyword)
141135(try_statement
142− ["try" "end" ] @exception)
136+ ["try" "end" ] @keyword.control.exception)
143137(finally_clause
144− "finally" @exception)
138+ "finally" @keyword.control.exception)
145139(catch_clause
146− "catch" @exception)
140+ "catch" @keyword.control.exception)
147141(quote_statement
148142 ["quote" "end"] @keyword)
149143(let_statement
150144 ["let" "end"] @keyword)
151145(for_statement
152− ["for" "end"] @repeat)
146+ ["for" "end"] @keyword.control.repeat)
153147(while_statement
154− ["while" "end"] @repeat)
155−(break_statement) @repeat
156−(continue_statement) @repeat
148+ ["while" "end"] @keyword.control.repeat)
149+(break_statement) @keyword.control.repeat
150+(continue_statement) @keyword.control.repeat
157151(for_binding
158− "in" @repeat)
152+ "in" @keyword.control.repeat)
159153(for_clause
160− "for" @repeat)
154+ "for" @keyword.control.repeat)
161155(do_clause
162156 ["do" "end"] @keyword)
163157
164158(export_statement
165− ["export"] @include)
159+ ["export"] @keyword.control.import)
166160
167161[
168162 "using"
169163 "module"
170164 "import"
171−] @include
165+] @keyword.control.import
172166
173−((identifier) @include (#eq? @include "baremodule"))
167+((identifier) @keyword.control.import (#eq? @keyword.control.import "baremodule"))
174168
175169(((identifier) @constant.builtin) (match? @constant.builtin "^(nothing|Inf|NaN)$"))
176−(((identifier) @boolean) (eq? @boolean "true"))
177−(((identifier) @boolean) (eq? @boolean "false"))
170+(((identifier) @constant.builtin.boolean) (#eq? @constant.builtin.boolean "true"))
171+(((identifier) @constant.builtin.boolean) (#eq? @constant.builtin.boolean "false"))
172+
178173
179174["::" ":" "." "," "..." "!"] @punctuation.delimiter
180175["[" "]" "(" ")" "{" "}"] @punctuation.bracket
176+
177+["="] @operator
178+
179+(identifier) @variable
180+;; In case you want type highlighting based on Julia naming conventions (this might collide with mathematical notation)
181+;((identifier) @type ; exception: mark `A_foo` sort of identifiers as variables
182+ ;(match? @type "^[A-Z][^_]"))
183+((identifier) @constant
184+ (match? @constant "^[A-Z][A-Z_]{2}[A-Z_]*$"))
runtime/queries/latex/highlights.scm+2−2
@@ -259,7 +259,7 @@
259259
260260(comment) @comment
261261
262−(bracket_group) @parameter
262+(bracket_group) @variable.parameter
263263
264264[(math_operator) "="] @operator
265265
@@ -312,7 +312,7 @@
312312 key: (word) @text.reference)
313313
314314(key_val_pair
315− key: (_) @parameter
315+ key: (_) @variable.parameter
316316 value: (_))
317317
318318["[" "]" "{" "}"] @punctuation.bracket ;"(" ")" is has no special meaning in LaTeX
runtime/queries/lua/highlights.scm+9−9
@@ -23,27 +23,27 @@
2323 "for"
2424 "do"
2525 "end"
26−] @keyword.control.loop)
26+] @keyword.control.repeat)
2727
2828(for_in_statement
2929[
3030 "for"
3131 "do"
3232 "end"
33−] @keyword.control.loop)
33+] @keyword.control.repeat)
3434
3535(while_statement
3636[
3737 "while"
3838 "do"
3939 "end"
40−] @keyword.control.loop)
40+] @keyword.control.repeat)
4141
4242(repeat_statement
4343[
4444 "repeat"
4545 "until"
46−] @keyword.control.loop)
46+] @keyword.control.repeat)
4747
4848(do_statement
4949[
@@ -65,7 +65,7 @@
6565 "not"
6666 "and"
6767 "or"
68−] @keyword.operator
68+] @operator
6969
7070[
7171"="
@@ -108,7 +108,7 @@
108108[
109109(false)
110110(true)
111−] @boolean
111+] @constant.builtin.boolean
112112(nil) @constant.builtin
113113(spread) @constant ;; "..."
114114((identifier) @constant
@@ -116,7 +116,7 @@
116116
117117;; Parameters
118118(parameters
119− (identifier) @parameter)
119+ (identifier) @variable.parameter)
120120
121121; ;; Functions
122122(function [(function_name) (identifier)] @function)
@@ -139,8 +139,8 @@
139139
140140(function_call
141141 [
142− ((identifier) @variable (method) @method)
143− ((_) (method) @method)
142+ ((identifier) @variable (method) @function.method)
143+ ((_) (method) @function.method)
144144 (identifier) @function
145145 (field_expression (property_identifier) @function)
146146 ]
runtime/queries/ocaml/highlights.scm+4−4
@@ -25,12 +25,12 @@
2525
2626(external (value_name) @function)
2727
28−(method_name) @method
28+(method_name) @function.method
2929
3030; Variables
3131;----------
3232
33−(value_pattern) @parameter
33+(value_pattern) @variable.parameter
3434
3535; Application
3636;------------
@@ -60,7 +60,7 @@
6060
6161[(number) (signed_number)] @number
6262
63−(character) @character
63+(character) @constant.character
6464
6565(string) @string
6666
@@ -92,7 +92,7 @@
9292
9393["include" "open"] @include
9494
95−["for" "to" "downto" "while" "do" "done"] @keyword.control.loop
95+["for" "to" "downto" "while" "do" "done"] @keyword.control.repeat
9696
9797; Macros
9898;-------
runtime/queries/ruby/highlights.scm+1−1
@@ -100,7 +100,7 @@
100100 (bare_symbol)
101101] @string.special.symbol
102102
103−(regex) @string.special.regex
103+(regex) @string.regexp
104104(escape_sequence) @escape
105105
106106[
runtime/queries/rust/highlights.scm+2−2
@@ -17,7 +17,7 @@
1717
1818(escape_sequence) @escape
1919(primitive_type) @type.builtin
20−(boolean_literal) @constant.builtin
20+(boolean_literal) @constant.builtin.boolean
2121[
2222 (integer_literal)
2323 (float_literal)
@@ -149,7 +149,7 @@
149149
150150(mutable_specifier) @keyword.mut
151151
152−
152+; TODO: variable.mut to highlight mutable identifiers via locals.scm
153153
154154; -------
155155; Guess Other Types
runtime/queries/rust/locals.scmnew file+17
@@ -0,0 +1,17 @@
1+; Scopes
2+
3+(block) @local.scope
4+
5+; Definitions
6+
7+(parameter
8+ (identifier) @local.definition)
9+
10+(let_declaration
11+ pattern: (identifier) @local.definition)
12+
13+(closure_parameters (identifier)) @local.definition
14+
15+; References
16+(identifier) @local.reference
17+
runtime/queries/tsx/highlights.scmnew file+1
@@ -0,0 +1 @@
1+; inherits: typescript
runtime/queries/yaml/highlights.scm+1−1
@@ -1,6 +1,6 @@
11(block_mapping_pair key: (_) @property)
22(flow_mapping (_ key: (_) @property))
3−(boolean_scalar) @boolean
3+(boolean_scalar) @constant.builtin.boolean
44(null_scalar) @constant.builtin
55(double_quote_scalar) @string
66(single_quote_scalar) @string
runtime/themes/dark_plus.toml+1
@@ -34,6 +34,7 @@
3434"comment" = { fg = "#6A9955" }
3535
3636"string" = { fg = "#ce9178" }
37+"string.regexp" = { fg = "regex" }
3738"number" = { fg = "#b5cea8" }
3839"escape" = { fg = "#d7ba7d" }
3940
runtime/themes/monokai.toml+1
@@ -34,6 +34,7 @@
3434"comment" = { fg = "#88846F" }
3535
3636"string" = { fg = "#e6db74" }
37+"string.regexp" = { fg = "regex" }
3738"number" = { fg = "#ae81ff" }
3839"escape" = { fg = "#ae81ff" }
3940
theme.toml+2−4
@@ -9,7 +9,7 @@ special = "honey"
99property = "white"
1010variable = "lavender"
1111# variable = "almond" # TODO: metavariables only
12−"variable.parameter" = "lavender"
12+"variable.parameter" = { fg = "lavender", modifiers = ["underlined"] }
1313"variable.builtin" = "mint"
1414type = "white"
1515"type.builtin" = "white" # TODO: distinguish?
@@ -28,9 +28,7 @@ escape = "honey"
2828label = "honey"
2929
3030# TODO: diferentiate doc comment
31−# concat (ERROR) @syntax-error and "MISSING ;" selectors for errors
32−
33−module = "#ff0000"
31+# concat (ERROR) @error.syntax and "MISSING ;" selectors for errors
3432
3533"ui.background" = { bg = "midnight" }
3634"ui.linenr" = { fg = "comet" }