on master help sign in
Configure language servers via LanguageConfiguration.
bb87b08Blaž Hrastnik
helix-core/src/syntax.rs+18−4
@@ -11,20 +11,27 @@ use once_cell::sync::{Lazy, OnceCell};
11
11
12// largely based on tree-sitter/cli/src/loader.rs
12// largely based on tree-sitter/cli/src/loader.rs
13pub struct LanguageConfiguration {
13pub struct LanguageConfiguration {
14− pub(crate) scope: String, // source.rust
14+ pub scope: String, // source.rust
15− pub(crate) file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
15+ pub file_types: Vec<String>, // filename ends_with? <Gemfile, rb, etc>
16+ pub roots: Vec<String>, // these indicate project roots <.git, Cargo.toml>
16
17
17− pub(crate) path: PathBuf,
18+ pub path: PathBuf,
19+ // root_path for tree-sitter (^)
18
20
19 // content_regex
21 // content_regex
20 // injection_regex
22 // injection_regex
21 // first_line_regex
23 // first_line_regex
22 //
24 //
23− // root_path
24 //
25 //
25 pub(crate) language_id: Lang,
26 pub(crate) language_id: Lang,
26 pub(crate) highlight_config: OnceCell<Option<Arc<HighlightConfiguration>>>,
27 pub(crate) highlight_config: OnceCell<Option<Arc<HighlightConfiguration>>>,
27 // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583
28 // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583
29+ pub language_server_config: Option<LanguageServerConfiguration>,
30+}
31+
32+pub struct LanguageServerConfiguration {
33+ pub command: String,
34+ pub args: Vec<String>,
28}
35}
29
36
30impl LanguageConfiguration {
37impl LanguageConfiguration {
@@ -90,6 +97,11 @@ impl Loader {
90 highlight_config: OnceCell::new(),
97 highlight_config: OnceCell::new(),
91 //
98 //
92 path: "../helix-syntax/languages/tree-sitter-rust".into(),
99 path: "../helix-syntax/languages/tree-sitter-rust".into(),
100+ roots: vec![],
101+ language_server_config: Some(LanguageServerConfiguration {
102+ command: "rust-analyzer".to_string(),
103+ args: vec![],
104+ }),
93 },
105 },
94 LanguageConfiguration {
106 LanguageConfiguration {
95 scope: "source.toml".to_string(),
107 scope: "source.toml".to_string(),
@@ -98,6 +110,8 @@ impl Loader {
98 highlight_config: OnceCell::new(),
110 highlight_config: OnceCell::new(),
99 //
111 //
100 path: "../helix-syntax/languages/tree-sitter-toml".into(),
112 path: "../helix-syntax/languages/tree-sitter-toml".into(),
113+ roots: vec![],
114+ language_server_config: None,
101 },
115 },
102 ];
116 ];
103
117
helix-lsp/src/lib.rs+26−16
@@ -10,6 +10,8 @@ pub use once_cell::sync::{Lazy, OnceCell};
10pub use client::Client;
10pub use client::Client;
11pub use lsp::{Position, Url};
11pub use lsp::{Position, Url};
12
12
13+use helix_core::syntax::LanguageConfiguration;
14+
13use thiserror::Error;
15use thiserror::Error;
14
16
15use std::{collections::HashMap, sync::Arc};
17use std::{collections::HashMap, sync::Arc};
@@ -106,7 +108,7 @@ use crate::select_all::SelectAll;
106use smol::channel::Receiver;
108use smol::channel::Receiver;
107
109
108pub struct Registry {
110pub struct Registry {
109− inner: HashMap<LanguageId, OnceCell<Arc<Client>>>,
111+ inner: HashMap<LanguageId, Arc<Client>>,
110
112
111 pub incoming: SelectAll<Receiver<Call>>,
113 pub incoming: SelectAll<Receiver<Call>>,
112}
114}
@@ -119,35 +121,43 @@ impl Default for Registry {
119
121
120impl Registry {
122impl Registry {
121 pub fn new() -> Self {
123 pub fn new() -> Self {
122− let mut inner = HashMap::new();
123−
124− inner.insert("source.rust".to_string(), OnceCell::new());
125−
126 Self {
124 Self {
127− inner,
125+ inner: HashMap::new(),
128 incoming: SelectAll::new(),
126 incoming: SelectAll::new(),
129 }
127 }
130 }
128 }
131
129
130+ pub fn get(
131+ &mut self,
132+ language_config: &LanguageConfiguration,
133+ ex: &smol::Executor,
132− pub fn get(&self, id: &str, ex: &smol::Executor) -> Option<Arc<Client>> {
134+ ) -> Option<Arc<Client>> {
133− // TODO: use get_or_try_init and propagate the error
135+ // TODO: propagate the error
136+ if let Some(config) = &language_config.language_server_config {
137+ // avoid borrow issues
134− self.inner
138+ let inner = &mut self.inner;
135− .get(id)
139+ let s_incoming = &self.incoming;
136− .map(|cell| {
140+
137− cell.get_or_init(|| {
141+ let language_server = inner
142+ .entry(language_config.scope.clone()) // can't use entry with Borrow keys: https://github.com/rust-lang/rfcs/pull/1769
143+ .or_insert_with(|| {
138 // TODO: lookup defaults for id (name, args)
144 // TODO: lookup defaults for id (name, args)
139
145
140 // initialize a new client
146 // initialize a new client
141− let (mut client, incoming) = Client::start(&ex, "rust-analyzer", &[]);
147+ let (mut client, incoming) = Client::start(&ex, &config.command, &config.args);
142 // TODO: run this async without blocking
148 // TODO: run this async without blocking
143 smol::block_on(client.initialize()).unwrap();
149 smol::block_on(client.initialize()).unwrap();
144
150
145− self.incoming.push(incoming);
151+ s_incoming.push(incoming);
146
152
147 Arc::new(client)
153 Arc::new(client)
148 })
154 })
149− })
155+ .clone();
150− .cloned()
156+
157+ return Some(language_server);
158+ }
159+
160+ None
151 }
161 }
152}
162}
153
163
helix-view/src/document.rs+13−17
@@ -4,7 +4,8 @@ use std::path::{Path, PathBuf};
4use std::sync::Arc;
4use std::sync::Arc;
5
5
6use helix_core::{
6use helix_core::{
7+ syntax::{LanguageConfiguration, LOADER},
7− syntax::LOADER, ChangeSet, Diagnostic, History, Rope, Selection, State, Syntax, Transaction,
8+ ChangeSet, Diagnostic, History, Rope, Selection, State, Syntax, Transaction,
8};
9};
9
10
10#[derive(Copy, Clone, PartialEq, Eq, Hash)]
11#[derive(Copy, Clone, PartialEq, Eq, Hash)]
@@ -26,8 +27,8 @@ pub struct Document {
26
27
27 /// Tree-sitter AST tree
28 /// Tree-sitter AST tree
28 pub syntax: Option<Syntax>,
29 pub syntax: Option<Syntax>,
29− /// Corresponding language scope name. Usually `source.<lang>`.
30+ // /// Corresponding language scope name. Usually `source.<lang>`.
30− language: Option<String>,
31+ pub(crate) language: Option<Arc<LanguageConfiguration>>,
31
32
32 /// Pending changes since last history commit.
33 /// Pending changes since last history commit.
33 changes: ChangeSet,
34 changes: ChangeSet,
@@ -144,20 +145,13 @@ impl Document {
144 scopes: &[String],
145 scopes: &[String],
145 ) {
146 ) {
146 if let Some(language_config) = language_config {
147 if let Some(language_config) = language_config {
147− // TODO: maybe just keep an Arc<> pointer to the language_config?
148− self.language = Some(language_config.scope().to_string());
149−
150− // TODO: this ties lsp support to tree-sitter enabled languages for now. Language
151− // config should use Option<HighlightConfig> to let us have non-tree-sitter configs.
152−
153− let highlight_config = language_config
148+ if let Some(highlight_config) = language_config.highlight_config(scopes) {
154− .highlight_config(scopes)
155− .expect("No highlight_config found!");
156− // TODO: config.configure(scopes) is now delayed, is that ok?
157−
158− let syntax = Syntax::new(&self.state.doc, highlight_config);
149+ let syntax = Syntax::new(&self.state.doc, highlight_config);
150+ self.syntax = Some(syntax);
151+ // TODO: config.configure(scopes) is now delayed, is that ok?
152+ }
159
153
160− self.syntax = Some(syntax);
154+ self.language = Some(language_config);
161 } else {
155 } else {
162 self.syntax = None;
156 self.syntax = None;
163 self.language = None;
157 self.language = None;
@@ -286,7 +280,9 @@ impl Document {
286 #[inline]
280 #[inline]
287 /// Corresponding language scope name. Usually `source.<lang>`.
281 /// Corresponding language scope name. Usually `source.<lang>`.
288 pub fn language(&self) -> Option<&str> {
282 pub fn language(&self) -> Option<&str> {
289− self.language.as_deref()
283+ self.language
284+ .as_ref()
285+ .map(|language| language.scope.as_str())
290 }
286 }
291
287
292 #[inline]
288 #[inline]
helix-view/src/editor.rs+2−1
@@ -39,7 +39,8 @@ impl Editor {
39
39
40 // try to find a language server based on the language name
40 // try to find a language server based on the language name
41 let language_server = doc
41 let language_server = doc
42− .language()
42+ .language
43+ .as_ref()
43 .and_then(|language| self.language_servers.get(language, &executor));
44 .and_then(|language| self.language_servers.get(language, &executor));
44
45
45 if let Some(language_server) = language_server {
46 if let Some(language_server) = language_server {