on master help sign in
Auto generate docs for language support
a78b789Gokul Soumya
Cargo.lock+2
@@ -1264,5 +1264,7 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
1264name = "xtask"
1264name = "xtask"
1265version = "0.5.0"
1265version = "0.5.0"
1266dependencies = [
1266dependencies = [
1267+ "helix-core",
1267 "helix-term",
1268 "helix-term",
1269+ "toml",
1268]
1270]
book/src/SUMMARY.md+1
@@ -4,6 +4,7 @@
4- [Usage](./usage.md)
4- [Usage](./usage.md)
5 - [Keymap](./keymap.md)
5 - [Keymap](./keymap.md)
6 - [Commands](./commands.md)
6 - [Commands](./commands.md)
7+ - [Language Support](./lang-support.md)
7- [Migrating from Vim](./from-vim.md)
8- [Migrating from Vim](./from-vim.md)
8- [Configuration](./configuration.md)
9- [Configuration](./configuration.md)
9 - [Themes](./themes.md)
10 - [Themes](./themes.md)
book/src/generated/lang-support.mdnew file+41
@@ -0,0 +1,41 @@
1+| Language | Syntax Highlighting | Treesitter Textobjects | Auto Indent | Default LSP |
2+| --- | --- | --- | --- | --- |
3+| Bash | ✓ | | | `bash-language-server` |
4+| C | ✓ | | | `clangd` |
5+| C# | ✓ | | | |
6+| CMake | ✓ | | | `cmake-language-server` |
7+| C++ | ✓ | | | `clangd` |
8+| CSS | ✓ | | | |
9+| Elixir | ✓ | | | `elixir-ls` |
10+| GLSL | ✓ | | ✓ | |
11+| Go | ✓ | ✓ | ✓ | `gopls` |
12+| HTML | ✓ | | | |
13+| Java | ✓ | | | |
14+| JavaScript | ✓ | | ✓ | |
15+| JSON | ✓ | | ✓ | |
16+| Julia | ✓ | | | `julia` |
17+| LaTeX | ✓ | | | |
18+| Ledger | ✓ | | | |
19+| LLVM | ✓ | | | |
20+| Lua | ✓ | | ✓ | |
21+| Mint | | | | `mint` |
22+| Nix | ✓ | | ✓ | `rnix-lsp` |
23+| OCaml | ✓ | | ✓ | |
24+| OCaml-Interface | ✓ | | | |
25+| Perl | ✓ | ✓ | | |
26+| PHP | ✓ | | ✓ | |
27+| Prolog | | | | `swipl` |
28+| Protobuf | ✓ | | ✓ | |
29+| Python | ✓ | ✓ | ✓ | `pylsp` |
30+| Racket | | | | `racket` |
31+| Ruby | ✓ | | | `solargraph` |
32+| Rust | ✓ | ✓ | ✓ | `rust-analyzer` |
33+| Svelte | ✓ | | ✓ | `svelteserver` |
34+| TOML | ✓ | | | |
35+| TSQ | ✓ | | | |
36+| TSX | ✓ | | | `typescript-language-server` |
37+| TypeScript | ✓ | | ✓ | `typescript-language-server` |
38+| Vue | ✓ | | | |
39+| WGSL | ✓ | | | |
40+| YAML | ✓ | | ✓ | |
41+| Zig | ✓ | | ✓ | `zls` |
book/src/generated/typable-cmd.md+1−1
@@ -1,5 +1,5 @@
1| Name | Description |
1| Name | Description |
2−| --- | --- |
2+| --- | --- |
3| `:quit`, `:q` | Close the current view. |
3| `:quit`, `:q` | Close the current view. |
4| `:quit!`, `:q!` | Close the current view forcefully (ignoring unsaved changes). |
4| `:quit!`, `:q!` | Close the current view forcefully (ignoring unsaved changes). |
5| `:open`, `:o` | Open a file from disk into the current view. |
5| `:open`, `:o` | Open a file from disk into the current view. |
book/src/lang-support.mdnew file+10
@@ -0,0 +1,10 @@
1+# Language Support
2+
3+For more information like arguments passed to default LSP server,
4+extensions assosciated with a filetype, custom LSP settings, filetype
5+specific indent settings, etc see the default
6+[`languages.toml`][languages.toml] file.
7+
8+{{#include ./generated/lang-support.md}}
9+
10+[languages.toml]: https://github.com/helix-editor/helix/blob/master/languages.toml
docs/CONTRIBUTING.md+1−1
@@ -23,7 +23,7 @@ like the list of `:commands` and supported languages. To generate these
23files, run
23files, run
24
24
25```shell
25```shell
26−cargo xtask bookgen
26+cargo xtask docgen
27```
27```
28
28
29inside the project. We use [xtask][xtask] as an ad-hoc task runner and
29inside the project. We use [xtask][xtask] as an ad-hoc task runner and
helix-core/src/indent.rs+1
@@ -452,6 +452,7 @@ where
452 file_types: vec!["rs".to_string()],
452 file_types: vec!["rs".to_string()],
453 shebangs: vec![],
453 shebangs: vec![],
454 language_id: "Rust".to_string(),
454 language_id: "Rust".to_string(),
455+ display_name: "Rust".to_string(),
455 highlight_config: OnceCell::new(),
456 highlight_config: OnceCell::new(),
456 config: None,
457 config: None,
457 //
458 //
helix-core/src/syntax.rs+2−1
@@ -50,7 +50,8 @@ pub struct Configuration {
50#[serde(rename_all = "kebab-case", deny_unknown_fields)]
50#[serde(rename_all = "kebab-case", deny_unknown_fields)]
51pub struct LanguageConfiguration {
51pub struct LanguageConfiguration {
52 #[serde(rename = "name")]
52 #[serde(rename = "name")]
53− pub language_id: String,
53+ pub language_id: String, // c-sharp, rust
54+ pub display_name: String, // C#, Rust
54 pub scope: String, // source.rust
55 pub scope: String, // source.rust
55 pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
56 pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
56 #[serde(default)]
57 #[serde(default)]
languages.toml+40
@@ -1,5 +1,6 @@
1[[language]]
1[[language]]
2name = "rust"
2name = "rust"
3+display-name = "Rust"
3scope = "source.rust"
4scope = "source.rust"
4injection-regex = "rust"
5injection-regex = "rust"
5file-types = ["rs"]
6file-types = ["rs"]
@@ -14,6 +15,7 @@ procMacro = { enable = false }
14
15
15[[language]]
16[[language]]
16name = "toml"
17name = "toml"
18+display-name = "TOML"
17scope = "source.toml"
19scope = "source.toml"
18injection-regex = "toml"
20injection-regex = "toml"
19file-types = ["toml"]
21file-types = ["toml"]
@@ -24,6 +26,7 @@ indent = { tab-width = 2, unit = " " }
24
26
25[[language]]
27[[language]]
26name = "protobuf"
28name = "protobuf"
29+display-name = "Protobuf"
27scope = "source.proto"
30scope = "source.proto"
28injection-regex = "protobuf"
31injection-regex = "protobuf"
29file-types = ["proto"]
32file-types = ["proto"]
@@ -34,6 +37,7 @@ indent = { tab-width = 2, unit = " " }
34
37
35[[language]]
38[[language]]
36name = "elixir"
39name = "elixir"
40+display-name = "Elixir"
37scope = "source.elixir"
41scope = "source.elixir"
38injection-regex = "elixir"
42injection-regex = "elixir"
39file-types = ["ex", "exs"]
43file-types = ["ex", "exs"]
@@ -46,6 +50,7 @@ indent = { tab-width = 2, unit = " " }
46
50
47[[language]]
51[[language]]
48name = "mint"
52name = "mint"
53+display-name = "Mint"
49scope = "source.mint"
54scope = "source.mint"
50injection-regex = "mint"
55injection-regex = "mint"
51file-types = ["mint"]
56file-types = ["mint"]
@@ -58,6 +63,7 @@ indent = { tab-width = 2, unit = " " }
58
63
59[[language]]
64[[language]]
60name = "json"
65name = "json"
66+display-name = "JSON"
61scope = "source.json"
67scope = "source.json"
62injection-regex = "json"
68injection-regex = "json"
63file-types = ["json"]
69file-types = ["json"]
@@ -67,6 +73,7 @@ indent = { tab-width = 2, unit = " " }
67
73
68[[language]]
74[[language]]
69name = "c"
75name = "c"
76+display-name = "C"
70scope = "source.c"
77scope = "source.c"
71injection-regex = "c"
78injection-regex = "c"
72file-types = ["c"] # TODO: ["h"]
79file-types = ["c"] # TODO: ["h"]
@@ -78,6 +85,7 @@ indent = { tab-width = 2, unit = " " }
78
85
79[[language]]
86[[language]]
80name = "cpp"
87name = "cpp"
88+display-name = "C++"
81scope = "source.cpp"
89scope = "source.cpp"
82injection-regex = "cpp"
90injection-regex = "cpp"
83file-types = ["cc", "hh", "cpp", "hpp", "h", "ipp", "tpp", "cxx", "hxx", "ixx", "txx", "ino"]
91file-types = ["cc", "hh", "cpp", "hpp", "h", "ipp", "tpp", "cxx", "hxx", "ixx", "txx", "ino"]
@@ -89,6 +97,7 @@ indent = { tab-width = 2, unit = " " }
89
97
90[[language]]
98[[language]]
91name = "c-sharp"
99name = "c-sharp"
100+display-name = "C#"
92scope = "source.csharp"
101scope = "source.csharp"
93injection-regex = "c-?sharp"
102injection-regex = "c-?sharp"
94file-types = ["cs"]
103file-types = ["cs"]
@@ -99,6 +108,7 @@ indent = { tab-width = 4, unit = "\t" }
99
108
100[[language]]
109[[language]]
101name = "go"
110name = "go"
111+display-name = "Go"
102scope = "source.go"
112scope = "source.go"
103injection-regex = "go"
113injection-regex = "go"
104file-types = ["go"]
114file-types = ["go"]
@@ -112,6 +122,7 @@ indent = { tab-width = 4, unit = "\t" }
112
122
113[[language]]
123[[language]]
114name = "javascript"
124name = "javascript"
125+display-name = "JavaScript"
115scope = "source.js"
126scope = "source.js"
116injection-regex = "^(js|javascript)$"
127injection-regex = "^(js|javascript)$"
117file-types = ["js", "mjs"]
128file-types = ["js", "mjs"]
@@ -124,6 +135,7 @@ indent = { tab-width = 2, unit = " " }
124
135
125[[language]]
136[[language]]
126name = "typescript"
137name = "typescript"
138+display-name = "TypeScript"
127scope = "source.ts"
139scope = "source.ts"
128injection-regex = "^(ts|typescript)$"
140injection-regex = "^(ts|typescript)$"
129file-types = ["ts"]
141file-types = ["ts"]
@@ -136,6 +148,7 @@ indent = { tab-width = 2, unit = " " }
136
148
137[[language]]
149[[language]]
138name = "tsx"
150name = "tsx"
151+display-name = "TSX"
139scope = "source.tsx"
152scope = "source.tsx"
140injection-regex = "^(tsx)$" # |typescript
153injection-regex = "^(tsx)$" # |typescript
141file-types = ["tsx"]
154file-types = ["tsx"]
@@ -147,6 +160,7 @@ indent = { tab-width = 2, unit = " " }
147
160
148[[language]]
161[[language]]
149name = "css"
162name = "css"
163+display-name = "CSS"
150scope = "source.css"
164scope = "source.css"
151injection-regex = "css"
165injection-regex = "css"
152file-types = ["css"]
166file-types = ["css"]
@@ -156,6 +170,7 @@ indent = { tab-width = 2, unit = " " }
156
170
157[[language]]
171[[language]]
158name = "html"
172name = "html"
173+display-name = "HTML"
159scope = "text.html.basic"
174scope = "text.html.basic"
160injection-regex = "html"
175injection-regex = "html"
161file-types = ["html"]
176file-types = ["html"]
@@ -165,6 +180,7 @@ indent = { tab-width = 2, unit = " " }
165
180
166[[language]]
181[[language]]
167name = "python"
182name = "python"
183+display-name = "Python"
168scope = "source.python"
184scope = "source.python"
169injection-regex = "python"
185injection-regex = "python"
170file-types = ["py"]
186file-types = ["py"]
@@ -178,6 +194,7 @@ indent = { tab-width = 4, unit = " " }
178
194
179[[language]]
195[[language]]
180name = "nix"
196name = "nix"
197+display-name = "Nix"
181scope = "source.nix"
198scope = "source.nix"
182injection-regex = "nix"
199injection-regex = "nix"
183file-types = ["nix"]
200file-types = ["nix"]
@@ -190,6 +207,7 @@ indent = { tab-width = 2, unit = " " }
190
207
191[[language]]
208[[language]]
192name = "ruby"
209name = "ruby"
210+display-name = "Ruby"
193scope = "source.ruby"
211scope = "source.ruby"
194injection-regex = "ruby"
212injection-regex = "ruby"
195file-types = ["rb"]
213file-types = ["rb"]
@@ -202,6 +220,7 @@ indent = { tab-width = 2, unit = " " }
202
220
203[[language]]
221[[language]]
204name = "bash"
222name = "bash"
223+display-name = "Bash"
205scope = "source.bash"
224scope = "source.bash"
206injection-regex = "bash"
225injection-regex = "bash"
207file-types = ["sh", "bash"]
226file-types = ["sh", "bash"]
@@ -214,6 +233,7 @@ indent = { tab-width = 2, unit = " " }
214
233
215[[language]]
234[[language]]
216name = "php"
235name = "php"
236+display-name = "PHP"
217scope = "source.php"
237scope = "source.php"
218injection-regex = "php"
238injection-regex = "php"
219file-types = ["php"]
239file-types = ["php"]
@@ -224,6 +244,7 @@ indent = { tab-width = 4, unit = " " }
224
244
225[[language]]
245[[language]]
226name = "latex"
246name = "latex"
247+display-name = "LaTeX"
227scope = "source.tex"
248scope = "source.tex"
228injection-regex = "tex"
249injection-regex = "tex"
229file-types = ["tex"]
250file-types = ["tex"]
@@ -234,6 +255,7 @@ indent = { tab-width = 4, unit = "\t" }
234
255
235[[language]]
256[[language]]
236name = "julia"
257name = "julia"
258+display-name = "Julia"
237scope = "source.julia"
259scope = "source.julia"
238injection-regex = "julia"
260injection-regex = "julia"
239file-types = ["jl"]
261file-types = ["jl"]
@@ -259,6 +281,7 @@ indent = { tab-width = 2, unit = " " }
259
281
260[[language]]
282[[language]]
261name = "java"
283name = "java"
284+display-name = "Java"
262scope = "source.java"
285scope = "source.java"
263injection-regex = "java"
286injection-regex = "java"
264file-types = ["java"]
287file-types = ["java"]
@@ -267,6 +290,7 @@ indent = { tab-width = 4, unit = " " }
267
290
268[[language]]
291[[language]]
269name = "ledger"
292name = "ledger"
293+display-name = "Ledger"
270scope = "source.ledger"
294scope = "source.ledger"
271injection-regex = "ledger"
295injection-regex = "ledger"
272file-types = ["ldg", "ledger", "journal"]
296file-types = ["ldg", "ledger", "journal"]
@@ -276,6 +300,7 @@ indent = { tab-width = 4, unit = " " }
276
300
277[[language]]
301[[language]]
278name = "ocaml"
302name = "ocaml"
303+display-name = "OCaml"
279scope = "source.ocaml"
304scope = "source.ocaml"
280injection-regex = "ocaml"
305injection-regex = "ocaml"
281file-types = ["ml"]
306file-types = ["ml"]
@@ -286,6 +311,7 @@ indent = { tab-width = 2, unit = " " }
286
311
287[[language]]
312[[language]]
288name = "ocaml-interface"
313name = "ocaml-interface"
314+display-name = "OCaml-Interface"
289scope = "source.ocaml.interface"
315scope = "source.ocaml.interface"
290file-types = ["mli"]
316file-types = ["mli"]
291shebangs = []
317shebangs = []
@@ -295,6 +321,7 @@ indent = { tab-width = 2, unit = " "}
295
321
296[[language]]
322[[language]]
297name = "lua"
323name = "lua"
324+display-name = "Lua"
298scope = "source.lua"
325scope = "source.lua"
299file-types = ["lua"]
326file-types = ["lua"]
300shebangs = ["lua"]
327shebangs = ["lua"]
@@ -304,6 +331,7 @@ indent = { tab-width = 2, unit = " " }
304
331
305[[language]]
332[[language]]
306name = "svelte"
333name = "svelte"
334+display-name = "Svelte"
307scope = "source.svelte"
335scope = "source.svelte"
308injection-regex = "svelte"
336injection-regex = "svelte"
309file-types = ["svelte"]
337file-types = ["svelte"]
@@ -314,6 +342,7 @@ language-server = { command = "svelteserver", args = ["--stdio"] }
314
342
315[[language]]
343[[language]]
316name = "vue"
344name = "vue"
345+display-name = "Vue"
317scope = "source.vue"
346scope = "source.vue"
318injection-regex = "vue"
347injection-regex = "vue"
319file-types = ["vue"]
348file-types = ["vue"]
@@ -322,6 +351,7 @@ indent = { tab-width = 2, unit = " " }
322
351
323[[language]]
352[[language]]
324name = "yaml"
353name = "yaml"
354+display-name = "YAML"
325scope = "source.yaml"
355scope = "source.yaml"
326file-types = ["yml", "yaml"]
356file-types = ["yml", "yaml"]
327roots = []
357roots = []
@@ -330,6 +360,7 @@ indent = { tab-width = 2, unit = " " }
330
360
331# [[language]]
361# [[language]]
332# name = "haskell"
362# name = "haskell"
363+# display-name = "Haskell"
333# scope = "source.haskell"
364# scope = "source.haskell"
334# injection-regex = "haskell"
365# injection-regex = "haskell"
335# file-types = ["hs"]
366# file-types = ["hs"]
@@ -340,6 +371,7 @@ indent = { tab-width = 2, unit = " " }
340
371
341[[language]]
372[[language]]
342name = "zig"
373name = "zig"
374+display-name = "Zig"
343scope = "source.zig"
375scope = "source.zig"
344injection-regex = "zig"
376injection-regex = "zig"
345file-types = ["zig"]
377file-types = ["zig"]
@@ -352,6 +384,7 @@ indent = { tab-width = 4, unit = " " }
352
384
353[[language]]
385[[language]]
354name = "prolog"
386name = "prolog"
387+display-name = "Prolog"
355scope = "source.prolog"
388scope = "source.prolog"
356roots = []
389roots = []
357file-types = ["pl", "prolog"]
390file-types = ["pl", "prolog"]
@@ -365,6 +398,7 @@ language-server = { command = "swipl", args = [
365
398
366[[language]]
399[[language]]
367name = "tsq"
400name = "tsq"
401+display-name = "TSQ"
368scope = "source.tsq"
402scope = "source.tsq"
369file-types = ["scm"]
403file-types = ["scm"]
370roots = []
404roots = []
@@ -373,6 +407,7 @@ indent = { tab-width = 2, unit = " " }
373
407
374[[language]]
408[[language]]
375name = "cmake"
409name = "cmake"
410+display-name = "CMake"
376scope = "source.cmake"
411scope = "source.cmake"
377file-types = ["cmake", "CMakeLists.txt"]
412file-types = ["cmake", "CMakeLists.txt"]
378roots = []
413roots = []
@@ -382,6 +417,7 @@ language-server = { command = "cmake-language-server" }
382
417
383[[language]]
418[[language]]
384name = "glsl"
419name = "glsl"
420+display-name = "GLSL"
385scope = "source.glsl"
421scope = "source.glsl"
386file-types = ["glsl", "vert", "tesc", "tese", "geom", "frag", "comp" ]
422file-types = ["glsl", "vert", "tesc", "tese", "geom", "frag", "comp" ]
387roots = []
423roots = []
@@ -390,6 +426,7 @@ indent = { tab-width = 4, unit = " " }
390
426
391[[language]]
427[[language]]
392name = "perl"
428name = "perl"
429+display-name = "Perl"
393scope = "source.perl"
430scope = "source.perl"
394file-types = ["pl", "pm"]
431file-types = ["pl", "pm"]
395shebangs = ["perl"]
432shebangs = ["perl"]
@@ -399,6 +436,7 @@ indent = { tab-width = 2, unit = " " }
399
436
400[[language]]
437[[language]]
401name = "racket"
438name = "racket"
439+display-name = "Racket"
402scope = "source.rkt"
440scope = "source.rkt"
403roots = []
441roots = []
404file-types = ["rkt"]
442file-types = ["rkt"]
@@ -408,6 +446,7 @@ language-server = { command = "racket", args = ["-l", "racket-langserver"] }
408
446
409[[language]]
447[[language]]
410name = "wgsl"
448name = "wgsl"
449+display-name = "WGSL"
411scope = "source.wgsl"
450scope = "source.wgsl"
412file-types = ["wgsl"]
451file-types = ["wgsl"]
413roots = []
452roots = []
@@ -416,6 +455,7 @@ indent = { tab-width = 4, unit = " " }
416
455
417[[language]]
456[[language]]
418name = "llvm"
457name = "llvm"
458+display-name = "LLVM"
419scope = "source.llvm"
459scope = "source.llvm"
420roots = []
460roots = []
421file-types = ["ll"]
461file-types = ["ll"]
xtask/Cargo.toml+2
@@ -7,3 +7,5 @@ edition = "2021"
7
7
8[dependencies]
8[dependencies]
9helix-term = { version = "0.5", path = "../helix-term" }
9helix-term = { version = "0.5", path = "../helix-term" }
10+helix-core = { version = "0.5", path = "../helix-core" }
11+toml = "0.5"
xtask/src/main.rs+210−19
@@ -1,17 +1,138 @@
1−use std::env;
1+use std::{env, error::Error};
2+
3+type DynError = Box<dyn Error>;
4+
5+pub mod helpers {
6+ use std::{
7+ fmt::Display,
8+ path::{Path, PathBuf},
9+ };
10+
11+ use crate::path;
12+ use helix_core::syntax::Configuration as LangConfig;
13+
14+ #[derive(Copy, Clone)]
15+ pub enum TsFeature {
16+ Highlight,
17+ TextObjects,
18+ AutoIndent,
19+ }
20+
21+ impl TsFeature {
22+ pub fn all() -> &'static [Self] {
23+ &[Self::Highlight, Self::TextObjects, Self::AutoIndent]
24+ }
25+
26+ pub fn runtime_filename(&self) -> &'static str {
27+ match *self {
28+ Self::Highlight => "highlights.scm",
29+ Self::TextObjects => "textobjects.scm",
30+ Self::AutoIndent => "indents.toml",
31+ }
32+ }
33+ }
34+
35+ impl Display for TsFeature {
36+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37+ write!(
38+ f,
39+ "{}",
40+ match *self {
41+ Self::Highlight => "Syntax Highlighting",
42+ Self::TextObjects => "Treesitter Textobjects",
43+ Self::AutoIndent => "Auto Indent",
44+ }
45+ )
46+ }
47+ }
48+
49+ /// Get the list of languages that support a particular tree-sitter
50+ /// based feature.
51+ pub fn ts_lang_support(feat: TsFeature) -> Vec<String> {
52+ let queries_dir = path::ts_queries();
53+
54+ find_files(&queries_dir, feat.runtime_filename())
55+ .iter()
56+ .map(|f| {
57+ // .../helix/runtime/queries/python/highlights.scm
58+ let tail = f.strip_prefix(&queries_dir).unwrap(); // python/highlights.scm
59+ let lang = tail.components().next().unwrap(); // python
60+ lang.as_os_str().to_string_lossy().to_string()
61+ })
62+ .collect()
63+ }
64+
65+ /// Get the list of languages that have any form of tree-sitter
66+ /// queries defined in the runtime directory.
67+ pub fn langs_with_ts_queries() -> Vec<String> {
68+ std::fs::read_dir(path::ts_queries())
69+ .unwrap()
70+ .filter_map(|entry| {
71+ let entry = entry.ok()?;
72+ entry
73+ .file_type()
74+ .ok()?
75+ .is_dir()
76+ .then(|| entry.file_name().to_string_lossy().to_string())
77+ })
78+ .collect()
79+ }
80+
81+ // naive implementation, but suffices for our needs
82+ pub fn find_files(dir: &Path, filename: &str) -> Vec<PathBuf> {
83+ std::fs::read_dir(dir)
84+ .unwrap()
85+ .filter_map(|entry| {
86+ let path = entry.ok()?.path();
87+ if path.is_dir() {
88+ Some(find_files(&path, filename))
89+ } else {
90+ (path.file_name()?.to_string_lossy() == filename).then(|| vec![path])
91+ }
92+ })
93+ .flatten()
94+ .collect()
95+ }
96+
97+ pub fn lang_config() -> LangConfig {
98+ let bytes = std::fs::read(path::lang_config()).unwrap();
99+ toml::from_slice(&bytes).unwrap()
100+ }
101+}
2
102
3pub mod md_gen {
103pub mod md_gen {
4− use super::path;
104+ use crate::DynError;
105+
106+ use crate::helpers;
107+ use crate::path;
5 use std::fs;
108 use std::fs;
6
109
7 use helix_term::commands::cmd::TYPABLE_COMMAND_LIST;
110 use helix_term::commands::cmd::TYPABLE_COMMAND_LIST;
8
111
9 pub const TYPABLE_COMMANDS_MD_OUTPUT: &str = "typable-cmd.md";
112 pub const TYPABLE_COMMANDS_MD_OUTPUT: &str = "typable-cmd.md";
113+ pub const LANG_SUPPORT_MD_OUTPUT: &str = "lang-support.md";
114+
115+ fn md_table_heading(cols: &[String]) -> String {
116+ let mut header = String::new();
117+ header += &md_table_row(cols);
118+ header += &md_table_row(&vec!["---".to_string(); cols.len()]);
119+ header
120+ }
121+
122+ fn md_table_row(cols: &[String]) -> String {
123+ "| ".to_owned() + &cols.join(" | ") + " |\n"
124+ }
125+
126+ fn md_mono(s: &str) -> String {
127+ format!("`{}`", s)
128+ }
10
129
11− pub fn typable_commands() -> String {
130+ pub fn typable_commands() -> Result<String, DynError> {
12 let mut md = String::new();
131 let mut md = String::new();
132+ md.push_str(&md_table_heading(&[
133+ "Name".to_owned(),
13− md.push_str("| Name | Description |\n");
134+ "Description".to_owned(),
14− md.push_str("| --- | --- |\n");
135+ ]));
15
136
16 let cmdify = |s: &str| format!("`:{}`", s);
137 let cmdify = |s: &str| format!("`:{}`", s);
17
138
@@ -22,11 +143,72 @@ pub mod md_gen {
22 .collect::<Vec<_>>()
143 .collect::<Vec<_>>()
23 .join(", ");
144 .join(", ");
24
145
25− let entry = format!("| {} | {} |\n", names, cmd.doc);
146+ md.push_str(&md_table_row(&[names.to_owned(), cmd.doc.to_owned()]));
147+ }
148+
149+ Ok(md)
150+ }
151+
152+ pub fn lang_features() -> Result<String, DynError> {
153+ let mut md = String::new();
154+ let ts_features = helpers::TsFeature::all();
155+
156+ let mut cols = vec!["Language".to_owned()];
157+ cols.append(
158+ &mut ts_features
159+ .iter()
160+ .map(|t| t.to_string())
161+ .collect::<Vec<_>>(),
162+ );
163+ cols.push("Default LSP".to_owned());
164+
26− md.push_str(&entry);
165+ md.push_str(&md_table_heading(&cols));
166+ let config = helpers::lang_config();
167+
168+ let mut langs = config
169+ .language
170+ .iter()
171+ .map(|l| l.language_id.clone())
172+ .collect::<Vec<_>>();
173+ langs.sort_unstable();
174+
175+ let mut ts_features_to_langs = Vec::new();
176+ for &feat in ts_features {
177+ ts_features_to_langs.push((feat, helpers::ts_lang_support(feat)));
27 }
178 }
28
179
29− md
180+ let mut row = Vec::new();
181+ for lang in langs {
182+ let lc = config
183+ .language
184+ .iter()
185+ .find(|l| l.language_id == lang)
186+ .unwrap(); // lang comes from config
187+ row.push(lc.display_name.clone());
188+
189+ for (_feat, support_list) in &ts_features_to_langs {
190+ row.push(
191+ if support_list.contains(&lang) {
192+ "✓"
193+ } else {
194+ ""
195+ }
196+ .to_owned(),
197+ );
198+ }
199+ row.push(
200+ lc.language_server
201+ .as_ref()
202+ .map(|s| s.command.clone())
203+ .map(|c| md_mono(&c))
204+ .unwrap_or_default(),
205+ );
206+
207+ md.push_str(&md_table_row(&row));
208+ row.clear();
209+ }
210+
211+ Ok(md)
30 }
212 }
31
213
32 pub fn write(filename: &str, data: &str) {
214 pub fn write(filename: &str, data: &str) {
@@ -49,37 +231,46 @@ pub mod path {
49 pub fn book_gen() -> PathBuf {
231 pub fn book_gen() -> PathBuf {
50 project_root().join("book/src/generated/")
232 project_root().join("book/src/generated/")
51 }
233 }
234+
235+ pub fn ts_queries() -> PathBuf {
236+ project_root().join("runtime/queries")
237+ }
238+
239+ pub fn lang_config() -> PathBuf {
240+ project_root().join("languages.toml")
241+ }
52}
242}
53
243
54pub mod tasks {
244pub mod tasks {
55− use super::md_gen;
245+ use crate::md_gen;
246+ use crate::DynError;
56
247
57− pub fn bookgen() {
248+ pub fn docgen() -> Result<(), DynError> {
58− md_gen::write(
249+ use md_gen::*;
59− md_gen::TYPABLE_COMMANDS_MD_OUTPUT,
250+ write(TYPABLE_COMMANDS_MD_OUTPUT, &typable_commands()?);
60− &md_gen::typable_commands(),
251+ write(LANG_SUPPORT_MD_OUTPUT, &lang_features()?);
61− );
252+ Ok(())
62 }
253 }
63
254
64 pub fn print_help() {
255 pub fn print_help() {
65 println!(
256 println!(
66 "
257 "
67−Usage: Run with `cargo xtask <task>`, eg. `cargo xtask bookgen`.
258+Usage: Run with `cargo xtask <task>`, eg. `cargo xtask docgen`.
68
259
69 Tasks:
260 Tasks:
70− bookgen: Generate files to be included in the mdbook output.
261+ docgen: Generate files to be included in the mdbook output.
71"
262"
72 );
263 );
73 }
264 }
74}
265}
75
266
76−fn main() -> Result<(), String> {
267+fn main() -> Result<(), DynError> {
77 let task = env::args().nth(1);
268 let task = env::args().nth(1);
78 match task {
269 match task {
79 None => tasks::print_help(),
270 None => tasks::print_help(),
80 Some(t) => match t.as_str() {
271 Some(t) => match t.as_str() {
81− "bookgen" => tasks::bookgen(),
272+ "docgen" => tasks::docgen()?,
82− invalid => return Err(format!("Invalid task name: {}", invalid)),
273+ invalid => return Err(format!("Invalid task name: {}", invalid).into()),
83 },
274 },
84 };
275 };
85 Ok(())
276 Ok(())