on master help sign in
Add the machinery to load syntax config from TOML.

It's embedded into the binary at build time for now, but it's progress.

commite3c4edae32f6d70bf20655c7c25b3cdf39f3f281 authorBlaž Hrastnik <blaz@mxxn.io> dateThu 25 Mar 2021, 15:26 (6 years ago) parenta900159
12 files changed+86−55
Cargo.lock+12 helix-core/Cargo.toml+2 helix-core/src/syntax.rs+25−43 helix-lsp/src/lib.rs+1−1 helix-syntax/Cargo.toml+1 helix-syntax/languages.toml−5 helix-syntax/src/lib.rs+3−1 helix-term/Cargo.toml+3 helix-term/src/main.rs+8 helix-term/src/ui/markdown.rs+2 helix-view/src/document.rs+9−5 languages.toml+20
Cargo.lock+12
@@ -489,6 +489,7 @@ dependencies = [
489489 "once_cell",
490490 "regex",
491491 "ropey",
492+ "serde",
492493 "smallvec",
493494 "tendril",
494495 "tree-sitter",
@@ -523,6 +524,7 @@ name = "helix-syntax"
523524version = "0.1.0"
524525dependencies = [
525526 "cc",
527+ "serde",
526528 "threadpool",
527529 "tree-sitter",
528530]
@@ -549,6 +551,7 @@ dependencies = [
549551 "pulldown-cmark",
550552 "smol",
551553 "smol-timeout",
554+ "toml",
552555 "tui",
553556]
554557
@@ -1207,6 +1210,15 @@ version = "0.1.0"
12071210source = "registry+https://github.com/rust-lang/crates.io-index"
12081211checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
12091212
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+
12101222[[package]]
12111223name = "tree-sitter"
12121224version = "0.17.1"
helix-core/Cargo.toml+2
@@ -19,3 +19,5 @@ unicode-width = "0.1"
1919tree-sitter = "0.17"
2020once_cell = "1.4"
2121regex = "1"
22+
23+serde = { version = "1.0", features = ["derive"] }
helix-core/src/syntax.rs+25−43
@@ -10,9 +10,19 @@ use std::{
1010};
1111
1212use once_cell::sync::{Lazy, OnceCell};
13+use serde::{Deserialize, Serialize};
14+
15+#[derive(Serialize, Deserialize)]
16+pub struct Configuration {
17+ language: Vec<LanguageConfiguration>,
18+}
1319
1420// largely based on tree-sitter/cli/src/loader.rs
21+#[derive(Serialize, Deserialize)]
22+#[serde(rename_all = "kebab-case")]
1523pub struct LanguageConfiguration {
24+ #[serde(rename = "name")]
25+ pub(crate) language_id: Lang,
1626 pub scope: String, // source.rust
1727 pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
1828 pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
@@ -24,22 +34,29 @@ pub struct LanguageConfiguration {
2434 // injection_regex
2535 // first_line_regex
2636 //
27− //
28− pub(crate) language_id: Lang,
37+ #[serde(skip)]
2938 pub(crate) highlight_config: OnceCell<Option<Arc<HighlightConfiguration>>>,
3039 // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583
31− pub language_server_config: Option<LanguageServerConfiguration>,
32− pub indent_config: Option<IndentationConfiguration>,
40+ #[serde(skip_serializing_if = "Option::is_none")]
41+ pub language_server: Option<LanguageServerConfiguration>,
42+ #[serde(skip_serializing_if = "Option::is_none")]
43+ pub indent: Option<IndentationConfiguration>,
3344}
3445
46+#[derive(Serialize, Deserialize)]
47+#[serde(rename_all = "kebab-case")]
3548pub struct LanguageServerConfiguration {
3649 pub command: String,
50+ #[serde(default)]
51+ #[serde(skip_serializing_if = "Vec::is_empty")]
3752 pub args: Vec<String>,
3853}
3954
55+#[derive(Serialize, Deserialize)]
56+#[serde(rename_all = "kebab-case")]
4057pub struct IndentationConfiguration {
4158 pub tab_width: usize,
42− pub indent_unit: String,
59+ pub unit: String,
4360}
4461
4562impl LanguageConfiguration {
@@ -81,7 +98,7 @@ impl LanguageConfiguration {
8198 }
8299}
83100
84−pub static LOADER: Lazy<Loader> = Lazy::new(Loader::init);
101+pub static LOADER: OnceCell<Loader> = OnceCell::new();
85102
86103pub struct Loader {
87104 // highlight_names ?
@@ -90,48 +107,13 @@ pub struct Loader {
90107}
91108
92109impl Loader {
93− fn init() -> Self {
110+ pub fn new(config: Configuration) -> Self {
94111 let mut loader = Self {
95112 language_configs: Vec::new(),
96113 language_config_ids_by_file_type: HashMap::new(),
97114 };
98115
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 {
135117 // get the next id
136118 let language_id = loader.language_configs.len();
137119
helix-lsp/src/lib.rs+1−1
@@ -127,7 +127,7 @@ impl Registry {
127127 ex: &smol::Executor,
128128 ) -> Option<Arc<Client>> {
129129 // TODO: propagate the error
130− if let Some(config) = &language_config.language_server_config {
130+ if let Some(config) = &language_config.language_server {
131131 // avoid borrow issues
132132 let inner = &mut self.inner;
133133 let s_incoming = &self.incoming;
helix-syntax/Cargo.toml+1
@@ -8,6 +8,7 @@ edition = "2018"
88
99[dependencies]
1010tree-sitter = "0.17"
11+serde = { version = "1.0", features = ["derive"] }
1112
1213[build-dependencies]
1314cc = { 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};
12use tree_sitter::Language;
23
34#[macro_export]
@@ -12,7 +13,8 @@ macro_rules! mk_extern {
1213#[macro_export]
1314macro_rules! mk_enum {
1415 ( $( $camel:ident ),* ) => {
15− #[derive(Clone, Copy, Debug, PartialEq)]
16+ #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
17+ #[serde(rename_all = "lowercase")]
1618 pub enum Lang {
1719 $(
1820 $camel,
helix-term/Cargo.toml+3
@@ -41,3 +41,6 @@ ignore = "0.4"
4141dirs-next = "2.0"
4242# markdown doc rendering
4343pulldown-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() {
7373
7474 setup_logging(verbosity).expect("failed to initialize logging.");
7575
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+
7684 for _ in 0..num_cpus::get() {
7785 std::thread::spawn(move || smol::block_on(EX.run(smol::future::pending::<()>())));
7886 }
helix-term/src/ui/markdown.rs+2
@@ -80,6 +80,8 @@ fn parse<'a>(contents: &'a str, theme: Option<&Theme>) -> tui::text::Text<'a> {
8080
8181 let rope = Rope::from(text.as_ref());
8282 let syntax = syntax::LOADER
83+ .get()
84+ .unwrap()
8385 .language_config_for_scope(&format!("source.{}", language))
8486 .and_then(|config| config.highlight_config(theme.scopes()))
8587 .map(|config| Syntax::new(&rope, config));
helix-view/src/document.rs+9−5
@@ -97,7 +97,10 @@ impl Document {
9797
9898 let mut doc = Self::new(doc);
9999
100− let language_config = LOADER.language_config_for_file_name(path.as_path());
100+ let language_config = LOADER
101+ .get()
102+ .unwrap()
103+ .language_config_for_file_name(path.as_path());
101104 doc.set_language(language_config, scopes);
102105
103106 // canonicalize path to absolute value
@@ -161,7 +164,8 @@ impl Document {
161164 }
162165
163166 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+
165169 self.set_language(language_config, scopes);
166170 }
167171
@@ -304,7 +308,7 @@ impl Document {
304308 pub fn tab_width(&self) -> usize {
305309 self.language
306310 .as_ref()
307− .and_then(|config| config.indent_config.as_ref())
311+ .and_then(|config| config.indent.as_ref())
308312 .map(|config| config.tab_width)
309313 .unwrap_or(4) // fallback to 4 columns
310314 }
@@ -313,8 +317,8 @@ impl Document {
313317 pub fn indent_unit(&self) -> &str {
314318 self.language
315319 .as_ref()
316− .and_then(|config| config.indent_config.as_ref())
317− .map(|config| config.indent_unit.as_str())
320+ .and_then(|config| config.indent.as_ref())
321+ .map(|config| config.unit.as_str())
318322 .unwrap_or(" ") // fallback to 2 spaces
319323
320324 // " ".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 = " " }