on master help sign in
Merge branch 'master' into cursor-shape-new
4496249Gokul Soumya
.github/workflows/release.yml+1−1
@@ -102,7 +102,7 @@ jobs:
102 fi
102 fi
103 cp -r runtime dist
103 cp -r runtime dist
104
104
105− - uses: actions/upload-artifact@v2.3.0
105+ - uses: actions/upload-artifact@v2.3.1
106 with:
106 with:
107 name: bins-${{ matrix.build }}
107 name: bins-${{ matrix.build }}
108 path: dist
108 path: dist
.gitmodules+37−1
@@ -142,11 +142,15 @@
142 path = helix-syntax/languages/tree-sitter-perl
142 path = helix-syntax/languages/tree-sitter-perl
143 url = https://github.com/ganezdragon/tree-sitter-perl
143 url = https://github.com/ganezdragon/tree-sitter-perl
144 shallow = true
144 shallow = true
145+[submodule "helix-syntax/languages/tree-sitter-comment"]
146+ path = helix-syntax/languages/tree-sitter-comment
147+ url = https://github.com/stsewd/tree-sitter-comment
148+ shallow = true
145[submodule "helix-syntax/languages/tree-sitter-wgsl"]
149[submodule "helix-syntax/languages/tree-sitter-wgsl"]
146 path = helix-syntax/languages/tree-sitter-wgsl
150 path = helix-syntax/languages/tree-sitter-wgsl
147 url = https://github.com/szebniok/tree-sitter-wgsl
151 url = https://github.com/szebniok/tree-sitter-wgsl
148 shallow = true
152 shallow = true
149−[submodule "helix-syntax/tree-sitter-llvm"]
153+[submodule "helix-syntax/languages/tree-sitter-llvm"]
150 path = helix-syntax/languages/tree-sitter-llvm
154 path = helix-syntax/languages/tree-sitter-llvm
151 url = https://github.com/benwilliamgraham/tree-sitter-llvm
155 url = https://github.com/benwilliamgraham/tree-sitter-llvm
152 shallow = true
156 shallow = true
@@ -154,3 +158,35 @@
154 path = helix-syntax/languages/tree-sitter-markdown
158 path = helix-syntax/languages/tree-sitter-markdown
155 url = https://github.com/MDeiml/tree-sitter-markdown
159 url = https://github.com/MDeiml/tree-sitter-markdown
156 shallow = true
160 shallow = true
161+[submodule "helix-syntax/languages/tree-sitter-dart"]
162+ path = helix-syntax/languages/tree-sitter-dart
163+ url = https://github.com/UserNobody14/tree-sitter-dart.git
164+ shallow = true
165+[submodule "helix-syntax/languages/tree-sitter-dockerfile"]
166+ path = helix-syntax/languages/tree-sitter-dockerfile
167+ url = https://github.com/camdencheek/tree-sitter-dockerfile.git
168+ shallow = true
169+[submodule "helix-syntax/languages/tree-sitter-fish"]
170+ path = helix-syntax/languages/tree-sitter-fish
171+ url = https://github.com/ram02z/tree-sitter-fish
172+ shallow = true
173+[submodule "helix-syntax/languages/tree-sitter-git-commit"]
174+ path = helix-syntax/languages/tree-sitter-git-commit
175+ url = https://github.com/the-mikedavis/tree-sitter-git-commit.git
176+ shallow = true
177+[submodule "helix-syntax/languages/tree-sitter-llvm-mir"]
178+ path = helix-syntax/languages/tree-sitter-llvm-mir
179+ url = https://github.com/Flakebi/tree-sitter-llvm-mir.git
180+ shallow = true
181+[submodule "helix-syntax/languages/tree-sitter-git-diff"]
182+ path = helix-syntax/languages/tree-sitter-git-diff
183+ url = https://github.com/the-mikedavis/tree-sitter-git-diff.git
184+ shallow = true
185+[submodule "helix-syntax/languages/tree-sitter-tablegen"]
186+ path = helix-syntax/languages/tree-sitter-tablegen
187+ url = https://github.com/Flakebi/tree-sitter-tablegen
188+ shallow = true
189+[submodule "helix-syntax/languages/tree-sitter-git-rebase"]
190+ path = helix-syntax/languages/tree-sitter-git-rebase
191+ url = https://github.com/the-mikedavis/tree-sitter-git-rebase.git
192+ shallow = true
CHANGELOG.md+119
@@ -1,4 +1,123 @@
1
1
2+# 0.6.0 (2022-01-04)
3+
4+Happy new year and a big shout out to all the contributors! We had 55 contributors in this release.
5+
6+Helix has popped up in DPorts and Fedora Linux via COPR ([#1270](https://github.com/helix-editor/helix/pull/1270))
7+
8+As usual the following is a brief summary, refer to the git history for a full log:
9+
10+Breaking changes:
11+
12+- fix: Normalize backtab into shift-tab
13+
14+Features:
15+
16+- Macros ([#1234](https://github.com/helix-editor/helix/pull/1234))
17+- Add reverse search functionality ([#958](https://github.com/helix-editor/helix/pull/958))
18+- Allow keys to be mapped to sequences of commands ([#589](https://github.com/helix-editor/helix/pull/589))
19+- Make it possible to keybind TypableCommands ([#1169](https://github.com/helix-editor/helix/pull/1169))
20+- Detect workspace root using language markers ([#1370](https://github.com/helix-editor/helix/pull/1370))
21+- Add WORD textobject ([#991](https://github.com/helix-editor/helix/pull/991))
22+- Add LSP rename_symbol (space-r) ([#1011](https://github.com/helix-editor/helix/pull/1011))
23+- Added workspace_symbol_picker ([#1041](https://github.com/helix-editor/helix/pull/1041))
24+- Detect filetype from shebang line ([#1001](https://github.com/helix-editor/helix/pull/1001))
25+- Allow piping from stdin into a buffer on startup ([#996](https://github.com/helix-editor/helix/pull/996))
26+- Add auto pairs for same-char pairs ([#1219](https://github.com/helix-editor/helix/pull/1219))
27+- Update settings at runtime ([#798](https://github.com/helix-editor/helix/pull/798))
28+- Enable thin LTO (cccc194)
29+
30+Commands:
31+- :wonly -- window only ([#1057](https://github.com/helix-editor/helix/pull/1057))
32+- buffer-close (:bc, :bclose) ([#1035](https://github.com/helix-editor/helix/pull/1035))
33+- Add :<line> and :goto <line> commands ([#1128](https://github.com/helix-editor/helix/pull/1128))
34+- :sort command ([#1288](https://github.com/helix-editor/helix/pull/1288))
35+- Add m textobject for pair under cursor ([#961](https://github.com/helix-editor/helix/pull/961))
36+- Implement "Goto next buffer / Goto previous buffer" commands ([#950](https://github.com/helix-editor/helix/pull/950))
37+- Implement "Goto last modification" command ([#1067](https://github.com/helix-editor/helix/pull/1067))
38+- Add trim_selections command ([#1092](https://github.com/helix-editor/helix/pull/1092))
39+- Add movement shortcut for history ([#1088](https://github.com/helix-editor/helix/pull/1088))
40+- Add command to inc/dec number under cursor ([#1027](https://github.com/helix-editor/helix/pull/1027))
41+ - Add support for dates for increment/decrement
42+- Align selections (&) ([#1101](https://github.com/helix-editor/helix/pull/1101))
43+- Implement no-yank delete/change ([#1099](https://github.com/helix-editor/helix/pull/1099))
44+- Implement black hole register ([#1165](https://github.com/helix-editor/helix/pull/1165))
45+- gf as goto_file (gf) ([#1102](https://github.com/helix-editor/helix/pull/1102))
46+- Add last modified file (gm) ([#1093](https://github.com/helix-editor/helix/pull/1093))
47+- ensure_selections_forward ([#1393](https://github.com/helix-editor/helix/pull/1393))
48+- Readline style insert mode ([#1039](https://github.com/helix-editor/helix/pull/1039))
49+
50+Usability improvements and fixes:
51+
52+- Detect filetype on :write ([#1141](https://github.com/helix-editor/helix/pull/1141))
53+- Add single and double quotes to matching pairs ([#995](https://github.com/helix-editor/helix/pull/995))
54+- Launch with defaults upon invalid config/theme (rather than panicking) ([#982](https://github.com/helix-editor/helix/pull/982))
55+- If switching away from an empty scratch buffer, remove it ([#935](https://github.com/helix-editor/helix/pull/935))
56+- Truncate the starts of file paths instead of the ends in picker ([#951](https://github.com/helix-editor/helix/pull/951))
57+- Truncate the start of file paths in the StatusLine ([#1351](https://github.com/helix-editor/helix/pull/1351))
58+- Prevent picker from previewing binaries or large file ([#939](https://github.com/helix-editor/helix/pull/939))
59+- Inform when reaching undo/redo bounds ([#981](https://github.com/helix-editor/helix/pull/981))
60+- search_impl will only align cursor center when it isn't in view ([#959](https://github.com/helix-editor/helix/pull/959))
61+- Add <C-h>, <C-u>, <C-d>, Delete in prompt mode ([#1034](https://github.com/helix-editor/helix/pull/1034))
62+- Restore screen position when aborting search ([#1047](https://github.com/helix-editor/helix/pull/1047))
63+- Buffer picker: show is_modifier flag ([#1020](https://github.com/helix-editor/helix/pull/1020))
64+- Add commit hash to version info, if present ([#957](https://github.com/helix-editor/helix/pull/957))
65+- Implement indent-aware delete ([#1120](https://github.com/helix-editor/helix/pull/1120))
66+- Jump to end char of surrounding pair from any cursor pos ([#1121](https://github.com/helix-editor/helix/pull/1121))
67+- File picker configuration ([#988](https://github.com/helix-editor/helix/pull/988))
68+- Fix surround cursor position calculation ([#1183](https://github.com/helix-editor/helix/pull/1183))
69+- Accept count for goto_window ([#1033](https://github.com/helix-editor/helix/pull/1033))
70+- Make kill_to_line_end behave like emacs ([#1235](https://github.com/helix-editor/helix/pull/1235))
71+- Only use a single documentation popup ([#1241](https://github.com/helix-editor/helix/pull/1241))
72+- ui: popup: Don't allow scrolling past the end of content (3307f44c)
73+- Open files with spaces in filename, allow opening multiple files ([#1231](https://github.com/helix-editor/helix/pull/1231))
74+- Allow paste commands to take a count ([#1261](https://github.com/helix-editor/helix/pull/1261))
75+- Auto pairs selection ([#1254](https://github.com/helix-editor/helix/pull/1254))
76+- Use a fuzzy matcher for commands ([#1386](https://github.com/helix-editor/helix/pull/1386))
77+- Add c-s to pick word under doc cursor to prompt line & search completion ([#831](https://github.com/helix-editor/helix/pull/831))
78+- Fix :earlier/:later missing changeset update ([#1069](https://github.com/helix-editor/helix/pull/1069))
79+- Support extend for multiple goto ([#909](https://github.com/helix-editor/helix/pull/909))
80+- Add arrow-key bindings for window switching ([#933](https://github.com/helix-editor/helix/pull/933))
81+- Implement key ordering for info box ([#952](https://github.com/helix-editor/helix/pull/952))
82+
83+LSP:
84+- Implement MarkedString rendering (e128a8702)
85+- Don't panic if init fails (d31bef7)
86+- Configurable diagnostic severity ([#1325](https://github.com/helix-editor/helix/pull/1325))
87+- Resolve completion item ([#1315](https://github.com/helix-editor/helix/pull/1315))
88+- Code action command support ([#1304](https://github.com/helix-editor/helix/pull/1304))
89+
90+Grammars:
91+
92+- Adds mint language server ([#974](https://github.com/helix-editor/helix/pull/974))
93+- Perl ([#978](https://github.com/helix-editor/helix/pull/978)) ([#1280](https://github.com/helix-editor/helix/pull/1280))
94+- GLSL ([#993](https://github.com/helix-editor/helix/pull/993))
95+- Racket ([#1143](https://github.com/helix-editor/helix/pull/1143))
96+- WGSL ([#1166](https://github.com/helix-editor/helix/pull/1166))
97+- LLVM ([#1167](https://github.com/helix-editor/helix/pull/1167)) ([#1388](https://github.com/helix-editor/helix/pull/1388)) ([#1409](https://github.com/helix-editor/helix/pull/1409)) ([#1398](https://github.com/helix-editor/helix/pull/1398))
98+- Markdown (49e06787)
99+- Scala ([#1278](https://github.com/helix-editor/helix/pull/1278))
100+- Dart ([#1250](https://github.com/helix-editor/helix/pull/1250))
101+- Fish ([#1308](https://github.com/helix-editor/helix/pull/1308))
102+- Dockerfile ([#1303](https://github.com/helix-editor/helix/pull/1303))
103+- Git (commit, rebase, diff) ([#1338](https://github.com/helix-editor/helix/pull/1338)) ([#1402](https://github.com/helix-editor/helix/pull/1402)) ([#1373](https://github.com/helix-editor/helix/pull/1373))
104+- tree-sitter-comment ([#1300](https://github.com/helix-editor/helix/pull/1300))
105+- Highlight comments in c, cpp, cmake and llvm ([#1309](https://github.com/helix-editor/helix/pull/1309))
106+- Improve yaml syntax highlighting highlighting ([#1294](https://github.com/helix-editor/helix/pull/1294))
107+- Improve rust syntax highlighting ([#1295](https://github.com/helix-editor/helix/pull/1295))
108+- Add textobjects and indents to cmake ([#1307](https://github.com/helix-editor/helix/pull/1307))
109+- Add textobjects and indents to c and cpp ([#1293](https://github.com/helix-editor/helix/pull/1293))
110+
111+New themes:
112+
113+- Solarized dark ([#999](https://github.com/helix-editor/helix/pull/999))
114+- Solarized light ([#1010](https://github.com/helix-editor/helix/pull/1010))
115+- Spacebones light ([#1131](https://github.com/helix-editor/helix/pull/1131))
116+- Monokai Pro ([#1206](https://github.com/helix-editor/helix/pull/1206))
117+- Base16 Light and Terminal ([#1078](https://github.com/helix-editor/helix/pull/1078))
118+ - and a default 16 color theme, truecolor detection
119+- Dracula ([#1258](https://github.com/helix-editor/helix/pull/1258))
120+
2# 0.5.0 (2021-10-28)
121# 0.5.0 (2021-10-28)
3
122
4A big shout out to all the contributors! We had 46 contributors in this release.
123A big shout out to all the contributors! We had 46 contributors in this release.
Cargo.lock+40−41
@@ -13,9 +13,9 @@ dependencies = [
13
13
14[[package]]
14[[package]]
15name = "anyhow"
15name = "anyhow"
16−version = "1.0.51"
16+version = "1.0.52"
17source = "registry+https://github.com/rust-lang/crates.io-index"
17source = "registry+https://github.com/rust-lang/crates.io-index"
18−checksum = "8b26702f315f53b6071259e15dd9d64528213b44d61de1ec926eca7715d62203"
18+checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
19
19
20[[package]]
20[[package]]
21name = "arc-swap"
21name = "arc-swap"
@@ -78,9 +78,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
78
78
79[[package]]
79[[package]]
80name = "chardetng"
80name = "chardetng"
81−version = "0.1.15"
81+version = "0.1.17"
82source = "registry+https://github.com/rust-lang/crates.io-index"
82source = "registry+https://github.com/rust-lang/crates.io-index"
83−checksum = "83ee29c16b81c32fbc882ecc568305793338a8353952573db837f4f4a6cd5c2e"
83+checksum = "14b8f0b65b7b08ae3c8187e8d77174de20cb6777864c6b832d8ad365999cf1ea"
84dependencies = [
84dependencies = [
85 "cfg-if",
85 "cfg-if",
86 "encoding_rs",
86 "encoding_rs",
@@ -258,15 +258,15 @@ dependencies = [
258
258
259[[package]]
259[[package]]
260name = "futures-core"
260name = "futures-core"
261−version = "0.3.18"
261+version = "0.3.19"
262source = "registry+https://github.com/rust-lang/crates.io-index"
262source = "registry+https://github.com/rust-lang/crates.io-index"
263−checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445"
263+checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7"
264
264
265[[package]]
265[[package]]
266name = "futures-executor"
266name = "futures-executor"
267−version = "0.3.18"
267+version = "0.3.19"
268source = "registry+https://github.com/rust-lang/crates.io-index"
268source = "registry+https://github.com/rust-lang/crates.io-index"
269−checksum = "7b808bf53348a36cab739d7e04755909b9fcaaa69b7d7e588b37b6ec62704c97"
269+checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a"
270dependencies = [
270dependencies = [
271 "futures-core",
271 "futures-core",
272 "futures-task",
272 "futures-task",
@@ -275,15 +275,15 @@ dependencies = [
275
275
276[[package]]
276[[package]]
277name = "futures-task"
277name = "futures-task"
278−version = "0.3.18"
278+version = "0.3.19"
279source = "registry+https://github.com/rust-lang/crates.io-index"
279source = "registry+https://github.com/rust-lang/crates.io-index"
280−checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12"
280+checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72"
281
281
282[[package]]
282[[package]]
283name = "futures-util"
283name = "futures-util"
284−version = "0.3.18"
284+version = "0.3.19"
285source = "registry+https://github.com/rust-lang/crates.io-index"
285source = "registry+https://github.com/rust-lang/crates.io-index"
286−checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e"
286+checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164"
287dependencies = [
287dependencies = [
288 "futures-core",
288 "futures-core",
289 "futures-task",
289 "futures-task",
@@ -366,10 +366,11 @@ dependencies = [
366
366
367[[package]]
367[[package]]
368name = "helix-core"
368name = "helix-core"
369−version = "0.5.0"
369+version = "0.6.0"
370dependencies = [
370dependencies = [
371 "arc-swap",
371 "arc-swap",
372 "chrono",
372 "chrono",
373+ "encoding_rs",
373 "etcetera",
374 "etcetera",
374 "helix-syntax",
375 "helix-syntax",
375 "log",
376 "log",
@@ -391,7 +392,7 @@ dependencies = [
391
392
392[[package]]
393[[package]]
393name = "helix-lsp"
394name = "helix-lsp"
394−version = "0.5.0"
395+version = "0.6.0"
395dependencies = [
396dependencies = [
396 "anyhow",
397 "anyhow",
397 "futures-executor",
398 "futures-executor",
@@ -409,7 +410,7 @@ dependencies = [
409
410
410[[package]]
411[[package]]
411name = "helix-syntax"
412name = "helix-syntax"
412−version = "0.5.0"
413+version = "0.6.0"
413dependencies = [
414dependencies = [
414 "anyhow",
415 "anyhow",
415 "cc",
416 "cc",
@@ -420,7 +421,7 @@ dependencies = [
420
421
421[[package]]
422[[package]]
422name = "helix-term"
423name = "helix-term"
423−version = "0.5.0"
424+version = "0.6.0"
424dependencies = [
425dependencies = [
425 "anyhow",
426 "anyhow",
426 "chrono",
427 "chrono",
@@ -451,7 +452,7 @@ dependencies = [
451
452
452[[package]]
453[[package]]
453name = "helix-tui"
454name = "helix-tui"
454−version = "0.5.0"
455+version = "0.6.0"
455dependencies = [
456dependencies = [
456 "bitflags",
457 "bitflags",
457 "cassowary",
458 "cassowary",
@@ -464,14 +465,13 @@ dependencies = [
464
465
465[[package]]
466[[package]]
466name = "helix-view"
467name = "helix-view"
467−version = "0.5.0"
468+version = "0.6.0"
468dependencies = [
469dependencies = [
469 "anyhow",
470 "anyhow",
470 "bitflags",
471 "bitflags",
471 "chardetng",
472 "chardetng",
472 "clipboard-win",
473 "clipboard-win",
473 "crossterm",
474 "crossterm",
474− "encoding_rs",
475 "futures-util",
475 "futures-util",
476 "helix-core",
476 "helix-core",
477 "helix-lsp",
477 "helix-lsp",
@@ -690,9 +690,9 @@ dependencies = [
690
690
691[[package]]
691[[package]]
692name = "num_cpus"
692name = "num_cpus"
693−version = "1.13.0"
693+version = "1.13.1"
694source = "registry+https://github.com/rust-lang/crates.io-index"
694source = "registry+https://github.com/rust-lang/crates.io-index"
695−checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
695+checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
696dependencies = [
696dependencies = [
697 "hermit-abi",
697 "hermit-abi",
698 "libc",
698 "libc",
@@ -700,9 +700,9 @@ dependencies = [
700
700
701[[package]]
701[[package]]
702name = "once_cell"
702name = "once_cell"
703−version = "1.8.0"
703+version = "1.9.0"
704source = "registry+https://github.com/rust-lang/crates.io-index"
704source = "registry+https://github.com/rust-lang/crates.io-index"
705−checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56"
705+checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
706
706
707[[package]]
707[[package]]
708name = "parking_lot"
708name = "parking_lot"
@@ -847,9 +847,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
847
847
848[[package]]
848[[package]]
849name = "ropey"
849name = "ropey"
850−version = "1.3.1"
850+version = "1.3.2"
851source = "registry+https://github.com/rust-lang/crates.io-index"
851source = "registry+https://github.com/rust-lang/crates.io-index"
852−checksum = "9150aff6deb25b20ed110889f070a678bcd1033e46e5e9d6fb1abeab17947f28"
852+checksum = "e6b9aa65bcd9f308d37c7158b4a1afaaa32b8450213e20c9b98e7d5b3cc2fec3"
853dependencies = [
853dependencies = [
854 "smallvec",
854 "smallvec",
855]
855]
@@ -877,18 +877,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
877
877
878[[package]]
878[[package]]
879name = "serde"
879name = "serde"
880−version = "1.0.131"
880+version = "1.0.133"
881source = "registry+https://github.com/rust-lang/crates.io-index"
881source = "registry+https://github.com/rust-lang/crates.io-index"
882−checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1"
882+checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
883dependencies = [
883dependencies = [
884 "serde_derive",
884 "serde_derive",
885]
885]
886
886
887[[package]]
887[[package]]
888name = "serde_derive"
888name = "serde_derive"
889−version = "1.0.131"
889+version = "1.0.133"
890source = "registry+https://github.com/rust-lang/crates.io-index"
890source = "registry+https://github.com/rust-lang/crates.io-index"
891−checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2"
891+checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
892dependencies = [
892dependencies = [
893 "proc-macro2",
893 "proc-macro2",
894 "quote",
894 "quote",
@@ -897,9 +897,9 @@ dependencies = [
897
897
898[[package]]
898[[package]]
899name = "serde_json"
899name = "serde_json"
900−version = "1.0.73"
900+version = "1.0.74"
901source = "registry+https://github.com/rust-lang/crates.io-index"
901source = "registry+https://github.com/rust-lang/crates.io-index"
902−checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5"
902+checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142"
903dependencies = [
903dependencies = [
904 "itoa",
904 "itoa",
905 "ryu",
905 "ryu",
@@ -919,9 +919,9 @@ dependencies = [
919
919
920[[package]]
920[[package]]
921name = "signal-hook"
921name = "signal-hook"
922−version = "0.3.12"
922+version = "0.3.13"
923source = "registry+https://github.com/rust-lang/crates.io-index"
923source = "registry+https://github.com/rust-lang/crates.io-index"
924−checksum = "c35dfd12afb7828318348b8c408383cf5071a086c1d4ab1c0f9840ec92dbb922"
924+checksum = "647c97df271007dcea485bb74ffdb57f2e683f1306c854f468a0c244badabf2d"
925dependencies = [
925dependencies = [
926 "libc",
926 "libc",
927 "signal-hook-registry",
927 "signal-hook-registry",
@@ -1069,11 +1069,10 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
1069
1069
1070[[package]]
1070[[package]]
1071name = "tokio"
1071name = "tokio"
1072−version = "1.14.0"
1072+version = "1.15.0"
1073source = "registry+https://github.com/rust-lang/crates.io-index"
1073source = "registry+https://github.com/rust-lang/crates.io-index"
1074−checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144"
1074+checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
1075dependencies = [
1075dependencies = [
1076− "autocfg",
1077 "bytes",
1076 "bytes",
1078 "libc",
1077 "libc",
1079 "memchr",
1078 "memchr",
@@ -1089,9 +1088,9 @@ dependencies = [
1089
1088
1090[[package]]
1089[[package]]
1091name = "tokio-macros"
1090name = "tokio-macros"
1092−version = "1.6.0"
1091+version = "1.7.0"
1093source = "registry+https://github.com/rust-lang/crates.io-index"
1092source = "registry+https://github.com/rust-lang/crates.io-index"
1094−checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e"
1093+checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
1095dependencies = [
1094dependencies = [
1096 "proc-macro2",
1095 "proc-macro2",
1097 "quote",
1096 "quote",
@@ -1120,9 +1119,9 @@ dependencies = [
1120
1119
1121[[package]]
1120[[package]]
1122name = "tree-sitter"
1121name = "tree-sitter"
1123−version = "0.20.1"
1122+version = "0.20.2"
1124source = "registry+https://github.com/rust-lang/crates.io-index"
1123source = "registry+https://github.com/rust-lang/crates.io-index"
1125−checksum = "9394e9dbfe967b5f3d6ab79e302e78b5fb7b530c368d634ff3b8d67ede138bf1"
1124+checksum = "c36be3222512d85a112491ae0cc280a38076022414f00b64582da1b7565ffd82"
1126dependencies = [
1125dependencies = [
1127 "cc",
1126 "cc",
1128 "regex",
1127 "regex",
@@ -1262,7 +1261,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1262
1261
1263[[package]]
1262[[package]]
1264name = "xtask"
1263name = "xtask"
1265−version = "0.5.0"
1264+version = "0.6.0"
1266dependencies = [
1265dependencies = [
1267 "helix-core",
1266 "helix-core",
1268 "helix-term",
1267 "helix-term",
base16_theme.toml+4
@@ -29,6 +29,10 @@
29"namespace" = "magenta"
29"namespace" = "magenta"
30"ui.help" = { fg = "white", bg = "black" }
30"ui.help" = { fg = "white", bg = "black" }
31
31
32+"diff.plus" = "green"
33+"diff.delta" = "yellow"
34+"diff.minus" = "red"
35+
32"diagnostic" = { modifiers = ["underlined"] }
36"diagnostic" = { modifiers = ["underlined"] }
33"ui.gutter" = { bg = "black" }
37"ui.gutter" = { bg = "black" }
34"info" = "blue"
38"info" = "blue"
book/src/generated/lang-support.md+16−5
@@ -1,12 +1,19 @@
1| Language | Syntax Highlighting | Treesitter Textobjects | Auto Indent | Default LSP |
1| Language | Syntax Highlighting | Treesitter Textobjects | Auto Indent | Default LSP |
2| --- | --- | --- | --- | --- |
2| --- | --- | --- | --- | --- |
3| bash | ✓ | | | `bash-language-server` |
3| bash | ✓ | | | `bash-language-server` |
4−| c | ✓ | | | `clangd` |
4+| c | ✓ | ✓ | ✓ | `clangd` |
5| c-sharp | ✓ | | | |
5| c-sharp | ✓ | | | |
6−| cmake | ✓ | | | `cmake-language-server` |
6+| cmake | ✓ | ✓ | ✓ | `cmake-language-server` |
7+| comment | ✓ | | | |
7−| cpp | ✓ | | | `clangd` |
8+| cpp | ✓ | ✓ | ✓ | `clangd` |
8| css | ✓ | | | |
9| css | ✓ | | | |
10+| dart | ✓ | | ✓ | `dart` |
11+| dockerfile | ✓ | | | `docker-langserver` |
9| elixir | ✓ | | | `elixir-ls` |
12| elixir | ✓ | | | `elixir-ls` |
13+| fish | ✓ | ✓ | ✓ | |
14+| git-commit | ✓ | | | |
15+| git-diff | ✓ | | | |
16+| git-rebase | ✓ | | | |
10| glsl | ✓ | | ✓ | |
17| glsl | ✓ | | ✓ | |
11| go | ✓ | ✓ | ✓ | `gopls` |
18| go | ✓ | ✓ | ✓ | `gopls` |
12| html | ✓ | | | |
19| html | ✓ | | | |
@@ -16,7 +23,9 @@
16| julia | ✓ | | | `julia` |
23| julia | ✓ | | | `julia` |
17| latex | ✓ | | | |
24| latex | ✓ | | | |
18| ledger | ✓ | | | |
25| ledger | ✓ | | | |
19−| llvm | ✓ | | | |
26+| llvm | ✓ | ✓ | ✓ | |
27+| llvm-mir | ✓ | ✓ | ✓ | |
28+| llvm-mir-yaml | ✓ | | ✓ | |
20| lua | ✓ | | ✓ | |
29| lua | ✓ | | ✓ | |
21| markdown | ✓ | | | |
30| markdown | ✓ | | | |
22| mint | | | | `mint` |
31| mint | | | | `mint` |
@@ -29,9 +38,11 @@
29| protobuf | ✓ | | ✓ | |
38| protobuf | ✓ | | ✓ | |
30| python | ✓ | ✓ | ✓ | `pylsp` |
39| python | ✓ | ✓ | ✓ | `pylsp` |
31| racket | | | | `racket` |
40| racket | | | | `racket` |
32−| ruby | ✓ | | | `solargraph` |
41+| ruby | ✓ | | ✓ | `solargraph` |
33| rust | ✓ | ✓ | ✓ | `rust-analyzer` |
42| rust | ✓ | ✓ | ✓ | `rust-analyzer` |
43+| scala | ✓ | | ✓ | `metals` |
34| svelte | ✓ | | ✓ | `svelteserver` |
44| svelte | ✓ | | ✓ | `svelteserver` |
45+| tablegen | ✓ | ✓ | ✓ | |
35| toml | ✓ | | | |
46| toml | ✓ | | | |
36| tsq | ✓ | | | |
47| tsq | ✓ | | | |
37| tsx | ✓ | | | `typescript-language-server` |
48| tsx | ✓ | | | `typescript-language-server` |
book/src/generated/typable-cmd.md+4
@@ -20,6 +20,7 @@
20| `:quit-all`, `:qa` | Close all views. |
20| `:quit-all`, `:qa` | Close all views. |
21| `:quit-all!`, `:qa!` | Close all views forcefully (ignoring unsaved changes). |
21| `:quit-all!`, `:qa!` | Close all views forcefully (ignoring unsaved changes). |
22| `:cquit`, `:cq` | Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2). |
22| `:cquit`, `:cq` | Quit with exit code (default 1). Accepts an optional integer exit code (:cq 2). |
23+| `:cquit!`, `:cq!` | Quit with exit code (default 1) forcefully (ignoring unsaved changes). Accepts an optional integer exit code (:cq! 2). |
23| `:theme` | Change the editor theme. |
24| `:theme` | Change the editor theme. |
24| `:clipboard-yank` | Yank main selection into system clipboard. |
25| `:clipboard-yank` | Yank main selection into system clipboard. |
25| `:clipboard-yank-join` | Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline. |
26| `:clipboard-yank-join` | Yank joined selections into system clipboard. A separator can be provided as first argument. Default value is newline. |
@@ -41,3 +42,6 @@
41| `:hsplit`, `:hs`, `:sp` | Open the file in a horizontal split. |
42| `:hsplit`, `:hs`, `:sp` | Open the file in a horizontal split. |
42| `:tutor` | Open the tutorial. |
43| `:tutor` | Open the tutorial. |
43| `:goto`, `:g` | Go to line number. |
44| `:goto`, `:g` | Go to line number. |
45+| `:set-option`, `:set` | Set a config option at runtime |
46+| `:sort` | Sort ranges in selection. |
47+| `:rsort` | Sort ranges in selection in reverse order. |
book/src/guides/adding_languages.md+24−13
@@ -27,22 +27,32 @@ directory](../configuration.md).
27
27
28These are the available keys and descriptions for the file.
28These are the available keys and descriptions for the file.
29
29
30−| Key | Description |
30+| Key | Description |
31−| ---- | ----------- |
31+| ---- | ----------- |
32−| name | The name of the language |
32+| name | The name of the language |
33−| scope | A string like `source.js` that identifies the language. Currently, we strive to match the scope names used by popular TextMate grammars and by the Linguist library. Usually `source.<name>` or `text.<name>` in case of markup languages |
33+| scope | A string like `source.js` that identifies the language. Currently, we strive to match the scope names used by popular TextMate grammars and by the Linguist library. Usually `source.<name>` or `text.<name>` in case of markup languages |
34−| injection-regex | regex pattern that will be tested against a language name in order to determine whether this language should be used for a potential [language injection][treesitter-language-injection] site. |
34+| injection-regex | regex pattern that will be tested against a language name in order to determine whether this language should be used for a potential [language injection][treesitter-language-injection] site. |
35−| file-types | The filetypes of the language, for example `["yml", "yaml"]` |
35+| file-types | The filetypes of the language, for example `["yml", "yaml"]` |
36−| shebangs | The interpreters from the shebang line, for example `["sh", "bash"]` |
36+| shebangs | The interpreters from the shebang line, for example `["sh", "bash"]` |
37−| roots | A set of marker files to look for when trying to find the workspace root. For example `Cargo.lock`, `yarn.lock` |
37+| roots | A set of marker files to look for when trying to find the workspace root. For example `Cargo.lock`, `yarn.lock` |
38−| auto-format | Whether to autoformat this language when saving |
38+| auto-format | Whether to autoformat this language when saving |
39+| diagnostic-severity | Minimal severity of diagnostic for it to be displayed. (Allowed values: `Error`, `Warning`, `Info`, `Hint`) |
39−| comment-token | The token to use as a comment-token |
40+| comment-token | The token to use as a comment-token |
40−| indent | The indent to use. Has sub keys `tab-width` and `unit` |
41+| indent | The indent to use. Has sub keys `tab-width` and `unit` |
41−| config | Language server configuration |
42+| config | Language server configuration |
42
43
43## Queries
44## Queries
44
45
46+For a language to have syntax-highlighting and indentation among
47+other things, you have to add queries. Add a directory for your
48+language with the path `runtime/queries/<name>/`. The tree-sitter
45−For a language to have syntax-highlighting and indentation among other things, you have to add queries. Add a directory for your language with the path `runtime/queries/<name>/`. The tree-sitter [website](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#queries) gives more info on how to write queries.
49+[website](https://tree-sitter.github.io/tree-sitter/syntax-highlighting#queries)
50+gives more info on how to write queries.
51+
52+> NOTE: When evaluating queries, the first matching query takes
53+precedence, which is different from other editors like neovim where
54+the last matching query supercedes the ones before it. See
55+[this issue][neovim-query-precedence] for an example.
46
56
47## Common Issues
57## Common Issues
48
58
@@ -58,3 +68,4 @@ For a language to have syntax-highlighting and indentation among other things, y
58
68
59[treesitter-language-injection]: https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection
69[treesitter-language-injection]: https://tree-sitter.github.io/tree-sitter/syntax-highlighting#language-injection
60[languages.toml]: https://github.com/helix-editor/helix/blob/master/languages.toml
70[languages.toml]: https://github.com/helix-editor/helix/blob/master/languages.toml
71+[neovim-query-precedence]: https://github.com/helix-editor/helix/pull/1170#issuecomment-997294090
book/src/keymap.md+36−34
@@ -46,39 +46,39 @@
46
46
47### Changes
47### Changes
48
48
49−| Key | Description | Command |
49+| Key | Description | Command |
50−| ----- | ----------- | ------- |
50+| ----- | ----------- | ------- |
51−| `r` | Replace with a character | `replace` |
51+| `r` | Replace with a character | `replace` |
52−| `R` | Replace with yanked text | `replace_with_yanked` |
52+| `R` | Replace with yanked text | `replace_with_yanked` |
53−| `~` | Switch case of the selected text | `switch_case` |
53+| `~` | Switch case of the selected text | `switch_case` |
54−| `` ` `` | Set the selected text to lower case | `switch_to_lowercase` |
54+| `` ` `` | Set the selected text to lower case | `switch_to_lowercase` |
55−| `` Alt-` `` | Set the selected text to upper case | `switch_to_uppercase` |
55+| `` Alt-` `` | Set the selected text to upper case | `switch_to_uppercase` |
56−| `i` | Insert before selection | `insert_mode` |
56+| `i` | Insert before selection | `insert_mode` |
57−| `a` | Insert after selection (append) | `append_mode` |
57+| `a` | Insert after selection (append) | `append_mode` |
58−| `I` | Insert at the start of the line | `prepend_to_line` |
58+| `I` | Insert at the start of the line | `prepend_to_line` |
59−| `A` | Insert at the end of the line | `append_to_line` |
59+| `A` | Insert at the end of the line | `append_to_line` |
60−| `o` | Open new line below selection | `open_below` |
60+| `o` | Open new line below selection | `open_below` |
61−| `O` | Open new line above selection | `open_above` |
61+| `O` | Open new line above selection | `open_above` |
62−| `.` | Repeat last change | N/A |
62+| `.` | Repeat last change | N/A |
63−| `u` | Undo change | `undo` |
63+| `u` | Undo change | `undo` |
64−| `U` | Redo change | `redo` |
64+| `U` | Redo change | `redo` |
65−| `Alt-u` | Move backward in history | `earlier` |
65+| `Alt-u` | Move backward in history | `earlier` |
66−| `Alt-U` | Move forward in history | `later` |
66+| `Alt-U` | Move forward in history | `later` |
67−| `y` | Yank selection | `yank` |
67+| `y` | Yank selection | `yank` |
68−| `p` | Paste after selection | `paste_after` |
68+| `p` | Paste after selection | `paste_after` |
69−| `P` | Paste before selection | `paste_before` |
69+| `P` | Paste before selection | `paste_before` |
70−| `"` `<reg>` | Select a register to yank to or paste from | `select_register` |
70+| `"` `<reg>` | Select a register to yank to or paste from | `select_register` |
71−| `>` | Indent selection | `indent` |
71+| `>` | Indent selection | `indent` |
72−| `<` | Unindent selection | `unindent` |
72+| `<` | Unindent selection | `unindent` |
73−| `=` | Format selection (currently nonfunctional/disabled) (**LSP**) | `format_selections` |
73+| `=` | Format selection (currently nonfunctional/disabled) (**LSP**) | `format_selections` |
74−| `d` | Delete selection | `delete_selection` |
74+| `d` | Delete selection | `delete_selection` |
75−| `Alt-d` | Delete selection, without yanking | `delete_selection_noyank` |
75+| `Alt-d` | Delete selection, without yanking | `delete_selection_noyank` |
76−| `c` | Change selection (delete and enter insert mode) | `change_selection` |
76+| `c` | Change selection (delete and enter insert mode) | `change_selection` |
77−| `Alt-c` | Change selection (delete and enter insert mode, without yanking) | `change_selection_noyank` |
77+| `Alt-c` | Change selection (delete and enter insert mode, without yanking) | `change_selection_noyank` |
78−| `Ctrl-a` | Increment object (number) under cursor | `increment` |
78+| `Ctrl-a` | Increment object (number) under cursor | `increment` |
79−| `Ctrl-x` | Decrement object (number) under cursor | `decrement` |
79+| `Ctrl-x` | Decrement object (number) under cursor | `decrement` |
80−| `q` | Start/stop macro recording to the selected register | `record_macro` |
80+| `Q` | Start/stop macro recording to the selected register (experimental) | `record_macro` |
81−| `Q` | Play back a recorded macro from the selected register | `play_macro` |
81+| `q` | Play back a recorded macro from the selected register (experimental) | `replay_macro` |
82
82
83#### Shell
83#### Shell
84
84
@@ -161,7 +161,7 @@ Jumps to various locations.
161
161
162| Key | Description | Command |
162| Key | Description | Command |
163| ----- | ----------- | ------- |
163| ----- | ----------- | ------- |
164−| `g` | Go to the start of the file | `goto_file_start` |
164+| `g` | Go to line number `<n>` else start of file | `goto_file_start` |
165| `e` | Go to the end of the file | `goto_last_line` |
165| `e` | Go to the end of the file | `goto_last_line` |
166| `f` | Go to files in the selection | `goto_file` |
166| `f` | Go to files in the selection | `goto_file` |
167| `h` | Go to the start of the line | `goto_line_start` |
167| `h` | Go to the start of the line | `goto_line_start` |
@@ -261,6 +261,8 @@ Mappings in the style of [vim-unimpaired](https://github.com/tpope/vim-unimpaire
261| `]D` | Go to last diagnostic in document (**LSP**) | `goto_last_diag` |
261| `]D` | Go to last diagnostic in document (**LSP**) | `goto_last_diag` |
262| `[space` | Add newline above | `add_newline_above` |
262| `[space` | Add newline above | `add_newline_above` |
263| `]space` | Add newline below | `add_newline_below` |
263| `]space` | Add newline below | `add_newline_below` |
264+| `]o` | Expand syntax tree object selection. | `expand_selection` |
265+| `[o` | Shrink syntax tree object selection. | `shrink_selection` |
264
266
265## Insert Mode
267## Insert Mode
266
268
book/src/languages.md−1
@@ -11,4 +11,3 @@ Changes made to the `languages.toml` file in a user's [configuration directory](
11name = "rust"
11name = "rust"
12auto-format = false
12auto-format = false
13```
13```
14−
book/src/themes.md+10−2
@@ -105,6 +105,7 @@ We use a similar set of scopes as
105
105
106- `type` - Types
106- `type` - Types
107 - `builtin` - Primitive types provided by the language (`int`, `usize`)
107 - `builtin` - Primitive types provided by the language (`int`, `usize`)
108+- `constructor`
108
109
109- `constant` (TODO: constant.other.placeholder for %v)
110- `constant` (TODO: constant.other.placeholder for %v)
110 - `builtin` Special constants provided by the language (`true`, `false`, `nil` etc)
111 - `builtin` Special constants provided by the language (`true`, `false`, `nil` etc)
@@ -169,13 +170,20 @@ We use a similar set of scopes as
169 - `numbered`
170 - `numbered`
170 - `bold`
171 - `bold`
171 - `italic`
172 - `italic`
172− - `underline`
173− - `link`
173+ - `link`
174+ - `url`
175+ - `label`
174 - `quote`
176 - `quote`
175 - `raw`
177 - `raw`
176 - `inline`
178 - `inline`
177 - `block`
179 - `block`
178
180
181+- `diff` - version control changes
182+ - `plus` - additions
183+ - `minus` - deletions
184+ - `delta` - modifications
185+ - `moved` - renamed or moved files/changes
186+
179#### Interface
187#### Interface
180
188
181These scopes are used for theming the editor interface.
189These scopes are used for theming the editor interface.
flake.lock+12−12
@@ -2,11 +2,11 @@
2 "nodes": {
2 "nodes": {
3 "devshell": {
3 "devshell": {
4 "locked": {
4 "locked": {
5− "lastModified": 1637575296,
5+ "lastModified": 1639692811,
6− "narHash": "sha256-ZY8YR5u8aglZPe27+AJMnPTG6645WuavB+w0xmhTarw=",
6+ "narHash": "sha256-wOOBH0fVsfNqw/5ZWRoKspyesoXBgiwEOUBH4c7JKEo=",
7 "owner": "numtide",
7 "owner": "numtide",
8 "repo": "devshell",
8 "repo": "devshell",
9− "rev": "0e56ef21ba1a717169953122c7415fa6a8cd2618",
9+ "rev": "d3a1f5bec3632b33346865b1c165bf2420bb2f52",
10 "type": "github"
10 "type": "github"
11 },
11 },
12 "original": {
12 "original": {
@@ -41,11 +41,11 @@
41 ]
41 ]
42 },
42 },
43 "locked": {
43 "locked": {
44− "lastModified": 1638425401,
44+ "lastModified": 1639807801,
45− "narHash": "sha256-xc8ayvR3u90hSCMEy0zHHKav7lEgljAFXL4oIkWRp3M=",
45+ "narHash": "sha256-y32tMq1LTRVbMW3QN5i98iOQjQt2QSsif3ayUkD1o3g=",
46 "owner": "yusdacra",
46 "owner": "yusdacra",
47 "repo": "nix-cargo-integration",
47 "repo": "nix-cargo-integration",
48− "rev": "1f8b511bb30f7d7b9051dfbb4784390bc0d48d37",
48+ "rev": "b5bbaa4f5239e6f0619846f9a5380f07baa853d3",
49 "type": "github"
49 "type": "github"
50 },
50 },
51 "original": {
51 "original": {
@@ -56,11 +56,11 @@
56 },
56 },
57 "nixpkgs": {
57 "nixpkgs": {
58 "locked": {
58 "locked": {
59− "lastModified": 1638376152,
59+ "lastModified": 1639699734,
60− "narHash": "sha256-ucgLpVqhFnClH7YRUHBHnmiOd82RZdFR3XJt36ks5fE=",
60+ "narHash": "sha256-tlX6WebGmiHb2Hmniff+ltYp+7dRfdsBxw9YczLsP60=",
61 "owner": "nixos",
61 "owner": "nixos",
62 "repo": "nixpkgs",
62 "repo": "nixpkgs",
63− "rev": "6daa4a5c045d40e6eae60a3b6e427e8700f1c07f",
63+ "rev": "03ec468b14067729a285c2c7cfa7b9434a04816c",
64 "type": "github"
64 "type": "github"
65 },
65 },
66 "original": {
66 "original": {
@@ -99,11 +99,11 @@
99 "nixpkgs": "nixpkgs_2"
99 "nixpkgs": "nixpkgs_2"
100 },
100 },
101 "locked": {
101 "locked": {
102− "lastModified": 1638497756,
102+ "lastModified": 1639880499,
103− "narHash": "sha256-zKOvMKqGp71ZBnR+hBlPcv4TwNN82COW9EF+6ygrFs8=",
103+ "narHash": "sha256-/BibDmFwgWuuTUkNVO6YlvuTSWM9dpBvlZoTAPs7ORI=",
104 "owner": "oxalica",
104 "owner": "oxalica",
105 "repo": "rust-overlay",
105 "repo": "rust-overlay",
106− "rev": "783722a22ee5d762ac5c1c7b418b57b3010c827a",
106+ "rev": "c6c83589ae048af20d93d01eb07a4176012093d0",
107 "type": "github"
107 "type": "github"
108 },
108 },
109 "original": {
109 "original": {
helix-core/Cargo.toml+4−3
@@ -1,6 +1,6 @@
1[package]
1[package]
2name = "helix-core"
2name = "helix-core"
3−version = "0.5.0"
3+version = "0.6.0"
4authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
4authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
5edition = "2021"
5edition = "2021"
6license = "MPL-2.0"
6license = "MPL-2.0"
@@ -13,7 +13,7 @@ include = ["src/**/*", "README.md"]
13[features]
13[features]
14
14
15[dependencies]
15[dependencies]
16−helix-syntax = { version = "0.5", path = "../helix-syntax" }
16+helix-syntax = { version = "0.6", path = "../helix-syntax" }
17
17
18ropey = "1.3"
18ropey = "1.3"
19smallvec = "1.7"
19smallvec = "1.7"
@@ -23,7 +23,7 @@ unicode-width = "0.1"
23unicode-general-category = "0.4"
23unicode-general-category = "0.4"
24# slab = "0.4.2"
24# slab = "0.4.2"
25tree-sitter = "0.20"
25tree-sitter = "0.20"
26−once_cell = "1.8"
26+once_cell = "1.9"
27arc-swap = "1"
27arc-swap = "1"
28regex = "1"
28regex = "1"
29
29
@@ -35,6 +35,7 @@ toml = "0.5"
35similar = "2.1"
35similar = "2.1"
36
36
37etcetera = "0.3"
37etcetera = "0.3"
38+encoding_rs = "0.8"
38
39
39chrono = { version = "0.4", default-features = false, features = ["alloc", "std"] }
40chrono = { version = "0.4", default-features = false, features = ["alloc", "std"] }
40
41
helix-core/src/auto_pairs.rs+255−80
@@ -1,7 +1,7 @@
1//! When typing the opening character of one of the possible pairs defined below,
1//! When typing the opening character of one of the possible pairs defined below,
2//! this module provides the functionality to insert the paired closing character.
2//! this module provides the functionality to insert the paired closing character.
3
3
4−use crate::{Range, Rope, Selection, Tendril, Transaction};
4+use crate::{movement::Direction, Range, Rope, Selection, Tendril, Transaction};
5use log::debug;
5use log::debug;
6use smallvec::SmallVec;
6use smallvec::SmallVec;
7
7
@@ -30,7 +30,6 @@ const CLOSE_BEFORE: &str = ")]}'\":;,> \n\r\u{000B}\u{000C}\u{0085}\u{2028}\u{20
30
30
31// [TODO]
31// [TODO]
32// * delete implementation where it erases the whole bracket (|) -> |
32// * delete implementation where it erases the whole bracket (|) -> |
33−// * do not reduce to cursors; use whole selections, and surround with pair
34// * change to multi character pairs to handle cases like placing the cursor in the
33// * change to multi character pairs to handle cases like placing the cursor in the
35// middle of triple quotes, and more exotic pairs like Jinja's {% %}
34// middle of triple quotes, and more exotic pairs like Jinja's {% %}
36
35
@@ -38,20 +37,18 @@ const CLOSE_BEFORE: &str = ")]}'\":;,> \n\r\u{000B}\u{000C}\u{0085}\u{2028}\u{20
38pub fn hook(doc: &Rope, selection: &Selection, ch: char) -> Option<Transaction> {
37pub fn hook(doc: &Rope, selection: &Selection, ch: char) -> Option<Transaction> {
39 debug!("autopairs hook selection: {:#?}", selection);
38 debug!("autopairs hook selection: {:#?}", selection);
40
39
41− let cursors = selection.clone().cursors(doc.slice(..));
42−
43 for &(open, close) in PAIRS {
40 for &(open, close) in PAIRS {
44 if open == ch {
41 if open == ch {
45 if open == close {
42 if open == close {
46− return Some(handle_same(doc, &cursors, open, CLOSE_BEFORE, OPEN_BEFORE));
43+ return Some(handle_same(doc, selection, open, CLOSE_BEFORE, OPEN_BEFORE));
47 } else {
44 } else {
48− return Some(handle_open(doc, &cursors, open, close, CLOSE_BEFORE));
45+ return Some(handle_open(doc, selection, open, close, CLOSE_BEFORE));
49 }
46 }
50 }
47 }
51
48
52 if close == ch {
49 if close == ch {
53 // && char_at pos == close
50 // && char_at pos == close
54− return Some(handle_close(doc, &cursors, open, close));
51+ return Some(handle_close(doc, selection, open, close));
55 }
52 }
56 }
53 }
57
54
@@ -66,6 +63,36 @@ fn prev_char(doc: &Rope, pos: usize) -> Option<char> {
66 doc.get_char(pos - 1)
63 doc.get_char(pos - 1)
67}
64}
68
65
66+/// calculate what the resulting range should be for an auto pair insertion
67+fn get_next_range(
68+ start_range: &Range,
69+ offset: usize,
70+ typed_char: char,
71+ len_inserted: usize,
72+) -> Range {
73+ let end_head = start_range.head + offset + typed_char.len_utf8();
74+
75+ let end_anchor = match (start_range.len(), start_range.direction()) {
76+ // if we have a zero width cursor, it shifts to the same number
77+ (0, _) => end_head,
78+
79+ // if we are inserting for a regular one-width cursor, the anchor
80+ // moves with the head
81+ (1, Direction::Forward) => end_head - 1,
82+ (1, Direction::Backward) => end_head + 1,
83+
84+ // if we are appending, the anchor stays where it is; only offset
85+ // for multiple range insertions
86+ (_, Direction::Forward) => start_range.anchor + offset,
87+
88+ // when we are inserting in front of a selection, we need to move
89+ // the anchor over by however many characters were inserted overall
90+ (_, Direction::Backward) => start_range.anchor + offset + len_inserted,
91+ };
92+
93+ Range::new(end_anchor, end_head)
94+}
95+
69fn handle_open(
96fn handle_open(
70 doc: &Rope,
97 doc: &Rope,
71 selection: &Selection,
98 selection: &Selection,
@@ -74,36 +101,32 @@ fn handle_open(
74 close_before: &str,
101 close_before: &str,
75) -> Transaction {
102) -> Transaction {
76 let mut end_ranges = SmallVec::with_capacity(selection.len());
103 let mut end_ranges = SmallVec::with_capacity(selection.len());
77−
78 let mut offs = 0;
104 let mut offs = 0;
79
105
80 let transaction = Transaction::change_by_selection(doc, selection, |start_range| {
106 let transaction = Transaction::change_by_selection(doc, selection, |start_range| {
81− let start_head = start_range.head;
107+ let cursor = start_range.cursor(doc.slice(..));
82−
83− let next = doc.get_char(start_head);
108+ let next_char = doc.get_char(cursor);
84− let end_head = start_head + offs + open.len_utf8();
109+ let len_inserted;
85−
86− let end_anchor = if start_range.is_empty() {
87− end_head
88− } else {
89− start_range.anchor + offs
90− };
91−
92− end_ranges.push(Range::new(end_anchor, end_head));
93
110
94− match next {
111+ let change = match next_char {
95 Some(ch) if !close_before.contains(ch) => {
112 Some(ch) if !close_before.contains(ch) => {
96− offs += open.len_utf8();
113+ len_inserted = open.len_utf8();
97− (start_head, start_head, Some(Tendril::from_char(open)))
114+ (cursor, cursor, Some(Tendril::from_char(open)))
98 }
115 }
99 // None | Some(ch) if close_before.contains(ch) => {}
116 // None | Some(ch) if close_before.contains(ch) => {}
100 _ => {
117 _ => {
101 // insert open & close
118 // insert open & close
102 let pair = Tendril::from_iter([open, close]);
119 let pair = Tendril::from_iter([open, close]);
103− offs += open.len_utf8() + close.len_utf8();
120+ len_inserted = open.len_utf8() + close.len_utf8();
104− (start_head, start_head, Some(pair))
121+ (cursor, cursor, Some(pair))
105 }
122 }
106− }
123+ };
124+
125+ let next_range = get_next_range(start_range, offs, open, len_inserted);
126+ end_ranges.push(next_range);
127+ offs += len_inserted;
128+
129+ change
107 });
130 });
108
131
109 let t = transaction.with_selection(Selection::new(end_ranges, selection.primary_index()));
132 let t = transaction.with_selection(Selection::new(end_ranges, selection.primary_index()));
@@ -117,28 +140,28 @@ fn handle_close(doc: &Rope, selection: &Selection, _open: char, close: char) ->
117 let mut offs = 0;
140 let mut offs = 0;
118
141
119 let transaction = Transaction::change_by_selection(doc, selection, |start_range| {
142 let transaction = Transaction::change_by_selection(doc, selection, |start_range| {
120− let start_head = start_range.head;
143+ let cursor = start_range.cursor(doc.slice(..));
121− let next = doc.get_char(start_head);
144+ let next_char = doc.get_char(cursor);
122− let end_head = start_head + offs + close.len_utf8();
145+ let mut len_inserted = 0;
123
146
124− let end_anchor = if start_range.is_empty() {
147+ let change = if next_char == Some(close) {
125− end_head
148+ // return transaction that moves past close
149+ (cursor, cursor, None) // no-op
126 } else {
150 } else {
127− start_range.anchor + offs
151+ len_inserted += close.len_utf8();
152+ (cursor, cursor, Some(Tendril::from_char(close)))
128 };
153 };
129
154
155+ let next_range = get_next_range(start_range, offs, close, len_inserted);
130− end_ranges.push(Range::new(end_anchor, end_head));
156+ end_ranges.push(next_range);
157+ offs += len_inserted;
131
158
132− if next == Some(close) {
159+ change
133− // return transaction that moves past close
134− (start_head, start_head, None) // no-op
135− } else {
136− offs += close.len_utf8();
137− (start_head, start_head, Some(Tendril::from_char(close)))
138− }
139 });
160 });
140
161
141− transaction.with_selection(Selection::new(end_ranges, selection.primary_index()))
162+ let t = transaction.with_selection(Selection::new(end_ranges, selection.primary_index()));
163+ debug!("auto pair transaction: {:#?}", t);
164+ t
142}
165}
143
166
144/// handle cases where open and close is the same, or in triples ("""docstring""")
167/// handle cases where open and close is the same, or in triples ("""docstring""")
@@ -154,42 +177,41 @@ fn handle_same(
154 let mut offs = 0;
177 let mut offs = 0;
155
178
156 let transaction = Transaction::change_by_selection(doc, selection, |start_range| {
179 let transaction = Transaction::change_by_selection(doc, selection, |start_range| {
157− let start_head = start_range.head;
180+ let cursor = start_range.cursor(doc.slice(..));
158− let end_head = start_head + offs + token.len_utf8();
181+ let mut len_inserted = 0;
159
182
160− // if selection, retain anchor, if cursor, move over
183+ let next_char = doc.get_char(cursor);
161− let end_anchor = if start_range.is_empty() {
184+ let prev_char = prev_char(doc, cursor);
162− end_head
163− } else {
164− start_range.anchor + offs
165− };
166
185
167− end_ranges.push(Range::new(end_anchor, end_head));
168−
169− let next = doc.get_char(start_head);
170− let prev = prev_char(doc, start_head);
171−
172− if next == Some(token) {
186+ let change = if next_char == Some(token) {
173 // return transaction that moves past close
187 // return transaction that moves past close
174− (start_head, start_head, None) // no-op
188+ (cursor, cursor, None) // no-op
175 } else {
189 } else {
176 let mut pair = Tendril::with_capacity(2 * token.len_utf8() as u32);
190 let mut pair = Tendril::with_capacity(2 * token.len_utf8() as u32);
177 pair.push_char(token);
191 pair.push_char(token);
178
192
179 // for equal pairs, don't insert both open and close if either
193 // for equal pairs, don't insert both open and close if either
180 // side has a non-pair char
194 // side has a non-pair char
181− if (next.is_none() || close_before.contains(next.unwrap()))
195+ if (next_char.is_none() || close_before.contains(next_char.unwrap()))
182− && (prev.is_none() || open_before.contains(prev.unwrap()))
196+ && (prev_char.is_none() || open_before.contains(prev_char.unwrap()))
183 {
197 {
184 pair.push_char(token);
198 pair.push_char(token);
185 }
199 }
186
200
187− offs += pair.len();
201+ len_inserted += pair.len();
188− (start_head, start_head, Some(pair))
202+ (cursor, cursor, Some(pair))
189− }
203+ };
204+
205+ let next_range = get_next_range(start_range, offs, token, len_inserted);
206+ end_ranges.push(next_range);
207+ offs += len_inserted;
208+
209+ change
190 });
210 });
191
211
192− transaction.with_selection(Selection::new(end_ranges, selection.primary_index()))
212+ let t = transaction.with_selection(Selection::new(end_ranges, selection.primary_index()));
213+ debug!("auto pair transaction: {:#?}", t);
214+ t
193}
215}
194
216
195#[cfg(test)]
217#[cfg(test)]
@@ -252,7 +274,20 @@ mod test {
252 &Selection::single(1, 0),
274 &Selection::single(1, 0),
253 PAIRS,
275 PAIRS,
254 |open, close| format!("{}{}", open, close),
276 |open, close| format!("{}{}", open, close),
255− &Selection::single(1, 1),
277+ &Selection::single(2, 1),
278+ );
279+ }
280+
281+ /// [] -> append ( -> ([])
282+ #[test]
283+ fn test_append_blank() {
284+ test_hooks_with_pairs(
285+ // this is what happens when you have a totally blank document and then append
286+ &Rope::from("\n\n"),
287+ &Selection::single(0, 2),
288+ PAIRS,
289+ |open, close| format!("\n{}{}\n", open, close),
290+ &Selection::single(0, 3),
256 );
291 );
257 }
292 }
258
293
@@ -276,26 +311,50 @@ mod test {
276 )
311 )
277 },
312 },
278 &Selection::new(
313 &Selection::new(
279− smallvec!(Range::point(1), Range::point(4), Range::point(7),),
314+ smallvec!(Range::new(2, 1), Range::new(5, 4), Range::new(8, 7),),
280 0,
315 0,
281 ),
316 ),
282 );
317 );
283 }
318 }
284
319
285− // [TODO] broken until it works with selections
286 /// fo[o] -> append ( -> fo[o(])
320 /// fo[o] -> append ( -> fo[o(])
287− #[ignore]
288 #[test]
321 #[test]
289 fn test_append() {
322 fn test_append() {
290 test_hooks_with_pairs(
323 test_hooks_with_pairs(
291− &Rope::from("foo"),
324+ &Rope::from("foo\n"),
292 &Selection::single(2, 4),
325 &Selection::single(2, 4),
293− PAIRS,
326+ differing_pairs(),
294− |open, close| format!("foo{}{}", open, close),
327+ |open, close| format!("foo{}{}\n", open, close),
295 &Selection::single(2, 5),
328 &Selection::single(2, 5),
296 );
329 );
297 }
330 }
298
331
332+ /// fo[o] fo[o(])
333+ /// fo[o] -> append ( -> fo[o(])
334+ /// fo[o] fo[o(])
335+ #[test]
336+ fn test_append_multi() {
337+ test_hooks_with_pairs(
338+ &Rope::from("foo\nfoo\nfoo\n"),
339+ &Selection::new(
340+ smallvec!(Range::new(2, 4), Range::new(6, 8), Range::new(10, 12)),
341+ 0,
342+ ),
343+ differing_pairs(),
344+ |open, close| {
345+ format!(
346+ "foo{open}{close}\nfoo{open}{close}\nfoo{open}{close}\n",
347+ open = open,
348+ close = close
349+ )
350+ },
351+ &Selection::new(
352+ smallvec!(Range::new(2, 5), Range::new(8, 11), Range::new(14, 17)),
353+ 0,
354+ ),
355+ );
356+ }
357+
299 /// ([]) -> insert ) -> ()[]
358 /// ([]) -> insert ) -> ()[]
300 #[test]
359 #[test]
301 fn test_insert_close_inside_pair() {
360 fn test_insert_close_inside_pair() {
@@ -307,7 +366,23 @@ mod test {
307 &Selection::single(2, 1),
366 &Selection::single(2, 1),
308 *close,
367 *close,
309 &doc,
368 &doc,
310− &Selection::point(2),
369+ &Selection::single(3, 2),
370+ );
371+ }
372+ }
373+
374+ /// [(]) -> append ) -> [()]
375+ #[test]
376+ fn test_append_close_inside_pair() {
377+ for (open, close) in PAIRS {
378+ let doc = Rope::from(format!("{}{}\n", open, close));
379+
380+ test_hooks(
381+ &doc,
382+ &Selection::single(0, 2),
383+ *close,
384+ &doc,
385+ &Selection::single(0, 3),
311 );
386 );
312 }
387 }
313 }
388 }
@@ -323,8 +398,33 @@ mod test {
323 );
398 );
324
399
325 let expected_sel = Selection::new(
400 let expected_sel = Selection::new(
326− // smallvec!(Range::new(3, 2), Range::new(6, 5), Range::new(9, 8),),
401+ smallvec!(Range::new(3, 2), Range::new(6, 5), Range::new(9, 8),),
402+ 0,
403+ );
404+
405+ for (open, close) in PAIRS {
406+ let doc = Rope::from(format!(
407+ "{open}{close}\n{open}{close}\n{open}{close}\n",
408+ open = open,
409+ close = close
410+ ));
411+
412+ test_hooks(&doc, &sel, *close, &doc, &expected_sel);
413+ }
414+ }
415+
416+ /// [(]) [()]
417+ /// [(]) -> append ) -> [()]
418+ /// [(]) [()]
419+ #[test]
420+ fn test_append_close_inside_pair_multi_cursor() {
421+ let sel = Selection::new(
327− smallvec!(Range::point(2), Range::point(5), Range::point(8),),
422+ smallvec!(Range::new(0, 2), Range::new(3, 5), Range::new(6, 8),),
423+ 0,
424+ );
425+
426+ let expected_sel = Selection::new(
427+ smallvec!(Range::new(0, 3), Range::new(3, 6), Range::new(6, 9),),
328 0,
428 0,
329 );
429 );
330
430
@@ -343,7 +443,7 @@ mod test {
343 #[test]
443 #[test]
344 fn test_insert_open_inside_pair() {
444 fn test_insert_open_inside_pair() {
345 let sel = Selection::single(2, 1);
445 let sel = Selection::single(2, 1);
346− let expected_sel = Selection::point(2);
446+ let expected_sel = Selection::single(3, 2);
347
447
348 for (open, close) in differing_pairs() {
448 for (open, close) in differing_pairs() {
349 let doc = Rope::from(format!("{}{}", open, close));
449 let doc = Rope::from(format!("{}{}", open, close));
@@ -357,11 +457,49 @@ mod test {
357 }
457 }
358 }
458 }
359
459
460+ /// [word(]) -> append ( -> [word((]))
461+ #[test]
462+ fn test_append_open_inside_pair() {
463+ let sel = Selection::single(0, 6);
464+ let expected_sel = Selection::single(0, 7);
465+
466+ for (open, close) in differing_pairs() {
467+ let doc = Rope::from(format!("word{}{}", open, close));
468+ let expected_doc = Rope::from(format!(
469+ "word{open}{open}{close}{close}",
470+ open = open,
471+ close = close
472+ ));
473+
474+ test_hooks(&doc, &sel, *open, &expected_doc, &expected_sel);
475+ }
476+ }
477+
360 /// ([]) -> insert " -> ("[]")
478 /// ([]) -> insert " -> ("[]")
361 #[test]
479 #[test]
362 fn test_insert_nested_open_inside_pair() {
480 fn test_insert_nested_open_inside_pair() {
363 let sel = Selection::single(2, 1);
481 let sel = Selection::single(2, 1);
364− let expected_sel = Selection::point(2);
482+ let expected_sel = Selection::single(3, 2);
483+
484+ for (outer_open, outer_close) in differing_pairs() {
485+ let doc = Rope::from(format!("{}{}", outer_open, outer_close,));
486+
487+ for (inner_open, inner_close) in matching_pairs() {
488+ let expected_doc = Rope::from(format!(
489+ "{}{}{}{}",
490+ outer_open, inner_open, inner_close, outer_close
491+ ));
492+
493+ test_hooks(&doc, &sel, *inner_open, &expected_doc, &expected_sel);
494+ }
495+ }
496+ }
497+
498+ /// [(]) -> append " -> [("]")
499+ #[test]
500+ fn test_append_nested_open_inside_pair() {
501+ let sel = Selection::single(0, 2);
502+ let expected_sel = Selection::single(0, 3);
365
503
366 for (outer_open, outer_close) in differing_pairs() {
504 for (outer_open, outer_close) in differing_pairs() {
367 let doc = Rope::from(format!("{}{}", outer_open, outer_close,));
505 let doc = Rope::from(format!("{}{}", outer_open, outer_close,));
@@ -385,21 +523,44 @@ mod test {
385 &Selection::single(1, 0),
523 &Selection::single(1, 0),
386 PAIRS,
524 PAIRS,
387 |open, _| format!("{}word", open),
525 |open, _| format!("{}word", open),
388− &Selection::point(1),
526+ &Selection::single(2, 1),
389 )
527 )
390 }
528 }
391
529
392− // [TODO] broken until it works with selections
393 /// [wor]d -> insert ( -> ([wor]d
530 /// [wor]d -> insert ( -> ([wor]d
394 #[test]
531 #[test]
395− #[ignore]
396 fn test_insert_open_with_selection() {
532 fn test_insert_open_with_selection() {
397 test_hooks_with_pairs(
533 test_hooks_with_pairs(
398 &Rope::from("word"),
534 &Rope::from("word"),
399− &Selection::single(0, 4),
535+ &Selection::single(3, 0),
400 PAIRS,
536 PAIRS,
401 |open, _| format!("{}word", open),
537 |open, _| format!("{}word", open),
402− &Selection::single(1, 5),
538+ &Selection::single(4, 1),
539+ )
540+ }
541+
542+ /// [wor]d -> append ) -> [wor)]d
543+ #[test]
544+ fn test_append_close_inside_non_pair_with_selection() {
545+ let sel = Selection::single(0, 4);
546+ let expected_sel = Selection::single(0, 5);
547+
548+ for (_, close) in PAIRS {
549+ let doc = Rope::from("word");
550+ let expected_doc = Rope::from(format!("wor{}d", close));
551+ test_hooks(&doc, &sel, *close, &expected_doc, &expected_sel);
552+ }
553+ }
554+
555+ /// foo[ wor]d -> insert ( -> foo([) wor]d
556+ #[test]
557+ fn test_insert_open_trailing_word_with_selection() {
558+ test_hooks_with_pairs(
559+ &Rope::from("foo word"),
560+ &Selection::single(7, 3),
561+ differing_pairs(),
562+ |open, close| format!("foo{}{} word", open, close),
563+ &Selection::single(9, 4),
403 )
564 )
404 }
565 }
405
566
@@ -413,7 +574,7 @@ mod test {
413 fn test_insert_open_after_non_pair() {
574 fn test_insert_open_after_non_pair() {
414 let doc = Rope::from("word");
575 let doc = Rope::from("word");
415 let sel = Selection::single(5, 4);
576 let sel = Selection::single(5, 4);
416− let expected_sel = Selection::point(5);
577+ let expected_sel = Selection::single(6, 5);
417
578
418 test_hooks_with_pairs(
579 test_hooks_with_pairs(
419 &doc,
580 &doc,
@@ -431,4 +592,18 @@ mod test {
431 &expected_sel,
592 &expected_sel,
432 );
593 );
433 }
594 }
595+
596+ /// appending with only a cursor should stay a cursor
597+ ///
598+ /// [] -> append to end "foo -> "foo[]"
599+ #[test]
600+ fn test_append_single_cursor() {
601+ test_hooks_with_pairs(
602+ &Rope::from("\n"),
603+ &Selection::single(0, 1),
604+ PAIRS,
605+ |open, close| format!("{}{}\n", open, close),
606+ &Selection::single(1, 2),
607+ );
608+ }
434}
609}
helix-core/src/diagnostic.rs+11−4
@@ -1,12 +1,19 @@
1//! LSP diagnostic utility types.
1//! LSP diagnostic utility types.
2+use serde::{Deserialize, Serialize};
2
3
3/// Describes the severity level of a [`Diagnostic`].
4/// Describes the severity level of a [`Diagnostic`].
4−#[derive(Debug, Clone, Copy, Eq, PartialEq)]
5+#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Deserialize, Serialize)]
5pub enum Severity {
6pub enum Severity {
6− Error,
7− Warning,
8− Info,
9 Hint,
7 Hint,
8+ Info,
9+ Warning,
10+ Error,
11+}
12+
13+impl Default for Severity {
14+ fn default() -> Self {
15+ Self::Hint
16+ }
10}
17}
11
18
12/// A range of `char`s within the text.
19/// A range of `char`s within the text.
helix-core/src/indent.rs+65−78
@@ -1,6 +1,5 @@
1use crate::{
1use crate::{
2 chars::{char_is_line_ending, char_is_whitespace},
2 chars::{char_is_line_ending, char_is_whitespace},
3− find_first_non_whitespace_char,
4 syntax::{IndentQuery, LanguageConfiguration, Syntax},
3 syntax::{IndentQuery, LanguageConfiguration, Syntax},
5 tree_sitter::Node,
4 tree_sitter::Node,
6 Rope, RopeSlice,
5 Rope, RopeSlice,
@@ -174,8 +173,7 @@ pub fn auto_detect_indent_style(document_text: &Rope) -> Option<IndentStyle> {
174
173
175/// To determine indentation of a newly inserted line, figure out the indentation at the last col
174/// To determine indentation of a newly inserted line, figure out the indentation at the last col
176/// of the previous line.
175/// of the previous line.
177−#[allow(dead_code)]
178−fn indent_level_for_line(line: RopeSlice, tab_width: usize) -> usize {
176+pub fn indent_level_for_line(line: RopeSlice, tab_width: usize) -> usize {
179 let mut len = 0;
177 let mut len = 0;
180 for ch in line.chars() {
178 for ch in line.chars() {
181 match ch {
179 match ch {
@@ -210,10 +208,15 @@ fn get_highest_syntax_node_at_bytepos(syntax: &Syntax, pos: usize) -> Option<Nod
210 Some(node)
208 Some(node)
211}
209}
212
210
211+/// Calculate the indentation at a given treesitter node.
212+/// If newline is false, then any "indent" nodes on the line are ignored ("outdent" still applies).
213+/// This is because the indentation is only increased starting at the second line of the node.
213−fn calculate_indentation(query: &IndentQuery, node: Option<Node>, newline: bool) -> usize {
214+fn calculate_indentation(
214− // NOTE: can't use contains() on query because of comparing Vec<String> and &str
215+ query: &IndentQuery,
215− // https://doc.rust-lang.org/std/vec/struct.Vec.html#method.contains
216+ node: Option<Node>,
216−
217+ line: usize,
218+ newline: bool,
219+) -> usize {
217 let mut increment: isize = 0;
220 let mut increment: isize = 0;
218
221
219 let mut node = match node {
222 let mut node = match node {
@@ -221,70 +224,45 @@ fn calculate_indentation(query: &IndentQuery, node: Option<Node>, newline: bool)
221 None => return 0,
224 None => return 0,
222 };
225 };
223
226
224− let mut prev_start = node.start_position().row;
227+ let mut current_line = line;
225−
228+ let mut consider_indent = newline;
226− // if we're calculating indentation for a brand new line then the current node will become the
229+ let mut increment_from_line: isize = 0;
227− // parent node. We need to take it's indentation level into account too.
230+
228− let node_kind = node.kind();
231+ loop {
229− if newline && query.indent.contains(node_kind) {
232+ let node_kind = node.kind();
230− increment += 1;
231− }
232−
233− while let Some(parent) = node.parent() {
234− let parent_kind = parent.kind();
235− let start = parent.start_position().row;
233+ let start = node.start_position().row;
236−
234+ if current_line != start {
237− // detect deeply nested indents in the same line
235+ // Indent/dedent by at most one per line:
238− // .map(|a| { <-- ({ is two scopes
236+ // .map(|a| { <-- ({ is two scopes
239− // let len = 1; <-- indents one level
237+ // let len = 1; <-- indents one level
240− // }) <-- }) is two scopes
238+ // }) <-- }) is two scopes
241− let starts_same_line = start == prev_start;
239+ if consider_indent || increment_from_line < 0 {
242−
240+ increment += increment_from_line.signum();
243− if query.outdent.contains(node.kind()) && !starts_same_line {
241+ }
244− // we outdent by skipping the rules for the current level and jumping up
242+ increment_from_line = 0;
245− // node = parent;
243+ current_line = start;
246− increment -= 1;
244+ consider_indent = true;
247− // continue;
248 }
245 }
249
246
250− if query.indent.contains(parent_kind) // && not_first_or_last_sibling
247+ if query.outdent.contains(node_kind) {
251− && !starts_same_line
248+ increment_from_line -= 1;
252− {
249+ }
253− // println!("is_scope {}", parent_kind);
250+ if query.indent.contains(node_kind) {
254− prev_start = start;
251+ increment_from_line += 1;
255− increment += 1
256 }
252 }
257
253
258− // if last_scope && increment > 0 && ...{ ignore }
254+ if let Some(parent) = node.parent() {
259−
255+ node = parent;
260− node = parent;
256+ } else {
257+ break;
258+ }
259+ }
260+ if consider_indent || increment_from_line < 0 {
261+ increment += increment_from_line.signum();
261 }
262 }
262−
263 increment.max(0) as usize
263 increment.max(0) as usize
264}
264}
265
265
266−#[allow(dead_code)]
267−fn suggested_indent_for_line(
268− language_config: &LanguageConfiguration,
269− syntax: Option<&Syntax>,
270− text: RopeSlice,
271− line_num: usize,
272− _tab_width: usize,
273−) -> usize {
274− if let Some(start) = find_first_non_whitespace_char(text.line(line_num)) {
275− return suggested_indent_for_pos(
276− Some(language_config),
277− syntax,
278− text,
279− start + text.line_to_char(line_num),
280− false,
281− );
282− };
283−
284− // if the line is blank, indent should be zero
285− 0
286−}
287−
288// TODO: two usecases: if we are triggering this for a new, blank line:
266// TODO: two usecases: if we are triggering this for a new, blank line:
289// - it should return 0 when mass indenting stuff
267// - it should return 0 when mass indenting stuff
290// - it should look up the wrapper node and count it too when we press o/O
268// - it should look up the wrapper node and count it too when we press o/O
@@ -293,23 +271,20 @@ pub fn suggested_indent_for_pos(
293 syntax: Option<&Syntax>,
271 syntax: Option<&Syntax>,
294 text: RopeSlice,
272 text: RopeSlice,
295 pos: usize,
273 pos: usize,
274+ line: usize,
296 new_line: bool,
275 new_line: bool,
297−) -> usize {
276+) -> Option<usize> {
298 if let (Some(query), Some(syntax)) = (
277 if let (Some(query), Some(syntax)) = (
299 language_config.and_then(|config| config.indent_query()),
278 language_config.and_then(|config| config.indent_query()),
300 syntax,
279 syntax,
301 ) {
280 ) {
302 let byte_start = text.char_to_byte(pos);
281 let byte_start = text.char_to_byte(pos);
303 let node = get_highest_syntax_node_at_bytepos(syntax, byte_start);
282 let node = get_highest_syntax_node_at_bytepos(syntax, byte_start);
304−
305− // let config = load indentation query config from Syntax(should contain language_config)
306−
307 // TODO: special case for comments
283 // TODO: special case for comments
308 // TODO: if preserve_leading_whitespace
284 // TODO: if preserve_leading_whitespace
309− calculate_indentation(query, node, new_line)
285+ Some(calculate_indentation(query, node, line, new_line))
310 } else {
286 } else {
311− // TODO: heuristics for non-tree sitter grammars
287+ None
312− 0
313 }
288 }
314}
289}
315
290
@@ -442,6 +417,7 @@ where
442 );
417 );
443
418
444 let doc = Rope::from(doc);
419 let doc = Rope::from(doc);
420+ use crate::diagnostic::Severity;
445 use crate::syntax::{
421 use crate::syntax::{
446 Configuration, IndentationConfiguration, LanguageConfiguration, Loader,
422 Configuration, IndentationConfiguration, LanguageConfiguration, Loader,
447 };
423 };
@@ -459,6 +435,8 @@ where
459 roots: vec![],
435 roots: vec![],
460 comment_token: None,
436 comment_token: None,
461 auto_format: false,
437 auto_format: false,
438+ diagnostic_severity: Severity::Warning,
439+ tree_sitter_library: None,
462 language_server: None,
440 language_server: None,
463 indent: Some(IndentationConfiguration {
441 indent: Some(IndentationConfiguration {
464 tab_width: 4,
442 tab_width: 4,
@@ -482,14 +460,23 @@ where
482
460
483 for i in 0..doc.len_lines() {
461 for i in 0..doc.len_lines() {
484 let line = text.line(i);
462 let line = text.line(i);
463+ if let Some(pos) = crate::find_first_non_whitespace_char(line) {
485− let indent = indent_level_for_line(line, tab_width);
464+ let indent = indent_level_for_line(line, tab_width);
486− assert_eq!(
465+ assert_eq!(
487− suggested_indent_for_line(&language_config, Some(&syntax), text, i, tab_width),
466+ suggested_indent_for_pos(
467+ Some(&language_config),
468+ Some(&syntax),
469+ text,
470+ text.line_to_char(i) + pos,
471+ i,
472+ false
473+ ),
488− indent,
474+ Some(indent),
489− "line {}: {}",
475+ "line {}: \"{}\"",
490− i,
476+ i,
491− line
477+ line
492− );
478+ );
479+ }
493 }
480 }
494 }
481 }
495}
482}
helix-core/src/lib.rs+28−6
@@ -1,3 +1,5 @@
1+pub use encoding_rs as encoding;
2+
1pub mod auto_pairs;
3pub mod auto_pairs;
2pub mod chars;
4pub mod chars;
3pub mod comment;
5pub mod comment;
@@ -37,8 +39,14 @@ pub fn find_first_non_whitespace_char(line: RopeSlice) -> Option<usize> {
37 line.chars().position(|ch| !ch.is_whitespace())
39 line.chars().position(|ch| !ch.is_whitespace())
38}
40}
39
41
40−/// Find `.git` root.
42+/// Find project root.
43+///
44+/// Order of detection:
45+/// * Top-most folder containing a root marker in current git repository
46+/// * Git repostory root if no marker detected
47+/// * Top-most folder containing a root marker if not git repository detected
48+/// * Current working directory as fallback
41−pub fn find_root(root: Option<&str>) -> Option<std::path::PathBuf> {
49+pub fn find_root(root: Option<&str>, root_markers: &[String]) -> Option<std::path::PathBuf> {
42 let current_dir = std::env::current_dir().expect("unable to determine current directory");
50 let current_dir = std::env::current_dir().expect("unable to determine current directory");
43
51
44 let root = match root {
52 let root = match root {
@@ -50,16 +58,30 @@ pub fn find_root(root: Option<&str>) -> Option<std::path::PathBuf> {
50 current_dir.join(root)
58 current_dir.join(root)
51 }
59 }
52 }
60 }
53− None => current_dir,
61+ None => current_dir.clone(),
54 };
62 };
55
63
64+ let mut top_marker = None;
56 for ancestor in root.ancestors() {
65 for ancestor in root.ancestors() {
57− // TODO: also use defined roots if git isn't found
66+ for marker in root_markers {
67+ if ancestor.join(marker).exists() {
68+ top_marker = Some(ancestor);
69+ break;
70+ }
71+ }
72+ // don't go higher than repo
58 if ancestor.join(".git").is_dir() {
73 if ancestor.join(".git").is_dir() {
74+ // Use workspace if detected from marker
59− return Some(ancestor.to_path_buf());
75+ return Some(top_marker.unwrap_or(ancestor).to_path_buf());
60 }
76 }
61 }
77 }
62− None
78+
79+ // In absence of git repo, use workspace if detected
80+ if top_marker.is_some() {
81+ top_marker.map(|a| a.to_path_buf())
82+ } else {
83+ Some(current_dir)
84+ }
63}
85}
64
86
65pub fn runtime_dir() -> std::path::PathBuf {
87pub fn runtime_dir() -> std::path::PathBuf {
helix-core/src/match_brackets.rs+1−1
@@ -11,7 +11,7 @@ const PAIRS: &[(char, char)] = &[
11 ('\"', '\"'),
11 ('\"', '\"'),
12];
12];
13
13
14−// limit matching pairs to only ( ) { } [ ] < >
14+// limit matching pairs to only ( ) { } [ ] < > ' ' " "
15
15
16// Returns the position of the matching bracket under cursor.
16// Returns the position of the matching bracket under cursor.
17//
17//
helix-core/src/movement.rs+25−27
@@ -307,8 +307,6 @@ fn reached_target(target: WordMotionTarget, prev_ch: char, next_ch: char) -> boo
307
307
308#[cfg(test)]
308#[cfg(test)]
309mod test {
309mod test {
310− use std::array::{self, IntoIter};
311−
312 use ropey::Rope;
310 use ropey::Rope;
313
311
314 use super::*;
312 use super::*;
@@ -360,7 +358,7 @@ mod test {
360 ((Direction::Backward, 999usize), (0, 0)), // |This is a simple alphabetic line
358 ((Direction::Backward, 999usize), (0, 0)), // |This is a simple alphabetic line
361 ];
359 ];
362
360
363− for ((direction, amount), coordinates) in IntoIter::new(moves_and_expected_coordinates) {
361+ for ((direction, amount), coordinates) in moves_and_expected_coordinates {
364 range = move_horizontally(slice, range, direction, amount, Movement::Move);
362 range = move_horizontally(slice, range, direction, amount, Movement::Move);
365 assert_eq!(coords_at_pos(slice, range.head), coordinates.into())
363 assert_eq!(coords_at_pos(slice, range.head), coordinates.into())
366 }
364 }
@@ -374,7 +372,7 @@ mod test {
374
372
375 let mut range = Range::point(position);
373 let mut range = Range::point(position);
376
374
377− let moves_and_expected_coordinates = IntoIter::new([
375+ let moves_and_expected_coordinates = [
378 ((Direction::Forward, 11usize), (1, 1)), // Multiline\nt|ext sample\n...
376 ((Direction::Forward, 11usize), (1, 1)), // Multiline\nt|ext sample\n...
379 ((Direction::Backward, 1usize), (1, 0)), // Multiline\n|text sample\n...
377 ((Direction::Backward, 1usize), (1, 0)), // Multiline\n|text sample\n...
380 ((Direction::Backward, 5usize), (0, 5)), // Multi|line\ntext sample\n...
378 ((Direction::Backward, 5usize), (0, 5)), // Multi|line\ntext sample\n...
@@ -384,7 +382,7 @@ mod test {
384 ((Direction::Backward, 0usize), (0, 3)), // Mul|tiline\ntext sample\n...
382 ((Direction::Backward, 0usize), (0, 3)), // Mul|tiline\ntext sample\n...
385 ((Direction::Forward, 999usize), (5, 0)), // ...and whitespaced\n|
383 ((Direction::Forward, 999usize), (5, 0)), // ...and whitespaced\n|
386 ((Direction::Forward, 999usize), (5, 0)), // ...and whitespaced\n|
384 ((Direction::Forward, 999usize), (5, 0)), // ...and whitespaced\n|
387− ]);
385+ ];
388
386
389 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
387 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
390 range = move_horizontally(slice, range, direction, amount, Movement::Move);
388 range = move_horizontally(slice, range, direction, amount, Movement::Move);
@@ -402,11 +400,11 @@ mod test {
402 let mut range = Range::point(position);
400 let mut range = Range::point(position);
403 let original_anchor = range.anchor;
401 let original_anchor = range.anchor;
404
402
405− let moves = IntoIter::new([
403+ let moves = [
406 (Direction::Forward, 1usize),
404 (Direction::Forward, 1usize),
407 (Direction::Forward, 5usize),
405 (Direction::Forward, 5usize),
408 (Direction::Backward, 3usize),
406 (Direction::Backward, 3usize),
409− ]);
407+ ];
410
408
411 for (direction, amount) in moves {
409 for (direction, amount) in moves {
412 range = move_horizontally(slice, range, direction, amount, Movement::Extend);
410 range = move_horizontally(slice, range, direction, amount, Movement::Extend);
@@ -420,7 +418,7 @@ mod test {
420 let slice = text.slice(..);
418 let slice = text.slice(..);
421 let position = pos_at_coords(slice, (0, 0).into(), true);
419 let position = pos_at_coords(slice, (0, 0).into(), true);
422 let mut range = Range::point(position);
420 let mut range = Range::point(position);
423− let moves_and_expected_coordinates = IntoIter::new([
421+ let moves_and_expected_coordinates = [
424 ((Direction::Forward, 1usize), (1, 0)),
422 ((Direction::Forward, 1usize), (1, 0)),
425 ((Direction::Forward, 2usize), (3, 0)),
423 ((Direction::Forward, 2usize), (3, 0)),
426 ((Direction::Forward, 1usize), (4, 0)),
424 ((Direction::Forward, 1usize), (4, 0)),
@@ -430,7 +428,7 @@ mod test {
430 ((Direction::Backward, 0usize), (4, 0)),
428 ((Direction::Backward, 0usize), (4, 0)),
431 ((Direction::Forward, 5), (5, 0)),
429 ((Direction::Forward, 5), (5, 0)),
432 ((Direction::Forward, 999usize), (5, 0)),
430 ((Direction::Forward, 999usize), (5, 0)),
433− ]);
431+ ];
434
432
435 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
433 for ((direction, amount), coordinates) in moves_and_expected_coordinates {
436 range = move_vertically(slice, range, direction, amount, Movement::Move);
434 range = move_vertically(slice, range, direction, amount, Movement::Move);
@@ -450,7 +448,7 @@ mod test {
450 H,
448 H,
451 V,
449 V,
452 }
450 }
453− let moves_and_expected_coordinates = IntoIter::new([
451+ let moves_and_expected_coordinates = [
454 // Places cursor at the end of line
452 // Places cursor at the end of line
455 ((Axis::H, Direction::Forward, 8usize), (0, 8)),
453 ((Axis::H, Direction::Forward, 8usize), (0, 8)),
456 // First descent preserves column as the target line is wider
454 // First descent preserves column as the target line is wider
@@ -463,7 +461,7 @@ mod test {
463 ((Axis::V, Direction::Backward, 999usize), (0, 8)),
461 ((Axis::V, Direction::Backward, 999usize), (0, 8)),
464 ((Axis::V, Direction::Forward, 4usize), (4, 8)),
462 ((Axis::V, Direction::Forward, 4usize), (4, 8)),
465 ((Axis::V, Direction::Forward, 999usize), (5, 0)),
463 ((Axis::V, Direction::Forward, 999usize), (5, 0)),
466− ]);
464+ ];
467
465
468 for ((axis, direction, amount), coordinates) in moves_and_expected_coordinates {
466 for ((axis, direction, amount), coordinates) in moves_and_expected_coordinates {
469 range = match axis {
467 range = match axis {
@@ -489,7 +487,7 @@ mod test {
489 H,
487 H,
490 V,
488 V,
491 }
489 }
492− let moves_and_expected_coordinates = IntoIter::new([
490+ let moves_and_expected_coordinates = [
493 // Places cursor at the fourth kana.
491 // Places cursor at the fourth kana.
494 ((Axis::H, Direction::Forward, 4), (0, 4)),
492 ((Axis::H, Direction::Forward, 4), (0, 4)),
495 // Descent places cursor at the 4th character.
493 // Descent places cursor at the 4th character.
@@ -498,7 +496,7 @@ mod test {
498 ((Axis::H, Direction::Backward, 1usize), (1, 3)),
496 ((Axis::H, Direction::Backward, 1usize), (1, 3)),
499 // Jumping back up 1 line.
497 // Jumping back up 1 line.
500 ((Axis::V, Direction::Backward, 1usize), (0, 3)),
498 ((Axis::V, Direction::Backward, 1usize), (0, 3)),
501− ]);
499+ ];
502
500
503 for ((axis, direction, amount), coordinates) in moves_and_expected_coordinates {
501 for ((axis, direction, amount), coordinates) in moves_and_expected_coordinates {
504 range = match axis {
502 range = match axis {
@@ -530,7 +528,7 @@ mod test {
530
528
531 #[test]
529 #[test]
532 fn test_behaviour_when_moving_to_start_of_next_words() {
530 fn test_behaviour_when_moving_to_start_of_next_words() {
533− let tests = array::IntoIter::new([
531+ let tests = [
534 ("Basic forward motion stops at the first space",
532 ("Basic forward motion stops at the first space",
535 vec![(1, Range::new(0, 0), Range::new(0, 6))]),
533 vec![(1, Range::new(0, 0), Range::new(0, 6))]),
536 (" Starting from a boundary advances the anchor",
534 (" Starting from a boundary advances the anchor",
@@ -604,7 +602,7 @@ mod test {
604 vec![
602 vec![
605 (1, Range::new(0, 0), Range::new(0, 6)),
603 (1, Range::new(0, 0), Range::new(0, 6)),
606 ]),
604 ]),
607− ]);
605+ ];
608
606
609 for (sample, scenario) in tests {
607 for (sample, scenario) in tests {
610 for (count, begin, expected_end) in scenario.into_iter() {
608 for (count, begin, expected_end) in scenario.into_iter() {
@@ -616,7 +614,7 @@ mod test {
616
614
617 #[test]
615 #[test]
618 fn test_behaviour_when_moving_to_start_of_next_long_words() {
616 fn test_behaviour_when_moving_to_start_of_next_long_words() {
619− let tests = array::IntoIter::new([
617+ let tests = [
620 ("Basic forward motion stops at the first space",
618 ("Basic forward motion stops at the first space",
621 vec![(1, Range::new(0, 0), Range::new(0, 6))]),
619 vec![(1, Range::new(0, 0), Range::new(0, 6))]),
622 (" Starting from a boundary advances the anchor",
620 (" Starting from a boundary advances the anchor",
@@ -688,7 +686,7 @@ mod test {
688 vec![
686 vec![
689 (1, Range::new(0, 0), Range::new(0, 8)),
687 (1, Range::new(0, 0), Range::new(0, 8)),
690 ]),
688 ]),
691− ]);
689+ ];
692
690
693 for (sample, scenario) in tests {
691 for (sample, scenario) in tests {
694 for (count, begin, expected_end) in scenario.into_iter() {
692 for (count, begin, expected_end) in scenario.into_iter() {
@@ -700,7 +698,7 @@ mod test {
700
698
701 #[test]
699 #[test]
702 fn test_behaviour_when_moving_to_start_of_previous_words() {
700 fn test_behaviour_when_moving_to_start_of_previous_words() {
703− let tests = array::IntoIter::new([
701+ let tests = [
704 ("Basic backward motion from the middle of a word",
702 ("Basic backward motion from the middle of a word",
705 vec![(1, Range::new(3, 3), Range::new(4, 0))]),
703 vec![(1, Range::new(3, 3), Range::new(4, 0))]),
706
704
@@ -773,7 +771,7 @@ mod test {
773 vec![
771 vec![
774 (1, Range::new(0, 6), Range::new(6, 0)),
772 (1, Range::new(0, 6), Range::new(6, 0)),
775 ]),
773 ]),
776− ]);
774+ ];
777
775
778 for (sample, scenario) in tests {
776 for (sample, scenario) in tests {
779 for (count, begin, expected_end) in scenario.into_iter() {
777 for (count, begin, expected_end) in scenario.into_iter() {
@@ -785,7 +783,7 @@ mod test {
785
783
786 #[test]
784 #[test]
787 fn test_behaviour_when_moving_to_start_of_previous_long_words() {
785 fn test_behaviour_when_moving_to_start_of_previous_long_words() {
788− let tests = array::IntoIter::new([
786+ let tests = [
789 (
787 (
790 "Basic backward motion from the middle of a word",
788 "Basic backward motion from the middle of a word",
791 vec![(1, Range::new(3, 3), Range::new(4, 0))],
789 vec![(1, Range::new(3, 3), Range::new(4, 0))],
@@ -870,7 +868,7 @@ mod test {
870 vec![
868 vec![
871 (1, Range::new(0, 8), Range::new(8, 0)),
869 (1, Range::new(0, 8), Range::new(8, 0)),
872 ]),
870 ]),
873− ]);
871+ ];
874
872
875 for (sample, scenario) in tests {
873 for (sample, scenario) in tests {
876 for (count, begin, expected_end) in scenario.into_iter() {
874 for (count, begin, expected_end) in scenario.into_iter() {
@@ -882,7 +880,7 @@ mod test {
882
880
883 #[test]
881 #[test]
884 fn test_behaviour_when_moving_to_end_of_next_words() {
882 fn test_behaviour_when_moving_to_end_of_next_words() {
885− let tests = array::IntoIter::new([
883+ let tests = [
886 ("Basic forward motion from the start of a word to the end of it",
884 ("Basic forward motion from the start of a word to the end of it",
887 vec![(1, Range::new(0, 0), Range::new(0, 5))]),
885 vec![(1, Range::new(0, 0), Range::new(0, 5))]),
888 ("Basic forward motion from the end of a word to the end of the next",
886 ("Basic forward motion from the end of a word to the end of the next",
@@ -954,7 +952,7 @@ mod test {
954 vec![
952 vec![
955 (1, Range::new(0, 0), Range::new(0, 5)),
953 (1, Range::new(0, 0), Range::new(0, 5)),
956 ]),
954 ]),
957− ]);
955+ ];
958
956
959 for (sample, scenario) in tests {
957 for (sample, scenario) in tests {
960 for (count, begin, expected_end) in scenario.into_iter() {
958 for (count, begin, expected_end) in scenario.into_iter() {
@@ -966,7 +964,7 @@ mod test {
966
964
967 #[test]
965 #[test]
968 fn test_behaviour_when_moving_to_end_of_previous_words() {
966 fn test_behaviour_when_moving_to_end_of_previous_words() {
969− let tests = array::IntoIter::new([
967+ let tests = [
970 ("Basic backward motion from the middle of a word",
968 ("Basic backward motion from the middle of a word",
971 vec![(1, Range::new(9, 9), Range::new(10, 5))]),
969 vec![(1, Range::new(9, 9), Range::new(10, 5))]),
972 ("Starting from after boundary retreats the anchor",
970 ("Starting from after boundary retreats the anchor",
@@ -1036,7 +1034,7 @@ mod test {
1036 vec![
1034 vec![
1037 (1, Range::new(0, 10), Range::new(10, 4)),
1035 (1, Range::new(0, 10), Range::new(10, 4)),
1038 ]),
1036 ]),
1039− ]);
1037+ ];
1040
1038
1041 for (sample, scenario) in tests {
1039 for (sample, scenario) in tests {
1042 for (count, begin, expected_end) in scenario.into_iter() {
1040 for (count, begin, expected_end) in scenario.into_iter() {
@@ -1048,7 +1046,7 @@ mod test {
1048
1046
1049 #[test]
1047 #[test]
1050 fn test_behaviour_when_moving_to_end_of_next_long_words() {
1048 fn test_behaviour_when_moving_to_end_of_next_long_words() {
1051− let tests = array::IntoIter::new([
1049+ let tests = [
1052 ("Basic forward motion from the start of a word to the end of it",
1050 ("Basic forward motion from the start of a word to the end of it",
1053 vec![(1, Range::new(0, 0), Range::new(0, 5))]),
1051 vec![(1, Range::new(0, 0), Range::new(0, 5))]),
1054 ("Basic forward motion from the end of a word to the end of the next",
1052 ("Basic forward motion from the end of a word to the end of the next",
@@ -1118,7 +1116,7 @@ mod test {
1118 vec![
1116 vec![
1119 (1, Range::new(0, 0), Range::new(0, 7)),
1117 (1, Range::new(0, 0), Range::new(0, 7)),
1120 ]),
1118 ]),
1121− ]);
1119+ ];
1122
1120
1123 for (sample, scenario) in tests {
1121 for (sample, scenario) in tests {
1124 for (count, begin, expected_end) in scenario.into_iter() {
1122 for (count, begin, expected_end) in scenario.into_iter() {
helix-core/src/object.rs+27−2
@@ -1,7 +1,5 @@
1use crate::{Range, RopeSlice, Selection, Syntax};
1use crate::{Range, RopeSlice, Selection, Syntax};
2
2
3−// TODO: to contract_selection we'd need to store the previous ranges before expand.
4−// Maybe just contract to the first child node?
5pub fn expand_selection(syntax: &Syntax, text: RopeSlice, selection: &Selection) -> Selection {
3pub fn expand_selection(syntax: &Syntax, text: RopeSlice, selection: &Selection) -> Selection {
6 let tree = syntax.tree();
4 let tree = syntax.tree();
7
5
@@ -34,3 +32,30 @@ pub fn expand_selection(syntax: &Syntax, text: RopeSlice, selection: &Selection)
34 }
32 }
35 })
33 })
36}
34}
35+
36+pub fn shrink_selection(syntax: &Syntax, text: RopeSlice, selection: &Selection) -> Selection {
37+ let tree = syntax.tree();
38+
39+ selection.clone().transform(|range| {
40+ let from = text.char_to_byte(range.from());
41+ let to = text.char_to_byte(range.to());
42+
43+ let descendant = match tree.root_node().descendant_for_byte_range(from, to) {
44+ // find first child, if not possible, fallback to the node that contains selection
45+ Some(descendant) => match descendant.child(0) {
46+ Some(child) => child,
47+ None => descendant,
48+ },
49+ None => return range,
50+ };
51+
52+ let from = text.byte_to_char(descendant.start_byte());
53+ let to = text.byte_to_char(descendant.end_byte());
54+
55+ if range.head < range.anchor {
56+ Range::new(to, from)
57+ } else {
58+ Range::new(from, to)
59+ }
60+ })
61+}
helix-core/src/selection.rs+93
@@ -7,6 +7,7 @@ use crate::{
7 ensure_grapheme_boundary_next, ensure_grapheme_boundary_prev, next_grapheme_boundary,
7 ensure_grapheme_boundary_next, ensure_grapheme_boundary_prev, next_grapheme_boundary,
8 prev_grapheme_boundary,
8 prev_grapheme_boundary,
9 },
9 },
10+ movement::Direction,
10 Assoc, ChangeSet, RopeSlice,
11 Assoc, ChangeSet, RopeSlice,
11};
12};
12use smallvec::{smallvec, SmallVec};
13use smallvec::{smallvec, SmallVec};
@@ -82,6 +83,13 @@ impl Range {
82 std::cmp::max(self.anchor, self.head)
83 std::cmp::max(self.anchor, self.head)
83 }
84 }
84
85
86+ /// Total length of the range.
87+ #[inline]
88+ #[must_use]
89+ pub fn len(&self) -> usize {
90+ self.to() - self.from()
91+ }
92+
85 /// The (inclusive) range of lines that the range overlaps.
93 /// The (inclusive) range of lines that the range overlaps.
86 #[inline]
94 #[inline]
87 #[must_use]
95 #[must_use]
@@ -102,6 +110,27 @@ impl Range {
102 self.anchor == self.head
110 self.anchor == self.head
103 }
111 }
104
112
113+ /// `Direction::Backward` when head < anchor.
114+ /// `Direction::Backward` otherwise.
115+ #[inline]
116+ #[must_use]
117+ pub fn direction(&self) -> Direction {
118+ if self.head < self.anchor {
119+ Direction::Backward
120+ } else {
121+ Direction::Forward
122+ }
123+ }
124+
125+ // flips the direction of the selection
126+ pub fn flip(&self) -> Self {
127+ Self {
128+ anchor: self.head,
129+ head: self.anchor,
130+ horiz: self.horiz,
131+ }
132+ }
133+
105 /// Check two ranges for overlap.
134 /// Check two ranges for overlap.
106 #[must_use]
135 #[must_use]
107 pub fn overlaps(&self, other: &Self) -> bool {
136 pub fn overlaps(&self, other: &Self) -> bool {
@@ -111,6 +140,11 @@ impl Range {
111 self.from() == other.from() || (self.to() > other.from() && other.to() > self.from())
140 self.from() == other.from() || (self.to() > other.from() && other.to() > self.from())
112 }
141 }
113
142
143+ #[inline]
144+ pub fn contains_range(&self, other: &Self) -> bool {
145+ self.from() <= other.from() && self.to() >= other.to()
146+ }
147+
114 pub fn contains(&self, pos: usize) -> bool {
148 pub fn contains(&self, pos: usize) -> bool {
115 self.from() <= pos && pos < self.to()
149 self.from() <= pos && pos < self.to()
116 }
150 }
@@ -515,6 +549,39 @@ impl Selection {
515 pub fn len(&self) -> usize {
549 pub fn len(&self) -> usize {
516 self.ranges.len()
550 self.ranges.len()
517 }
551 }
552+
553+ // returns true if self ⊇ other
554+ pub fn contains(&self, other: &Selection) -> bool {
555+ // can't contain other if it is larger
556+ if other.len() > self.len() {
557+ return false;
558+ }
559+
560+ let (mut iter_self, mut iter_other) = (self.iter(), other.iter());
561+ let (mut ele_self, mut ele_other) = (iter_self.next(), iter_other.next());
562+
563+ loop {
564+ match (ele_self, ele_other) {
565+ (Some(ra), Some(rb)) => {
566+ if !ra.contains_range(rb) {
567+ // `self` doesn't contain next element from `other`, advance `self`, we need to match all from `other`
568+ ele_self = iter_self.next();
569+ } else {
570+ // matched element from `other`, advance `other`
571+ ele_other = iter_other.next();
572+ };
573+ }
574+ (None, Some(_)) => {
575+ // exhausted `self`, we can't match the reminder of `other`
576+ return false;
577+ }
578+ (_, None) => {
579+ // no elements from `other` left to match, `self` contains `other`
580+ return true;
581+ }
582+ }
583+ }
584+ }
518}
585}
519
586
520impl<'a> IntoIterator for &'a Selection {
587impl<'a> IntoIterator for &'a Selection {
@@ -953,4 +1020,30 @@ mod test {
953 &["", "abcd", "efg", "rs", "xyz"]
1020 &["", "abcd", "efg", "rs", "xyz"]
954 );
1021 );
955 }
1022 }
1023+ #[test]
1024+ fn test_selection_contains() {
1025+ fn contains(a: Vec<(usize, usize)>, b: Vec<(usize, usize)>) -> bool {
1026+ let sela = Selection::new(a.iter().map(|a| Range::new(a.0, a.1)).collect(), 0);
1027+ let selb = Selection::new(b.iter().map(|b| Range::new(b.0, b.1)).collect(), 0);
1028+ sela.contains(&selb)
1029+ }
1030+
1031+ // exact match
1032+ assert!(contains(vec!((1, 1)), vec!((1, 1))));
1033+
1034+ // larger set contains smaller
1035+ assert!(contains(vec!((1, 1), (2, 2), (3, 3)), vec!((2, 2))));
1036+
1037+ // multiple matches
1038+ assert!(contains(vec!((1, 1), (2, 2)), vec!((1, 1), (2, 2))));
1039+
1040+ // smaller set can't contain bigger
1041+ assert!(!contains(vec!((1, 1)), vec!((1, 1), (2, 2))));
1042+
1043+ assert!(contains(
1044+ vec!((1, 1), (2, 4), (5, 6), (7, 9), (10, 13)),
1045+ vec!((3, 4), (7, 9))
1046+ ));
1047+ assert!(!contains(vec!((1, 1), (5, 6)), vec!((1, 6))));
1048+ }
956}
1049}
helix-core/src/syntax.rs+13−3
@@ -1,5 +1,6 @@
1use crate::{
1use crate::{
2 chars::char_is_line_ending,
2 chars::char_is_line_ending,
3+ diagnostic::Severity,
3 regex::Regex,
4 regex::Regex,
4 transaction::{ChangeSet, Operation},
5 transaction::{ChangeSet, Operation},
5 Rope, RopeSlice, Tendril,
6 Rope, RopeSlice, Tendril,
@@ -63,6 +64,10 @@ pub struct LanguageConfiguration {
63
64
64 #[serde(default)]
65 #[serde(default)]
65 pub auto_format: bool,
66 pub auto_format: bool,
67+ #[serde(default)]
68+ pub diagnostic_severity: Severity,
69+
70+ pub tree_sitter_library: Option<String>, // tree-sitter library name, defaults to language_id
66
71
67 // content_regex
72 // content_regex
68 #[serde(default, skip_serializing, deserialize_with = "deserialize_regex")]
73 #[serde(default, skip_serializing, deserialize_with = "deserialize_regex")]
@@ -189,9 +194,14 @@ impl LanguageConfiguration {
189 if highlights_query.is_empty() {
194 if highlights_query.is_empty() {
190 None
195 None
191 } else {
196 } else {
192− let language = get_language(&crate::RUNTIME_DIR, &self.language_id)
197+ let language = get_language(
198+ &crate::RUNTIME_DIR,
199+ self.tree_sitter_library
200+ .as_deref()
201+ .unwrap_or(&self.language_id),
202+ )
193− .map_err(|e| log::info!("{}", e))
203+ .map_err(|e| log::info!("{}", e))
194− .ok()?;
204+ .ok()?;
195 let config = HighlightConfiguration::new(
205 let config = HighlightConfiguration::new(
196 language,
206 language,
197 &highlights_query,
207 &highlights_query,
helix-lsp/Cargo.toml+3−3
@@ -1,6 +1,6 @@
1[package]
1[package]
2name = "helix-lsp"
2name = "helix-lsp"
3−version = "0.5.0"
3+version = "0.6.0"
4authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
4authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
5edition = "2021"
5edition = "2021"
6license = "MPL-2.0"
6license = "MPL-2.0"
@@ -12,7 +12,7 @@ homepage = "https://helix-editor.com"
12# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
12# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13
13
14[dependencies]
14[dependencies]
15−helix-core = { version = "0.5", path = "../helix-core" }
15+helix-core = { version = "0.6", path = "../helix-core" }
16
16
17anyhow = "1.0"
17anyhow = "1.0"
18futures-executor = "0.3"
18futures-executor = "0.3"
@@ -23,5 +23,5 @@ lsp-types = { version = "0.91", features = ["proposed"] }
23serde = { version = "1.0", features = ["derive"] }
23serde = { version = "1.0", features = ["derive"] }
24serde_json = "1.0"
24serde_json = "1.0"
25thiserror = "1.0"
25thiserror = "1.0"
26−tokio = { version = "1.14", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
26+tokio = { version = "1.15", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
27tokio-stream = "0.1.8"
27tokio-stream = "0.1.8"
helix-lsp/src/client.rs+26−2
@@ -31,6 +31,7 @@ pub struct Client {
31 pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>,
31 pub(crate) capabilities: OnceCell<lsp::ServerCapabilities>,
32 offset_encoding: OffsetEncoding,
32 offset_encoding: OffsetEncoding,
33 config: Option<Value>,
33 config: Option<Value>,
34+ root_markers: Vec<String>,
34}
35}
35
36
36impl Client {
37impl Client {
@@ -39,6 +40,7 @@ impl Client {
39 cmd: &str,
40 cmd: &str,
40 args: &[String],
41 args: &[String],
41 config: Option<Value>,
42 config: Option<Value>,
43+ root_markers: Vec<String>,
42 id: usize,
44 id: usize,
43 ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)> {
45 ) -> Result<(Self, UnboundedReceiver<(usize, Call)>, Arc<Notify>)> {
44 let process = Command::new(cmd)
46 let process = Command::new(cmd)
@@ -68,6 +70,7 @@ impl Client {
68 capabilities: OnceCell::new(),
70 capabilities: OnceCell::new(),
69 offset_encoding: OffsetEncoding::Utf8,
71 offset_encoding: OffsetEncoding::Utf8,
70 config,
72 config,
73+ root_markers,
71 };
74 };
72
75
73 Ok((client, server_rx, initialize_notify))
76 Ok((client, server_rx, initialize_notify))
@@ -202,7 +205,7 @@ impl Client {
202 Ok(result) => Output::Success(Success {
205 Ok(result) => Output::Success(Success {
203 jsonrpc: Some(Version::V2),
206 jsonrpc: Some(Version::V2),
204 id,
207 id,
205− result,
208+ result: serde_json::to_value(result)?,
206 }),
209 }),
207 Err(error) => Output::Failure(Failure {
210 Err(error) => Output::Failure(Failure {
208 jsonrpc: Some(Version::V2),
211 jsonrpc: Some(Version::V2),
@@ -225,7 +228,8 @@ impl Client {
225
228
226 pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> {
229 pub(crate) async fn initialize(&self) -> Result<lsp::InitializeResult> {
227 // TODO: delay any requests that are triggered prior to initialize
230 // TODO: delay any requests that are triggered prior to initialize
231+ let root = find_root(None, &self.root_markers)
228− let root = find_root(None).and_then(|root| lsp::Url::from_file_path(root).ok());
232+ .and_then(|root| lsp::Url::from_file_path(root).ok());
229
233
230 if self.config.is_some() {
234 if self.config.is_some() {
231 log::info!("Using custom LSP config: {}", self.config.as_ref().unwrap());
235 log::info!("Using custom LSP config: {}", self.config.as_ref().unwrap());
@@ -556,6 +560,14 @@ impl Client {
556 self.call::<lsp::request::Completion>(params)
560 self.call::<lsp::request::Completion>(params)
557 }
561 }
558
562
563+ pub async fn resolve_completion_item(
564+ &self,
565+ completion_item: lsp::CompletionItem,
566+ ) -> Result<lsp::CompletionItem> {
567+ self.request::<lsp::request::ResolveCompletionItem>(completion_item)
568+ .await
569+ }
570+
559 pub fn text_document_signature_help(
571 pub fn text_document_signature_help(
560 &self,
572 &self,
561 text_document: lsp::TextDocumentIdentifier,
573 text_document: lsp::TextDocumentIdentifier,
@@ -800,4 +812,16 @@ impl Client {
800 let response = self.request::<lsp::request::Rename>(params).await?;
812 let response = self.request::<lsp::request::Rename>(params).await?;
801 Ok(response.unwrap_or_default())
813 Ok(response.unwrap_or_default())
802 }
814 }
815+
816+ pub fn command(&self, command: lsp::Command) -> impl Future<Output = Result<Value>> {
817+ let params = lsp::ExecuteCommandParams {
818+ command: command.command,
819+ arguments: command.arguments.unwrap_or_default(),
820+ work_done_progress_params: lsp::WorkDoneProgressParams {
821+ work_done_token: None,
822+ },
823+ };
824+
825+ self.call::<lsp::request::ExecuteCommand>(params)
826+ }
803}
827}
helix-lsp/src/lib.rs+13−18
@@ -66,39 +66,26 @@ pub mod util {
66 pos: lsp::Position,
66 pos: lsp::Position,
67 offset_encoding: OffsetEncoding,
67 offset_encoding: OffsetEncoding,
68 ) -> Option<usize> {
68 ) -> Option<usize> {
69− let max_line = doc.lines().count().saturating_sub(1);
70 let pos_line = pos.line as usize;
69 let pos_line = pos.line as usize;
71− let pos_line = if pos_line > max_line {
70+ if pos_line > doc.len_lines() - 1 {
72 return None;
71 return None;
73− } else {
72+ }
74− pos_line
73+
75− };
76 match offset_encoding {
74 match offset_encoding {
77 OffsetEncoding::Utf8 => {
75 OffsetEncoding::Utf8 => {
78− let max_char = doc
79− .line_to_char(max_line)
80− .checked_add(doc.line(max_line).len_chars())?;
81 let line = doc.line_to_char(pos_line);
76 let line = doc.line_to_char(pos_line);
82 let pos = line.checked_add(pos.character as usize)?;
77 let pos = line.checked_add(pos.character as usize)?;
83− if pos <= max_char {
78+ if pos <= doc.len_chars() {
84 Some(pos)
79 Some(pos)
85 } else {
80 } else {
86 None
81 None
87 }
82 }
88 }
83 }
89 OffsetEncoding::Utf16 => {
84 OffsetEncoding::Utf16 => {
90− let max_char = doc
91− .line_to_char(max_line)
92− .checked_add(doc.line(max_line).len_chars())?;
93− let max_cu = doc.char_to_utf16_cu(max_char);
94 let line = doc.line_to_char(pos_line);
85 let line = doc.line_to_char(pos_line);
95 let line_start = doc.char_to_utf16_cu(line);
86 let line_start = doc.char_to_utf16_cu(line);
96 let pos = line_start.checked_add(pos.character as usize)?;
87 let pos = line_start.checked_add(pos.character as usize)?;
97− if pos <= max_cu {
88+ doc.try_utf16_cu_to_char(pos).ok()
98− Some(doc.utf16_cu_to_char(pos))
99− } else {
100− None
101− }
102 }
89 }
103 }
90 }
104 }
91 }
@@ -203,6 +190,7 @@ pub mod util {
203#[derive(Debug, PartialEq, Clone)]
190#[derive(Debug, PartialEq, Clone)]
204pub enum MethodCall {
191pub enum MethodCall {
205 WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
192 WorkDoneProgressCreate(lsp::WorkDoneProgressCreateParams),
193+ ApplyWorkspaceEdit(lsp::ApplyWorkspaceEditParams),
206}
194}
207
195
208impl MethodCall {
196impl MethodCall {
@@ -215,6 +203,12 @@ impl MethodCall {
215 .expect("Failed to parse WorkDoneCreate params");
203 .expect("Failed to parse WorkDoneCreate params");
216 Self::WorkDoneProgressCreate(params)
204 Self::WorkDoneProgressCreate(params)
217 }
205 }
206+ lsp::request::ApplyWorkspaceEdit::METHOD => {
207+ let params: lsp::ApplyWorkspaceEditParams = params
208+ .parse()
209+ .expect("Failed to parse ApplyWorkspaceEdit params");
210+ Self::ApplyWorkspaceEdit(params)
211+ }
218 _ => {
212 _ => {
219 log::warn!("unhandled lsp request: {}", method);
213 log::warn!("unhandled lsp request: {}", method);
220 return None;
214 return None;
@@ -319,6 +313,7 @@ impl Registry {
319 &config.command,
313 &config.command,
320 &config.args,
314 &config.args,
321 language_config.config.clone(),
315 language_config.config.clone(),
316+ language_config.roots.clone(),
322 id,
317 id,
323 )?;
318 )?;
324 self.incoming.push(UnboundedReceiverStream::new(incoming));
319 self.incoming.push(UnboundedReceiverStream::new(incoming));
helix-syntax/Cargo.toml+1−1
@@ -1,6 +1,6 @@
1[package]
1[package]
2name = "helix-syntax"
2name = "helix-syntax"
3−version = "0.5.0"
3+version = "0.6.0"
4authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
4authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
5edition = "2021"
5edition = "2021"
6license = "MPL-2.0"
6license = "MPL-2.0"
helix-syntax/README.mdnew file+13
@@ -0,0 +1,13 @@
1+helix-syntax
2+============
3+
4+Syntax highlighting for helix, (shallow) submodules resides here.
5+
6+Differences from nvim-treesitter
7+--------------------------------
8+
9+As the syntax are commonly ported from
10+<https://github.com/nvim-treesitter/nvim-treesitter>.
11+
12+Note that we do not support the custom `#any-of` predicate which is
13+supported by neovim so one needs to change it to `#match` with regex.
helix-syntax/languages/tree-sitter-commentnew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-dartnew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-dockerfilenew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-fishnew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-git-commitnew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-git-diffnew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-git-rebasenew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-llvmnew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-llvm-mirnew file+1

Submodule changed.

helix-syntax/languages/tree-sitter-tablegennew file+1

Submodule changed.

helix-term/Cargo.toml+5−5
@@ -1,6 +1,6 @@
1[package]
1[package]
2name = "helix-term"
2name = "helix-term"
3−version = "0.5.0"
3+version = "0.6.0"
4description = "A post-modern text editor."
4description = "A post-modern text editor."
5authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
5authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
6edition = "2021"
6edition = "2021"
@@ -22,12 +22,12 @@ name = "hx"
22path = "src/main.rs"
22path = "src/main.rs"
23
23
24[dependencies]
24[dependencies]
25−helix-core = { version = "0.5", path = "../helix-core" }
25+helix-core = { version = "0.6", path = "../helix-core" }
26−helix-view = { version = "0.5", path = "../helix-view" }
26+helix-view = { version = "0.6", path = "../helix-view" }
27−helix-lsp = { version = "0.5", path = "../helix-lsp" }
27+helix-lsp = { version = "0.6", path = "../helix-lsp" }
28
28
29anyhow = "1"
29anyhow = "1"
30−once_cell = "1.8"
30+once_cell = "1.9"
31
31
32tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
32tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
33num_cpus = "1"
33num_cpus = "1"
helix-term/src/application.rs+61−18
@@ -1,8 +1,12 @@
1use helix_core::{merge_toml_values, syntax};
1use helix_core::{merge_toml_values, syntax};
2use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap};
2use helix_lsp::{lsp, util::lsp_pos_to_pos, LspProgressMap};
3use helix_view::{theme, Editor};
3use helix_view::{theme, Editor};
4+use serde_json::json;
4
5
6+use crate::{
5−use crate::{args::Args, compositor::Compositor, config::Config, job::Jobs, ui};
7+ args::Args, commands::apply_workspace_edit, compositor::Compositor, config::Config, job::Jobs,
8+ ui,
9+};
6
10
7use log::{error, warn};
11use log::{error, warn};
8
12
@@ -374,6 +378,7 @@ impl Application {
374 let doc = self.editor.document_by_path_mut(&path);
378 let doc = self.editor.document_by_path_mut(&path);
375
379
376 if let Some(doc) = doc {
380 if let Some(doc) = doc {
381+ let lang_conf = doc.language_config();
377 let text = doc.text();
382 let text = doc.text();
378
383
379 let diagnostics = params
384 let diagnostics = params
@@ -411,19 +416,31 @@ impl Application {
411 return None;
416 return None;
412 };
417 };
413
418
419+ let severity =
420+ diagnostic.severity.map(|severity| match severity {
421+ DiagnosticSeverity::ERROR => Error,
422+ DiagnosticSeverity::WARNING => Warning,
423+ DiagnosticSeverity::INFORMATION => Info,
424+ DiagnosticSeverity::HINT => Hint,
425+ severity => unreachable!(
426+ "unrecognized diagnostic severity: {:?}",
427+ severity
428+ ),
429+ });
430+
431+ if let Some(lang_conf) = lang_conf {
432+ if let Some(severity) = severity {
433+ if severity < lang_conf.diagnostic_severity {
434+ return None;
435+ }
436+ }
437+ };
438+
414 Some(Diagnostic {
439 Some(Diagnostic {
415 range: Range { start, end },
440 range: Range { start, end },
416 line: diagnostic.range.start.line as usize,
441 line: diagnostic.range.start.line as usize,
417 message: diagnostic.message,
442 message: diagnostic.message,
418− severity: diagnostic.severity.map(
443+ severity,
419− |severity| match severity {
420− DiagnosticSeverity::ERROR => Error,
421− DiagnosticSeverity::WARNING => Warning,
422− DiagnosticSeverity::INFORMATION => Info,
423− DiagnosticSeverity::HINT => Hint,
424− severity => unimplemented!("{:?}", severity),
425− },
426− ),
427 // code
444 // code
428 // source
445 // source
429 })
446 })
@@ -530,14 +547,6 @@ impl Application {
530 Call::MethodCall(helix_lsp::jsonrpc::MethodCall {
547 Call::MethodCall(helix_lsp::jsonrpc::MethodCall {
531 method, params, id, ..
548 method, params, id, ..
532 }) => {
549 }) => {
533− let language_server = match self.editor.language_servers.get_by_id(server_id) {
534− Some(language_server) => language_server,
535− None => {
536− warn!("can't find language server with id `{}`", server_id);
537− return;
538− }
539− };
540−
541 let call = match MethodCall::parse(&method, params) {
550 let call = match MethodCall::parse(&method, params) {
542 Some(call) => call,
551 Some(call) => call,
543 None => {
552 None => {
@@ -567,8 +576,42 @@ impl Application {
567 if spinner.is_stopped() {
576 if spinner.is_stopped() {
568 spinner.start();
577 spinner.start();
569 }
578 }
579+ let language_server =
580+ match self.editor.language_servers.get_by_id(server_id) {
581+ Some(language_server) => language_server,
582+ None => {
583+ warn!("can't find language server with id `{}`", server_id);
584+ return;
585+ }
586+ };
587+
570 tokio::spawn(language_server.reply(id, Ok(serde_json::Value::Null)));
588 tokio::spawn(language_server.reply(id, Ok(serde_json::Value::Null)));
571 }
589 }
590+ MethodCall::ApplyWorkspaceEdit(params) => {
591+ apply_workspace_edit(
592+ &mut self.editor,
593+ helix_lsp::OffsetEncoding::Utf8,
594+ &params.edit,
595+ );
596+
597+ let language_server =
598+ match self.editor.language_servers.get_by_id(server_id) {
599+ Some(language_server) => language_server,
600+ None => {
601+ warn!("can't find language server with id `{}`", server_id);
602+ return;
603+ }
604+ };
605+
606+ tokio::spawn(language_server.reply(
607+ id,
608+ Ok(json!(lsp::ApplyWorkspaceEditResponse {
609+ applied: true,
610+ failure_reason: None,
611+ failed_change: None,
612+ })),
613+ ));
614+ }
572 }
615 }
573 }
616 }
574 e => unreachable!("{:?}", e),
617 e => unreachable!("{:?}", e),
helix-term/src/commands.rs+375−131
@@ -26,6 +26,7 @@ use helix_view::{
26};
26};
27
27
28use anyhow::{anyhow, bail, ensure, Context as _};
28use anyhow::{anyhow, bail, ensure, Context as _};
29+use fuzzy_matcher::FuzzyMatcher;
29use helix_lsp::{
30use helix_lsp::{
30 block_on, lsp,
31 block_on, lsp,
31 util::{lsp_pos_to_pos, lsp_range_to_range, pos_to_lsp_pos, range_to_lsp_range},
32 util::{lsp_pos_to_pos, lsp_range_to_range, pos_to_lsp_pos, range_to_lsp_range},
@@ -266,6 +267,7 @@ impl MappableCommand {
266 change_selection_noyank, "Change selection (delete and enter insert mode, without yanking)",
267 change_selection_noyank, "Change selection (delete and enter insert mode, without yanking)",
267 collapse_selection, "Collapse selection onto a single cursor",
268 collapse_selection, "Collapse selection onto a single cursor",
268 flip_selections, "Flip selection cursor and anchor",
269 flip_selections, "Flip selection cursor and anchor",
270+ ensure_selections_forward, "Ensure the selection is in forward direction",
269 insert_mode, "Insert before selection",
271 insert_mode, "Insert before selection",
270 append_mode, "Insert after selection (append)",
272 append_mode, "Insert after selection (append)",
271 command_mode, "Enter command mode",
273 command_mode, "Enter command mode",
@@ -287,7 +289,7 @@ impl MappableCommand {
287 add_newline_below, "Add newline below",
289 add_newline_below, "Add newline below",
288 goto_type_definition, "Goto type definition",
290 goto_type_definition, "Goto type definition",
289 goto_implementation, "Goto implementation",
291 goto_implementation, "Goto implementation",
290− goto_file_start, "Goto file start/line",
292+ goto_file_start, "Goto line number <n> else file start",
291 goto_file_end, "Goto file end",
293 goto_file_end, "Goto file end",
292 goto_file, "Goto files in selection",
294 goto_file, "Goto files in selection",
293 goto_file_hsplit, "Goto files in selection (hsplit)",
295 goto_file_hsplit, "Goto files in selection (hsplit)",
@@ -360,6 +362,7 @@ impl MappableCommand {
360 rotate_selection_contents_forward, "Rotate selection contents forward",
362 rotate_selection_contents_forward, "Rotate selection contents forward",
361 rotate_selection_contents_backward, "Rotate selections contents backward",
363 rotate_selection_contents_backward, "Rotate selections contents backward",
362 expand_selection, "Expand selection to parent syntax node",
364 expand_selection, "Expand selection to parent syntax node",
365+ shrink_selection, "Shrink selection to previously expanded syntax node",
363 jump_forward, "Jump forward on jumplist",
366 jump_forward, "Jump forward on jumplist",
364 jump_backward, "Jump backward on jumplist",
367 jump_backward, "Jump backward on jumplist",
365 save_selection, "Save the current selection to the jumplist",
368 save_selection, "Save the current selection to the jumplist",
@@ -396,7 +399,7 @@ impl MappableCommand {
396 increment, "Increment",
399 increment, "Increment",
397 decrement, "Decrement",
400 decrement, "Decrement",
398 record_macro, "Record macro",
401 record_macro, "Record macro",
399− play_macro, "Play macro",
402+ replay_macro, "Replay macro",
400 );
403 );
401}
404}
402
405
@@ -1280,16 +1283,23 @@ pub fn scroll(cx: &mut Context, offset: usize, direction: Direction) {
1280 .max(view.offset.row + scrolloff)
1283 .max(view.offset.row + scrolloff)
1281 .min(last_line.saturating_sub(scrolloff));
1284 .min(last_line.saturating_sub(scrolloff));
1282
1285
1286+ // If cursor needs moving, replace primary selection
1287+ if line != cursor.row {
1283− let head = pos_at_coords(text, Position::new(line, cursor.col), true); // this func will properly truncate to line end
1288+ let head = pos_at_coords(text, Position::new(line, cursor.col), true); // this func will properly truncate to line end
1284
1289
1285− let anchor = if doc.mode == Mode::Select {
1290+ let anchor = if doc.mode == Mode::Select {
1286− range.anchor
1291+ range.anchor
1287− } else {
1292+ } else {
1288− head
1293+ head
1289− };
1294+ };
1290
1295
1291− // TODO: only manipulate main selection
1296+ // replace primary selection with an empty selection at cursor pos
1292− doc.set_selection(view.id, Selection::single(anchor, head));
1297+ let prim_sel = Range::new(anchor, head);
1298+ let mut sel = doc.selection(view.id).clone();
1299+ let idx = sel.primary_index();
1300+ sel = sel.replace(idx, prim_sel);
1301+ doc.set_selection(view.id, sel);
1302+ }
1293}
1303}
1294
1304
1295fn page_up(cx: &mut Context) {
1305fn page_up(cx: &mut Context) {
@@ -1543,7 +1553,7 @@ fn searcher(cx: &mut Context, direction: Direction) {
1543 let reg = cx.register.unwrap_or('/');
1553 let reg = cx.register.unwrap_or('/');
1544 let scrolloff = cx.editor.config.scrolloff;
1554 let scrolloff = cx.editor.config.scrolloff;
1545
1555
1546− let (_, doc) = current!(cx.editor);
1556+ let doc = doc!(cx.editor);
1547
1557
1548 // TODO: could probably share with select_on_matches?
1558 // TODO: could probably share with select_on_matches?
1549
1559
@@ -1630,7 +1640,7 @@ fn search_selection(cx: &mut Context) {
1630 let query = doc.selection(view.id).primary().fragment(contents);
1640 let query = doc.selection(view.id).primary().fragment(contents);
1631 let regex = regex::escape(&query);
1641 let regex = regex::escape(&query);
1632 cx.editor.registers.get_mut('/').push(regex);
1642 cx.editor.registers.get_mut('/').push(regex);
1633− let msg = format!("register '{}' set to '{}'", '\\', query);
1643+ let msg = format!("register '{}' set to '{}'", '/', query);
1634 cx.editor.set_status(msg);
1644 cx.editor.set_status(msg);
1635}
1645}
1636
1646
@@ -1904,7 +1914,21 @@ fn flip_selections(cx: &mut Context) {
1904 let selection = doc
1914 let selection = doc
1905 .selection(view.id)
1915 .selection(view.id)
1906 .clone()
1916 .clone()
1907− .transform(|range| Range::new(range.head, range.anchor));
1917+ .transform(|range| range.flip());
1918+ doc.set_selection(view.id, selection);
1919+}
1920+
1921+fn ensure_selections_forward(cx: &mut Context) {
1922+ let (view, doc) = current!(cx.editor);
1923+
1924+ let selection = doc
1925+ .selection(view.id)
1926+ .clone()
1927+ .transform(|r| match r.direction() {
1928+ Direction::Forward => r,
1929+ Direction::Backward => r.flip(),
1930+ });
1931+
1908 doc.set_selection(view.id, selection);
1932 doc.set_selection(view.id, selection);
1909}
1933}
1910
1934
@@ -1938,7 +1962,7 @@ fn append_mode(cx: &mut Context) {
1938 if !last_range.is_empty() && last_range.head == end {
1962 if !last_range.is_empty() && last_range.head == end {
1939 let transaction = Transaction::change(
1963 let transaction = Transaction::change(
1940 doc.text(),
1964 doc.text(),
1941− std::array::IntoIter::new([(end, end, Some(doc.line_ending.as_str().into()))]),
1965+ [(end, end, Some(doc.line_ending.as_str().into()))].into_iter(),
1942 );
1966 );
1943 doc.apply(&transaction, view.id);
1967 doc.apply(&transaction, view.id);
1944 }
1968 }
@@ -2030,7 +2054,7 @@ pub mod cmd {
2030
2054
2031 fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
2055 fn write_impl(cx: &mut compositor::Context, path: Option<&Cow<str>>) -> anyhow::Result<()> {
2032 let jobs = &mut cx.jobs;
2056 let jobs = &mut cx.jobs;
2033− let (_, doc) = current!(cx.editor);
2057+ let doc = doc_mut!(cx.editor);
2034
2058
2035 if let Some(ref path) = path {
2059 if let Some(ref path) = path {
2036 doc.set_path(Some(path.as_ref().as_ref()))
2060 doc.set_path(Some(path.as_ref().as_ref()))
@@ -2083,8 +2107,7 @@ pub mod cmd {
2083 _args: &[Cow<str>],
2107 _args: &[Cow<str>],
2084 _event: PromptEvent,
2108 _event: PromptEvent,
2085 ) -> anyhow::Result<()> {
2109 ) -> anyhow::Result<()> {
2086− let (_, doc) = current!(cx.editor);
2110+ let doc = doc!(cx.editor);
2087−
2088 if let Some(format) = doc.format() {
2111 if let Some(format) = doc.format() {
2089 let callback =
2112 let callback =
2090 make_format_callback(doc.id(), doc.version(), Modified::LeaveModified, format);
2113 make_format_callback(doc.id(), doc.version(), Modified::LeaveModified, format);
@@ -2307,12 +2330,7 @@ pub mod cmd {
2307 write_all_impl(cx, args, event, true, true)
2330 write_all_impl(cx, args, event, true, true)
2308 }
2331 }
2309
2332
2310− fn quit_all_impl(
2333+ fn quit_all_impl(editor: &mut Editor, force: bool) -> anyhow::Result<()> {
2311− editor: &mut Editor,
2312− _args: &[Cow<str>],
2313− _event: PromptEvent,
2314− force: bool,
2315− ) -> anyhow::Result<()> {
2316 if !force {
2334 if !force {
2317 buffers_remaining_impl(editor)?;
2335 buffers_remaining_impl(editor)?;
2318 }
2336 }
@@ -2328,18 +2346,18 @@ pub mod cmd {
2328
2346
2329 fn quit_all(
2347 fn quit_all(
2330 cx: &mut compositor::Context,
2348 cx: &mut compositor::Context,
2331− args: &[Cow<str>],
2349+ _args: &[Cow<str>],
2332− event: PromptEvent,
2350+ _event: PromptEvent,
2333 ) -> anyhow::Result<()> {
2351 ) -> anyhow::Result<()> {
2334− quit_all_impl(cx.editor, args, event, false)
2352+ quit_all_impl(cx.editor, false)
2335 }
2353 }
2336
2354
2337 fn force_quit_all(
2355 fn force_quit_all(
2338 cx: &mut compositor::Context,
2356 cx: &mut compositor::Context,
2339− args: &[Cow<str>],
2357+ _args: &[Cow<str>],
2340− event: PromptEvent,
2358+ _event: PromptEvent,
2341 ) -> anyhow::Result<()> {
2359 ) -> anyhow::Result<()> {
2342− quit_all_impl(cx.editor, args, event, true)
2360+ quit_all_impl(cx.editor, true)
2343 }
2361 }
2344
2362
2345 fn cquit(
2363 fn cquit(
@@ -2353,12 +2371,21 @@ pub mod cmd {
2353 .unwrap_or(1);
2371 .unwrap_or(1);
2354 cx.editor.exit_code = exit_code;
2372 cx.editor.exit_code = exit_code;
2355
2373
2356− let views: Vec<_> = cx.editor.tree.views().map(|(view, _)| view.id).collect();
2374+ quit_all_impl(cx.editor, false)
2357− for view_id in views {
2358− cx.editor.close(view_id);
2359− }
2375+ }
2360
2376
2361− Ok(())
2377+ fn force_cquit(
2378+ cx: &mut compositor::Context,
2379+ args: &[Cow<str>],
2380+ _event: PromptEvent,
2381+ ) -> anyhow::Result<()> {
2382+ let exit_code = args
2383+ .first()
2384+ .and_then(|code| code.parse::<i32>().ok())
2385+ .unwrap_or(1);
2386+ cx.editor.exit_code = exit_code;
2387+
2388+ quit_all_impl(cx.editor, true)
2362 }
2389 }
2363
2390
2364 fn theme(
2391 fn theme(
@@ -2393,7 +2420,7 @@ pub mod cmd {
2393 args: &[Cow<str>],
2420 args: &[Cow<str>],
2394 _event: PromptEvent,
2421 _event: PromptEvent,
2395 ) -> anyhow::Result<()> {
2422 ) -> anyhow::Result<()> {
2396− let (_, doc) = current!(cx.editor);
2423+ let doc = doc!(cx.editor);
2397 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
2424 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
2398 let separator = args.first().unwrap_or(&default_sep);
2425 let separator = args.first().unwrap_or(&default_sep);
2399 yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Clipboard)
2426 yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Clipboard)
@@ -2412,7 +2439,7 @@ pub mod cmd {
2412 args: &[Cow<str>],
2439 args: &[Cow<str>],
2413 _event: PromptEvent,
2440 _event: PromptEvent,
2414 ) -> anyhow::Result<()> {
2441 ) -> anyhow::Result<()> {
2415− let (_, doc) = current!(cx.editor);
2442+ let doc = doc!(cx.editor);
2416 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
2443 let default_sep = Cow::Borrowed(doc.line_ending.as_str());
2417 let separator = args.first().unwrap_or(&default_sep);
2444 let separator = args.first().unwrap_or(&default_sep);
2418 yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Selection)
2445 yank_joined_to_clipboard_impl(cx.editor, separator, ClipboardType::Selection)
@@ -2539,7 +2566,7 @@ pub mod cmd {
2539 args: &[Cow<str>],
2566 args: &[Cow<str>],
2540 _event: PromptEvent,
2567 _event: PromptEvent,
2541 ) -> anyhow::Result<()> {
2568 ) -> anyhow::Result<()> {
2542− let (_, doc) = current!(cx.editor);
2569+ let doc = doc_mut!(cx.editor);
2543 if let Some(label) = args.first() {
2570 if let Some(label) = args.first() {
2544 doc.set_encoding(label)
2571 doc.set_encoding(label)
2545 } else {
2572 } else {
@@ -2637,6 +2664,86 @@ pub mod cmd {
2637 let (view, doc) = current!(cx.editor);
2664 let (view, doc) = current!(cx.editor);
2638
2665
2639 view.ensure_cursor_in_view(doc, line);
2666 view.ensure_cursor_in_view(doc, line);
2667+ Ok(())
2668+ }
2669+
2670+ fn setting(
2671+ cx: &mut compositor::Context,
2672+ args: &[Cow<str>],
2673+ _event: PromptEvent,
2674+ ) -> anyhow::Result<()> {
2675+ let runtime_config = &mut cx.editor.config;
2676+
2677+ if args.len() != 2 {
2678+ anyhow::bail!("Bad arguments. Usage: `:set key field`");
2679+ }
2680+
2681+ let (key, arg) = (&args[0].to_lowercase(), &args[1]);
2682+
2683+ match key.as_ref() {
2684+ "scrolloff" => runtime_config.scrolloff = arg.parse()?,
2685+ "scroll-lines" => runtime_config.scroll_lines = arg.parse()?,
2686+ "mouse" => runtime_config.mouse = arg.parse()?,
2687+ "line-number" => runtime_config.line_number = arg.parse()?,
2688+ "middle-click_paste" => runtime_config.middle_click_paste = arg.parse()?,
2689+ "smart-case" => runtime_config.smart_case = arg.parse()?,
2690+ "auto-pairs" => runtime_config.auto_pairs = arg.parse()?,
2691+ "auto-completion" => runtime_config.auto_completion = arg.parse()?,
2692+ "completion-trigger-len" => runtime_config.completion_trigger_len = arg.parse()?,
2693+ "auto-info" => runtime_config.auto_info = arg.parse()?,
2694+ "true-color" => runtime_config.true_color = arg.parse()?,
2695+ _ => anyhow::bail!("Unknown key `{}`.", args[0]),
2696+ }
2697+
2698+ Ok(())
2699+ }
2700+
2701+ fn sort(
2702+ cx: &mut compositor::Context,
2703+ args: &[Cow<str>],
2704+ _event: PromptEvent,
2705+ ) -> anyhow::Result<()> {
2706+ sort_impl(cx, args, false)
2707+ }
2708+
2709+ fn sort_reverse(
2710+ cx: &mut compositor::Context,
2711+ args: &[Cow<str>],
2712+ _event: PromptEvent,
2713+ ) -> anyhow::Result<()> {
2714+ sort_impl(cx, args, true)
2715+ }
2716+
2717+ fn sort_impl(
2718+ cx: &mut compositor::Context,
2719+ _args: &[Cow<str>],
2720+ reverse: bool,
2721+ ) -> anyhow::Result<()> {
2722+ let (view, doc) = current!(cx.editor);
2723+ let text = doc.text().slice(..);
2724+
2725+ let selection = doc.selection(view.id);
2726+
2727+ let mut fragments: Vec<_> = selection
2728+ .fragments(text)
2729+ .map(|fragment| Tendril::from_slice(&fragment))
2730+ .collect();
2731+
2732+ fragments.sort_by(match reverse {
2733+ true => |a: &Tendril, b: &Tendril| b.cmp(a),
2734+ false => |a: &Tendril, b: &Tendril| a.cmp(b),
2735+ });
2736+
2737+ let transaction = Transaction::change(
2738+ doc.text(),
2739+ selection
2740+ .into_iter()
2741+ .zip(fragments)
2742+ .map(|(s, fragment)| (s.from(), s.to(), Some(fragment))),
2743+ );
2744+
2745+ doc.apply(&transaction, view.id);
2746+ doc.append_changes_to_history(view.id);
2640
2747
2641 Ok(())
2748 Ok(())
2642 }
2749 }
@@ -2664,18 +2771,18 @@ pub mod cmd {
2664 completer: Some(completers::filename),
2771 completer: Some(completers::filename),
2665 },
2772 },
2666 TypableCommand {
2773 TypableCommand {
2667− name: "buffer-close",
2774+ name: "buffer-close",
2668− aliases: &["bc", "bclose"],
2775+ aliases: &["bc", "bclose"],
2669− doc: "Close the current buffer.",
2776+ doc: "Close the current buffer.",
2670− fun: buffer_close,
2777+ fun: buffer_close,
2671− completer: None, // FIXME: buffer completer
2778+ completer: None, // FIXME: buffer completer
2672 },
2779 },
2673 TypableCommand {
2780 TypableCommand {
2674− name: "buffer-close!",
2781+ name: "buffer-close!",
2675− aliases: &["bc!", "bclose!"],
2782+ aliases: &["bc!", "bclose!"],
2676− doc: "Close the current buffer forcefully (ignoring unsaved changes).",
2783+ doc: "Close the current buffer forcefully (ignoring unsaved changes).",
2677− fun: force_buffer_close,
2784+ fun: force_buffer_close,
2678− completer: None, // FIXME: buffer completer
2785+ completer: None, // FIXME: buffer completer
2679 },
2786 },
2680 TypableCommand {
2787 TypableCommand {
2681 name: "write",
2788 name: "write",
@@ -2782,6 +2889,13 @@ pub mod cmd {
2782 fun: cquit,
2889 fun: cquit,
2783 completer: None,
2890 completer: None,
2784 },
2891 },
2892+ TypableCommand {
2893+ name: "cquit!",
2894+ aliases: &["cq!"],
2895+ doc: "Quit with exit code (default 1) forcefully (ignoring unsaved changes). Accepts an optional integer exit code (:cq! 2).",
2896+ fun: force_cquit,
2897+ completer: None,
2898+ },
2785 TypableCommand {
2899 TypableCommand {
2786 name: "theme",
2900 name: "theme",
2787 aliases: &[],
2901 aliases: &[],
@@ -2928,7 +3042,28 @@ pub mod cmd {
2928 doc: "Go to line number.",
3042 doc: "Go to line number.",
2929 fun: goto_line_number,
3043 fun: goto_line_number,
2930 completer: None,
3044 completer: None,
2931− }
3045+ },
3046+ TypableCommand {
3047+ name: "set-option",
3048+ aliases: &["set"],
3049+ doc: "Set a config option at runtime",
3050+ fun: setting,
3051+ completer: Some(completers::setting),
3052+ },
3053+ TypableCommand {
3054+ name: "sort",
3055+ aliases: &[],
3056+ doc: "Sort ranges in selection.",
3057+ fun: sort,
3058+ completer: None,
3059+ },
3060+ TypableCommand {
3061+ name: "rsort",
3062+ aliases: &[],
3063+ doc: "Sort ranges in selection in reverse order.",
3064+ fun: sort_reverse,
3065+ completer: None,
3066+ },
2932 ];
3067 ];
2933
3068
2934 pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =
3069 pub static TYPABLE_COMMAND_MAP: Lazy<HashMap<&'static str, &'static TypableCommand>> =
@@ -2948,17 +3083,28 @@ fn command_mode(cx: &mut Context) {
2948 ":".into(),
3083 ":".into(),
2949 Some(':'),
3084 Some(':'),
2950 |input: &str| {
3085 |input: &str| {
3086+ static FUZZY_MATCHER: Lazy<fuzzy_matcher::skim::SkimMatcherV2> =
3087+ Lazy::new(fuzzy_matcher::skim::SkimMatcherV2::default);
3088+
2951 // we use .this over split_whitespace() because we care about empty segments
3089 // we use .this over split_whitespace() because we care about empty segments
2952 let parts = input.split(' ').collect::<Vec<&str>>();
3090 let parts = input.split(' ').collect::<Vec<&str>>();
2953
3091
2954 // simple heuristic: if there's no just one part, complete command name.
3092 // simple heuristic: if there's no just one part, complete command name.
2955 // if there's a space, per command completion kicks in.
3093 // if there's a space, per command completion kicks in.
2956 if parts.len() <= 1 {
3094 if parts.len() <= 1 {
2957− let end = 0..;
2958− cmd::TYPABLE_COMMAND_LIST
3095+ let mut matches: Vec<_> = cmd::TYPABLE_COMMAND_LIST
2959 .iter()
3096 .iter()
3097+ .filter_map(|command| {
3098+ FUZZY_MATCHER
2960− .filter(|command| command.name.contains(input))
3099+ .fuzzy_match(command.name, input)
2961− .map(|command| (end.clone(), Cow::Borrowed(command.name)))
3100+ .map(|score| (command.name, score))
3101+ })
3102+ .collect();
3103+
3104+ matches.sort_unstable_by_key(|(_file, score)| std::cmp::Reverse(*score));
3105+ matches
3106+ .into_iter()
3107+ .map(|(name, _)| (0.., name.into()))
2962 .collect()
3108 .collect()
2963 } else {
3109 } else {
2964 let part = parts.last().unwrap();
3110 let part = parts.last().unwrap();
@@ -3002,7 +3148,16 @@ fn command_mode(cx: &mut Context) {
3002
3148
3003 // Handle typable commands
3149 // Handle typable commands
3004 if let Some(cmd) = cmd::TYPABLE_COMMAND_MAP.get(parts[0]) {
3150 if let Some(cmd) = cmd::TYPABLE_COMMAND_MAP.get(parts[0]) {
3151+ let args = if cfg!(unix) {
3005− let args = shellwords::shellwords(input);
3152+ shellwords::shellwords(input)
3153+ } else {
3154+ // Windows doesn't support POSIX, so fallback for now
3155+ parts
3156+ .into_iter()
3157+ .map(|part| part.into())
3158+ .collect::<Vec<_>>()
3159+ };
3160+
3006 if let Err(e) = (cmd.fun)(cx, &args[1..], event) {
3161 if let Err(e) = (cmd.fun)(cx, &args[1..], event) {
3007 cx.editor.set_error(format!("{}", e));
3162 cx.editor.set_error(format!("{}", e));
3008 }
3163 }
@@ -3026,7 +3181,8 @@ fn command_mode(cx: &mut Context) {
3026}
3181}
3027
3182
3028fn file_picker(cx: &mut Context) {
3183fn file_picker(cx: &mut Context) {
3184+ // We don't specify language markers, root will be the root of the current git repo
3029− let root = find_root(None).unwrap_or_else(|| PathBuf::from("./"));
3185+ let root = find_root(None, &[]).unwrap_or_else(|| PathBuf::from("./"));
3030 let picker = ui::file_picker(root, &cx.editor.config);
3186 let picker = ui::file_picker(root, &cx.editor.config);
3031 cx.push_layer(Box::new(picker));
3187 cx.push_layer(Box::new(picker));
3032}
3188}
@@ -3118,7 +3274,7 @@ fn symbol_picker(cx: &mut Context) {
3118 nested_to_flat(list, file, child);
3274 nested_to_flat(list, file, child);
3119 }
3275 }
3120 }
3276 }
3121− let (_, doc) = current!(cx.editor);
3277+ let doc = doc!(cx.editor);
3122
3278
3123 let language_server = match doc.language_server() {
3279 let language_server = match doc.language_server() {
3124 Some(language_server) => language_server,
3280 Some(language_server) => language_server,
@@ -3139,7 +3295,7 @@ fn symbol_picker(cx: &mut Context) {
3139 let symbols = match symbols {
3295 let symbols = match symbols {
3140 lsp::DocumentSymbolResponse::Flat(symbols) => symbols,
3296 lsp::DocumentSymbolResponse::Flat(symbols) => symbols,
3141 lsp::DocumentSymbolResponse::Nested(symbols) => {
3297 lsp::DocumentSymbolResponse::Nested(symbols) => {
3142− let (_view, doc) = current!(editor);
3298+ let doc = doc!(editor);
3143 let mut flat_symbols = Vec::new();
3299 let mut flat_symbols = Vec::new();
3144 for symbol in symbols {
3300 for symbol in symbols {
3145 nested_to_flat(&mut flat_symbols, &doc.identifier(), symbol)
3301 nested_to_flat(&mut flat_symbols, &doc.identifier(), symbol)
@@ -3181,17 +3337,15 @@ fn symbol_picker(cx: &mut Context) {
3181}
3337}
3182
3338
3183fn workspace_symbol_picker(cx: &mut Context) {
3339fn workspace_symbol_picker(cx: &mut Context) {
3184− let (_, doc) = current!(cx.editor);
3340+ let doc = doc!(cx.editor);
3185−
3341+ let current_path = doc.path().cloned();
3186 let language_server = match doc.language_server() {
3342 let language_server = match doc.language_server() {
3187 Some(language_server) => language_server,
3343 Some(language_server) => language_server,
3188 None => return,
3344 None => return,
3189 };
3345 };
3190 let offset_encoding = language_server.offset_encoding();
3346 let offset_encoding = language_server.offset_encoding();
3191−
3192 let future = language_server.workspace_symbols("".to_string());
3347 let future = language_server.workspace_symbols("".to_string());
3193
3348
3194− let current_path = doc_mut!(cx.editor).path().cloned();
3195 cx.callback(
3349 cx.callback(
3196 future,
3350 future,
3197 move |_editor: &mut Editor,
3351 move |_editor: &mut Editor,
@@ -3277,12 +3431,19 @@ pub fn code_action(cx: &mut Context) {
3277 move |editor, code_action, _action| match code_action {
3431 move |editor, code_action, _action| match code_action {
3278 lsp::CodeActionOrCommand::Command(command) => {
3432 lsp::CodeActionOrCommand::Command(command) => {
3279 log::debug!("code action command: {:?}", command);
3433 log::debug!("code action command: {:?}", command);
3280− editor.set_error(String::from("Handling code action command is not implemented yet, see https://github.com/helix-editor/helix/issues/183"));
3434+ execute_lsp_command(editor, command.clone());
3281 }
3435 }
3282 lsp::CodeActionOrCommand::CodeAction(code_action) => {
3436 lsp::CodeActionOrCommand::CodeAction(code_action) => {
3283 log::debug!("code action: {:?}", code_action);
3437 log::debug!("code action: {:?}", code_action);
3284 if let Some(ref workspace_edit) = code_action.edit {
3438 if let Some(ref workspace_edit) = code_action.edit {
3439+ log::debug!("edit: {:?}", workspace_edit);
3285− apply_workspace_edit(editor, offset_encoding, workspace_edit)
3440+ apply_workspace_edit(editor, offset_encoding, workspace_edit);
3441+ }
3442+
3443+ // if code action provides both edit and command first the edit
3444+ // should be applied and then the command
3445+ if let Some(command) = &code_action.command {
3446+ execute_lsp_command(editor, command.clone());
3286 }
3447 }
3287 }
3448 }
3288 },
3449 },
@@ -3293,6 +3454,25 @@ pub fn code_action(cx: &mut Context) {
3293 )
3454 )
3294}
3455}
3295
3456
3457+pub fn execute_lsp_command(editor: &mut Editor, cmd: lsp::Command) {
3458+ let doc = doc!(editor);
3459+ let language_server = match doc.language_server() {
3460+ Some(language_server) => language_server,
3461+ None => return,
3462+ };
3463+
3464+ // the command is executed on the server and communicated back
3465+ // to the client asynchronously using workspace edits
3466+ let command_future = language_server.command(cmd);
3467+ tokio::spawn(async move {
3468+ let res = command_future.await;
3469+
3470+ if let Err(e) = res {
3471+ log::error!("execute LSP command: {}", e);
3472+ }
3473+ });
3474+}
3475+
3296pub fn apply_document_resource_op(op: &lsp::ResourceOp) -> std::io::Result<()> {
3476pub fn apply_document_resource_op(op: &lsp::ResourceOp) -> std::io::Result<()> {
3297 use lsp::ResourceOp;
3477 use lsp::ResourceOp;
3298 use std::fs;
3478 use std::fs;
@@ -3346,7 +3526,7 @@ pub fn apply_document_resource_op(op: &lsp::ResourceOp) -> std::io::Result<()> {
3346 }
3526 }
3347}
3527}
3348
3528
3349−fn apply_workspace_edit(
3529+pub fn apply_workspace_edit(
3350 editor: &mut Editor,
3530 editor: &mut Editor,
3351 offset_encoding: OffsetEncoding,
3531 offset_encoding: OffsetEncoding,
3352 workspace_edit: &lsp::WorkspaceEdit,
3532 workspace_edit: &lsp::WorkspaceEdit,
@@ -3537,22 +3717,22 @@ fn open(cx: &mut Context, open: Open) {
3537 let mut offs = 0;
3717 let mut offs = 0;
3538
3718
3539 let mut transaction = Transaction::change_by_selection(contents, selection, |range| {
3719 let mut transaction = Transaction::change_by_selection(contents, selection, |range| {
3540− let line = range.cursor_line(text);
3720+ let cursor_line = range.cursor_line(text);
3541
3721
3542− let line = match open {
3722+ let new_line = match open {
3543 // adjust position to the end of the line (next line - 1)
3723 // adjust position to the end of the line (next line - 1)
3544− Open::Below => line + 1,
3724+ Open::Below => cursor_line + 1,
3545 // adjust position to the end of the previous line (current line - 1)
3725 // adjust position to the end of the previous line (current line - 1)
3546− Open::Above => line,
3726+ Open::Above => cursor_line,
3547 };
3727 };
3548
3728
3549 // Index to insert newlines after, as well as the char width
3729 // Index to insert newlines after, as well as the char width
3550 // to use to compensate for those inserted newlines.
3730 // to use to compensate for those inserted newlines.
3551− let (line_end_index, line_end_offset_width) = if line == 0 {
3731+ let (line_end_index, line_end_offset_width) = if new_line == 0 {
3552 (0, 0)
3732 (0, 0)
3553 } else {
3733 } else {
3554 (
3734 (
3555− line_end_char_index(&doc.text().slice(..), line.saturating_sub(1)),
3735+ line_end_char_index(&doc.text().slice(..), new_line.saturating_sub(1)),
3556 doc.line_ending.len_chars(),
3736 doc.line_ending.len_chars(),
3557 )
3737 )
3558 };
3738 };
@@ -3563,8 +3743,10 @@ fn open(cx: &mut Context, open: Open) {
3563 doc.syntax(),
3743 doc.syntax(),
3564 text,
3744 text,
3565 line_end_index,
3745 line_end_index,
3746+ new_line.saturating_sub(1),
3566 true,
3747 true,
3567− );
3748+ )
3749+ .unwrap_or_else(|| indent::indent_level_for_line(text.line(cursor_line), doc.tab_width()));
3568 let indent = doc.indent_unit().repeat(indent_level);
3750 let indent = doc.indent_unit().repeat(indent_level);
3569 let indent_len = indent.len();
3751 let indent_len = indent.len();
3570 let mut text = String::with_capacity(1 + indent_len);
3752 let mut text = String::with_capacity(1 + indent_len);
@@ -3610,6 +3792,7 @@ fn normal_mode(cx: &mut Context) {
3610
3792
3611 doc.mode = Mode::Normal;
3793 doc.mode = Mode::Normal;
3612
3794
3795+ try_restore_indent(doc, view.id);
3613 doc.append_changes_to_history(view.id);
3796 doc.append_changes_to_history(view.id);
3614
3797
3615 // if leaving append mode, move cursor back by 1
3798 // if leaving append mode, move cursor back by 1
@@ -3627,6 +3810,40 @@ fn normal_mode(cx: &mut Context) {
3627 }
3810 }
3628}
3811}
3629
3812
3813+fn try_restore_indent(doc: &mut Document, view_id: ViewId) {
3814+ use helix_core::chars::char_is_whitespace;
3815+ use helix_core::Operation;
3816+
3817+ fn inserted_a_new_blank_line(changes: &[Operation], pos: usize, line_end_pos: usize) -> bool {
3818+ if let [Operation::Retain(move_pos), Operation::Insert(ref inserted_str), Operation::Retain(_)] =
3819+ changes
3820+ {
3821+ move_pos + inserted_str.len32() as usize == pos
3822+ && inserted_str.starts_with('\n')
3823+ && inserted_str.chars().skip(1).all(char_is_whitespace)
3824+ && pos == line_end_pos // ensure no characters exists after current position
3825+ } else {
3826+ false
3827+ }
3828+ }
3829+
3830+ let doc_changes = doc.changes().changes();
3831+ let text = doc.text().slice(..);
3832+ let range = doc.selection(view_id).primary();
3833+ let pos = range.cursor(text);
3834+ let line_end_pos = line_end_char_index(&text, range.cursor_line(text));
3835+
3836+ if inserted_a_new_blank_line(doc_changes, pos, line_end_pos) {
3837+ // Removes tailing whitespaces.
3838+ let transaction =
3839+ Transaction::change_by_selection(doc.text(), doc.selection(view_id), |range| {
3840+ let line_start_pos = text.line_to_char(range.cursor_line(text));
3841+ (line_start_pos, pos, None)
3842+ });
3843+ doc.apply(&transaction, view_id);
3844+ }
3845+}
3846+
3630// Store a jump on the jumplist.
3847// Store a jump on the jumplist.
3631fn push_jump(editor: &mut Editor) {
3848fn push_jump(editor: &mut Editor) {
3632 let (view, doc) = current!(editor);
3849 let (view, doc) = current!(editor);
@@ -3994,27 +4211,21 @@ fn goto_pos(editor: &mut Editor, pos: usize) {
3994}
4211}
3995
4212
3996fn goto_first_diag(cx: &mut Context) {
4213fn goto_first_diag(cx: &mut Context) {
3997− let editor = &mut cx.editor;
4214+ let doc = doc!(cx.editor);
3998− let (_, doc) = current!(editor);
3999−
4000 let pos = match doc.diagnostics().first() {
4215 let pos = match doc.diagnostics().first() {
4001 Some(diag) => diag.range.start,
4216 Some(diag) => diag.range.start,
4002 None => return,
4217 None => return,
4003 };
4218 };
4004−
4005− goto_pos(editor, pos);
4219+ goto_pos(cx.editor, pos);
4006}
4220}
4007
4221
4008fn goto_last_diag(cx: &mut Context) {
4222fn goto_last_diag(cx: &mut Context) {
4009− let editor = &mut cx.editor;
4223+ let doc = doc!(cx.editor);
4010− let (_, doc) = current!(editor);
4011−
4012 let pos = match doc.diagnostics().last() {
4224 let pos = match doc.diagnostics().last() {
4013 Some(diag) => diag.range.start,
4225 Some(diag) => diag.range.start,
4014 None => return,
4226 None => return,
4015 };
4227 };
4016−
4017− goto_pos(editor, pos);
4228+ goto_pos(cx.editor, pos);
4018}
4229}
4019
4230
4020fn goto_next_diag(cx: &mut Context) {
4231fn goto_next_diag(cx: &mut Context) {
@@ -4270,48 +4481,48 @@ pub mod insert {
4270 };
4481 };
4271 let curr = contents.get_char(pos).unwrap_or(' ');
4482 let curr = contents.get_char(pos).unwrap_or(' ');
4272
4483
4273− // TODO: offset range.head by 1? when calculating?
4484+ let current_line = text.char_to_line(pos);
4274 let indent_level = indent::suggested_indent_for_pos(
4485 let indent_level = indent::suggested_indent_for_pos(
4275 doc.language_config(),
4486 doc.language_config(),
4276 doc.syntax(),
4487 doc.syntax(),
4277 text,
4488 text,
4278− pos.saturating_sub(1),
4489+ pos,
4490+ current_line,
4279 true,
4491 true,
4280− );
4492+ )
4281− let indent = doc.indent_unit().repeat(indent_level);
4493+ .unwrap_or_else(|| {
4282− let mut text = String::with_capacity(1 + indent.len());
4494+ indent::indent_level_for_line(text.line(current_line), doc.tab_width())
4283− text.push_str(doc.line_ending.as_str());
4495+ });
4284− text.push_str(&indent);
4285
4496
4497+ let indent = doc.indent_unit().repeat(indent_level);
4498+ let mut text = String::new();
4499+ // If we are between pairs (such as brackets), we want to insert an additional line which is indented one level more and place the cursor there
4500+ let new_head_pos = if helix_core::auto_pairs::PAIRS.contains(&(prev, curr)) {
4501+ let inner_indent = doc.indent_unit().repeat(indent_level + 1);
4502+ text.reserve_exact(2 + indent.len() + inner_indent.len());
4503+ text.push_str(doc.line_ending.as_str());
4504+ text.push_str(&inner_indent);
4286− let head = pos + offs + text.chars().count();
4505+ let new_head_pos = pos + offs + text.chars().count();
4506+ text.push_str(doc.line_ending.as_str());
4507+ text.push_str(&indent);
4508+ new_head_pos
4509+ } else {
4510+ text.reserve_exact(1 + indent.len());
4511+ text.push_str(doc.line_ending.as_str());
4512+ text.push_str(&indent);
4513+ pos + offs + text.chars().count()
4514+ };
4287
4515
4288 // TODO: range replace or extend
4516 // TODO: range replace or extend
4289 // range.replace(|range| range.is_empty(), head); -> fn extend if cond true, new head pos
4517 // range.replace(|range| range.is_empty(), head); -> fn extend if cond true, new head pos
4290 // can be used with cx.mode to do replace or extend on most changes
4518 // can be used with cx.mode to do replace or extend on most changes
4291− ranges.push(Range::new(
4519+ ranges.push(Range::new(new_head_pos, new_head_pos));
4292− if range.is_empty() {
4293− head
4294− } else {
4295− range.anchor + offs
4296− },
4297− head,
4298− ));
4299−
4300− // if between a bracket pair
4301− if helix_core::auto_pairs::PAIRS.contains(&(prev, curr)) {
4302− // another newline, indent the end bracket one level less
4303− let indent = doc.indent_unit().repeat(indent_level.saturating_sub(1));
4304− text.push_str(doc.line_ending.as_str());
4305− text.push_str(&indent);
4306− }
4307−
4308 offs += text.chars().count();
4520 offs += text.chars().count();
4309
4521
4310 (pos, pos, Some(text.into()))
4522 (pos, pos, Some(text.into()))
4311 });
4523 });
4312
4524
4313 transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
4525 transaction = transaction.with_selection(Selection::new(ranges, selection.primary_index()));
4314− //
4315
4526
4316 doc.apply(&transaction, view.id);
4527 doc.apply(&transaction, view.id);
4317 }
4528 }
@@ -5079,7 +5290,7 @@ pub fn completion(cx: &mut Context) {
5079 move |editor: &mut Editor,
5290 move |editor: &mut Editor,
5080 compositor: &mut Compositor,
5291 compositor: &mut Compositor,
5081 response: Option<lsp::CompletionResponse>| {
5292 response: Option<lsp::CompletionResponse>| {
5082− let (_, doc) = current!(editor);
5293+ let doc = doc!(editor);
5083 if doc.mode() != Mode::Insert {
5294 if doc.mode() != Mode::Insert {
5084 // we're not in insert mode anymore
5295 // we're not in insert mode anymore
5085 return;
5296 return;
@@ -5257,6 +5468,7 @@ fn rotate_selection_contents(cx: &mut Context, direction: Direction) {
5257 doc.apply(&transaction, view.id);
5468 doc.apply(&transaction, view.id);
5258 doc.append_changes_to_history(view.id);
5469 doc.append_changes_to_history(view.id);
5259}
5470}
5471+
5260fn rotate_selection_contents_forward(cx: &mut Context) {
5472fn rotate_selection_contents_forward(cx: &mut Context) {
5261 rotate_selection_contents(cx, Direction::Forward)
5473 rotate_selection_contents(cx, Direction::Forward)
5262}
5474}
@@ -5272,7 +5484,39 @@ fn expand_selection(cx: &mut Context) {
5272
5484
5273 if let Some(syntax) = doc.syntax() {
5485 if let Some(syntax) = doc.syntax() {
5274 let text = doc.text().slice(..);
5486 let text = doc.text().slice(..);
5487+
5488+ let current_selection = doc.selection(view.id);
5489+
5490+ // save current selection so it can be restored using shrink_selection
5491+ view.object_selections.push(current_selection.clone());
5492+
5275− let selection = object::expand_selection(syntax, text, doc.selection(view.id));
5493+ let selection = object::expand_selection(syntax, text, current_selection);
5494+ doc.set_selection(view.id, selection);
5495+ }
5496+ };
5497+ motion(cx.editor);
5498+ cx.editor.last_motion = Some(Motion(Box::new(motion)));
5499+}
5500+
5501+fn shrink_selection(cx: &mut Context) {
5502+ let motion = |editor: &mut Editor| {
5503+ let (view, doc) = current!(editor);
5504+ let current_selection = doc.selection(view.id);
5505+ // try to restore previous selection
5506+ if let Some(prev_selection) = view.object_selections.pop() {
5507+ if current_selection.contains(&prev_selection) {
5508+ // allow shrinking the selection only if current selection contains the previous object selection
5509+ doc.set_selection(view.id, prev_selection);
5510+ return;
5511+ } else {
5512+ // clear existing selection as they can't be shrinked to anyway
5513+ view.object_selections.clear();
5514+ }
5515+ }
5516+ // if not previous selection, shrink to first child
5517+ if let Some(syntax) = doc.syntax() {
5518+ let text = doc.text().slice(..);
5519+ let selection = object::shrink_selection(syntax, text, current_selection);
5276 doc.set_selection(view.id, selection);
5520 doc.set_selection(view.id, selection);
5277 }
5521 }
5278 };
5522 };
@@ -5920,42 +6164,42 @@ fn record_macro(cx: &mut Context) {
5920 keys.pop();
6164 keys.pop();
5921 let s = keys
6165 let s = keys
5922 .into_iter()
6166 .into_iter()
5923− .map(|key| format!("{}", key))
6167+ .map(|key| {
6168+ let s = key.to_string();
6169+ if s.chars().count() == 1 {
6170+ s
6171+ } else {
6172+ format!("<{}>", s)
6173+ }
6174+ })
5924− .collect::<Vec<_>>()
6175+ .collect::<String>();
5925− .join(" ");
5926 cx.editor.registers.get_mut(reg).write(vec![s]);
6176 cx.editor.registers.get_mut(reg).write(vec![s]);
5927 cx.editor
6177 cx.editor
5928− .set_status(format!("Recorded to register {}", reg));
6178+ .set_status(format!("Recorded to register [{}]", reg));
5929 } else {
6179 } else {
5930 let reg = cx.register.take().unwrap_or('@');
6180 let reg = cx.register.take().unwrap_or('@');
5931 cx.editor.macro_recording = Some((reg, Vec::new()));
6181 cx.editor.macro_recording = Some((reg, Vec::new()));
5932 cx.editor
6182 cx.editor
5933− .set_status(format!("Recording to register {}", reg));
6183+ .set_status(format!("Recording to register [{}]", reg));
5934 }
6184 }
5935}
6185}
5936
6186
5937−fn play_macro(cx: &mut Context) {
6187+fn replay_macro(cx: &mut Context) {
5938 let reg = cx.register.unwrap_or('@');
6188 let reg = cx.register.unwrap_or('@');
5939− let keys = match cx
6189+ let keys: Vec<KeyEvent> = if let Some([keys_str]) = cx.editor.registers.read(reg) {
5940− .editor
6190+ match helix_view::input::parse_macro(keys_str) {
5941− .registers
5942− .get(reg)
5943− .and_then(|reg| reg.read().get(0))
5944− .context("Register empty")
5945− .and_then(|s| {
5946− s.split_whitespace()
5947− .map(str::parse::<KeyEvent>)
5948− .collect::<Result<Vec<_>, _>>()
5949− .context("Failed to parse macro")
5950− }) {
5951− Ok(keys) => keys,
6191+ Ok(keys) => keys,
5952− Err(e) => {
6192+ Err(err) => {
5953− cx.editor.set_error(format!("{}", e));
6193+ cx.editor.set_error(format!("Invalid macro: {}", err));
5954− return;
6194+ return;
6195+ }
5955 }
6196 }
6197+ } else {
6198+ cx.editor.set_error(format!("Register [{}] empty", reg));
6199+ return;
5956 };
6200 };
5957− let count = cx.count();
5958
6201
6202+ let count = cx.count();
5959 cx.callback = Some(Box::new(
6203 cx.callback = Some(Box::new(
5960 move |compositor: &mut Compositor, cx: &mut compositor::Context| {
6204 move |compositor: &mut Compositor, cx: &mut compositor::Context| {
5961 for _ in 0..count {
6205 for _ in 0..count {
helix-term/src/keymap.rs+6−2
@@ -569,11 +569,13 @@ impl Default for Keymaps {
569 "d" => goto_prev_diag,
569 "d" => goto_prev_diag,
570 "D" => goto_first_diag,
570 "D" => goto_first_diag,
571 "space" => add_newline_above,
571 "space" => add_newline_above,
572+ "o" => shrink_selection,
572 },
573 },
573 "]" => { "Right bracket"
574 "]" => { "Right bracket"
574 "d" => goto_next_diag,
575 "d" => goto_next_diag,
575 "D" => goto_last_diag,
576 "D" => goto_last_diag,
576 "space" => add_newline_below,
577 "space" => add_newline_below,
578+ "o" => expand_selection,
577 },
579 },
578
580
579 "/" => search,
581 "/" => search,
@@ -593,8 +595,8 @@ impl Default for Keymaps {
593 // paste_all
595 // paste_all
594 "P" => paste_before,
596 "P" => paste_before,
595
597
596− "q" => record_macro,
598+ "Q" => record_macro,
597− "Q" => play_macro,
599+ "q" => replay_macro,
598
600
599 ">" => indent,
601 ">" => indent,
600 "<" => unindent,
602 "<" => unindent,
@@ -617,6 +619,8 @@ impl Default for Keymaps {
617 "A-(" => rotate_selection_contents_backward,
619 "A-(" => rotate_selection_contents_backward,
618 "A-)" => rotate_selection_contents_forward,
620 "A-)" => rotate_selection_contents_forward,
619
621
622+ "A-:" => ensure_selections_forward,
623+
620 "esc" => normal_mode,
624 "esc" => normal_mode,
621 "C-b" | "pageup" => page_up,
625 "C-b" | "pageup" => page_up,
622 "C-f" | "pagedown" => page_down,
626 "C-f" | "pagedown" => page_down,
helix-term/src/ui/completion.rs+38−2
@@ -154,8 +154,19 @@ impl Completion {
154 );
154 );
155 doc.apply(&transaction, view.id);
155 doc.apply(&transaction, view.id);
156
156
157− if let Some(additional_edits) = &item.additional_text_edits {
157+ // apply additional edits, mostly used to auto import unqualified types
158− // gopls uses this to add extra imports
158+ let resolved_additional_text_edits = if item.additional_text_edits.is_some() {
159+ None
160+ } else {
161+ Completion::resolve_completion_item(doc, item.clone())
162+ .and_then(|item| item.additional_text_edits)
163+ };
164+
165+ if let Some(additional_edits) = item
166+ .additional_text_edits
167+ .as_ref()
168+ .or_else(|| resolved_additional_text_edits.as_ref())
169+ {
159 if !additional_edits.is_empty() {
170 if !additional_edits.is_empty() {
160 let transaction = util::generate_transaction_from_edits(
171 let transaction = util::generate_transaction_from_edits(
161 doc.text(),
172 doc.text(),
@@ -181,6 +192,31 @@ impl Completion {
181 completion
192 completion
182 }
193 }
183
194
195+ fn resolve_completion_item(
196+ doc: &Document,
197+ completion_item: lsp::CompletionItem,
198+ ) -> Option<CompletionItem> {
199+ let language_server = doc.language_server()?;
200+ let completion_resolve_provider = language_server
201+ .capabilities()
202+ .completion_provider
203+ .as_ref()?
204+ .resolve_provider;
205+ if completion_resolve_provider != Some(true) {
206+ return None;
207+ }
208+
209+ let future = language_server.resolve_completion_item(completion_item);
210+ let response = helix_lsp::block_on(future);
211+ match response {
212+ Ok(completion_item) => Some(completion_item),
213+ Err(err) => {
214+ log::error!("execute LSP command: {}", err);
215+ None
216+ }
217+ }
218+ }
219+
184 pub fn recompute_filter(&mut self, editor: &Editor) {
220 pub fn recompute_filter(&mut self, editor: &Editor) {
185 // recompute menu based on matches
221 // recompute menu based on matches
186 let menu = self.popup.contents_mut();
222 let menu = self.popup.contents_mut();
helix-term/src/ui/editor.rs+61−38
@@ -7,7 +7,7 @@ use crate::{
7};
7};
8
8
9use helix_core::{
9use helix_core::{
10− coords_at_pos,
10+ coords_at_pos, encoding,
11 graphemes::{ensure_grapheme_boundary_next, next_grapheme_boundary, prev_grapheme_boundary},
11 graphemes::{ensure_grapheme_boundary_next, next_grapheme_boundary, prev_grapheme_boundary},
12 movement::Direction,
12 movement::Direction,
13 syntax::{self, HighlightEvent},
13 syntax::{self, HighlightEvent},
@@ -566,21 +566,6 @@ impl EditorView {
566 }
566 }
567 surface.set_string(viewport.x + 5, viewport.y, progress, base_style);
567 surface.set_string(viewport.x + 5, viewport.y, progress, base_style);
568
568
569− let rel_path = doc.relative_path();
570− let path = rel_path
571− .as_ref()
572− .map(|p| p.to_string_lossy())
573− .unwrap_or_else(|| SCRATCH_BUFFER_NAME.into());
574−
575− let title = format!("{}{}", path, if doc.is_modified() { "[+]" } else { "" });
576− surface.set_stringn(
577− viewport.x + 8,
578− viewport.y,
579− title,
580− viewport.width.saturating_sub(6) as usize,
581− base_style,
582− );
583−
584 //-------------------------------
569 //-------------------------------
585 // Right side of the status line.
570 // Right side of the status line.
586 //-------------------------------
571 //-------------------------------
@@ -654,6 +639,13 @@ impl EditorView {
654 base_style,
639 base_style,
655 ));
640 ));
656
641
642+ let enc = doc.encoding();
643+ if enc != encoding::UTF_8 {
644+ right_side_text
645+ .0
646+ .push(Span::styled(format!(" {} ", enc.name()), base_style));
647+ }
648+
657 // Render to the statusline.
649 // Render to the statusline.
658 surface.set_spans(
650 surface.set_spans(
659 viewport.x
651 viewport.x
@@ -664,6 +656,31 @@ impl EditorView {
664 &right_side_text,
656 &right_side_text,
665 right_side_text.width() as u16,
657 right_side_text.width() as u16,
666 );
658 );
659+
660+ //-------------------------------
661+ // Middle / File path / Title
662+ //-------------------------------
663+ let title = {
664+ let rel_path = doc.relative_path();
665+ let path = rel_path
666+ .as_ref()
667+ .map(|p| p.to_string_lossy())
668+ .unwrap_or_else(|| SCRATCH_BUFFER_NAME.into());
669+ format!("{}{}", path, if doc.is_modified() { "[+]" } else { "" })
670+ };
671+
672+ surface.set_string_truncated(
673+ viewport.x + 8, // 8: 1 space + 3 char mode string + 1 space + 1 spinner + 1 space
674+ viewport.y,
675+ title,
676+ viewport
677+ .width
678+ .saturating_sub(6)
679+ .saturating_sub(right_side_text.width() as u16 + 1) as usize, // "+ 1": a space between the title and the selection info
680+ base_style,
681+ true,
682+ true,
683+ );
667 }
684 }
668
685
669 /// Handle events by looking them up in `self.keymaps`. Returns None
686 /// Handle events by looking them up in `self.keymaps`. Returns None
@@ -782,8 +799,9 @@ impl EditorView {
782
799
783 pub fn clear_completion(&mut self, editor: &mut Editor) {
800 pub fn clear_completion(&mut self, editor: &mut Editor) {
784 self.completion = None;
801 self.completion = None;
802+
785 // Clear any savepoints
803 // Clear any savepoints
786− let (_, doc) = current!(editor);
804+ let doc = doc_mut!(editor);
787 doc.savepoint = None;
805 doc.savepoint = None;
788 editor.clear_idle_timer(); // don't retrigger
806 editor.clear_idle_timer(); // don't retrigger
789 }
807 }
@@ -941,14 +959,18 @@ impl EditorView {
941}
959}
942
960
943impl Component for EditorView {
961impl Component for EditorView {
944− fn handle_event(&mut self, event: Event, cx: &mut Context) -> EventResult {
962+ fn handle_event(
963+ &mut self,
964+ event: Event,
965+ context: &mut crate::compositor::Context,
966+ ) -> EventResult {
945− let mut cxt = commands::Context {
967+ let mut cx = commands::Context {
946− editor: cx.editor,
968+ editor: context.editor,
947 count: None,
969 count: None,
948 register: None,
970 register: None,
949 callback: None,
971 callback: None,
950 on_next_key_callback: None,
972 on_next_key_callback: None,
951− jobs: cx.jobs,
973+ jobs: context.jobs,
952 };
974 };
953
975
954 match event {
976 match event {
@@ -958,18 +980,19 @@ impl Component for EditorView {
958 EventResult::Consumed(None)
980 EventResult::Consumed(None)
959 }
981 }
960 Event::Key(key) => {
982 Event::Key(key) => {
961− cxt.editor.reset_idle_timer();
983+ cx.editor.reset_idle_timer();
962 let mut key = KeyEvent::from(key);
984 let mut key = KeyEvent::from(key);
963 canonicalize_key(&mut key);
985 canonicalize_key(&mut key);
986+
964 // clear status
987 // clear status
965− cxt.editor.status_msg = None;
988+ cx.editor.status_msg = None;
966
989
967− let (_, doc) = current!(cxt.editor);
990+ let doc = doc!(cx.editor);
968 let mode = doc.mode();
991 let mode = doc.mode();
969
992
970 if let Some(on_next_key) = self.on_next_key.take() {
993 if let Some(on_next_key) = self.on_next_key.take() {
971 // if there's a command waiting input, do that first
994 // if there's a command waiting input, do that first
972− on_next_key(&mut cxt, key);
995+ on_next_key(&mut cx, key);
973 } else {
996 } else {
974 match mode {
997 match mode {
975 Mode::Insert => {
998 Mode::Insert => {
@@ -981,8 +1004,8 @@ impl Component for EditorView {
981 if let Some(completion) = &mut self.completion {
1004 if let Some(completion) = &mut self.completion {
982 // use a fake context here
1005 // use a fake context here
983 let mut cx = Context {
1006 let mut cx = Context {
984− editor: cxt.editor,
1007+ editor: cx.editor,
985− jobs: cxt.jobs,
1008+ jobs: cx.jobs,
986 scroll: None,
1009 scroll: None,
987 };
1010 };
988 let res = completion.handle_event(event, &mut cx);
1011 let res = completion.handle_event(event, &mut cx);
@@ -992,40 +1015,40 @@ impl Component for EditorView {
992
1015
993 if callback.is_some() {
1016 if callback.is_some() {
994 // assume close_fn
1017 // assume close_fn
995− self.clear_completion(cxt.editor);
1018+ self.clear_completion(cx.editor);
996 }
1019 }
997 }
1020 }
998 }
1021 }
999
1022
1000 // if completion didn't take the event, we pass it onto commands
1023 // if completion didn't take the event, we pass it onto commands
1001 if !consumed {
1024 if !consumed {
1002− self.insert_mode(&mut cxt, key);
1025+ self.insert_mode(&mut cx, key);
1003
1026
1004 // lastly we recalculate completion
1027 // lastly we recalculate completion
1005 if let Some(completion) = &mut self.completion {
1028 if let Some(completion) = &mut self.completion {
1006− completion.update(&mut cxt);
1029+ completion.update(&mut cx);
1007 if completion.is_empty() {
1030 if completion.is_empty() {
1008− self.clear_completion(cxt.editor);
1031+ self.clear_completion(cx.editor);
1009 }
1032 }
1010 }
1033 }
1011 }
1034 }
1012 }
1035 }
1013− mode => self.command_mode(mode, &mut cxt, key),
1036+ mode => self.command_mode(mode, &mut cx, key),
1014 }
1037 }
1015 }
1038 }
1016
1039
1017− self.on_next_key = cxt.on_next_key_callback.take();
1040+ self.on_next_key = cx.on_next_key_callback.take();
1018 // appease borrowck
1041 // appease borrowck
1019− let callback = cxt.callback.take();
1042+ let callback = cx.callback.take();
1020
1043
1021 // if the command consumed the last view, skip the render.
1044 // if the command consumed the last view, skip the render.
1022 // on the next loop cycle the Application will then terminate.
1045 // on the next loop cycle the Application will then terminate.
1023− if cxt.editor.should_close() {
1046+ if cx.editor.should_close() {
1024 return EventResult::Ignored;
1047 return EventResult::Ignored;
1025 }
1048 }
1026
1049
1027− let (view, doc) = current!(cxt.editor);
1050+ let (view, doc) = current!(cx.editor);
1028− view.ensure_cursor_in_view(doc, cxt.editor.config.scrolloff);
1051+ view.ensure_cursor_in_view(doc, cx.editor.config.scrolloff);
1029
1052
1030 // mode transitions
1053 // mode transitions
1031 match (mode, doc.mode()) {
1054 match (mode, doc.mode()) {
@@ -1054,7 +1077,7 @@ impl Component for EditorView {
1054 EventResult::Consumed(callback)
1077 EventResult::Consumed(callback)
1055 }
1078 }
1056
1079
1057− Event::Mouse(event) => self.handle_mouse_event(event, &mut cxt),
1080+ Event::Mouse(event) => self.handle_mouse_event(event, &mut cx),
1058 }
1081 }
1059 }
1082 }
1060
1083
helix-term/src/ui/mod.rs+28−1
@@ -174,7 +174,9 @@ pub mod completers {
174 use crate::ui::prompt::Completion;
174 use crate::ui::prompt::Completion;
175 use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
175 use fuzzy_matcher::skim::SkimMatcherV2 as Matcher;
176 use fuzzy_matcher::FuzzyMatcher;
176 use fuzzy_matcher::FuzzyMatcher;
177+ use helix_view::editor::Config;
177 use helix_view::theme;
178 use helix_view::theme;
179+ use once_cell::sync::Lazy;
178 use std::borrow::Cow;
180 use std::borrow::Cow;
179 use std::cmp::Reverse;
181 use std::cmp::Reverse;
180
182
@@ -208,6 +210,31 @@ pub mod completers {
208 names
210 names
209 }
211 }
210
212
213+ pub fn setting(input: &str) -> Vec<Completion> {
214+ static KEYS: Lazy<Vec<String>> = Lazy::new(|| {
215+ serde_json::to_value(Config::default())
216+ .unwrap()
217+ .as_object()
218+ .unwrap()
219+ .keys()
220+ .cloned()
221+ .collect()
222+ });
223+
224+ let matcher = Matcher::default();
225+
226+ let mut matches: Vec<_> = KEYS
227+ .iter()
228+ .filter_map(|name| matcher.fuzzy_match(name, input).map(|score| (name, score)))
229+ .collect();
230+
231+ matches.sort_unstable_by_key(|(_file, score)| Reverse(*score));
232+ matches
233+ .into_iter()
234+ .map(|(name, _)| ((0..), name.into()))
235+ .collect()
236+ }
237+
211 pub fn filename(input: &str) -> Vec<Completion> {
238 pub fn filename(input: &str) -> Vec<Completion> {
212 filename_impl(input, |entry| {
239 filename_impl(input, |entry| {
213 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
240 let is_dir = entry.file_type().map_or(false, |entry| entry.is_dir());
@@ -256,7 +283,7 @@ pub mod completers {
256 let is_tilde = input.starts_with('~') && input.len() == 1;
283 let is_tilde = input.starts_with('~') && input.len() == 1;
257 let path = helix_core::path::expand_tilde(Path::new(input));
284 let path = helix_core::path::expand_tilde(Path::new(input));
258
285
259− let (dir, file_name) = if input.ends_with('/') {
286+ let (dir, file_name) = if input.ends_with(std::path::MAIN_SEPARATOR) {
260 (path, None)
287 (path, None)
261 } else {
288 } else {
262 let file_name = path
289 let file_name = path
helix-term/src/ui/prompt.rs+2−2
@@ -127,7 +127,7 @@ impl Prompt {
127 let mut char_position = char_indices
127 let mut char_position = char_indices
128 .iter()
128 .iter()
129 .position(|(idx, _)| *idx == self.cursor)
129 .position(|(idx, _)| *idx == self.cursor)
130− .unwrap_or_else(|| char_indices.len());
130+ .unwrap_or(char_indices.len());
131
131
132 for _ in 0..rep {
132 for _ in 0..rep {
133 // Skip any non-whitespace characters
133 // Skip any non-whitespace characters
@@ -473,7 +473,7 @@ impl Component for Prompt {
473 }
473 }
474 }
474 }
475 key!(Enter) => {
475 key!(Enter) => {
476− if self.selection.is_some() && self.line.ends_with('/') {
476+ if self.selection.is_some() && self.line.ends_with(std::path::MAIN_SEPARATOR) {
477 self.completion = (self.completion_fn)(&self.line);
477 self.completion = (self.completion_fn)(&self.line);
478 self.exit_selection();
478 self.exit_selection();
479 } else {
479 } else {
helix-tui/Cargo.toml+3−3
@@ -1,6 +1,6 @@
1[package]
1[package]
2name = "helix-tui"
2name = "helix-tui"
3−version = "0.5.0"
3+version = "0.6.0"
4authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
4authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
5description = """
5description = """
6A library to build rich terminal user interfaces or dashboards
6A library to build rich terminal user interfaces or dashboards
@@ -21,5 +21,5 @@ cassowary = "0.3"
21unicode-segmentation = "1.8"
21unicode-segmentation = "1.8"
22crossterm = { version = "0.22", optional = true }
22crossterm = { version = "0.22", optional = true }
23serde = { version = "1", "optional" = true, features = ["derive"]}
23serde = { version = "1", "optional" = true, features = ["derive"]}
24−helix-view = { version = "0.5", path = "../helix-view", features = ["term"] }
24+helix-view = { version = "0.6", path = "../helix-view", features = ["term"] }
25−helix-core = { version = "0.5", path = "../helix-core" }
25+helix-core = { version = "0.6", path = "../helix-core" }
helix-tui/README.md+1−1
@@ -2,5 +2,5 @@
2
2
3This library is a fork of the great library
3This library is a fork of the great library
4[tui-rs](https://github.com/fdehau/tui-rs/). We've mainly relied on the double
4[tui-rs](https://github.com/fdehau/tui-rs/). We've mainly relied on the double
5−buffer implementation and render diffing, side-stepping it's widget and
5+buffer implementation and render diffing, side-stepping its widget and
6layouting.
6layouting.
helix-view/Cargo.toml+4−5
@@ -1,6 +1,6 @@
1[package]
1[package]
2name = "helix-view"
2name = "helix-view"
3−version = "0.5.0"
3+version = "0.6.0"
4authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
4authors = ["Blaž Hrastnik <blaz@mxxn.io>"]
5edition = "2021"
5edition = "2021"
6license = "MPL-2.0"
6license = "MPL-2.0"
@@ -16,12 +16,12 @@ term = ["crossterm"]
16[dependencies]
16[dependencies]
17bitflags = "1.3"
17bitflags = "1.3"
18anyhow = "1"
18anyhow = "1"
19−helix-core = { version = "0.5", path = "../helix-core" }
19+helix-core = { version = "0.6", path = "../helix-core" }
20−helix-lsp = { version = "0.5", path = "../helix-lsp"}
20+helix-lsp = { version = "0.6", path = "../helix-lsp"}
21crossterm = { version = "0.22", optional = true }
21crossterm = { version = "0.22", optional = true }
22
22
23# Conversion traits
23# Conversion traits
24−once_cell = "1.8"
24+once_cell = "1.9"
25url = "2"
25url = "2"
26
26
27tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
27tokio = { version = "1", features = ["rt", "rt-multi-thread", "io-util", "io-std", "time", "process", "macros", "fs", "parking_lot"] }
@@ -29,7 +29,6 @@ futures-util = { version = "0.3", features = ["std", "async-await"], default-fea
29
29
30slotmap = "1"
30slotmap = "1"
31
31
32−encoding_rs = "0.8"
33chardetng = "0.1"
32chardetng = "0.1"
34
33
35serde = { version = "1.0", features = ["derive"] }
34serde = { version = "1.0", features = ["derive"] }
helix-view/src/document.rs+31−16
@@ -1,5 +1,6 @@
1use anyhow::{anyhow, Context, Error};
1use anyhow::{anyhow, Context, Error};
2use serde::de::{self, Deserialize, Deserializer};
2use serde::de::{self, Deserialize, Deserializer};
3+use serde::Serialize;
3use std::cell::Cell;
4use std::cell::Cell;
4use std::collections::HashMap;
5use std::collections::HashMap;
5use std::fmt::Display;
6use std::fmt::Display;
@@ -9,6 +10,7 @@ use std::str::FromStr;
9use std::sync::Arc;
10use std::sync::Arc;
10
11
11use helix_core::{
12use helix_core::{
13+ encoding,
12 history::History,
14 history::History,
13 indent::{auto_detect_indent_style, IndentStyle},
15 indent::{auto_detect_indent_style, IndentStyle},
14 line_ending::auto_detect_line_ending,
16 line_ending::auto_detect_line_ending,
@@ -68,13 +70,22 @@ impl<'de> Deserialize<'de> for Mode {
68 }
70 }
69}
71}
70
72
73+impl Serialize for Mode {
74+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
75+ where
76+ S: serde::Serializer,
77+ {
78+ serializer.collect_str(self)
79+ }
80+}
81+
71pub struct Document {
82pub struct Document {
72 pub(crate) id: DocumentId,
83 pub(crate) id: DocumentId,
73 text: Rope,
84 text: Rope,
74 pub(crate) selections: HashMap<ViewId, Selection>,
85 pub(crate) selections: HashMap<ViewId, Selection>,
75
86
76 path: Option<PathBuf>,
87 path: Option<PathBuf>,
77− encoding: &'static encoding_rs::Encoding,
88+ encoding: &'static encoding::Encoding,
78
89
79 /// Current editing mode.
90 /// Current editing mode.
80 pub mode: Mode,
91 pub mode: Mode,
@@ -143,8 +154,8 @@ impl fmt::Debug for Document {
143/// be used to override encoding auto-detection.
154/// be used to override encoding auto-detection.
144pub fn from_reader<R: std::io::Read + ?Sized>(
155pub fn from_reader<R: std::io::Read + ?Sized>(
145 reader: &mut R,
156 reader: &mut R,
146− encoding: Option<&'static encoding_rs::Encoding>,
157+ encoding: Option<&'static encoding::Encoding>,
147−) -> Result<(Rope, &'static encoding_rs::Encoding), Error> {
158+) -> Result<(Rope, &'static encoding::Encoding), Error> {
148 // These two buffers are 8192 bytes in size each and are used as
159 // These two buffers are 8192 bytes in size each and are used as
149 // intermediaries during the decoding process. Text read into `buf`
160 // intermediaries during the decoding process. Text read into `buf`
150 // from `reader` is decoded into `buf_out` as UTF-8. Once either
161 // from `reader` is decoded into `buf_out` as UTF-8. Once either
@@ -212,11 +223,11 @@ pub fn from_reader<R: std::io::Read + ?Sized>(
212 total_read += read;
223 total_read += read;
213 total_written += written;
224 total_written += written;
214 match result {
225 match result {
215− encoding_rs::CoderResult::InputEmpty => {
226+ encoding::CoderResult::InputEmpty => {
216 debug_assert_eq!(slice.len(), total_read);
227 debug_assert_eq!(slice.len(), total_read);
217 break;
228 break;
218 }
229 }
219− encoding_rs::CoderResult::OutputFull => {
230+ encoding::CoderResult::OutputFull => {
220 debug_assert!(slice.len() > total_read);
231 debug_assert!(slice.len() > total_read);
221 builder.append(&buf_str[..total_written]);
232 builder.append(&buf_str[..total_written]);
222 total_written = 0;
233 total_written = 0;
@@ -251,7 +262,7 @@ pub fn from_reader<R: std::io::Read + ?Sized>(
251/// replacement characters may appear in the encoded text.
262/// replacement characters may appear in the encoded text.
252pub async fn to_writer<'a, W: tokio::io::AsyncWriteExt + Unpin + ?Sized>(
263pub async fn to_writer<'a, W: tokio::io::AsyncWriteExt + Unpin + ?Sized>(
253 writer: &'a mut W,
264 writer: &'a mut W,
254− encoding: &'static encoding_rs::Encoding,
265+ encoding: &'static encoding::Encoding,
255 rope: &'a Rope,
266 rope: &'a Rope,
256) -> Result<(), Error> {
267) -> Result<(), Error> {
257 // Text inside a `Rope` is stored as non-contiguous blocks of data called
268 // Text inside a `Rope` is stored as non-contiguous blocks of data called
@@ -286,12 +297,12 @@ pub async fn to_writer<'a, W: tokio::io::AsyncWriteExt + Unpin + ?Sized>(
286 total_read += read;
297 total_read += read;
287 total_written += written;
298 total_written += written;
288 match result {
299 match result {
289− encoding_rs::CoderResult::InputEmpty => {
300+ encoding::CoderResult::InputEmpty => {
290 debug_assert_eq!(chunk.len(), total_read);
301 debug_assert_eq!(chunk.len(), total_read);
291 debug_assert!(buf.len() >= total_written);
302 debug_assert!(buf.len() >= total_written);
292 break;
303 break;
293 }
304 }
294− encoding_rs::CoderResult::OutputFull => {
305+ encoding::CoderResult::OutputFull => {
295 debug_assert!(chunk.len() > total_read);
306 debug_assert!(chunk.len() > total_read);
296 writer.write_all(&buf[..total_written]).await?;
307 writer.write_all(&buf[..total_written]).await?;
297 total_written = 0;
308 total_written = 0;
@@ -322,8 +333,8 @@ use helix_lsp::lsp;
322use url::Url;
333use url::Url;
323
334
324impl Document {
335impl Document {
325− pub fn from(text: Rope, encoding: Option<&'static encoding_rs::Encoding>) -> Self {
336+ pub fn from(text: Rope, encoding: Option<&'static encoding::Encoding>) -> Self {
326− let encoding = encoding.unwrap_or(encoding_rs::UTF_8);
337+ let encoding = encoding.unwrap_or(encoding::UTF_8);
327 let changes = ChangeSet::new(&text);
338 let changes = ChangeSet::new(&text);
328 let old_state = None;
339 let old_state = None;
329
340
@@ -356,7 +367,7 @@ impl Document {
356 /// overwritten with the `encoding` parameter.
367 /// overwritten with the `encoding` parameter.
357 pub fn open(
368 pub fn open(
358 path: &Path,
369 path: &Path,
359− encoding: Option<&'static encoding_rs::Encoding>,
370+ encoding: Option<&'static encoding::Encoding>,
360 theme: Option<&Theme>,
371 theme: Option<&Theme>,
361 config_loader: Option<&syntax::Loader>,
372 config_loader: Option<&syntax::Loader>,
362 ) -> Result<Self, Error> {
373 ) -> Result<Self, Error> {
@@ -366,7 +377,7 @@ impl Document {
366 std::fs::File::open(path).context(format!("unable to open {:?}", path))?;
377 std::fs::File::open(path).context(format!("unable to open {:?}", path))?;
367 from_reader(&mut file, encoding)?
378 from_reader(&mut file, encoding)?
368 } else {
379 } else {
369− let encoding = encoding.unwrap_or(encoding_rs::UTF_8);
380+ let encoding = encoding.unwrap_or(encoding::UTF_8);
370 (Rope::from(DEFAULT_LINE_ENDING.as_str()), encoding)
381 (Rope::from(DEFAULT_LINE_ENDING.as_str()), encoding)
371 };
382 };
372
383
@@ -548,7 +559,7 @@ impl Document {
548
559
549 /// Sets the [`Document`]'s encoding with the encoding correspondent to `label`.
560 /// Sets the [`Document`]'s encoding with the encoding correspondent to `label`.
550 pub fn set_encoding(&mut self, label: &str) -> Result<(), Error> {
561 pub fn set_encoding(&mut self, label: &str) -> Result<(), Error> {
551− match encoding_rs::Encoding::for_label(label.as_bytes()) {
562+ match encoding::Encoding::for_label(label.as_bytes()) {
552 Some(encoding) => self.encoding = encoding,
563 Some(encoding) => self.encoding = encoding,
553 None => return Err(anyhow::anyhow!("unknown encoding")),
564 None => return Err(anyhow::anyhow!("unknown encoding")),
554 }
565 }
@@ -556,7 +567,7 @@ impl Document {
556 }
567 }
557
568
558 /// Returns the [`Document`]'s current encoding.
569 /// Returns the [`Document`]'s current encoding.
559− pub fn encoding(&self) -> &'static encoding_rs::Encoding {
570+ pub fn encoding(&self) -> &'static encoding::Encoding {
560 self.encoding
571 self.encoding
561 }
572 }
562
573
@@ -889,6 +900,10 @@ impl Document {
889 self.indent_style.as_str()
900 self.indent_style.as_str()
890 }
901 }
891
902
903+ pub fn changes(&self) -> &ChangeSet {
904+ &self.changes
905+ }
906+
892 #[inline]
907 #[inline]
893 /// File path on disk.
908 /// File path on disk.
894 pub fn path(&self) -> Option<&PathBuf> {
909 pub fn path(&self) -> Option<&PathBuf> {
@@ -1119,7 +1134,7 @@ mod test {
1119
1134
1120 macro_rules! test_decode {
1135 macro_rules! test_decode {
1121 ($label:expr, $label_override:expr) => {
1136 ($label:expr, $label_override:expr) => {
1122− let encoding = encoding_rs::Encoding::for_label($label_override.as_bytes()).unwrap();
1137+ let encoding = encoding::Encoding::for_label($label_override.as_bytes()).unwrap();
1123 let base_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/encoding");
1138 let base_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/encoding");
1124 let path = base_path.join(format!("{}_in.txt", $label));
1139 let path = base_path.join(format!("{}_in.txt", $label));
1125 let ref_path = base_path.join(format!("{}_in_ref.txt", $label));
1140 let ref_path = base_path.join(format!("{}_in_ref.txt", $label));
@@ -1138,7 +1153,7 @@ mod test {
1138
1153
1139 macro_rules! test_encode {
1154 macro_rules! test_encode {
1140 ($label:expr, $label_override:expr) => {
1155 ($label:expr, $label_override:expr) => {
1141− let encoding = encoding_rs::Encoding::for_label($label_override.as_bytes()).unwrap();
1156+ let encoding = encoding::Encoding::for_label($label_override.as_bytes()).unwrap();
1142 let base_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/encoding");
1157 let base_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/encoding");
1143 let path = base_path.join(format!("{}_out.txt", $label));
1158 let path = base_path.join(format!("{}_out.txt", $label));
1144 let ref_path = base_path.join(format!("{}_out_ref.txt", $label));
1159 let ref_path = base_path.join(format!("{}_out_ref.txt", $label));
helix-view/src/editor.rs+30−4
@@ -27,7 +27,7 @@ pub use helix_core::register::Registers;
27use helix_core::syntax;
27use helix_core::syntax;
28use helix_core::{Position, Selection};
28use helix_core::{Position, Selection};
29
29
30−use serde::{Deserialize, Deserializer};
30+use serde::{ser::SerializeMap, Deserialize, Deserializer, Serialize};
31
31
32fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
32fn deserialize_duration_millis<'de, D>(deserializer: D) -> Result<Duration, D::Error>
33where
33where
@@ -37,7 +37,7 @@ where
37 Ok(Duration::from_millis(millis))
37 Ok(Duration::from_millis(millis))
38}
38}
39
39
40−#[derive(Debug, Clone, PartialEq, Deserialize)]
40+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
41#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
41#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
42pub struct FilePickerConfig {
42pub struct FilePickerConfig {
43 /// IgnoreOptions
43 /// IgnoreOptions
@@ -77,7 +77,7 @@ impl Default for FilePickerConfig {
77 }
77 }
78}
78}
79
79
80−#[derive(Debug, Clone, PartialEq, Deserialize)]
80+#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
81#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
81#[serde(rename_all = "kebab-case", default, deny_unknown_fields)]
82pub struct Config {
82pub struct Config {
83 /// Padding to keep between the edge of the screen and the cursor when scrolling. Defaults to 5.
83 /// Padding to keep between the edge of the screen and the cursor when scrolling. Defaults to 5.
@@ -137,6 +137,20 @@ impl<'de> Deserialize<'de> for CursorShapeConfig {
137 }
137 }
138}
138}
139
139
140+impl Serialize for CursorShapeConfig {
141+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
142+ where
143+ S: serde::Serializer,
144+ {
145+ let mut map = serializer.serialize_map(Some(self.len()))?;
146+ let modes = [Mode::Normal, Mode::Select, Mode::Insert];
147+ for mode in modes {
148+ map.serialize_entry(&mode, &self.from_mode(mode))?;
149+ }
150+ map.end()
151+ }
152+}
153+
140impl std::ops::Deref for CursorShapeConfig {
154impl std::ops::Deref for CursorShapeConfig {
141 type Target = [CursorKind; 3];
155 type Target = [CursorKind; 3];
142
156
@@ -151,7 +165,7 @@ impl Default for CursorShapeConfig {
151 }
165 }
152}
166}
153
167
154−#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
168+#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
155#[serde(rename_all = "kebab-case")]
169#[serde(rename_all = "kebab-case")]
156pub enum LineNumber {
170pub enum LineNumber {
157 /// Show absolute line number
171 /// Show absolute line number
@@ -160,6 +174,18 @@ pub enum LineNumber {
160 Relative,
174 Relative,
161}
175}
162
176
177+impl std::str::FromStr for LineNumber {
178+ type Err = anyhow::Error;
179+
180+ fn from_str(s: &str) -> Result<Self, Self::Err> {
181+ match s.to_lowercase().as_str() {
182+ "absolute" | "abs" => Ok(Self::Absolute),
183+ "relative" | "rel" => Ok(Self::Relative),
184+ _ => anyhow::bail!("Line number can only be `absolute` or `relative`."),
185+ }
186+ }
187+}
188+
163impl Default for Config {
189impl Default for Config {
164 fn default() -> Self {
190 fn default() -> Self {
165 Self {
191 Self {
helix-view/src/graphics.rs+2−2
@@ -1,11 +1,11 @@
1use bitflags::bitflags;
1use bitflags::bitflags;
2−use serde::Deserialize;
2+use serde::{Deserialize, Serialize};
3use std::{
3use std::{
4 cmp::{max, min},
4 cmp::{max, min},
5 str::FromStr,
5 str::FromStr,
6};
6};
7
7
8−#[derive(Debug, Clone, Copy, PartialEq, Deserialize)]
8+#[derive(Debug, Clone, Copy, PartialEq, Deserialize, Serialize)]
9#[serde(rename_all = "lowercase")]
9#[serde(rename_all = "lowercase")]
10/// UNSTABLE
10/// UNSTABLE
11pub enum CursorKind {
11pub enum CursorKind {
helix-view/src/input.rs+153
@@ -254,6 +254,43 @@ impl From<KeyEvent> for crossterm::event::KeyEvent {
254 }
254 }
255}
255}
256
256
257+pub fn parse_macro(keys_str: &str) -> anyhow::Result<Vec<KeyEvent>> {
258+ use anyhow::Context;
259+ let mut keys_res: anyhow::Result<_> = Ok(Vec::new());
260+ let mut i = 0;
261+ while let Ok(keys) = &mut keys_res {
262+ if i >= keys_str.len() {
263+ break;
264+ }
265+ if !keys_str.is_char_boundary(i) {
266+ i += 1;
267+ continue;
268+ }
269+
270+ let s = &keys_str[i..];
271+ let mut end_i = 1;
272+ while !s.is_char_boundary(end_i) {
273+ end_i += 1;
274+ }
275+ let c = &s[..end_i];
276+ if c == ">" {
277+ keys_res = Err(anyhow!("Unmatched '>'"));
278+ } else if c != "<" {
279+ keys.push(c);
280+ i += end_i;
281+ } else {
282+ match s.find('>').context("'>' expected") {
283+ Ok(end_i) => {
284+ keys.push(&s[1..end_i]);
285+ i += end_i + 1;
286+ }
287+ Err(err) => keys_res = Err(err),
288+ }
289+ }
290+ }
291+ keys_res.and_then(|keys| keys.into_iter().map(str::parse).collect())
292+}
293+
257#[cfg(test)]
294#[cfg(test)]
258mod test {
295mod test {
259 use super::*;
296 use super::*;
@@ -339,4 +376,120 @@ mod test {
339 assert!(str::parse::<KeyEvent>("123").is_err());
376 assert!(str::parse::<KeyEvent>("123").is_err());
340 assert!(str::parse::<KeyEvent>("S--").is_err());
377 assert!(str::parse::<KeyEvent>("S--").is_err());
341 }
378 }
379+
380+ #[test]
381+ fn parsing_valid_macros() {
382+ assert_eq!(
383+ parse_macro("xdo").ok(),
384+ Some(vec![
385+ KeyEvent {
386+ code: KeyCode::Char('x'),
387+ modifiers: KeyModifiers::NONE,
388+ },
389+ KeyEvent {
390+ code: KeyCode::Char('d'),
391+ modifiers: KeyModifiers::NONE,
392+ },
393+ KeyEvent {
394+ code: KeyCode::Char('o'),
395+ modifiers: KeyModifiers::NONE,
396+ },
397+ ]),
398+ );
399+
400+ assert_eq!(
401+ parse_macro("<C-w>v<C-w>h<C-o>xx<A-s>").ok(),
402+ Some(vec![
403+ KeyEvent {
404+ code: KeyCode::Char('w'),
405+ modifiers: KeyModifiers::CONTROL,
406+ },
407+ KeyEvent {
408+ code: KeyCode::Char('v'),
409+ modifiers: KeyModifiers::NONE,
410+ },
411+ KeyEvent {
412+ code: KeyCode::Char('w'),
413+ modifiers: KeyModifiers::CONTROL,
414+ },
415+ KeyEvent {
416+ code: KeyCode::Char('h'),
417+ modifiers: KeyModifiers::NONE,
418+ },
419+ KeyEvent {
420+ code: KeyCode::Char('o'),
421+ modifiers: KeyModifiers::CONTROL,
422+ },
423+ KeyEvent {
424+ code: KeyCode::Char('x'),
425+ modifiers: KeyModifiers::NONE,
426+ },
427+ KeyEvent {
428+ code: KeyCode::Char('x'),
429+ modifiers: KeyModifiers::NONE,
430+ },
431+ KeyEvent {
432+ code: KeyCode::Char('s'),
433+ modifiers: KeyModifiers::ALT,
434+ },
435+ ])
436+ );
437+
438+ assert_eq!(
439+ parse_macro(":o foo.bar<ret>").ok(),
440+ Some(vec![
441+ KeyEvent {
442+ code: KeyCode::Char(':'),
443+ modifiers: KeyModifiers::NONE,
444+ },
445+ KeyEvent {
446+ code: KeyCode::Char('o'),
447+ modifiers: KeyModifiers::NONE,
448+ },
449+ KeyEvent {
450+ code: KeyCode::Char(' '),
451+ modifiers: KeyModifiers::NONE,
452+ },
453+ KeyEvent {
454+ code: KeyCode::Char('f'),
455+ modifiers: KeyModifiers::NONE,
456+ },
457+ KeyEvent {
458+ code: KeyCode::Char('o'),
459+ modifiers: KeyModifiers::NONE,
460+ },
461+ KeyEvent {
462+ code: KeyCode::Char('o'),
463+ modifiers: KeyModifiers::NONE,
464+ },
465+ KeyEvent {
466+ code: KeyCode::Char('.'),
467+ modifiers: KeyModifiers::NONE,
468+ },
469+ KeyEvent {
470+ code: KeyCode::Char('b'),
471+ modifiers: KeyModifiers::NONE,
472+ },
473+ KeyEvent {
474+ code: KeyCode::Char('a'),
475+ modifiers: KeyModifiers::NONE,
476+ },
477+ KeyEvent {
478+ code: KeyCode::Char('r'),
479+ modifiers: KeyModifiers::NONE,
480+ },
481+ KeyEvent {
482+ code: KeyCode::Enter,
483+ modifiers: KeyModifiers::NONE,
484+ },
485+ ])
486+ );
487+ }
488+
489+ #[test]
490+ fn parsing_invalid_macros_fails() {
491+ assert!(parse_macro("abc<C-").is_err());
492+ assert!(parse_macro("abc>123").is_err());
493+ assert!(parse_macro("wd<foo>").is_err());
494+ }
342}
495}
helix-view/src/view.rs+3
@@ -80,6 +80,8 @@ pub struct View {
80 // uses two docs because we want to be able to swap between the
80 // uses two docs because we want to be able to swap between the
81 // two last modified docs which we need to manually keep track of
81 // two last modified docs which we need to manually keep track of
82 pub last_modified_docs: [Option<DocumentId>; 2],
82 pub last_modified_docs: [Option<DocumentId>; 2],
83+ /// used to store previous selections of tree-sitter objecs
84+ pub object_selections: Vec<Selection>,
83}
85}
84
86
85impl View {
87impl View {
@@ -92,6 +94,7 @@ impl View {
92 jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
94 jumps: JumpList::new((doc, Selection::point(0))), // TODO: use actual sel
93 last_accessed_doc: None,
95 last_accessed_doc: None,
94 last_modified_docs: [None, None],
96 last_modified_docs: [None, None],
97+ object_selections: Vec::new(),
95 }
98 }
96 }
99 }
97
100
languages.toml+105−5
@@ -3,15 +3,11 @@ name = "rust"
3scope = "source.rust"
3scope = "source.rust"
4injection-regex = "rust"
4injection-regex = "rust"
5file-types = ["rs"]
5file-types = ["rs"]
6−roots = []
6+roots = ["Cargo.toml", "Cargo.lock"]
7auto-format = true
7auto-format = true
8comment-token = "//"
8comment-token = "//"
9language-server = { command = "rust-analyzer" }
9language-server = { command = "rust-analyzer" }
10indent = { tab-width = 4, unit = " " }
10indent = { tab-width = 4, unit = " " }
11−[language.config]
12−cargo = { loadOutDirsFromCheck = true }
13−procMacro = { enable = false }
14−diagnostics = { disabled = ["unresolved-proc-macro"] }
15
11
16[[language]]
12[[language]]
17name = "toml"
13name = "toml"
@@ -45,6 +41,17 @@ comment-token = "#"
45language-server = { command = "elixir-ls" }
41language-server = { command = "elixir-ls" }
46indent = { tab-width = 2, unit = " " }
42indent = { tab-width = 2, unit = " " }
47
43
44+[[language]]
45+name = "fish"
46+scope = "source.fish"
47+injection-regex = "fish"
48+file-types = ["fish"]
49+shebangs = ["fish"]
50+roots = []
51+comment-token = "#"
52+
53+indent = { tab-width = 4, unit = " " }
54+
48[[language]]
55[[language]]
49name = "mint"
56name = "mint"
50scope = "source.mint"
57scope = "source.mint"
@@ -327,6 +334,7 @@ file-types = ["yml", "yaml"]
327roots = []
334roots = []
328comment-token = "#"
335comment-token = "#"
329indent = { tab-width = 2, unit = " " }
336indent = { tab-width = 2, unit = " " }
337+injection-regex = "yml|yaml"
330
338
331# [[language]]
339# [[language]]
332# name = "haskell"
340# name = "haskell"
@@ -379,6 +387,7 @@ roots = []
379comment-token = "#"
387comment-token = "#"
380indent = { tab-width = 2, unit = " " }
388indent = { tab-width = 2, unit = " " }
381language-server = { command = "cmake-language-server" }
389language-server = { command = "cmake-language-server" }
390+injection-regex = "cmake"
382
391
383[[language]]
392[[language]]
384name = "glsl"
393name = "glsl"
@@ -387,6 +396,7 @@ file-types = ["glsl", "vert", "tesc", "tese", "geom", "frag", "comp" ]
387roots = []
396roots = []
388comment-token = "//"
397comment-token = "//"
389indent = { tab-width = 4, unit = " " }
398indent = { tab-width = 4, unit = " " }
399+injection-regex = "glsl"
390
400
391[[language]]
401[[language]]
392name = "perl"
402name = "perl"
@@ -406,6 +416,13 @@ shebangs = ["racket"]
406comment-token = ";"
416comment-token = ";"
407language-server = { command = "racket", args = ["-l", "racket-langserver"] }
417language-server = { command = "racket", args = ["-l", "racket-langserver"] }
408
418
419+[[language]]
420+name = "comment"
421+scope = "scope.comment"
422+roots = []
423+file-types = []
424+injection-regex = "comment"
425+
409[[language]]
426[[language]]
410name = "wgsl"
427name = "wgsl"
411scope = "source.wgsl"
428scope = "source.wgsl"
@@ -421,6 +438,34 @@ roots = []
421file-types = ["ll"]
438file-types = ["ll"]
422comment-token = ";"
439comment-token = ";"
423indent = { tab-width = 2, unit = " " }
440indent = { tab-width = 2, unit = " " }
441+injection-regex = "llvm"
442+
443+[[language]]
444+name = "llvm-mir"
445+scope = "source.llvm_mir"
446+roots = []
447+file-types = []
448+comment-token = ";"
449+indent = { tab-width = 2, unit = " " }
450+injection-regex = "mir"
451+
452+[[language]]
453+name = "llvm-mir-yaml"
454+tree-sitter-library = "yaml"
455+scope = "source.yaml"
456+roots = []
457+file-types = ["mir"]
458+comment-token = "#"
459+indent = { tab-width = 2, unit = " " }
460+
461+[[language]]
462+name = "tablegen"
463+scope = "source.tablegen"
464+roots = []
465+file-types = ["td"]
466+comment-token = "//"
467+indent = { tab-width = 2, unit = " " }
468+injection-regex = "tablegen"
424
469
425[[language]]
470[[language]]
426name = "markdown"
471name = "markdown"
@@ -430,3 +475,58 @@ file-types = ["md"]
430roots = []
475roots = []
431
476
432indent = { tab-width = 2, unit = " " }
477indent = { tab-width = 2, unit = " " }
478+
479+[[language]]
480+name = "dart"
481+scope = "source.dart"
482+file-types = ["dart"]
483+roots = ["pubspec.yaml"]
484+auto-format = true
485+comment-token = "//"
486+language-server = { command = "dart", args = ["language-server", "--client-id=helix"] }
487+indent = { tab-width = 2, unit = " " }
488+
489+[[language]]
490+name = "scala"
491+scope = "source.scala"
492+roots = ["build.sbt"]
493+file-types = ["scala", "sbt"]
494+comment-token = "//"
495+indent = { tab-width = 2, unit = " " }
496+language-server = { command = "metals" }
497+
498+[[language]]
499+name = "dockerfile"
500+scope = "source.dockerfile"
501+injection-regex = "docker|dockerfile"
502+roots = ["Dockerfile"]
503+file-types = ["Dockerfile", "dockerfile"]
504+comment-token = "#"
505+indent = { tab-width = 2, unit = " " }
506+language-server = { command = "docker-langserver", args = ["--stdio"] }
507+
508+[[language]]
509+name = "git-commit"
510+scope = "git.commitmsg"
511+roots = []
512+file-types = ["COMMIT_EDITMSG"]
513+comment-token = "#"
514+indent = { tab-width = 2, unit = " " }
515+
516+[[language]]
517+name = "git-diff"
518+scope = "source.diff"
519+roots = []
520+file-types = ["diff"]
521+injection-regex = "diff"
522+comment-token = "#"
523+indent = { tab-width = 2, unit = " " }
524+
525+[[language]]
526+name = "git-rebase"
527+scope = "source.gitrebase"
528+roots = []
529+file-types = ["git-rebase-todo"]
530+injection-regex = "git-rebase"
531+comment-token = "#"
532+indent = { tab-width = 2, unit = " " }
runtime/queries/c/indents.tomlnew file+16
@@ -0,0 +1,16 @@
1+indent = [
2+ "compound_statement",
3+ "field_declaration_list",
4+ "enumerator_list",
5+ "parameter_list",
6+ "init_declarator",
7+ "case_statement",
8+ "condition_clause",
9+ "expression_statement",
10+]
11+
12+outdent = [
13+ "case",
14+ "}",
15+ "]",
16+]
runtime/queries/c/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+((comment) @injection.content
2+ (#set! injection.language "comment"))
runtime/queries/c/textobjects.scmnew file+13
@@ -0,0 +1,13 @@
1+(function_definition
2+ body: (_) @function.inside) @function.around
3+
4+(struct_specifier
5+ body: (_) @class.inside) @class.around
6+
7+(enum_specifier
8+ body: (_) @class.inside) @class.around
9+
10+(union_specifier
11+ body: (_) @class.inside) @class.around
12+
13+(parameter_declaration) @parameter.inside
runtime/queries/cmake/indents.tomlnew file+12
@@ -0,0 +1,12 @@
1+indent = [
2+ "if_condition",
3+ "foreach_loop",
4+ "while_loop",
5+ "function_def",
6+ "macro_def",
7+ "normal_command",
8+]
9+
10+outdent = [
11+ ")"
12+]
runtime/queries/cmake/injections.scmnew file+4
@@ -0,0 +1,4 @@
1+((line_comment) @injection.content
2+ (#set! injection.language "comment"))
3+((bracket_comment) @injection.content
4+ (#set! injection.language "comment"))
runtime/queries/cmake/textobjects.scmnew file+3
@@ -0,0 +1,3 @@
1+(macro_def) @function.around
2+
3+(argument) @parameter.inside
runtime/queries/comment/highlights.scmnew file+30
@@ -0,0 +1,30 @@
1+[
2+ "("
3+ ")"
4+] @punctuation.bracket
5+
6+":" @punctuation.delimiter
7+
8+((tag (name) @warning)
9+ (#match? @warning "^(TODO|HACK|WARNING)$"))
10+
11+("text" @warning
12+ (#match? @warning "^(TODO|HACK|WARNING)$"))
13+
14+((tag (name) @error)
15+ (match? @error "^(FIXME|XXX|BUG)$"))
16+
17+("text" @error
18+ (match? @error "^(FIXME|XXX|BUG)$"))
19+
20+(tag
21+ (name) @ui.text
22+ (user)? @constant)
23+
24+; Issue number (#123)
25+("text" @constant.numeric
26+ (#match? @constant.numeric "^#[0-9]+$"))
27+
28+; User mention (@user)
29+("text" @tag
30+ (#match? @tag "^[@][a-zA-Z0-9_-]+$"))
runtime/queries/cpp/indents.tomlnew file+17
@@ -0,0 +1,17 @@
1+indent = [
2+ "compound_statement",
3+ "field_declaration_list",
4+ "enumerator_list",
5+ "parameter_list",
6+ "init_declarator",
7+ "case_statement",
8+ "condition_clause",
9+ "expression_statement",
10+]
11+
12+outdent = [
13+ "case",
14+ "access_specifier",
15+ "}",
16+ "]",
17+]
runtime/queries/cpp/injections.scmnew file+1
@@ -0,0 +1 @@
1+; inherits: c
runtime/queries/cpp/textobjects.scmnew file+7
@@ -0,0 +1,7 @@
1+; inherits: c
2+
3+(lambda_expression
4+ body: (_) @function.inside) @function.around
5+
6+(class_specifier
7+ body: (_) @class.inside) @class.around
runtime/queries/dart/highlights.scmnew file+237
@@ -0,0 +1,237 @@
1+(dotted_identifier_list) @string
2+
3+; Methods
4+; --------------------
5+(super) @function.builtin
6+
7+(function_expression_body (identifier) @function.method)
8+((identifier)(selector (argument_part)) @function.method)
9+
10+; Annotations
11+; --------------------
12+(annotation
13+ name: (identifier) @attribute)
14+(marker_annotation
15+ name: (identifier) @attribute)
16+
17+; Types
18+; --------------------
19+(class_definition
20+ name: (identifier) @type)
21+
22+(constructor_signature
23+ name: (identifier) @function.method)
24+
25+(function_signature
26+ name: (identifier) @function.method)
27+
28+(getter_signature
29+ (identifier) @function.builtin)
30+
31+(setter_signature
32+ name: (identifier) @function.builtin)
33+
34+(enum_declaration
35+ name: (identifier) @type)
36+
37+(enum_constant
38+ name: (identifier) @type.builtin)
39+
40+(void_type) @type.builtin
41+
42+((scoped_identifier
43+ scope: (identifier) @type)
44+ (#match? @type "^[a-zA-Z]"))
45+
46+((scoped_identifier
47+ scope: (identifier) @type
48+ name: (identifier) @type)
49+ (#match? @type "^[a-zA-Z]"))
50+
51+; the DisabledDrawerButtons in : const DisabledDrawerButtons(history: true),
52+(type_identifier) @type.builtin
53+
54+; Variables
55+; --------------------
56+; the "File" in var file = File();
57+((identifier) @namespace
58+ (#match? @namespace "^_?[A-Z].*[a-z]")) ; catch Classes or IClasses not CLASSES
59+
60+("Function" @type.builtin)
61+(inferred_type) @type.builtin
62+
63+; properties
64+(unconditional_assignable_selector
65+ (identifier) @variable.other.member)
66+
67+(conditional_assignable_selector
68+ (identifier) @variable.other.member)
69+
70+; assignments
71+; --------------------
72+; the "strings" in : strings = "some string"
73+(assignment_expression
74+ left: (assignable_expression) @variable)
75+
76+(this) @variable.builtin
77+
78+; Parameters
79+; --------------------
80+(formal_parameter
81+ name: (identifier) @variable)
82+
83+(named_argument
84+ (label (identifier) @variable))
85+
86+; Literals
87+; --------------------
88+[
89+ (hex_integer_literal)
90+ (decimal_integer_literal)
91+ (decimal_floating_point_literal)
92+ ;(octal_integer_literal)
93+ ;(hex_floating_point_literal)
94+] @constant.numeric.integer
95+
96+(symbol_literal) @string.special.symbol
97+(string_literal) @string
98+
99+[
100+ (const_builtin)
101+ (final_builtin)
102+] @variable.builtin
103+
104+[
105+ (true)
106+ (false)
107+] @constant.builtin.boolean
108+
109+(null_literal) @constant.builtin
110+
111+(comment) @comment.line
112+(documentation_comment) @comment.block.documentation
113+
114+; Tokens
115+; --------------------
116+(template_substitution
117+ "$" @punctuation.special
118+ "{" @punctuation.special
119+ "}" @punctuation.special
120+) @embedded
121+
122+(template_substitution
123+ "$" @punctuation.special
124+ (identifier_dollar_escaped) @variable
125+) @embedded
126+
127+(escape_sequence) @constant.character.escape
128+
129+; Punctuation
130+;---------------------
131+[
132+ "("
133+ ")"
134+ "["
135+ "]"
136+ "{"
137+ "}"
138+] @punctuation.bracket
139+
140+[
141+ ";"
142+ "."
143+ ","
144+ ":"
145+] @punctuation.delimiter
146+
147+; Operators
148+;---------------------
149+[
150+ "@"
151+ "?"
152+ "=>"
153+ ".."
154+ "=="
155+ "&&"
156+ "%"
157+ "<"
158+ ">"
159+ "="
160+ ">="
161+ "<="
162+ "||"
163+ (multiplicative_operator)
164+ (increment_operator)
165+ (is_operator)
166+ (prefix_operator)
167+ (equality_operator)
168+ (additive_operator)
169+] @operator
170+
171+; Keywords
172+; --------------------
173+["import" "library" "export"] @keyword.control.import
174+["do" "while" "continue" "for"] @keyword.control.repeat
175+["return" "yield"] @keyword.control.return
176+["as" "in" "is"] @keyword.operator
177+
178+[
179+ "?."
180+ "??"
181+ "if"
182+ "else"
183+ "switch"
184+ "default"
185+ "late"
186+] @keyword.control.conditional
187+
188+[
189+ "try"
190+ "throw"
191+ "catch"
192+ "finally"
193+ (break_statement)
194+] @keyword.control.exception
195+
196+; Reserved words (cannot be used as identifiers)
197+[
198+ (case_builtin)
199+ "abstract"
200+ "async"
201+ "async*"
202+ "await"
203+ "class"
204+ "covariant"
205+ "deferred"
206+ "dynamic"
207+ "enum"
208+ "extends"
209+ "extension"
210+ "external"
211+ "factory"
212+ "Function"
213+ "get"
214+ "implements"
215+ "interface"
216+ "mixin"
217+ "new"
218+ "on"
219+ "operator"
220+ "part"
221+ "required"
222+ "set"
223+ "show"
224+ "static"
225+ "super"
226+ "sync*"
227+ "typedef"
228+ "with"
229+] @keyword
230+
231+; when used as an identifier:
232+((identifier) @variable.builtin
233+ (#match? @variable.builtin "^(abstract|as|covariant|deferred|dynamic|export|external|factory|Function|get|implements|import|interface|library|operator|mixin|part|set|static|typedef)$"))
234+
235+; Error
236+(ERROR) @error
237+
runtime/queries/dart/indents.tomlnew file+20
@@ -0,0 +1,20 @@
1+indent = [
2+ "class_body",
3+ "function_body",
4+ "function_expression_body",
5+ "declaration",
6+ "initializers",
7+ "switch_block",
8+ "if_statement",
9+ "formal_parameter_list",
10+ "formal_parameter",
11+ "list_literal",
12+ "return_statement",
13+ "arguments"
14+]
15+
16+outdent = [
17+ "}",
18+ "]",
19+ ")"
20+]
runtime/queries/dart/locals.scmnew file+20
@@ -0,0 +1,20 @@
1+; Scopes
2+;-------
3+
4+[
5+ (block)
6+ (try_statement)
7+ (catch_clause)
8+ (finally_clause)
9+] @local.scope
10+
11+; Definitions
12+;------------
13+
14+(class_definition
15+ body: (_) @local.definition)
16+
17+; References
18+;------------
19+
20+(identifier) @local.reference
runtime/queries/dockerfile/highlights.scmnew file+51
@@ -0,0 +1,51 @@
1+[
2+ "FROM"
3+ "AS"
4+ "RUN"
5+ "CMD"
6+ "LABEL"
7+ "EXPOSE"
8+ "ENV"
9+ "ADD"
10+ "COPY"
11+ "ENTRYPOINT"
12+ "VOLUME"
13+ "USER"
14+ "WORKDIR"
15+ "ARG"
16+ "ONBUILD"
17+ "STOPSIGNAL"
18+ "HEALTHCHECK"
19+ "SHELL"
20+ "MAINTAINER"
21+ "CROSS_BUILD"
22+] @keyword
23+
24+[
25+ ":"
26+ "@"
27+] @operator
28+
29+(comment) @comment
30+
31+
32+(image_spec
33+ (image_tag
34+ ":" @punctuation.special)
35+ (image_digest
36+ "@" @punctuation.special))
37+
38+(double_quoted_string) @string
39+
40+(expansion
41+ [
42+ "$"
43+ "{"
44+ "}"
45+ ] @punctuation.special
46+) @none
47+
48+((variable) @constant
49+ (#match? @constant "^[A-Z][A-Z_0-9]*$"))
50+
51+
runtime/queries/dockerfile/injections.scmnew file+6
@@ -0,0 +1,6 @@
1+((comment) @injection.content
2+ (#set! injection.language "comment"))
3+
4+([(shell_command) (shell_fragment)] @injection.content
5+ (#set! injection.language "bash"))
6+
runtime/queries/elixir/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+((comment) @injection.content
2+ (#set! injection.language "comment"))
runtime/queries/fish/highlights.scmnew file+156
@@ -0,0 +1,156 @@
1+;; Operators
2+
3+[
4+ "&&"
5+ "||"
6+ "|"
7+ "&"
8+ "="
9+ "!="
10+ ".."
11+ "!"
12+ (direction)
13+ (stream_redirect)
14+ (test_option)
15+] @operator
16+
17+[
18+ "not"
19+ "and"
20+ "or"
21+] @keyword.operator
22+
23+;; Conditionals
24+
25+(if_statement
26+[
27+ "if"
28+ "end"
29+] @keyword.control.conditional)
30+
31+(switch_statement
32+[
33+ "switch"
34+ "end"
35+] @keyword.control.conditional)
36+
37+(case_clause
38+[
39+ "case"
40+] @keyword.control.conditional)
41+
42+(else_clause
43+[
44+ "else"
45+] @keyword.control.conditional)
46+
47+(else_if_clause
48+[
49+ "else"
50+ "if"
51+] @keyword.control.conditional)
52+
53+;; Loops/Blocks
54+
55+(while_statement
56+[
57+ "while"
58+ "end"
59+] @keyword.control.repeat)
60+
61+(for_statement
62+[
63+ "for"
64+ "end"
65+] @keyword.control.repeat)
66+
67+(begin_statement
68+[
69+ "begin"
70+ "end"
71+] @keyword.control.repeat)
72+
73+;; Keywords
74+
75+[
76+ "in"
77+ (break)
78+ (continue)
79+] @keyword
80+
81+"return" @keyword.control.return
82+
83+;; Punctuation
84+
85+[
86+ "["
87+ "]"
88+ "{"
89+ "}"
90+ "("
91+ ")"
92+] @punctuation.bracket
93+
94+"," @punctuation.delimiter
95+
96+;; Commands
97+
98+(command
99+ argument: [
100+ (word) @variable.parameter (#match? @variable.parameter "^-")
101+ ]
102+)
103+
104+; non-bultin command names
105+(command name: (word) @function)
106+
107+; derived from builtin -n (fish 3.2.2)
108+(command
109+ name: [
110+ (word) @function.builtin
111+ (#match? @function.builtin "^(\.|:|_|alias|argparse|bg|bind|block|breakpoint|builtin|cd|command|commandline|complete|contains|count|disown|echo|emit|eval|exec|exit|fg|functions|history|isatty|jobs|math|printf|pwd|random|read|realpath|set|set_color|source|status|string|test|time|type|ulimit|wait)$")
112+ ]
113+)
114+
115+(test_command "test" @function.builtin)
116+
117+;; Functions
118+
119+(function_definition ["function" "end"] @keyword.function)
120+
121+(function_definition
122+ name: [
123+ (word) (concatenation)
124+ ]
125+@function)
126+
127+(function_definition
128+ option: [
129+ (word)
130+ (concatenation (word))
131+ ] @variable.parameter (#match? @variable.parameter "^-")
132+)
133+
134+;; Strings
135+
136+[(double_quote_string) (single_quote_string)] @string
137+(escape_sequence) @constant.character.escape
138+
139+;; Variables
140+
141+(variable_name) @variable
142+(variable_expansion) @constant
143+
144+;; Nodes
145+
146+(integer) @constant.numeric.integer
147+(float) @constant.numeric.float
148+(comment) @comment
149+(test_option) @string
150+
151+((word) @constant.builtin.boolean
152+(#match? @constant.builtin.boolean "^(true|false)$"))
153+
154+;; Error
155+
156+(ERROR) @error
runtime/queries/fish/indents.tomlnew file+12
@@ -0,0 +1,12 @@
1+indent = [
2+ "function_definition",
3+ "while_statement",
4+ "for_statement",
5+ "if_statement",
6+ "begin_statement",
7+ "switch_statement",
8+]
9+
10+outdent = [
11+ "end"
12+]
runtime/queries/fish/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+((comment) @injection.content
2+ (#set! injection.language "comment"))
runtime/queries/fish/textobjects.scmnew file+1
@@ -0,0 +1 @@
1+(function_definition) @function.around
runtime/queries/git-commit/highlights.scmnew file+14
@@ -0,0 +1,14 @@
1+(subject) @markup.heading
2+(path) @string.special.path
3+(branch) @string.special.symbol
4+(commit) @constant
5+(item) @markup.link.url
6+(header) @tag
7+
8+(change kind: "new file" @diff.plus)
9+(change kind: "deleted" @diff.minus)
10+(change kind: "modified" @diff.delta)
11+(change kind: "renamed" @diff.delta.moved)
12+
13+[":" "->"] @punctuation.delimeter
14+(comment) @comment
runtime/queries/git-commit/injections.scmnew file+8
@@ -0,0 +1,8 @@
1+((comment (scissors))
2+ (message) @injection.content
3+ (#set! injection.include-children)
4+ (#set! injection.language "diff"))
5+
6+((rebase_command) @injection.content
7+ (#set! injection.include-children)
8+ (#set! injection.language "git-rebase"))
runtime/queries/git-diff/highlights.scmnew file+6
@@ -0,0 +1,6 @@
1+[(addition) (new_file)] @diff.plus
2+[(deletion) (old_file)] @diff.minus
3+
4+(commit) @constant
5+(location) @attribute
6+(command) @markup.bold
runtime/queries/git-rebase/highlights.scmnew file+11
@@ -0,0 +1,11 @@
1+(operation operator: ["p" "pick" "r" "reword" "e" "edit" "s" "squash" "m" "merge" "d" "drop" "b" "break" "x" "exec"] @keyword)
2+(operation operator: ["l" "label" "t" "reset"] @function)
3+(operation operator: ["f" "fixup"] @function.special)
4+
5+(option) @operator
6+(label) @string.special.symbol
7+(commit) @constant
8+"#" @punctuation.delimiter
9+(comment) @comment
10+
11+(ERROR) @error
runtime/queries/git-rebase/injections.scmnew file+4
@@ -0,0 +1,4 @@
1+((operation
2+ operator: ["x" "exec"]
3+ (command) @injection.content)
4+ (#set! injection.language "bash"))
runtime/queries/glsl/injections.scm+3−2
@@ -1,3 +1,4 @@
1−(preproc_arg) @glsl
1+; inherits: c
2
2
3−(comment) @comment
3+((preproc_arg) @injection.content
4+ (#set! injection.language "glsl"))
runtime/queries/julia/injections.scm+5−3
@@ -1,5 +1,7 @@
1; TODO: re-add when markdown is added.
1; TODO: re-add when markdown is added.
2−; ((triple_string) @markdown
2+; ((triple_string) @injection.content
3−; (#offset! @markdown 0 3 0 -3))
3+; (#offset! @injection.content 0 3 0 -3)
4+; (#set! injection.language "markdown"))
4
5
5−(comment) @comment
6+((comment) @injection.content
7+ (#set! injection.language "comment"))
runtime/queries/latex/highlights.scm+1−1
@@ -371,7 +371,7 @@
371((generic_command
371((generic_command
372 name:(generic_command_name) @_name
372 name:(generic_command_name) @_name
373 .
373 .
374− arg: (_) @markup.underline.link)
374+ arg: (_) @markup.link.url)
375 (#match? @_name "^(\\\\url|\\\\href)$"))
375 (#match? @_name "^(\\\\url|\\\\href)$"))
376
376
377(ERROR) @error
377(ERROR) @error
runtime/queries/ledger/injections.scm+2−2
@@ -1,2 +1,2 @@
1−(comment) @comment
1+([(comment) (note)] @injection.content
2−(note) @comment
2+ (#set! injection.language "comment"))
runtime/queries/llvm-mir-yaml/highlights.scmnew file+1
@@ -0,0 +1 @@
1+; inherits: yaml
runtime/queries/llvm-mir-yaml/indents.tomlnew file+3
@@ -0,0 +1,3 @@
1+indent = [
2+ "block_mapping_pair",
3+]
runtime/queries/llvm-mir-yaml/injections.scmnew file+9
@@ -0,0 +1,9 @@
1+; inherits: yaml
2+
3+((document (block_node (block_scalar) @injection.content))
4+ (#set! injection.language "llvm"))
5+
6+((document (block_node (block_mapping (block_mapping_pair
7+ key: (flow_node (plain_scalar (string_scalar))) ; "body"
8+ value: (block_node (block_scalar) @injection.content)))))
9+ (#set! injection.language "mir"))
runtime/queries/llvm-mir/highlights.scmnew file+136
@@ -0,0 +1,136 @@
1+[
2+ (label)
3+ (bb_ref)
4+] @label
5+
6+[
7+ (comment)
8+ (multiline_comment)
9+] @comment
10+
11+[
12+ "("
13+ ")"
14+ "["
15+ "]"
16+ "{"
17+ "}"
18+ "<"
19+ ">"
20+] @punctuation.bracket
21+
22+[
23+ ","
24+ ":"
25+ "|"
26+ "*"
27+] @punctuation.delimiter
28+
29+[
30+ "="
31+ "x"
32+] @operator
33+
34+[
35+ "true"
36+ "false"
37+] @constant.builtin.boolean
38+
39+[
40+ "null"
41+ "_"
42+ "unknown-address"
43+] @constant.builtin
44+
45+[
46+ (stack_object)
47+ (constant_pool_index)
48+ (jump_table_index)
49+ (var)
50+ (physical_register)
51+ (ir_block)
52+ (external_symbol)
53+ (global_var)
54+ (ir_local_var)
55+ (metadata_ref)
56+ (mnemonic)
57+] @variable
58+
59+(low_level_type) @type
60+
61+[
62+ (immediate_type)
63+ (primitive_type)
64+] @type.builtin
65+
66+(number) @constant.numeric.integer
67+(float) @constant.numeric.float
68+(string) @string
69+
70+(instruction name: _ @keyword.operator)
71+
72+[
73+ "successors"
74+ "liveins"
75+ "pre-instr-symbol"
76+ "post-instr-symbol"
77+ "heap-alloc-marker"
78+ "debug-instr-number"
79+ "debug-location"
80+ "mcsymbol"
81+ "tied-def"
82+ "target-flags"
83+ "CustomRegMask"
84+ "same_value"
85+ "def_cfa_register"
86+ "restore"
87+ "undefined"
88+ "offset"
89+ "rel_offset"
90+ "def_cfa"
91+ "llvm_def_aspace_cfa"
92+ "register"
93+ "escape"
94+ "remember_state"
95+ "restore_state"
96+ "window_save"
97+ "negate_ra_sign_state"
98+ "intpred"
99+ "floatpred"
100+ "shufflemask"
101+ "liveout"
102+ "target-index"
103+ "blockaddress"
104+ "intrinsic"
105+ "load"
106+ "store"
107+ "unknown-size"
108+ "on"
109+ "from"
110+ "into"
111+ "align"
112+ "basealign"
113+ "addrspace"
114+ "call-entry"
115+ "custom"
116+ "constant-pool"
117+ "stack"
118+ "got"
119+ "jump-table"
120+ "syncscope"
121+ "address-taken"
122+ "landing-pad"
123+ "inlineasm-br-indirect-target"
124+ "ehfunclet-entry"
125+ "bbsections"
126+
127+ (intpred)
128+ (floatpred)
129+ (memory_operand_flag)
130+ (atomic_ordering)
131+ (register_flag)
132+ (instruction_flag)
133+ (float_keyword)
134+] @keyword
135+
136+(ERROR) @error
runtime/queries/llvm-mir/indents.tomlnew file+7
@@ -0,0 +1,7 @@
1+indent = [
2+ "basic_block",
3+]
4+
5+outdent = [
6+ "label",
7+]
runtime/queries/llvm-mir/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+([ (comment) (multiline_comment)] @injection.content
2+ (#set! injection.language "comment"))
runtime/queries/llvm-mir/textobjects.scmnew file+3
@@ -0,0 +1,3 @@
1+(basic_block) @function.around
2+
3+(argument) @parameter.inside
runtime/queries/llvm/highlights.scm+153−9
@@ -1,14 +1,158 @@
1(type) @type
1(type) @type
2+(type_keyword) @type.builtin
3+
4+(type [
5+ (local_var)
6+ (global_var)
7+ ] @type)
8+
9+(argument) @variable.parameter
10+
11+(_ inst_name: _ @keyword.operator)
12+
13+[
14+ "catch"
15+ "filter"
2−(statement) @keyword.operator
16+] @keyword.operator
17+
18+[
19+ "to"
20+ "nuw"
21+ "nsw"
22+ "exact"
23+ "unwind"
24+ "from"
25+ "cleanup"
26+ "swifterror"
27+ "volatile"
28+ "inbounds"
29+ "inrange"
30+ (icmp_cond)
31+ (fcmp_cond)
32+ (fast_math)
33+] @keyword.control
34+
35+(_ callee: _ @function)
36+(function_header name: _ @function)
37+
38+[
39+ "declare"
40+ "define"
41+ (calling_conv)
42+] @keyword.function
43+
44+[
45+ "target"
46+ "triple"
47+ "datalayout"
48+ "source_filename"
49+ "addrspace"
50+ "blockaddress"
51+ "align"
52+ "syncscope"
53+ "within"
54+ "uselistorder"
55+ "uselistorder_bb"
56+ "module"
57+ "asm"
58+ "sideeffect"
59+ "alignstack"
60+ "inteldialect"
61+ "unwind"
62+ "type"
63+ "global"
64+ "constant"
65+ "externally_initialized"
66+ "alias"
67+ "ifunc"
68+ "section"
69+ "comdat"
70+ "thread_local"
71+ "localdynamic"
72+ "initialexec"
73+ "localexec"
74+ "any"
75+ "exactmatch"
76+ "largest"
77+ "nodeduplicate"
78+ "samesize"
79+ "distinct"
80+ "attributes"
81+ "vscale"
82+ "no_cfi"
83+ (linkage_aux)
84+ (dso_local)
85+ (visibility)
86+ (dll_storage_class)
87+ (unnamed_addr)
88+ (attribute_name)
89+] @keyword
90+
91+
92+(function_header [
93+ (linkage)
94+ (calling_conv)
95+ (unnamed_addr)
96+ ] @keyword.function)
97+
98+[
99+ (string)
100+ (cstring)
101+] @string
102+
3(number) @constant.numeric.integer
103(number) @constant.numeric.integer
4(comment) @comment
104(comment) @comment
5−(string) @string
6(label) @label
105(label) @label
7−(keyword) @keyword
8−"ret" @keyword.control.return
106+(_ inst_name: "ret" @keyword.control.return)
9−(boolean) @constant.builtin.boolean
10(float) @constant.numeric.float
107(float) @constant.numeric.float
11−(constant) @constant
108+
109+[
110+ (local_var)
111+ (global_var)
12−(identifier) @variable
112+] @variable
113+
114+[
115+ (struct_value)
116+ (array_value)
117+ (vector_value)
118+] @constructor
119+
120+[
121+ "("
122+ ")"
123+ "["
124+ "]"
125+ "{"
126+ "}"
127+ "<"
128+ ">"
129+ "<{"
130+ "}>"
131+] @punctuation.bracket
132+
133+[
134+ ","
135+ ":"
13−(symbol) @punctuation.delimiter
136+] @punctuation.delimiter
14−(bracket) @punctuation.bracket
137+
138+[
139+ "="
140+ "|"
141+ "x"
142+ "..."
143+] @operator
144+
145+[
146+ "true"
147+ "false"
148+] @constant.builtin.boolean
149+
150+[
151+ "undef"
152+ "poison"
153+ "null"
154+ "none"
155+ "zeroinitializer"
156+] @constant.builtin
157+
158+(ERROR) @error
runtime/queries/llvm/indents.tomlnew file+8
@@ -0,0 +1,8 @@
1+indent = [
2+ "function_body",
3+ "instruction",
4+]
5+
6+outdent = [
7+ "}",
8+]
runtime/queries/llvm/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+((comment) @injection.content
2+ (#set! injection.language "comment"))
runtime/queries/llvm/locals.scmnew file+14
@@ -0,0 +1,14 @@
1+; Scopes
2+
3+(function_body) @local.scope
4+
5+; Definitions
6+
7+(argument
8+ (value (var (local_var) @local.definition)))
9+
10+(instruction
11+ (local_var) @local.definition)
12+
13+; References
14+(local_var) @local.reference
runtime/queries/llvm/textobjects.scmnew file+16
@@ -0,0 +1,16 @@
1+(define
2+ body: (_) @function.inside) @function.around
3+
4+(struct_type
5+ (struct_body) @class.inside) @class.around
6+
7+(packed_struct_type
8+ (struct_body) @class.inside) @class.around
9+
10+(array_type
11+ (array_vector_body) @class.inside) @class.around
12+
13+(vector_type
14+ (array_vector_body) @class.inside) @class.around
15+
16+(argument) @parameter.inside
runtime/queries/markdown/highlights.scm+4−3
@@ -10,15 +10,16 @@
10 (fenced_code_block)
10 (fenced_code_block)
11] @markup.raw.block
11] @markup.raw.block
12
12
13+(block_quote) @markup.quote
14+
13(code_span) @markup.raw.inline
15(code_span) @markup.raw.inline
14
16
15(emphasis) @markup.italic
17(emphasis) @markup.italic
16
18
17(strong_emphasis) @markup.bold
19(strong_emphasis) @markup.bold
18
20
19−(link_destination) @markup.underline.link
21+(link_destination) @markup.link.url
20−
21−; (link_label) @markup.label ; TODO: rename
22+(link_label) @markup.link.label
22
23
23[
24[
24 (list_marker_plus)
25 (list_marker_plus)
runtime/queries/markdown/injections.scm+2−1
@@ -1,6 +1,7 @@
1(fenced_code_block
1(fenced_code_block
2 (info_string) @injection.language
2 (info_string) @injection.language
3− (code_fence_content) @injection.content)
3+ (code_fence_content) @injection.content
4+ (#set! injection.include-children))
4
5
5((html_block) @injection.content
6((html_block) @injection.content
6 (#set! injection.language "html"))
7 (#set! injection.language "html"))
runtime/queries/nix/highlights.scm+6−1
@@ -13,7 +13,7 @@
13] @keyword
13] @keyword
14
14
15((identifier) @variable.builtin
15((identifier) @variable.builtin
16− (#match? @variable.builtin "^(__currentSystem|__currentTime|__nixPath|__nixVersion|__storeDir|builtins|false|null|true)$")
16+ (#match? @variable.builtin "^(__currentSystem|__currentTime|__nixPath|__nixVersion|__storeDir|builtins)$")
17 (#is-not? local))
17 (#is-not? local))
18
18
19((identifier) @function.builtin
19((identifier) @function.builtin
@@ -33,6 +33,11 @@
33
33
34(uri) @string.special.uri
34(uri) @string.special.uri
35
35
36+; boolean
37+((identifier) @constant.builtin.boolean (#match? @constant.builtin.boolean "^(true|false)$")) @constant.builtin.boolean
38+; null
39+((identifier) @constant.builtin (#eq? @constant.builtin "null")) @constant.builtin
40+
36(integer) @constant.numeric.integer
41(integer) @constant.numeric.integer
37(float) @constant.numeric.float
42(float) @constant.numeric.float
38
43
runtime/queries/ocaml/indents.toml+1−1
@@ -8,6 +8,6 @@ indent = [
8 "match_case",
8 "match_case",
9]
9]
10
10
11−oudent = [
11+outdent = [
12 "}",
12 "}",
13]
13]
runtime/queries/python/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+((comment) @injection.content
2+ (#set! injection.language "comment"))
runtime/queries/ruby/indents.tomlnew file+25
@@ -0,0 +1,25 @@
1+indent = [
2+ "argument_list",
3+ "array",
4+ "begin",
5+ "block",
6+ "call",
7+ "class",
8+ "case",
9+ "do_block",
10+ "elsif",
11+ "if",
12+ "hash",
13+ "method",
14+ "module",
15+ "singleton_class",
16+ "singleton_method",
17+]
18+
19+outdent = [
20+ ")",
21+ "}",
22+ "]",
23+ "end",
24+ "when",
25+]
runtime/queries/rust/highlights.scm+9−5
@@ -127,11 +127,16 @@
127 "await"
127 "await"
128] @keyword.control
128] @keyword.control
129
129
130+"use" @keyword.control.import
131+(mod_item "mod" @keyword.control.import !body)
132+(use_as_clause "as" @keyword.control.import)
133+
134+(type_cast_expression "as" @keyword.operator)
135+
130[
136[
131 (crate)
137 (crate)
132 (super)
138 (super)
133 "as"
139 "as"
134− "use"
135 "pub"
140 "pub"
136 "mod"
141 "mod"
137 "extern"
142 "extern"
@@ -242,10 +247,9 @@
242; ---
247; ---
243; Macros
248; Macros
244; ---
249; ---
245−
246(meta_item
250(meta_item
247− (identifier) @attribute)
251+ (identifier) @function.macro)
248−(attribute_item) @attribute
252+
249(inner_attribute_item) @attribute
253(inner_attribute_item) @attribute
250
254
251(macro_definition
255(macro_definition
@@ -259,7 +263,7 @@
259 "!" @function.macro)
263 "!" @function.macro)
260
264
261(metavariable) @variable.parameter
265(metavariable) @variable.parameter
262−(fragment_specifier) @variable.parameter
266+(fragment_specifier) @type
263
267
264
268
265
269
runtime/queries/rust/indents.toml+1
@@ -9,6 +9,7 @@ indent = [
9 "field_initializer_list",
9 "field_initializer_list",
10 "struct_pattern",
10 "struct_pattern",
11 "tuple_pattern",
11 "tuple_pattern",
12+ "unit_expression",
12 "enum_variant_list",
13 "enum_variant_list",
13 "call_expression",
14 "call_expression",
14 "binary_expression",
15 "binary_expression",
runtime/queries/rust/injections.scm+3
@@ -1,3 +1,6 @@
1+([(line_comment) (block_comment)] @injection.content
2+ (#set! injection.language "comment"))
3+
1((macro_invocation
4((macro_invocation
2 (token_tree) @injection.content)
5 (token_tree) @injection.content)
3 (#set! injection.language "rust")
6 (#set! injection.language "rust")
runtime/queries/scala/highlights.scmnew file+203
@@ -0,0 +1,203 @@
1+; CREDITS @stumash (stuart.mashaal@gmail.com)
2+
3+;; variables
4+
5+
6+((identifier) @variable.builtin
7+ (#match? @variable.builtin "^this$"))
8+
9+(interpolation) @none
10+
11+; Assume other uppercase names constants.
12+; NOTE: In order to distinguish constants we highlight
13+; all the identifiers that are uppercased. But this solution
14+; is not suitable for all occurrences e.g. it will highlight
15+; an uppercased method as a constant if used with no params.
16+; Introducing highlighting for those specific cases, is probably
17+; best way to resolve the issue.
18+((identifier) @constant (#match? @constant "^[A-Z]"))
19+
20+;; types
21+
22+(type_identifier) @type
23+
24+(class_definition
25+ name: (identifier) @type)
26+
27+(object_definition
28+ name: (identifier) @type)
29+
30+(trait_definition
31+ name: (identifier) @type)
32+
33+(type_definition
34+ name: (type_identifier) @type)
35+
36+; method definition
37+
38+(class_definition
39+ body: (template_body
40+ (function_definition
41+ name: (identifier) @function.method)))
42+(object_definition
43+ body: (template_body
44+ (function_definition
45+ name: (identifier) @function.method)))
46+(trait_definition
47+ body: (template_body
48+ (function_definition
49+ name: (identifier) @function.method)))
50+
51+; imports
52+
53+(import_declaration
54+ path: (identifier) @namespace)
55+((stable_identifier (identifier) @namespace))
56+
57+((import_declaration
58+ path: (identifier) @type) (#match? @type "^[A-Z]"))
59+((stable_identifier (identifier) @type) (#match? @type "^[A-Z]"))
60+
61+((import_selectors (identifier) @type) (#match? @type "^[A-Z]"))
62+
63+; method invocation
64+
65+
66+(call_expression
67+ function: (identifier) @function)
68+
69+(call_expression
70+ function: (field_expression
71+ field: (identifier) @function.method))
72+
73+((call_expression
74+ function: (identifier) @variable.other.member)
75+ (#match? @variable.other.member "^[A-Z]"))
76+
77+(generic_function
78+ function: (identifier) @function)
79+
80+(
81+ (identifier) @function.builtin
82+ (#match? @function.builtin "^super$")
83+)
84+
85+; function definitions
86+
87+(function_definition
88+ name: (identifier) @function)
89+
90+(parameter
91+ name: (identifier) @variable.parameter)
92+
93+; expressions
94+
95+
96+(field_expression field: (identifier) @variable.other.member)
97+(field_expression value: (identifier) @type
98+ (#match? @type "^[A-Z]"))
99+
100+(infix_expression operator: (identifier) @operator)
101+(infix_expression operator: (operator_identifier) @operator)
102+(infix_type operator: (operator_identifier) @operator)
103+(infix_type operator: (operator_identifier) @operator)
104+
105+; literals
106+(boolean_literal) @constant.builtin.boolean
107+(integer_literal) @constant.numeric.integer
108+(floating_point_literal) @constant.numeric.float
109+
110+
111+(symbol_literal) @string.special.symbol
112+
113+[
114+(string)
115+(character_literal)
116+(interpolated_string_expression)
117+] @string
118+
119+(interpolation "$" @punctuation.special)
120+
121+;; keywords
122+
123+[
124+ "abstract"
125+ "case"
126+ "class"
127+ "extends"
128+ "final"
129+ "finally"
130+;; `forSome` existential types not implemented yet
131+ "implicit"
132+ "lazy"
133+;; `macro` not implemented yet
134+ "object"
135+ "override"
136+ "package"
137+ "private"
138+ "protected"
139+ "sealed"
140+ "trait"
141+ "type"
142+ "val"
143+ "var"
144+ "with"
145+] @keyword
146+
147+(null_literal) @constant.builtin
148+(wildcard) @keyword
149+
150+;; special keywords
151+
152+"new" @keyword.operator
153+
154+[
155+ "else"
156+ "if"
157+ "match"
158+ "try"
159+ "catch"
160+ "throw"
161+] @keyword.control.conditional
162+
163+[
164+ "("
165+ ")"
166+ "["
167+ "]"
168+ "{"
169+ "}"
170+] @punctuation.bracket
171+
172+[
173+ "."
174+ ","
175+] @punctuation.delimiter
176+
177+[
178+ "do"
179+ "for"
180+ "while"
181+ "yield"
182+] @keyword.control.repeat
183+
184+"def" @keyword.function
185+
186+[
187+ "=>"
188+ "<-"
189+ "@"
190+] @keyword.operator
191+
192+"import" @keyword.control.import
193+
194+"return" @keyword.control.return
195+
196+(comment) @comment
197+
198+;; `case` is a conditional keyword in case_block
199+
200+(case_block
201+ (case_clause ("case") @keyword.control.conditional))
202+
203+(identifier) @variable
runtime/queries/scala/indents.tomlnew file+23
@@ -0,0 +1,23 @@
1+
2+indent = [
3+ "block",
4+ "arguments",
5+ "parameter",
6+ "class_definition",
7+ "trait_definition",
8+ "object_definition",
9+ "function_definition",
10+ "val_definition",
11+ "import_declaration",
12+ "while_expression",
13+ "do_while_expression",
14+ "for_expression",
15+ "try_expression",
16+ "match_expression"
17+]
18+
19+outdent = [
20+ "}",
21+ "]",
22+ ")"
23+]
runtime/queries/scala/injections.scmnew file+1
@@ -0,0 +1 @@
1+(comment) @comment
runtime/queries/svelte/highlights.scm+2−2
@@ -20,12 +20,12 @@
20((element (start_tag (tag_name) @_tag) (text) @markup.inline)
20((element (start_tag (tag_name) @_tag) (text) @markup.inline)
21 (#match? @_tag "^(code|kbd)$"))
21 (#match? @_tag "^(code|kbd)$"))
22
22
23−((element (start_tag (tag_name) @_tag) (text) @markup.underline.link)
23+((element (start_tag (tag_name) @_tag) (text) @markup.link.url)
24 (#eq? @_tag "a"))
24 (#eq? @_tag "a"))
25
25
26((attribute
26((attribute
27 (attribute_name) @_attr
27 (attribute_name) @_attr
28− (quoted_attribute_value (attribute_value) @markup.underline.link))
28+ (quoted_attribute_value (attribute_value) @markup.link.url))
29 (#match? @_attr "^(href|src)$"))
29 (#match? @_attr "^(href|src)$"))
30
30
31(tag_name) @tag
31(tag_name) @tag
runtime/queries/svelte/injections.scm+2−2
@@ -26,5 +26,5 @@
26 (#set! injection.language "typescript")
26 (#set! injection.language "typescript")
27)
27)
28
28
29−(comment) @comment
29+((comment) @injection.content
30−
30+ (#set! injection.language "comment"))
runtime/queries/tablegen/highlights.scmnew file+90
@@ -0,0 +1,90 @@
1+[
2+ (comment)
3+ (multiline_comment)
4+] @comment
5+
6+[
7+ "("
8+ ")"
9+ "["
10+ "]"
11+ "{"
12+ "}"
13+ "<"
14+ ">"
15+] @punctuation.bracket
16+
17+[
18+ ","
19+ ";"
20+ "."
21+] @punctuation.delimiter
22+
23+[
24+ "#"
25+ "-"
26+ "..."
27+ ":"
28+] @operator
29+
30+[
31+ "="
32+ "!cond"
33+ (operator_keyword)
34+] @function
35+
36+[
37+ "true"
38+ "false"
39+] @constant.builtin.boolean
40+
41+[
42+ "?"
43+] @constant.builtin
44+
45+(var) @variable
46+
47+(template_arg (identifier) @variable.parameter)
48+
49+(_ argument: (value (identifier) @variable.parameter))
50+
51+(type) @type
52+
53+"code" @type.builtin
54+
55+(number) @constant.numeric.integer
56+[
57+ (string_string)
58+ (code_string)
59+] @string
60+
61+(preprocessor) @keyword.directive
62+
63+[
64+ "class"
65+ "field"
66+ "let"
67+ "defvar"
68+ "def"
69+ "defset"
70+ "defvar"
71+ "assert"
72+] @keyword
73+
74+[
75+ "let"
76+ "in"
77+ "foreach"
78+ "if"
79+ "then"
80+ "else"
81+] @keyword.operator
82+
83+"include" @keyword.control.import
84+
85+[
86+ "multiclass"
87+ "defm"
88+] @namespace
89+
90+(ERROR) @error
runtime/queries/tablegen/indents.tomlnew file+7
@@ -0,0 +1,7 @@
1+indent = [
2+ "statement",
3+]
4+
5+outdent = [
6+ "}",
7+]
runtime/queries/tablegen/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+([ (comment) (multiline_comment)] @injection.content
2+ (#set! injection.language "comment"))
runtime/queries/tablegen/textobjects.scmnew file+7
@@ -0,0 +1,7 @@
1+(class
2+ body: (_) @class.inside) @class.around
3+
4+(multiclass
5+ body: (_) @class.inside) @class.around
6+
7+(_ argument: _ @parameter.inside)
runtime/queries/tsq/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+((comment) @injection.content
2+ (#set! injection.language "comment"))
runtime/queries/yaml/highlights.scm+13−3
@@ -1,9 +1,19 @@
1−(block_mapping_pair key: (_) @variable.other.member)
1+(block_mapping_pair
2+ key: (flow_node [(double_quote_scalar) (single_quote_scalar)] @variable.other.member))
3+(block_mapping_pair
4+ key: (flow_node (plain_scalar (string_scalar) @variable.other.member)))
5+
6+(flow_mapping
7+ (_ key: (flow_node [(double_quote_scalar) (single_quote_scalar)] @variable.other.member)))
2−(flow_mapping (_ key: (_) @variable.other.member))
8+(flow_mapping
9+ (_ key: (flow_node (plain_scalar (string_scalar) @variable.other.member))))
10+
3(boolean_scalar) @constant.builtin.boolean
11(boolean_scalar) @constant.builtin.boolean
4(null_scalar) @constant.builtin
12(null_scalar) @constant.builtin
5(double_quote_scalar) @string
13(double_quote_scalar) @string
6(single_quote_scalar) @string
14(single_quote_scalar) @string
15+(block_scalar) @string
16+(string_scalar) @string
7(escape_sequence) @constant.character.escape
17(escape_sequence) @constant.character.escape
8(integer_scalar) @constant.numeric.integer
18(integer_scalar) @constant.numeric.integer
9(float_scalar) @constant.numeric.float
19(float_scalar) @constant.numeric.float
@@ -30,4 +40,4 @@
30"}"
40"}"
31] @punctuation.bracket
41] @punctuation.bracket
32
42
33−["*" "&"] @punctuation.special
43+["*" "&" "---" "..."] @punctuation.special
runtime/queries/yaml/injections.scmnew file+2
@@ -0,0 +1,2 @@
1+((comment) @injection.content
2+ (#set! injection.language "comment"))
runtime/themes/base16_default_dark.toml+4
@@ -33,6 +33,10 @@
33"namespace" = "base0E"
33"namespace" = "base0E"
34"ui.help" = { fg = "base06", bg = "base01" }
34"ui.help" = { fg = "base06", bg = "base01" }
35
35
36+"diff.plus" = "base0B"
37+"diff.delta" = "base09"
38+"diff.minus" = "base08"
39+
36"diagnostic" = { modifiers = ["underlined"] }
40"diagnostic" = { modifiers = ["underlined"] }
37"ui.gutter" = { bg = "base01" }
41"ui.gutter" = { bg = "base01" }
38"info" = "base0D"
42"info" = "base0D"
runtime/themes/base16_default_light.toml+4
@@ -33,6 +33,10 @@
33"namespace" = "base0E"
33"namespace" = "base0E"
34"ui.help" = { fg = "base06", bg = "base01" }
34"ui.help" = { fg = "base06", bg = "base01" }
35
35
36+"diff.plus" = "base0B"
37+"diff.delta" = "base09"
38+"diff.minus" = "base08"
39+
36"diagnostic" = { modifiers = ["underlined"] }
40"diagnostic" = { modifiers = ["underlined"] }
37"ui.gutter" = { bg = "base01" }
41"ui.gutter" = { bg = "base01" }
38"info" = "base0D"
42"info" = "base0D"
runtime/themes/base16_terminal.toml+4
@@ -30,6 +30,10 @@
30"namespace" = "light-magenta"
30"namespace" = "light-magenta"
31"ui.help" = { fg = "white", bg = "black" }
31"ui.help" = { fg = "white", bg = "black" }
32
32
33+"diff.plus" = "light-green"
34+"diff.delta" = "yellow"
35+"diff.minus" = "light-red"
36+
33"diagnostic" = { modifiers = ["underlined"] }
37"diagnostic" = { modifiers = ["underlined"] }
34"ui.gutter" = { bg = "black" }
38"ui.gutter" = { bg = "black" }
35"info" = "light-blue"
39"info" = "light-blue"
runtime/themes/bogster.toml+7
@@ -28,6 +28,10 @@
28
28
29"module" = "#d32c5d"
29"module" = "#d32c5d"
30
30
31+"diff.plus" = "#59dcb7"
32+"diff.delta" = "#dc7759"
33+"diff.minus" = "#dc597f"
34+
31"ui.background" = { bg = "#161c23" }
35"ui.background" = { bg = "#161c23" }
32"ui.linenr" = { fg = "#415367" }
36"ui.linenr" = { fg = "#415367" }
33"ui.linenr.selected" = { fg = "#e5ded6" } # TODO
37"ui.linenr.selected" = { fg = "#e5ded6" } # TODO
@@ -49,3 +53,6 @@
49"error" = "#dc597f"
53"error" = "#dc597f"
50"info" = "#59dcb7"
54"info" = "#59dcb7"
51"hint" = "#59c0dc"
55"hint" = "#59c0dc"
56+
57+# make diagnostic underlined, to distinguish with selection text.
58+diagnostic = { modifiers = ["underlined"] }
runtime/themes/dark_plus.toml+4
@@ -39,6 +39,10 @@
39"constant.numeric" = { fg = "pale_green" }
39"constant.numeric" = { fg = "pale_green" }
40"constant.character.escape" = { fg = "gold" }
40"constant.character.escape" = { fg = "gold" }
41
41
42+"diff.plus" = { fg = "pale_green" }
43+"diff.delta" = { fg = "gold" }
44+"diff.minus" = { fg = "red" }
45+
42"ui.background" = { fg = "light_gray", bg = "dark_gray2" }
46"ui.background" = { fg = "light_gray", bg = "dark_gray2" }
43
47
44"ui.window" = { bg = "widget" }
48"ui.window" = { bg = "widget" }
runtime/themes/dracula.tomlnew file+54
@@ -0,0 +1,54 @@
1+# Author : Sebastian Zivota <loewenheim@mailbox.org>
2+"comment" = { fg = "comment" }
3+"constant" = { fg = "purple" }
4+"constant.character.escape" = { fg = "pink" }
5+"function" = { fg = "green" }
6+"keyword" = { fg = "pink" }
7+"operator" = { fg = "pink" }
8+"punctuation" = { fg = "foreground" }
9+"string" = { fg = "yellow" }
10+"string.regexp" = { fg = "red" }
11+"tag" = { fg = "pink" }
12+"type" = { fg = "cyan", modifiers = ["italic"] }
13+"type.enum.variant" = { fg = "foreground", modifiers = ["italic"] }
14+"variable" = { fg = "foreground" }
15+"variable.builtin" = { fg = "cyan", modifiers = ["italic"] }
16+"variable.parameter" = { fg ="orange", modifiers = ["italic"] }
17+
18+"diff.plus" = { fg = "green" }
19+"diff.delta" = { fg = "orange" }
20+"diff.minus" = { fg = "red" }
21+
22+"ui.background" = { fg = "foreground", bg = "background" }
23+"ui.cursor" = { fg = "background", bg = "orange", modifiers = ["dim"] }
24+"ui.cursor.match" = { fg = "green", modifiers = ["underlined"] }
25+"ui.cursor.primary" = { fg = "background", bg = "cyan", modifier = ["dim"] }
26+"ui.help" = { fg = "foreground", bg = "background_dark" }
27+"ui.linenr" = { fg = "comment" }
28+"ui.linenr.selected" = { fg = "foreground" }
29+"ui.menu" = { fg = "foreground", bg = "background_dark" }
30+"ui.menu.selected" = { fg = "cyan", bg = "background_dark" }
31+"ui.popup" = { fg = "foreground" }
32+"ui.selection" = { fg = "background", bg = "purple", modifiers = ["dim"] }
33+"ui.selection.primary" = { fg = "background", bg = "pink" }
34+"ui.statusline" = { fg = "foreground", bg = "background_dark" }
35+"ui.statusline.inactive" = { fg = "comment", bg = "background_dark" }
36+"ui.text" = { fg = "foreground" }
37+"ui.text.focus" = { fg = "cyan" }
38+"ui.window" = { fg = "foreground" }
39+
40+"error" = { fg = "red" }
41+"warning" = { fg = "cyan" }
42+
43+[palette]
44+background = "#282a36"
45+background_dark = "#21222c"
46+foreground = "#f8f8f2"
47+comment = "#6272a4"
48+red = "#ff5555"
49+orange = "#ffb86c"
50+yellow = "#f1fa8c"
51+green = "#50fa7b"
52+purple = "#bd93f9"
53+cyan = "#8be9fd"
54+pink = "#ff79c6"
runtime/themes/everforest_dark.toml+5−1
@@ -12,7 +12,7 @@
12"type" = "yellow"
12"type" = "yellow"
13"constant" = "purple"
13"constant" = "purple"
14"constant.numeric" = "purple"
14"constant.numeric" = "purple"
15−"string" = "grey2"
15+"string" = "green"
16"comment" = "grey0"
16"comment" = "grey0"
17"variable" = "fg"
17"variable" = "fg"
18"variable.builtin" = "blue"
18"variable.builtin" = "blue"
@@ -34,6 +34,10 @@
34"module" = "blue"
34"module" = "blue"
35"special" = "orange"
35"special" = "orange"
36
36
37+"diff.plus" = "green"
38+"diff.delta" = "orange"
39+"diff.minus" = "red"
40+
37"ui.background" = { bg = "bg0" }
41"ui.background" = { bg = "bg0" }
38"ui.cursor" = { fg = "bg0", bg = "fg" }
42"ui.cursor" = { fg = "bg0", bg = "fg" }
39"ui.cursor.match" = { fg = "orange", bg = "bg_yellow" }
43"ui.cursor.match" = { fg = "orange", bg = "bg_yellow" }
runtime/themes/everforest_light.tomlnew file+90
@@ -0,0 +1,90 @@
1+# Everforest (Dark Hard)
2+# Author: CptPotato
3+
4+# Original Author:
5+# URL: https://github.com/sainnhe/everforest
6+# Filename: autoload/everforest.vim
7+# Author: sainnhe
8+# Email: sainnhe@gmail.com
9+# License: MIT License
10+
11+"constant.character.escape" = "orange"
12+"type" = "yellow"
13+"constant" = "purple"
14+"constant.numeric" = "purple"
15+"string" = "green"
16+"comment" = "grey0"
17+"variable" = "fg"
18+"variable.builtin" = "blue"
19+"variable.parameter" = "fg"
20+"variable.other.member" = "fg"
21+"label" = "aqua"
22+"punctuation" = "grey2"
23+"punctuation.delimiter" = "grey2"
24+"punctuation.bracket" = "fg"
25+"keyword" = "red"
26+"operator" = "orange"
27+"function" = "green"
28+"function.builtin" = "blue"
29+"function.macro" = "aqua"
30+"tag" = "yellow"
31+"namespace" = "aqua"
32+"attribute" = "aqua"
33+"constructor" = "yellow"
34+"module" = "blue"
35+"special" = "orange"
36+
37+"diff.plus" = "green"
38+"diff.delta" = "orange"
39+"diff.minus" = "red"
40+
41+"ui.background" = { bg = "bg0" }
42+"ui.cursor" = { fg = "bg0", bg = "fg" }
43+"ui.cursor.match" = { fg = "orange", bg = "bg_yellow" }
44+"ui.cursor.insert" = { fg = "bg0", bg = "grey1" }
45+"ui.cursor.select" = { fg = "bg0", bg = "blue" }
46+"ui.linenr" = "grey0"
47+"ui.linenr.selected" = "fg"
48+"ui.statusline" = { fg = "grey2", bg = "bg2" }
49+"ui.statusline.inactive" = { fg = "grey0", bg = "bg1" }
50+"ui.popup" = { fg = "grey2", bg = "bg1" }
51+"ui.window" = { fg = "grey2", bg = "bg1" }
52+"ui.help" = { fg = "fg", bg = "bg1" }
53+"ui.text" = "fg"
54+"ui.text.focus" = "fg"
55+"ui.menu" = { fg = "fg", bg = "bg2" }
56+"ui.menu.selected" = { fg = "bg0", bg = "green" }
57+"ui.selection" = { bg = "bg3" }
58+
59+"hint" = "blue"
60+"info" = "aqua"
61+"warning" = "yellow"
62+"error" = "red"
63+"diagnostic" = { modifiers = ["underlined"] }
64+
65+
66+[palette]
67+
68+bg0 = "#fff9e8"
69+bg1 = "#f7f4e0"
70+bg2 = "#f0eed9"
71+bg3 = "#e9e8d2"
72+bg4 = "#e1ddcb"
73+bg5 = "#bec5b2"
74+bg_visual = "#edf0cd"
75+bg_red = "#fce5dc"
76+bg_green = "#f1f3d4"
77+bg_blue = "#eaf2eb"
78+bg_yellow = "#fbefd0"
79+
80+fg = "#5c6a72"
81+red = "#f85552"
82+orange = "#f57d26"
83+yellow = "#dfa000"
84+green = "#8da101"
85+aqua = "#35a77c"
86+blue = "#3a94c5"
87+purple = "#df69ba"
88+grey0 = "#a6b0a0"
89+grey1 = "#939f91"
90+grey2 = "#829181"
runtime/themes/gruvbox.toml+4
@@ -28,6 +28,10 @@
28"label" = "aqua1"
28"label" = "aqua1"
29"module" = "aqua1"
29"module" = "aqua1"
30
30
31+"diff.plus" = "green1"
32+"diff.delta" = "orange1"
33+"diff.minus" = "red1"
34+
31"warning" = { fg = "orange1", bg = "bg1" }
35"warning" = { fg = "orange1", bg = "bg1" }
32"error" = { fg = "red1", bg = "bg1" }
36"error" = { fg = "red1", bg = "bg1" }
33"info" = { fg = "aqua1", bg = "bg1" }
37"info" = { fg = "aqua1", bg = "bg1" }
runtime/themes/ingrid.toml+4
@@ -28,6 +28,10 @@
28
28
29"module" = "#839A53"
29"module" = "#839A53"
30
30
31+"diff.plus" = "#839A53"
32+"diff.delta" = "#D4A520"
33+"diff.minus" = "#D74E50"
34+
31"ui.background" = { bg = "#FFFCFD" }
35"ui.background" = { bg = "#FFFCFD" }
32"ui.linenr" = { fg = "#bbbbbb" }
36"ui.linenr" = { fg = "#bbbbbb" }
33"ui.linenr.selected" = { fg = "#F3EAE9" } # TODO
37"ui.linenr.selected" = { fg = "#F3EAE9" } # TODO
runtime/themes/monokai.toml+5−1
@@ -39,6 +39,10 @@
39"constant.numeric" = { fg = "#ae81ff" }
39"constant.numeric" = { fg = "#ae81ff" }
40"constant.character.escape" = { fg = "#ae81ff" }
40"constant.character.escape" = { fg = "#ae81ff" }
41
41
42+"diff.plus" = { fg = "#a6e22e" }
43+"diff.delta" = { fg = "#fd971f" }
44+"diff.minus" = { fg = "#f92672" }
45+
42"ui.background" = { fg = "text", bg = "background" }
46"ui.background" = { fg = "text", bg = "background" }
43
47
44"ui.window" = { bg = "widget" }
48"ui.window" = { bg = "widget" }
@@ -65,7 +69,7 @@
65"warning" = { fg = "#cca700" }
69"warning" = { fg = "#cca700" }
66"error" = { fg = "#f48771" }
70"error" = { fg = "#f48771" }
67"info" = { fg = "#75beff" }
71"info" = { fg = "#75beff" }
68−"hint" = { fg = "#eeeeeeb3" }
72+"hint" = { fg = "#eeeeeb3" }
69
73
70diagnostic = { modifiers = ["underlined"] }
74diagnostic = { modifiers = ["underlined"] }
71
75
runtime/themes/monokai_pro.toml+5
@@ -77,6 +77,11 @@
77# integer, floating point
77# integer, floating point
78"constant.numeric" = "purple"
78"constant.numeric" = "purple"
79
79
80+# vcs
81+"diff.plus" = "green"
82+"diff.delta" = "orange"
83+"diff.minus" = "red"
84+
80# make diagnostic underlined, to distinguish with selection text.
85# make diagnostic underlined, to distinguish with selection text.
81diagnostic = { modifiers = ["underlined"] }
86diagnostic = { modifiers = ["underlined"] }
82
87
runtime/themes/monokai_pro_machine.toml+5
@@ -77,6 +77,11 @@
77# integer, floating point
77# integer, floating point
78"constant.numeric" = "purple"
78"constant.numeric" = "purple"
79
79
80+# vcs
81+"diff.plus" = "green"
82+"diff.delta" = "orange"
83+"diff.minus" = "red"
84+
80# make diagnostic underlined, to distinguish with selection text.
85# make diagnostic underlined, to distinguish with selection text.
81diagnostic = { modifiers = ["underlined"] }
86diagnostic = { modifiers = ["underlined"] }
82
87
runtime/themes/monokai_pro_octagon.toml+5
@@ -77,6 +77,11 @@
77# integer, floating point
77# integer, floating point
78"constant.numeric" = "purple"
78"constant.numeric" = "purple"
79
79
80+# vcs
81+"diff.plus" = "green"
82+"diff.delta" = "orange"
83+"diff.minus" = "red"
84+
80# make diagnostic underlined, to distinguish with selection text.
85# make diagnostic underlined, to distinguish with selection text.
81diagnostic = { modifiers = ["underlined"] }
86diagnostic = { modifiers = ["underlined"] }
82
87
runtime/themes/monokai_pro_ristretto.toml+5
@@ -77,6 +77,11 @@
77# integer, floating point
77# integer, floating point
78"constant.numeric" = "purple"
78"constant.numeric" = "purple"
79
79
80+# vcs
81+"diff.plus" = "green"
82+"diff.delta" = "orange"
83+"diff.minus" = "red"
84+
80# make diagnostic underlined, to distinguish with selection text.
85# make diagnostic underlined, to distinguish with selection text.
81diagnostic = { modifiers = ["underlined"] }
86diagnostic = { modifiers = ["underlined"] }
82
87
runtime/themes/monokai_pro_spectrum.toml+5
@@ -77,6 +77,11 @@
77# integer, floating point
77# integer, floating point
78"constant.numeric" = "purple"
78"constant.numeric" = "purple"
79
79
80+# vcs
81+"diff.plus" = "green"
82+"diff.delta" = "orange"
83+"diff.minus" = "red"
84+
80# make diagnostic underlined, to distinguish with selection text.
85# make diagnostic underlined, to distinguish with selection text.
81diagnostic = { modifiers = ["underlined"] }
86diagnostic = { modifiers = ["underlined"] }
82
87
runtime/themes/nord.toml+5
@@ -84,6 +84,11 @@
84# nord15 - integer, floating point
84# nord15 - integer, floating point
85"constant.numeric" = "nord15"
85"constant.numeric" = "nord15"
86
86
87+# vcs
88+"diff.plus" = "nord14"
89+"diff.delta" = "nord12"
90+"diff.minus" = "nord11"
91+
87[palette]
92[palette]
88nord0 = "#2e3440"
93nord0 = "#2e3440"
89nord1 = "#3b4252"
94nord1 = "#3b4252"
runtime/themes/onedark.toml+19−4
@@ -3,27 +3,42 @@
3"attribute" = { fg = "yellow" }
3"attribute" = { fg = "yellow" }
4"comment" = { fg = "light-gray", modifiers = ["italic"] }
4"comment" = { fg = "light-gray", modifiers = ["italic"] }
5"constant" = { fg = "cyan" }
5"constant" = { fg = "cyan" }
6+"constant.numeric" = { fg = "gold" }
6−"constant.builtin" = { fg = "blue" }
7+"constant.builtin" = { fg = "gold" }
8+"constant.character.escape" = { fg = "gold" }
7"constructor" = { fg = "blue" }
9"constructor" = { fg = "blue" }
8−"escape" = { fg = "gold" }
9"function" = { fg = "blue" }
10"function" = { fg = "blue" }
10"function.builtin" = { fg = "blue" }
11"function.builtin" = { fg = "blue" }
11"function.macro" = { fg = "purple" }
12"function.macro" = { fg = "purple" }
12"keyword" = { fg = "red" }
13"keyword" = { fg = "red" }
13"keyword.control" = { fg = "purple" }
14"keyword.control" = { fg = "purple" }
15+"keyword.control.import" = { fg = "red" }
14"keyword.directive" = { fg = "purple" }
16"keyword.directive" = { fg = "purple" }
15"label" = { fg = "purple" }
17"label" = { fg = "purple" }
16"namespace" = { fg = "blue" }
18"namespace" = { fg = "blue" }
17−"number" = { fg = "gold" }
18"operator" = { fg = "purple" }
19"operator" = { fg = "purple" }
20+"keyword.operator" = { fg = "purple" }
19"property" = { fg = "red" }
21"property" = { fg = "red" }
20"special" = { fg = "blue" }
22"special" = { fg = "blue" }
21"string" = { fg = "green" }
23"string" = { fg = "green" }
22"type" = { fg = "yellow" }
24"type" = { fg = "yellow" }
23−"type.builtin" = { fg = "yellow" }
24# "variable" = { fg = "blue" }
25# "variable" = { fg = "blue" }
25"variable.builtin" = { fg = "blue" }
26"variable.builtin" = { fg = "blue" }
26"variable.parameter" = { fg = "red" }
27"variable.parameter" = { fg = "red" }
28+"variable.other.member" = { fg = "red" }
29+
30+"markup.heading" = { fg = "red" }
31+"markup.raw.inline" = { fg = "green" }
32+"markup.bold" = { fg = "gold", modifiers = ["bold"] }
33+"markup.italic" = { fg = "purple", modifiers = ["italic"] }
34+"markup.list" = { fg = "red" }
35+"markup.quote" = { fg = "yellow" }
36+"markup.link.url" = { fg = "cyan", modifiers = ["underlined"]}
37+"markup.link.label" = { fg = "purple" }
38+
39+"diff.plus" = "green"
40+"diff.delta" = "gold"
41+"diff.minus" = "red"
27
42
28diagnostic = { modifiers = ["underlined"] }
43diagnostic = { modifiers = ["underlined"] }
29"info" = { fg = "blue", modifiers = ["bold"] }
44"info" = { fg = "blue", modifiers = ["bold"] }
runtime/themes/rose_pine.toml+13
@@ -1,4 +1,5 @@
1# Author: RayGervais<raygervais@hotmail.ca>
1# Author: RayGervais<raygervais@hotmail.ca>
2+# Author: ChrisHa<chunghha@users.noreply.github.com>
2
3
3"ui.background" = { bg = "base" }
4"ui.background" = { bg = "base" }
4"ui.menu" = "surface"
5"ui.menu" = "surface"
@@ -37,6 +38,9 @@
37"ui.window" = { bg = "base" }
38"ui.window" = { bg = "base" }
38"ui.help" = { bg = "overlay", fg = "foam" }
39"ui.help" = { bg = "overlay", fg = "foam" }
39"text" = "text"
40"text" = "text"
41+"diff.plus" = "foam"
42+"diff.delta" = "rose"
43+"diff.minus" = "love"
40
44
41"info" = "gold"
45"info" = "gold"
42"hint" = "gold"
46"hint" = "gold"
@@ -44,6 +48,15 @@
44"diagnostic" = "rose"
48"diagnostic" = "rose"
45"error" = "love"
49"error" = "love"
46
50
51+"markup.heading" = { fg = "rose" }
52+"markup.raw.inline" = { fg = "foam" }
53+"markup.bold" = { fg = "gold", modifiers = ["bold"] }
54+"markup.italic" = { fg = "iris", modifiers = ["italic"] }
55+"markup.list" = { fg = "love" }
56+"markup.quote" = { fg = "rose" }
57+"markup.link.url" = { fg = "pine", modifiers = ["underlined"]}
58+"markup.link.label" = { fg = "foam" }
59+
47[palette]
60[palette]
48base = "#191724"
61base = "#191724"
49surface = "#1f1d2e"
62surface = "#1f1d2e"
runtime/themes/rose_pine_dawn.toml+15−3
@@ -1,8 +1,8 @@
1−# Author: ChrisHa<chunghha@users.noreply.github.com>
2# Author: RayGervais<raygervais@hotmail.ca>
1# Author: RayGervais<raygervais@hotmail.ca>
2+# Author: ChrisHa<chunghha@users.noreply.github.com>
3
3
4−"ui.background" = { bg = "base" }
4+"ui.background" = { bg = "surface" }
5−"ui.menu" = "surface"
5+"ui.menu" = "base"
6"ui.menu.selected" = { fg = "iris", bg = "surface" }
6"ui.menu.selected" = { fg = "iris", bg = "surface" }
7"ui.linenr" = {fg = "subtle" }
7"ui.linenr" = {fg = "subtle" }
8"ui.popup" = { bg = "overlay" }
8"ui.popup" = { bg = "overlay" }
@@ -38,6 +38,9 @@
38"ui.window" = { bg = "base" }
38"ui.window" = { bg = "base" }
39"ui.help" = { bg = "overlay", fg = "foam" }
39"ui.help" = { bg = "overlay", fg = "foam" }
40"text" = "text"
40"text" = "text"
41+"diff.plus" = "foam"
42+"diff.delta" = "rose"
43+"diff.minus" = "love"
41
44
42"info" = "gold"
45"info" = "gold"
43"hint" = "gold"
46"hint" = "gold"
@@ -45,6 +48,15 @@
45"diagnostic" = "rose"
48"diagnostic" = "rose"
46"error" = "love"
49"error" = "love"
47
50
51+"markup.heading" = { fg = "rose" }
52+"markup.raw.inline" = { fg = "foam" }
53+"markup.bold" = { fg = "gold", modifiers = ["bold"] }
54+"markup.italic" = { fg = "iris", modifiers = ["italic"] }
55+"markup.list" = { fg = "love" }
56+"markup.quote" = { fg = "rose" }
57+"markup.link.url" = { fg = "pine", modifiers = ["underlined"]}
58+"markup.link.label" = { fg = "foam" }
59+
48[palette]
60[palette]
49base = "#faf4ed"
61base = "#faf4ed"
50surface = "#fffaf3"
62surface = "#fffaf3"
runtime/themes/solarized_dark.toml+4
@@ -22,6 +22,10 @@
22"module" = { fg = "violet" }
22"module" = { fg = "violet" }
23"tag" = { fg = "magenta" }
23"tag" = { fg = "magenta" }
24
24
25+"diff.plus" = { fg = "green" }
26+"diff.delta" = { fg = "orange" }
27+"diff.minus" = { fg = "red" }
28+
25# 背景
29# 背景
26"ui.background" = { bg = "base03" }
30"ui.background" = { bg = "base03" }
27
31
runtime/themes/solarized_light.toml+4
@@ -22,6 +22,10 @@
22"module" = { fg = "violet" }
22"module" = { fg = "violet" }
23"tag" = { fg = "magenta" }
23"tag" = { fg = "magenta" }
24
24
25+"diff.plus" = { fg = "green" }
26+"diff.delta" = { fg = "orange" }
27+"diff.minus" = { fg = "red" }
28+
25# 背景
29# 背景
26"ui.background" = { bg = "base03" }
30"ui.background" = { bg = "base03" }
27
31
runtime/themes/spacebones_light.toml+4
@@ -30,6 +30,10 @@
30"label" = "#b1951d"
30"label" = "#b1951d"
31"module" = "#b1951d"
31"module" = "#b1951d"
32
32
33+"diff.plus" = "#2d9574"
34+"diff.delta" = "#715ab1"
35+"diff.minus" = "#ba2f59"
36+
33"warning" = { fg = "#da8b55" }
37"warning" = { fg = "#da8b55" }
34"error" = { fg = "#e0211d" }
38"error" = { fg = "#e0211d" }
35"info" = { fg = "#b1951d" }
39"info" = { fg = "#b1951d" }
theme.toml+5−1
@@ -31,9 +31,13 @@ label = "honey"
31"markup.heading" = "lilac"
31"markup.heading" = "lilac"
32"markup.bold" = { modifiers = ["bold"] }
32"markup.bold" = { modifiers = ["bold"] }
33"markup.italic" = { modifiers = ["italic"] }
33"markup.italic" = { modifiers = ["italic"] }
34−"markup.underline.link" = { fg = "silver", modifiers = ["underlined"] }
34+"markup.link.url" = { fg = "silver", modifiers = ["underlined"] }
35"markup.raw" = "almond"
35"markup.raw" = "almond"
36
36
37+"diff.plus" = "#35bf86"
38+"diff.minus" = "#f22c86"
39+"diff.delta" = "#6f44f0"
40+
37# TODO: diferentiate doc comment
41# TODO: diferentiate doc comment
38# concat (ERROR) @error.syntax and "MISSING ;" selectors for errors
42# concat (ERROR) @error.syntax and "MISSING ;" selectors for errors
39
43
xtask/Cargo.toml+3−3
@@ -1,11 +1,11 @@
1[package]
1[package]
2name = "xtask"
2name = "xtask"
3−version = "0.5.0"
3+version = "0.6.0"
4edition = "2021"
4edition = "2021"
5
5
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
7
8[dependencies]
8[dependencies]
9−helix-term = { version = "0.5", path = "../helix-term" }
9+helix-term = { version = "0.6", path = "../helix-term" }
10−helix-core = { version = "0.5", path = "../helix-core" }
10+helix-core = { version = "0.6", path = "../helix-core" }
11toml = "0.5"
11toml = "0.5"