From 2cb83f1bbb0c91516e56a447cc02e0832851036c Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 25 Jan 2023 18:09:45 -0800 Subject: [PATCH] Merge pull request #2096 from zed-industries/lazy-load-languages Load languages lazily in the background --- crates/language/src/buffer_tests.rs | 20 +- crates/language/src/language.rs | 256 ++++++++++++++++++------- crates/language/src/syntax_map.rs | 26 +-- crates/project/src/project.rs | 8 +- crates/zed/src/languages.rs | 122 +++++------- crates/zed/src/languages/c.rs | 14 +- crates/zed/src/languages/go.rs | 5 +- crates/zed/src/languages/python.rs | 15 +- crates/zed/src/languages/rust.rs | 27 +-- crates/zed/src/languages/typescript.rs | 10 +- crates/zed/src/main.rs | 14 +- crates/zed/src/zed.rs | 4 +- 12 files changed, 311 insertions(+), 210 deletions(-) diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 0b2ef1d7a7..59fd6a534b 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -51,7 +51,7 @@ fn test_line_endings(cx: &mut gpui::MutableAppContext) { #[gpui::test] fn test_select_language() { - let registry = LanguageRegistry::test(); + let registry = Arc::new(LanguageRegistry::test()); registry.add(Arc::new(Language::new( LanguageConfig { name: "Rust".into(), @@ -71,27 +71,33 @@ fn test_select_language() { // matching file extension assert_eq!( - registry.select_language("zed/lib.rs").map(|l| l.name()), + registry.language_for_path("zed/lib.rs").map(|l| l.name()), Some("Rust".into()) ); assert_eq!( - registry.select_language("zed/lib.mk").map(|l| l.name()), + registry.language_for_path("zed/lib.mk").map(|l| l.name()), Some("Make".into()) ); // matching filename assert_eq!( - registry.select_language("zed/Makefile").map(|l| l.name()), + registry.language_for_path("zed/Makefile").map(|l| l.name()), Some("Make".into()) ); // matching suffix that is not the full file extension or filename - assert_eq!(registry.select_language("zed/cars").map(|l| l.name()), None); assert_eq!( - registry.select_language("zed/a.cars").map(|l| l.name()), + registry.language_for_path("zed/cars").map(|l| l.name()), + None + ); + assert_eq!( + registry.language_for_path("zed/a.cars").map(|l| l.name()), + None + ); + assert_eq!( + registry.language_for_path("zed/sumk").map(|l| l.name()), None ); - assert_eq!(registry.select_language("zed/sumk").map(|l| l.name()), None); } #[gpui::test] diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 6e1a120c81..4279ce6654 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -16,7 +16,7 @@ use futures::{ future::{BoxFuture, Shared}, FutureExt, TryFutureExt, }; -use gpui::{MutableAppContext, Task}; +use gpui::{executor::Background, MutableAppContext, Task}; use highlight_map::HighlightMap; use lazy_static::lazy_static; use parking_lot::{Mutex, RwLock}; @@ -26,6 +26,7 @@ use serde::{de, Deserialize, Deserializer}; use serde_json::Value; use std::{ any::Any, + borrow::Cow, cell::RefCell, fmt::Debug, hash::Hash, @@ -89,8 +90,7 @@ pub struct CachedLspAdapter { } impl CachedLspAdapter { - pub async fn new(adapter: T) -> Arc { - let adapter = Box::new(adapter); + pub async fn new(adapter: Box) -> Arc { let name = adapter.name().await; let server_args = adapter.server_args().await; let initialization_options = adapter.initialization_options().await; @@ -248,6 +248,16 @@ pub struct LanguageConfig { pub overrides: HashMap, } +#[derive(Debug, Default)] +pub struct LanguageQueries { + pub highlights: Option>, + pub brackets: Option>, + pub indents: Option>, + pub outline: Option>, + pub injections: Option>, + pub overrides: Option>, +} + #[derive(Clone)] pub struct LanguageScope { language: Arc, @@ -407,8 +417,17 @@ pub enum LanguageServerBinaryStatus { Failed { error: String }, } +struct AvailableLanguage { + path: &'static str, + config: LanguageConfig, + grammar: tree_sitter::Language, + lsp_adapter: Option>, + get_queries: fn(&str) -> LanguageQueries, +} + pub struct LanguageRegistry { languages: RwLock>>, + available_languages: RwLock>, language_server_download_dir: Option>, lsp_binary_statuses_tx: async_broadcast::Sender<(Arc, LanguageServerBinaryStatus)>, lsp_binary_statuses_rx: async_broadcast::Receiver<(Arc, LanguageServerBinaryStatus)>, @@ -422,6 +441,7 @@ pub struct LanguageRegistry { >, subscription: RwLock<(watch::Sender<()>, watch::Receiver<()>)>, theme: RwLock>>, + executor: Option>, version: AtomicUsize, } @@ -431,6 +451,7 @@ impl LanguageRegistry { Self { language_server_download_dir: None, languages: Default::default(), + available_languages: Default::default(), lsp_binary_statuses_tx, lsp_binary_statuses_rx, login_shell_env_loaded: login_shell_env_loaded.shared(), @@ -438,6 +459,7 @@ impl LanguageRegistry { subscription: RwLock::new(watch::channel()), theme: Default::default(), version: Default::default(), + executor: None, } } @@ -446,6 +468,44 @@ impl LanguageRegistry { Self::new(Task::ready(())) } + pub fn set_executor(&mut self, executor: Arc) { + self.executor = Some(executor); + } + + pub fn register( + &self, + path: &'static str, + config: LanguageConfig, + grammar: tree_sitter::Language, + lsp_adapter: Option>, + get_queries: fn(&str) -> LanguageQueries, + ) { + self.available_languages.write().push(AvailableLanguage { + path, + config, + grammar, + lsp_adapter, + get_queries, + }); + } + + pub fn language_names(&self) -> Vec { + let mut result = self + .available_languages + .read() + .iter() + .map(|l| l.config.name.to_string()) + .chain( + self.languages + .read() + .iter() + .map(|l| l.config.name.to_string()), + ) + .collect::>(); + result.sort_unstable(); + result + } + pub fn add(&self, language: Arc) { if let Some(theme) = self.theme.read().clone() { language.set_theme(&theme.editor.syntax); @@ -474,58 +534,79 @@ impl LanguageRegistry { self.language_server_download_dir = Some(path.into()); } - pub fn language_for_name(&self, name: &str) -> Option> { + pub fn language_for_name(self: &Arc, name: &str) -> Option> { let name = UniCase::new(name); - self.languages - .read() - .iter() - .find(|language| UniCase::new(language.name()) == name) - .cloned() + self.get_or_load_language(|config| UniCase::new(config.name.as_ref()) == name) } - pub fn language_for_extension(&self, extension: &str) -> Option> { - let extension = UniCase::new(extension); - self.languages - .read() - .iter() - .find(|language| { - language - .config + pub fn language_for_name_or_extension(self: &Arc, string: &str) -> Option> { + let string = UniCase::new(string); + self.get_or_load_language(|config| { + UniCase::new(config.name.as_ref()) == string + || config .path_suffixes .iter() - .any(|suffix| UniCase::new(suffix) == extension) - }) - .cloned() + .any(|suffix| UniCase::new(suffix) == string) + }) } - pub fn to_vec(&self) -> Vec> { - self.languages.read().iter().cloned().collect() - } - - pub fn language_names(&self) -> Vec { - self.languages - .read() - .iter() - .map(|language| language.name().to_string()) - .collect() - } - - pub fn select_language(&self, path: impl AsRef) -> Option> { + pub fn language_for_path(self: &Arc, path: impl AsRef) -> Option> { let path = path.as_ref(); let filename = path.file_name().and_then(|name| name.to_str()); let extension = path.extension().and_then(|name| name.to_str()); let path_suffixes = [extension, filename]; - self.languages + self.get_or_load_language(|config| { + config + .path_suffixes + .iter() + .any(|suffix| path_suffixes.contains(&Some(suffix.as_str()))) + }) + } + + fn get_or_load_language( + self: &Arc, + callback: impl Fn(&LanguageConfig) -> bool, + ) -> Option> { + if let Some(language) = self + .languages .read() .iter() - .find(|language| { - language - .config - .path_suffixes - .iter() - .any(|suffix| path_suffixes.contains(&Some(suffix.as_str()))) - }) - .cloned() + .find(|language| callback(&language.config)) + { + return Some(language.clone()); + } + + if let Some(executor) = self.executor.clone() { + let mut available_languages = self.available_languages.write(); + + if let Some(ix) = available_languages.iter().position(|l| callback(&l.config)) { + let language = available_languages.remove(ix); + drop(available_languages); + let name = language.config.name.clone(); + let this = self.clone(); + executor + .spawn(async move { + let queries = (language.get_queries)(&language.path); + let language = Language::new(language.config, Some(language.grammar)) + .with_lsp_adapter(language.lsp_adapter) + .await; + match language.with_queries(queries) { + Ok(language) => this.add(Arc::new(language)), + Err(err) => { + log::error!("failed to load language {}: {}", name, err); + return; + } + }; + }) + .detach(); + } + } + + None + } + + pub fn to_vec(&self) -> Vec> { + self.languages.read().iter().cloned().collect() } pub fn start_language_server( @@ -729,12 +810,70 @@ impl Language { self.grammar.as_ref().map(|g| g.id) } + pub fn with_queries(mut self, queries: LanguageQueries) -> Result { + if let Some(query) = queries.highlights { + self = self + .with_highlights_query(query.as_ref()) + .expect("failed to evaluate highlights query"); + } + if let Some(query) = queries.brackets { + self = self + .with_brackets_query(query.as_ref()) + .expect("failed to load brackets query"); + } + if let Some(query) = queries.indents { + self = self + .with_indents_query(query.as_ref()) + .expect("failed to load indents query"); + } + if let Some(query) = queries.outline { + self = self + .with_outline_query(query.as_ref()) + .expect("failed to load outline query"); + } + if let Some(query) = queries.injections { + self = self + .with_injection_query(query.as_ref()) + .expect("failed to load injection query"); + } + if let Some(query) = queries.overrides { + self = self + .with_override_query(query.as_ref()) + .expect("failed to load override query"); + } + Ok(self) + } pub fn with_highlights_query(mut self, source: &str) -> Result { let grammar = self.grammar_mut(); grammar.highlights_query = Some(Query::new(grammar.ts_language, source)?); Ok(self) } + pub fn with_outline_query(mut self, source: &str) -> Result { + let grammar = self.grammar_mut(); + let query = Query::new(grammar.ts_language, source)?; + let mut item_capture_ix = None; + let mut name_capture_ix = None; + let mut context_capture_ix = None; + get_capture_indices( + &query, + &mut [ + ("item", &mut item_capture_ix), + ("name", &mut name_capture_ix), + ("context", &mut context_capture_ix), + ], + ); + if let Some((item_capture_ix, name_capture_ix)) = item_capture_ix.zip(name_capture_ix) { + grammar.outline_config = Some(OutlineConfig { + query, + item_capture_ix, + name_capture_ix, + context_capture_ix, + }); + } + Ok(self) + } + pub fn with_brackets_query(mut self, source: &str) -> Result { let grammar = self.grammar_mut(); let query = Query::new(grammar.ts_language, source)?; @@ -785,31 +924,6 @@ impl Language { Ok(self) } - pub fn with_outline_query(mut self, source: &str) -> Result { - let grammar = self.grammar_mut(); - let query = Query::new(grammar.ts_language, source)?; - let mut item_capture_ix = None; - let mut name_capture_ix = None; - let mut context_capture_ix = None; - get_capture_indices( - &query, - &mut [ - ("item", &mut item_capture_ix), - ("name", &mut name_capture_ix), - ("context", &mut context_capture_ix), - ], - ); - if let Some((item_capture_ix, name_capture_ix)) = item_capture_ix.zip(name_capture_ix) { - grammar.outline_config = Some(OutlineConfig { - query, - item_capture_ix, - name_capture_ix, - context_capture_ix, - }); - } - Ok(self) - } - pub fn with_injection_query(mut self, source: &str) -> Result { let grammar = self.grammar_mut(); let query = Query::new(grammar.ts_language, source)?; @@ -882,8 +996,10 @@ impl Language { Arc::get_mut(self.grammar.as_mut().unwrap()).unwrap() } - pub fn with_lsp_adapter(mut self, lsp_adapter: Arc) -> Self { - self.adapter = Some(lsp_adapter); + pub async fn with_lsp_adapter(mut self, lsp_adapter: Option>) -> Self { + if let Some(adapter) = lsp_adapter { + self.adapter = Some(CachedLspAdapter::new(adapter).await); + } self } @@ -894,7 +1010,7 @@ impl Language { ) -> mpsc::UnboundedReceiver { let (servers_tx, servers_rx) = mpsc::unbounded(); self.fake_adapter = Some((servers_tx, fake_lsp_adapter.clone())); - let adapter = CachedLspAdapter::new(fake_lsp_adapter).await; + let adapter = CachedLspAdapter::new(Box::new(fake_lsp_adapter)).await; self.adapter = Some(adapter); servers_rx } diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index ada981ec26..41966c7596 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -381,7 +381,12 @@ impl SyntaxSnapshot { cursor.next(text); while let Some(layer) = cursor.item() { let SyntaxLayerContent::Pending { language_name } = &layer.content else { unreachable!() }; - if language_for_injection(language_name, ®istry).is_some() { + if { + let language_registry = ®istry; + language_registry.language_for_name_or_extension(language_name) + } + .is_some() + { resolved_injection_ranges.push(layer.range.to_offset(text)); } @@ -1066,7 +1071,7 @@ fn get_injections( config: &InjectionConfig, text: &BufferSnapshot, node: Node, - language_registry: &LanguageRegistry, + language_registry: &Arc, depth: usize, changed_ranges: &[Range], combined_injection_ranges: &mut HashMap, Vec>, @@ -1078,7 +1083,8 @@ fn get_injections( combined_injection_ranges.clear(); for pattern in &config.patterns { if let (Some(language_name), true) = (pattern.language.as_ref(), pattern.combined) { - if let Some(language) = language_for_injection(language_name, language_registry) { + if let Some(language) = language_registry.language_for_name_or_extension(language_name) + { combined_injection_ranges.insert(language, Vec::new()); } } @@ -1123,7 +1129,10 @@ fn get_injections( }; if let Some(language_name) = language_name { - let language = language_for_injection(&language_name, language_registry); + let language = { + let language_name: &str = &language_name; + language_registry.language_for_name_or_extension(language_name) + }; let range = text.anchor_before(step_range.start)..text.anchor_after(step_range.end); if let Some(language) = language { if combined { @@ -1171,15 +1180,6 @@ fn get_injections( } } -fn language_for_injection( - language_name: &str, - language_registry: &LanguageRegistry, -) -> Option> { - language_registry - .language_for_name(language_name) - .or_else(|| language_registry.language_for_extension(language_name)) -} - fn splice_included_ranges( mut ranges: Vec, changed_ranges: &[Range], diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 34117cf39e..54939af8d8 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1802,7 +1802,7 @@ impl Project { ) -> Option<()> { // If the buffer has a language, set it and start the language server if we haven't already. let full_path = buffer.read(cx).file()?.full_path(cx); - let new_language = self.languages.select_language(&full_path)?; + let new_language = self.languages.language_for_path(&full_path)?; buffer.update(cx, |buffer, cx| { if buffer.language().map_or(true, |old_language| { !Arc::ptr_eq(old_language, &new_language) @@ -2211,7 +2211,7 @@ impl Project { }) .collect(); for (worktree_id, worktree_abs_path, full_path) in language_server_lookup_info { - let language = self.languages.select_language(&full_path)?; + let language = self.languages.language_for_path(&full_path)?; self.restart_language_server(worktree_id, worktree_abs_path, language, cx); } @@ -3171,7 +3171,7 @@ impl Project { let signature = this.symbol_signature(&project_path); let language = this .languages - .select_language(&project_path.path) + .language_for_path(&project_path.path) .unwrap_or(adapter_language.clone()); let language_server_name = adapter.name.clone(); Some(async move { @@ -5947,7 +5947,7 @@ impl Project { worktree_id, path: PathBuf::from(serialized_symbol.path).into(), }; - let language = languages.select_language(&path.path); + let language = languages.language_for_path(&path.path); Ok(Symbol { language_server_name: LanguageServerName( serialized_symbol.language_server_name.into(), diff --git a/crates/zed/src/languages.rs b/crates/zed/src/languages.rs index 240d1dc49e..548c07fb82 100644 --- a/crates/zed/src/languages.rs +++ b/crates/zed/src/languages.rs @@ -1,7 +1,5 @@ use anyhow::Context; -use gpui::executor::Background; pub use language::*; -use lazy_static::lazy_static; use rust_embed::RustEmbed; use std::{borrow::Cow, str, sync::Arc}; @@ -32,32 +30,17 @@ mod typescript; #[exclude = "*.rs"] struct LanguageDir; -// TODO - Remove this once the `init` function is synchronous again. -lazy_static! { - pub static ref LANGUAGE_NAMES: Vec = LanguageDir::iter() - .filter_map(|path| { - if path.ends_with("config.toml") { - let config = LanguageDir::get(&path)?; - let config = toml::from_slice::(&config.data).ok()?; - Some(config.name.to_string()) - } else { - None - } - }) - .collect(); -} - -pub async fn init(languages: Arc, _executor: Arc) { +pub fn init(languages: Arc) { for (name, grammar, lsp_adapter) in [ ( "c", tree_sitter_c::language(), - Some(CachedLspAdapter::new(c::CLspAdapter).await), + Some(Box::new(c::CLspAdapter) as Box), ), ( "cpp", tree_sitter_cpp::language(), - Some(CachedLspAdapter::new(c::CLspAdapter).await), + Some(Box::new(c::CLspAdapter)), ), ( "css", @@ -67,17 +50,17 @@ pub async fn init(languages: Arc, _executor: Arc) ( "elixir", tree_sitter_elixir::language(), - Some(CachedLspAdapter::new(elixir::ElixirLspAdapter).await), + Some(Box::new(elixir::ElixirLspAdapter)), ), ( "go", tree_sitter_go::language(), - Some(CachedLspAdapter::new(go::GoLspAdapter).await), + Some(Box::new(go::GoLspAdapter)), ), ( "json", tree_sitter_json::language(), - Some(CachedLspAdapter::new(json::JsonLspAdapter).await), + Some(Box::new(json::JsonLspAdapter)), ), ( "markdown", @@ -87,12 +70,12 @@ pub async fn init(languages: Arc, _executor: Arc) ( "python", tree_sitter_python::language(), - Some(CachedLspAdapter::new(python::PythonLspAdapter).await), + Some(Box::new(python::PythonLspAdapter)), ), ( "rust", tree_sitter_rust::language(), - Some(CachedLspAdapter::new(rust::RustLspAdapter).await), + Some(Box::new(rust::RustLspAdapter)), ), ( "toml", @@ -102,89 +85,82 @@ pub async fn init(languages: Arc, _executor: Arc) ( "tsx", tree_sitter_typescript::language_tsx(), - Some(CachedLspAdapter::new(typescript::TypeScriptLspAdapter).await), + Some(Box::new(typescript::TypeScriptLspAdapter)), ), ( "typescript", tree_sitter_typescript::language_typescript(), - Some(CachedLspAdapter::new(typescript::TypeScriptLspAdapter).await), + Some(Box::new(typescript::TypeScriptLspAdapter)), ), ( "javascript", tree_sitter_typescript::language_tsx(), - Some(CachedLspAdapter::new(typescript::TypeScriptLspAdapter).await), + Some(Box::new(typescript::TypeScriptLspAdapter)), ), ( "html", tree_sitter_html::language(), - Some(CachedLspAdapter::new(html::HtmlLspAdapter).await), + Some(Box::new(html::HtmlLspAdapter)), ), ( "ruby", tree_sitter_ruby::language(), - Some(CachedLspAdapter::new(ruby::RubyLanguageServer).await), + Some(Box::new(ruby::RubyLanguageServer)), ), ( "erb", tree_sitter_embedded_template::language(), - Some(CachedLspAdapter::new(ruby::RubyLanguageServer).await), + Some(Box::new(ruby::RubyLanguageServer)), + ), + ( + "scheme", + tree_sitter_scheme::language(), + None, // + ), + ( + "racket", + tree_sitter_racket::language(), + None, // ), - ("scheme", tree_sitter_scheme::language(), None), - ("racket", tree_sitter_racket::language(), None), ] { - languages.add(language(name, grammar, lsp_adapter)); + languages.register(name, load_config(name), grammar, lsp_adapter, load_queries); } } -pub(crate) fn language( +#[cfg(any(test, feature = "test-support"))] +pub async fn language( name: &str, grammar: tree_sitter::Language, - lsp_adapter: Option>, + lsp_adapter: Option>, ) -> Arc { - let config = toml::from_slice( + Arc::new( + Language::new(load_config(name), Some(grammar)) + .with_lsp_adapter(lsp_adapter) + .await + .with_queries(load_queries(name)) + .unwrap(), + ) +} + +fn load_config(name: &str) -> LanguageConfig { + toml::from_slice( &LanguageDir::get(&format!("{}/config.toml", name)) .unwrap() .data, ) .with_context(|| format!("failed to load config.toml for language {name:?}")) - .unwrap(); + .unwrap() +} - let mut language = Language::new(config, Some(grammar)); - - if let Some(query) = load_query(name, "/highlights") { - language = language - .with_highlights_query(query.as_ref()) - .expect("failed to evaluate highlights query"); +fn load_queries(name: &str) -> LanguageQueries { + LanguageQueries { + highlights: load_query(name, "/highlights"), + brackets: load_query(name, "/brackets"), + indents: load_query(name, "/indents"), + outline: load_query(name, "/outline"), + injections: load_query(name, "/injections"), + overrides: load_query(name, "/overrides"), } - if let Some(query) = load_query(name, "/brackets") { - language = language - .with_brackets_query(query.as_ref()) - .expect("failed to load brackets query"); - } - if let Some(query) = load_query(name, "/indents") { - language = language - .with_indents_query(query.as_ref()) - .expect("failed to load indents query"); - } - if let Some(query) = load_query(name, "/outline") { - language = language - .with_outline_query(query.as_ref()) - .expect("failed to load outline query"); - } - if let Some(query) = load_query(name, "/injections") { - language = language - .with_injection_query(query.as_ref()) - .expect("failed to load injection query"); - } - if let Some(query) = load_query(name, "/overrides") { - language = language - .with_override_query(query.as_ref()) - .expect("failed to load override query"); - } - if let Some(lsp_adapter) = lsp_adapter { - language = language.with_lsp_adapter(lsp_adapter) - } - Arc::new(language) } fn load_query(name: &str, filename_prefix: &str) -> Option> { diff --git a/crates/zed/src/languages/c.rs b/crates/zed/src/languages/c.rs index 712e87101b..9fbb12857f 100644 --- a/crates/zed/src/languages/c.rs +++ b/crates/zed/src/languages/c.rs @@ -248,17 +248,19 @@ impl super::LspAdapter for CLspAdapter { #[cfg(test)] mod tests { - use gpui::MutableAppContext; + use gpui::TestAppContext; use language::{AutoindentMode, Buffer}; use settings::Settings; #[gpui::test] - fn test_c_autoindent(cx: &mut MutableAppContext) { + async fn test_c_autoindent(cx: &mut TestAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); - let language = crate::languages::language("c", tree_sitter_c::language(), None); + cx.update(|cx| { + let mut settings = Settings::test(cx); + settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); + cx.set_global(settings); + }); + let language = crate::languages::language("c", tree_sitter_c::language(), None).await; cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx).with_language(language, cx); diff --git a/crates/zed/src/languages/go.rs b/crates/zed/src/languages/go.rs index 19692fdf44..dc84599e4e 100644 --- a/crates/zed/src/languages/go.rs +++ b/crates/zed/src/languages/go.rs @@ -314,8 +314,9 @@ mod tests { let language = language( "go", tree_sitter_go::language(), - Some(CachedLspAdapter::new(GoLspAdapter).await), - ); + Some(Box::new(GoLspAdapter)), + ) + .await; let theme = SyntaxTheme::new(vec![ ("type".into(), Color::green().into()), diff --git a/crates/zed/src/languages/python.rs b/crates/zed/src/languages/python.rs index ba6ccf7bf0..1391494ab1 100644 --- a/crates/zed/src/languages/python.rs +++ b/crates/zed/src/languages/python.rs @@ -165,17 +165,20 @@ impl LspAdapter for PythonLspAdapter { #[cfg(test)] mod tests { - use gpui::{ModelContext, MutableAppContext}; + use gpui::{ModelContext, TestAppContext}; use language::{AutoindentMode, Buffer}; use settings::Settings; #[gpui::test] - fn test_python_autoindent(cx: &mut MutableAppContext) { + async fn test_python_autoindent(cx: &mut TestAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); - let language = crate::languages::language("python", tree_sitter_python::language(), None); - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); + let language = + crate::languages::language("python", tree_sitter_python::language(), None).await; + cx.update(|cx| { + let mut settings = Settings::test(cx); + settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); + cx.set_global(settings); + }); cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx).with_language(language, cx); diff --git a/crates/zed/src/languages/rust.rs b/crates/zed/src/languages/rust.rs index 30971fef1a..40948d5005 100644 --- a/crates/zed/src/languages/rust.rs +++ b/crates/zed/src/languages/rust.rs @@ -255,8 +255,8 @@ impl LspAdapter for RustLspAdapter { #[cfg(test)] mod tests { use super::*; - use crate::languages::{language, CachedLspAdapter}; - use gpui::{color::Color, MutableAppContext}; + use crate::languages::language; + use gpui::{color::Color, TestAppContext}; use settings::Settings; use theme::SyntaxTheme; @@ -306,8 +306,9 @@ mod tests { let language = language( "rust", tree_sitter_rust::language(), - Some(CachedLspAdapter::new(RustLspAdapter).await), - ); + Some(Box::new(RustLspAdapter)), + ) + .await; let grammar = language.grammar().unwrap(); let theme = SyntaxTheme::new(vec![ ("type".into(), Color::green().into()), @@ -391,8 +392,9 @@ mod tests { let language = language( "rust", tree_sitter_rust::language(), - Some(CachedLspAdapter::new(RustLspAdapter).await), - ); + Some(Box::new(RustLspAdapter)), + ) + .await; let grammar = language.grammar().unwrap(); let theme = SyntaxTheme::new(vec![ ("type".into(), Color::green().into()), @@ -431,12 +433,15 @@ mod tests { } #[gpui::test] - fn test_rust_autoindent(cx: &mut MutableAppContext) { + async fn test_rust_autoindent(cx: &mut TestAppContext) { cx.foreground().set_block_on_ticks(usize::MAX..=usize::MAX); - let language = crate::languages::language("rust", tree_sitter_rust::language(), None); - let mut settings = Settings::test(cx); - settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); - cx.set_global(settings); + cx.update(|cx| { + let mut settings = Settings::test(cx); + settings.editor_overrides.tab_size = Some(2.try_into().unwrap()); + cx.set_global(settings); + }); + + let language = crate::languages::language("rust", tree_sitter_rust::language(), None).await; cx.add_model(|cx| { let mut buffer = Buffer::new(0, "", cx).with_language(language, cx); diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 01b62577ad..5290158dea 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -154,17 +154,17 @@ impl LspAdapter for TypeScriptLspAdapter { #[cfg(test)] mod tests { - - use gpui::MutableAppContext; + use gpui::TestAppContext; use unindent::Unindent; #[gpui::test] - fn test_outline(cx: &mut MutableAppContext) { + async fn test_outline(cx: &mut TestAppContext) { let language = crate::languages::language( "typescript", tree_sitter_typescript::language_typescript(), None, - ); + ) + .await; let text = r#" function a() { @@ -183,7 +183,7 @@ mod tests { let buffer = cx.add_model(|cx| language::Buffer::new(0, text, cx).with_language(language, cx)); - let outline = buffer.read(cx).snapshot().outline(None).unwrap(); + let outline = buffer.read_with(cx, |buffer, _| buffer.snapshot().outline(None).unwrap()); assert_eq!( outline .items diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 3cd01a18a1..56f259339c 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -120,11 +120,10 @@ fn main() { let client = client::Client::new(http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); + languages.set_executor(cx.background().clone()); languages.set_language_server_download_dir(paths::LANGUAGES_DIR.clone()); let languages = Arc::new(languages); - let init_languages = cx - .background() - .spawn(languages::init(languages.clone(), cx.background().clone())); + languages::init(languages.clone()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); watch_keymap_file(keymap_file, cx); @@ -151,14 +150,7 @@ fn main() { cx.spawn(|cx| watch_themes(fs.clone(), themes.clone(), cx)) .detach(); - cx.spawn({ - let languages = languages.clone(); - |cx| async move { - cx.read(|cx| languages.set_theme(cx.global::().theme.clone())); - init_languages.await; - } - }) - .detach(); + languages.set_theme(cx.global::().theme.clone()); cx.observe_global::({ let languages = languages.clone(); move |cx| languages.set_theme(cx.global::().theme.clone()) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index c9f8b2d408..793172a111 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -306,7 +306,7 @@ pub fn initialize_workspace( ) .map(|meta| meta.name) .collect(); - let language_names = &languages::LANGUAGE_NAMES; + let language_names = app_state.languages.language_names(); workspace.project().update(cx, |project, cx| { let action_names = cx.all_action_names().collect::>(); @@ -318,7 +318,7 @@ pub fn initialize_workspace( "schemas": [ { "fileMatch": [schema_file_match(&paths::SETTINGS)], - "schema": settings_file_json_schema(theme_names, language_names), + "schema": settings_file_json_schema(theme_names, &language_names), }, { "fileMatch": [schema_file_match(&paths::KEYMAP)],