on master help sign in
Add the machinery to load syntax config from TOML.
e3c4edaBlaž Hrastnik
Cargo.lock+12
@@ -489,6 +489,7 @@ dependencies = [
489 "once_cell",
489 "once_cell",
490 "regex",
490 "regex",
491 "ropey",
491 "ropey",
492+ "serde",
492 "smallvec",
493 "smallvec",
493 "tendril",
494 "tendril",
494 "tree-sitter",
495 "tree-sitter",
@@ -523,6 +524,7 @@ name = "helix-syntax"
523version = "0.1.0"
524version = "0.1.0"
524dependencies = [
525dependencies = [
525 "cc",
526 "cc",
527+ "serde",
526 "threadpool",
528 "threadpool",
527 "tree-sitter",
529 "tree-sitter",
528]
530]
@@ -549,6 +551,7 @@ dependencies = [
549 "pulldown-cmark",
551 "pulldown-cmark",
550 "smol",
552 "smol",
551 "smol-timeout",
553 "smol-timeout",
554+ "toml",
552 "tui",
555 "tui",
553]
556]
554
557
@@ -1207,6 +1210,15 @@ version = "0.1.0"
1207source = "registry+https://github.com/rust-lang/crates.io-index"
1210source = "registry+https://github.com/rust-lang/crates.io-index"
1208checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
1211checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
1209
1212
1213+[[package]]
1214+name = "toml"
1215+version = "0.5.8"
1216+source = "registry+https://github.com/rust-lang/crates.io-index"
1217+checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
1218+dependencies = [
1219+ "serde",
1220+]
1221+
1210[[package]]
1222[[package]]
1211name = "tree-sitter"
1223name = "tree-sitter"
1212version = "0.17.1"
1224version = "0.17.1"
helix-core/Cargo.toml+2
@@ -19,3 +19,5 @@ unicode-width = "0.1"
19tree-sitter = "0.17"
19tree-sitter = "0.17"
20once_cell = "1.4"
20once_cell = "1.4"
21regex = "1"
21regex = "1"
22+
23+serde = { version = "1.0", features = ["derive"] }
helix-core/src/syntax.rs+25−43
@@ -10,9 +10,19 @@ use std::{
10};
10};
11
11
12use once_cell::sync::{Lazy, OnceCell};
12use once_cell::sync::{Lazy, OnceCell};
13+use serde::{Deserialize, Serialize};
14+
15+#[derive(Serialize, Deserialize)]
16+pub struct Configuration {
17+ language: Vec<LanguageConfiguration>,
18+}
13
19
14// largely based on tree-sitter/cli/src/loader.rs
20// largely based on tree-sitter/cli/src/loader.rs
21+#[derive(Serialize, Deserialize)]
22+#[serde(rename_all = "kebab-case")]
15pub struct LanguageConfiguration {
23pub struct LanguageConfiguration {
24+ #[serde(rename = "name")]
25+ pub(crate) language_id: Lang,
16 pub scope: String, // source.rust
26 pub scope: String, // source.rust
17 pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
27 pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
18 pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
28 pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
@@ -24,22 +34,29 @@ pub struct LanguageConfiguration {
24 // injection_regex
34 // injection_regex
25 // first_line_regex
35 // first_line_regex
26 //
36 //
27− //
37+ #[serde(skip)]
28− pub(crate) language_id: Lang,
29 pub(crate) highlight_config: OnceCell<Option<Arc<HighlightConfiguration>>>,
38 pub(crate) highlight_config: OnceCell<Option<Arc<HighlightConfiguration>>>,
30 // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583
39 // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583
40+ #[serde(skip_serializing_if = "Option::is_none")]
31− pub language_server_config: Option<LanguageServerConfiguration>,
41+ pub language_server: Option<LanguageServerConfiguration>,
42+ #[serde(skip_serializing_if = "Option::is_none")]
32− pub indent_config: Option<IndentationConfiguration>,
43+ pub indent: Option<IndentationConfiguration>,
33}
44}
34
45
46+#[derive(Serialize, Deserialize)]
47+#[serde(rename_all = "kebab-case")]
35pub struct LanguageServerConfiguration {
48pub struct LanguageServerConfiguration {
36 pub command: String,
49 pub command: String,
50+ #[serde(default)]
51+ #[serde(skip_serializing_if = "Vec::is_empty")]
37 pub args: Vec<String>,
52 pub args: Vec<String>,
38}
53}
39
54
55+#[derive(Serialize, Deserialize)]
56+#[serde(rename_all = "kebab-case")]
40pub struct IndentationConfiguration {
57pub struct IndentationConfiguration {
41 pub tab_width: usize,
58 pub tab_width: usize,
42− pub indent_unit: String,
59+ pub unit: String,
43}
60}
44
61
45impl LanguageConfiguration {
62impl LanguageConfiguration {
@@ -81,7 +98,7 @@ impl LanguageConfiguration {
81 }
98 }
82}
99}
83
100
84−pub static LOADER: Lazy<Loader> = Lazy::new(Loader::init);
101+pub static LOADER: OnceCell<Loader> = OnceCell::new();
85
102
86pub struct Loader {
103pub struct Loader {
87 // highlight_names ?
104 // highlight_names ?
@@ -90,48 +107,13 @@ pub struct Loader {
90}
107}
91
108
92impl Loader {
109impl Loader {
93− fn init() -> Self {
110+ pub fn new(config: Configuration) -> Self {
94 let mut loader = Self {
111 let mut loader = Self {
95 language_configs: Vec::new(),
112 language_configs: Vec::new(),
96 language_config_ids_by_file_type: HashMap::new(),
113 language_config_ids_by_file_type: HashMap::new(),
97 };
114 };
98
115
99− // hardcoded from now, might load from toml
100− let configs = vec![
101− LanguageConfiguration {
102− scope: "source.rust".to_string(),
103− file_types: vec!["rs".to_string()],
104− language_id: Lang::Rust,
105− highlight_config: OnceCell::new(),
106− //
107− path: "../helix-syntax/languages/tree-sitter-rust".into(),
108− roots: vec![],
109− language_server_config: Some(LanguageServerConfiguration {
110− command: "rust-analyzer".to_string(),
111− args: vec![],
112− }),
113− indent_config: Some(IndentationConfiguration {
114− tab_width: 4,
115− indent_unit: String::from(" "),
116− }),
117− },
118− LanguageConfiguration {
119− scope: "source.toml".to_string(),
120− file_types: vec!["toml".to_string()],
121− language_id: Lang::Toml,
122− highlight_config: OnceCell::new(),
123− //
124− path: "../helix-syntax/languages/tree-sitter-toml".into(),
125− roots: vec![],
126− language_server_config: None,
127− indent_config: Some(IndentationConfiguration {
128− tab_width: 2,
129− indent_unit: String::from(" "),
130− }),
131− },
132− ];
133−
134− for config in configs {
116+ for config in config.language {
135 // get the next id
117 // get the next id
136 let language_id = loader.language_configs.len();
118 let language_id = loader.language_configs.len();
137
119
helix-lsp/src/lib.rs+1−1
@@ -127,7 +127,7 @@ impl Registry {
127 ex: &smol::Executor,
127 ex: &smol::Executor,
128 ) -> Option<Arc<Client>> {
128 ) -> Option<Arc<Client>> {
129 // TODO: propagate the error
129 // TODO: propagate the error
130− if let Some(config) = &language_config.language_server_config {
130+ if let Some(config) = &language_config.language_server {
131 // avoid borrow issues
131 // avoid borrow issues
132 let inner = &mut self.inner;
132 let inner = &mut self.inner;
133 let s_incoming = &self.incoming;
133 let s_incoming = &self.incoming;
helix-syntax/Cargo.toml+1
@@ -8,6 +8,7 @@ edition = "2018"
8
8
9[dependencies]
9[dependencies]
10tree-sitter = "0.17"
10tree-sitter = "0.17"
11+serde = { version = "1.0", features = ["derive"] }
11
12
12[build-dependencies]
13[build-dependencies]
13cc = { version = "1", features = ["parallel"] }
14cc = { version = "1", features = ["parallel"] }
helix-syntax/languages.tomldeleted−5
@@ -1,5 +0,0 @@
1−[[language]]
2−name = "rust"
3−scope = "source.rust"
4−injection-regex = "rust"
5−file-types = ["rs"]
helix-syntax/src/lib.rs+3−1
@@ -1,3 +1,4 @@
1+use serde::{Deserialize, Serialize};
1use tree_sitter::Language;
2use tree_sitter::Language;
2
3
3#[macro_export]
4#[macro_export]
@@ -12,7 +13,8 @@ macro_rules! mk_extern {
12#[macro_export]
13#[macro_export]
13macro_rules! mk_enum {
14macro_rules! mk_enum {
14 ( $( $camel:ident ),* ) => {
15 ( $( $camel:ident ),* ) => {
15− #[derive(Clone, Copy, Debug, PartialEq)]
16+ #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
17+ #[serde(rename_all = "lowercase")]
16 pub enum Lang {
18 pub enum Lang {
17 $(
19 $(
18 $camel,
20 $camel,
helix-term/Cargo.toml+3
@@ -41,3 +41,6 @@ ignore = "0.4"
41dirs-next = "2.0"
41dirs-next = "2.0"
42# markdown doc rendering
42# markdown doc rendering
43pulldown-cmark = { version = "0.8", default-features = false }
43pulldown-cmark = { version = "0.8", default-features = false }
44+
45+# config
46+toml = "0.5"
helix-term/src/main.rs+8
@@ -73,6 +73,14 @@ fn main() {
73
73
74 setup_logging(verbosity).expect("failed to initialize logging.");
74 setup_logging(verbosity).expect("failed to initialize logging.");
75
75
76+ // initialize language registry
77+ use helix_core::syntax::{Loader, LOADER};
78+ let toml = include_str!("../../languages.toml");
79+ LOADER.get_or_init(|| {
80+ let config = toml::from_str(&toml).expect("Could not parse languages.toml");
81+ Loader::new(config)
82+ });
83+
76 for _ in 0..num_cpus::get() {
84 for _ in 0..num_cpus::get() {
77 std::thread::spawn(move || smol::block_on(EX.run(smol::future::pending::<()>())));
85 std::thread::spawn(move || smol::block_on(EX.run(smol::future::pending::<()>())));
78 }
86 }
helix-term/src/ui/markdown.rs+2
@@ -80,6 +80,8 @@ fn parse<'a>(contents: &'a str, theme: Option<&Theme>) -> tui::text::Text<'a> {
80
80
81 let rope = Rope::from(text.as_ref());
81 let rope = Rope::from(text.as_ref());
82 let syntax = syntax::LOADER
82 let syntax = syntax::LOADER
83+ .get()
84+ .unwrap()
83 .language_config_for_scope(&format!("source.{}", language))
85 .language_config_for_scope(&format!("source.{}", language))
84 .and_then(|config| config.highlight_config(theme.scopes()))
86 .and_then(|config| config.highlight_config(theme.scopes()))
85 .map(|config| Syntax::new(&rope, config));
87 .map(|config| Syntax::new(&rope, config));
helix-view/src/document.rs+9−5
@@ -97,7 +97,10 @@ impl Document {
97
97
98 let mut doc = Self::new(doc);
98 let mut doc = Self::new(doc);
99
99
100+ let language_config = LOADER
101+ .get()
102+ .unwrap()
100− let language_config = LOADER.language_config_for_file_name(path.as_path());
103+ .language_config_for_file_name(path.as_path());
101 doc.set_language(language_config, scopes);
104 doc.set_language(language_config, scopes);
102
105
103 // canonicalize path to absolute value
106 // canonicalize path to absolute value
@@ -161,7 +164,8 @@ impl Document {
161 }
164 }
162
165
163 pub fn set_language2(&mut self, scope: &str, scopes: &[String]) {
166 pub fn set_language2(&mut self, scope: &str, scopes: &[String]) {
164− let language_config = LOADER.language_config_for_scope(scope);
167+ let language_config = LOADER.get().unwrap().language_config_for_scope(scope);
168+
165 self.set_language(language_config, scopes);
169 self.set_language(language_config, scopes);
166 }
170 }
167
171
@@ -304,7 +308,7 @@ impl Document {
304 pub fn tab_width(&self) -> usize {
308 pub fn tab_width(&self) -> usize {
305 self.language
309 self.language
306 .as_ref()
310 .as_ref()
307− .and_then(|config| config.indent_config.as_ref())
311+ .and_then(|config| config.indent.as_ref())
308 .map(|config| config.tab_width)
312 .map(|config| config.tab_width)
309 .unwrap_or(4) // fallback to 4 columns
313 .unwrap_or(4) // fallback to 4 columns
310 }
314 }
@@ -313,8 +317,8 @@ impl Document {
313 pub fn indent_unit(&self) -> &str {
317 pub fn indent_unit(&self) -> &str {
314 self.language
318 self.language
315 .as_ref()
319 .as_ref()
316− .and_then(|config| config.indent_config.as_ref())
320+ .and_then(|config| config.indent.as_ref())
317− .map(|config| config.indent_unit.as_str())
321+ .map(|config| config.unit.as_str())
318 .unwrap_or(" ") // fallback to 2 spaces
322 .unwrap_or(" ") // fallback to 2 spaces
319
323
320 // " ".repeat(TAB_WIDTH)
324 // " ".repeat(TAB_WIDTH)
languages.tomlnew file+20
@@ -0,0 +1,20 @@
1+[[language]]
2+name = "rust"
3+scope = "source.rust"
4+injection-regex = "rust"
5+file-types = ["rs"]
6+roots = []
7+path = "../helix-syntax/languages/tree-sitter-rust"
8+
9+language-server = { command = "rust-analyzer" }
10+indent = { tab-width = 4, unit = " " }
11+
12+[[language]]
13+name = "toml"
14+scope = "source.toml"
15+injection-regex = "toml"
16+file-types = ["toml"]
17+roots = []
18+path = "../helix-syntax/languages/tree-sitter-toml"
19+
20+indent = { tab-width = 2, unit = " " }