diff --git a/helix-core/src/syntax.rs b/helix-core/src/syntax.rs index 54aa78cfa..499d91d13 100644 --- a/helix-core/src/syntax.rs +++ b/helix-core/src/syntax.rs @@ -11,20 +11,27 @@ // largely based on tree-sitter/cli/src/loader.rs pub struct LanguageConfiguration { - pub(crate) scope: String, // source.rust - pub(crate) file_types: Vec, // filename ends_with? + pub scope: String, // source.rust + pub file_types: Vec, // filename ends_with? + pub roots: Vec, // these indicate project roots <.git, Cargo.toml> - pub(crate) path: PathBuf, + pub path: PathBuf, + // root_path for tree-sitter (^) // content_regex // injection_regex // first_line_regex // - // root_path // pub(crate) language_id: Lang, pub(crate) highlight_config: OnceCell>>, // tags_config OnceCell<> https://github.com/tree-sitter/tree-sitter/pull/583 + pub language_server_config: Option, +} + +pub struct LanguageServerConfiguration { + pub command: String, + pub args: Vec, } impl LanguageConfiguration { @@ -90,6 +97,11 @@ fn init() -> Loader { highlight_config: OnceCell::new(), // path: "../helix-syntax/languages/tree-sitter-rust".into(), + roots: vec![], + language_server_config: Some(LanguageServerConfiguration { + command: "rust-analyzer".to_string(), + args: vec![], + }), }, LanguageConfiguration { scope: "source.toml".to_string(), @@ -98,6 +110,8 @@ fn init() -> Loader { highlight_config: OnceCell::new(), // path: "../helix-syntax/languages/tree-sitter-toml".into(), + roots: vec![], + language_server_config: None, }, ]; diff --git a/helix-lsp/src/lib.rs b/helix-lsp/src/lib.rs index 93bc06b54..0d04b7111 100644 --- a/helix-lsp/src/lib.rs +++ b/helix-lsp/src/lib.rs @@ -10,6 +10,8 @@ pub use client::Client; pub use lsp::{Position, Url}; +use helix_core::syntax::LanguageConfiguration; + use thiserror::Error; use std::{collections::HashMap, sync::Arc}; @@ -106,7 +108,7 @@ pub fn parse(method: &str, params: jsonrpc::Params) -> Notification { use smol::channel::Receiver; pub struct Registry { - inner: HashMap>>, + inner: HashMap>, pub incoming: SelectAll>, } @@ -119,35 +121,43 @@ fn default() -> Self { impl Registry { pub fn new() -> Self { - let mut inner = HashMap::new(); - - inner.insert("source.rust".to_string(), OnceCell::new()); - Self { - inner, + inner: HashMap::new(), incoming: SelectAll::new(), } } - pub fn get(&self, id: &str, ex: &smol::Executor) -> Option> { - // TODO: use get_or_try_init and propagate the error - self.inner - .get(id) - .map(|cell| { - cell.get_or_init(|| { + pub fn get( + &mut self, + language_config: &LanguageConfiguration, + ex: &smol::Executor, + ) -> Option> { + // TODO: propagate the error + if let Some(config) = &language_config.language_server_config { + // avoid borrow issues + let inner = &mut self.inner; + let s_incoming = &self.incoming; + + let language_server = inner + .entry(language_config.scope.clone()) // can't use entry with Borrow keys: https://github.com/rust-lang/rfcs/pull/1769 + .or_insert_with(|| { // TODO: lookup defaults for id (name, args) // initialize a new client - let (mut client, incoming) = Client::start(&ex, "rust-analyzer", &[]); + let (mut client, incoming) = Client::start(&ex, &config.command, &config.args); // TODO: run this async without blocking smol::block_on(client.initialize()).unwrap(); - self.incoming.push(incoming); + s_incoming.push(incoming); Arc::new(client) }) - }) - .cloned() + .clone(); + + return Some(language_server); + } + + None } } diff --git a/helix-view/src/document.rs b/helix-view/src/document.rs index f394f2beb..033a35930 100644 --- a/helix-view/src/document.rs +++ b/helix-view/src/document.rs @@ -4,7 +4,8 @@ use std::sync::Arc; use helix_core::{ - syntax::LOADER, ChangeSet, Diagnostic, History, Rope, Selection, State, Syntax, Transaction, + syntax::{LanguageConfiguration, LOADER}, + ChangeSet, Diagnostic, History, Rope, Selection, State, Syntax, Transaction, }; #[derive(Copy, Clone, PartialEq, Eq, Hash)] @@ -26,8 +27,8 @@ pub struct Document { /// Tree-sitter AST tree pub syntax: Option, - /// Corresponding language scope name. Usually `source.`. - language: Option, + // /// Corresponding language scope name. Usually `source.`. + pub(crate) language: Option>, /// Pending changes since last history commit. changes: ChangeSet, @@ -144,20 +145,13 @@ pub fn set_language( scopes: &[String], ) { if let Some(language_config) = language_config { - // TODO: maybe just keep an Arc<> pointer to the language_config? - self.language = Some(language_config.scope().to_string()); + if let Some(highlight_config) = language_config.highlight_config(scopes) { + let syntax = Syntax::new(&self.state.doc, highlight_config); + self.syntax = Some(syntax); + // TODO: config.configure(scopes) is now delayed, is that ok? + } - // TODO: this ties lsp support to tree-sitter enabled languages for now. Language - // config should use Option to let us have non-tree-sitter configs. - - let highlight_config = language_config - .highlight_config(scopes) - .expect("No highlight_config found!"); - // TODO: config.configure(scopes) is now delayed, is that ok? - - let syntax = Syntax::new(&self.state.doc, highlight_config); - - self.syntax = Some(syntax); + self.language = Some(language_config); } else { self.syntax = None; self.language = None; @@ -286,7 +280,9 @@ pub fn mode(&self) -> Mode { #[inline] /// Corresponding language scope name. Usually `source.`. pub fn language(&self) -> Option<&str> { - self.language.as_deref() + self.language + .as_ref() + .map(|language| language.scope.as_str()) } #[inline] diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs index c072d76ff..01a2dac57 100644 --- a/helix-view/src/editor.rs +++ b/helix-view/src/editor.rs @@ -39,7 +39,8 @@ pub fn open(&mut self, path: PathBuf, executor: &smol::Executor) -> Result<(), E // try to find a language server based on the language name let language_server = doc - .language() + .language + .as_ref() .and_then(|language| self.language_servers.get(language, &executor)); if let Some(language_server) = language_server {