use crate::{ CachedLspAdapter, File, Language, LanguageConfig, LanguageId, LanguageMatcher, LanguageServerName, LspAdapter, PLAIN_TEXT, ToolchainLister, language_settings::{ AllLanguageSettingsContent, LanguageSettingsContent, all_language_settings, }, task_context::ContextProvider, with_parser, }; use anyhow::{Context as _, Result, anyhow}; use collections::{HashMap, HashSet, hash_map}; use futures::{ Future, channel::{mpsc, oneshot}, }; use globset::GlobSet; use gpui::{App, BackgroundExecutor, SharedString}; use lsp::LanguageServerId; use parking_lot::{Mutex, RwLock}; use postage::watch; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::{ borrow::{Borrow, Cow}, ffi::OsStr, ops::Not, path::{Path, PathBuf}, sync::Arc, }; use sum_tree::Bias; use text::{Point, Rope}; use theme::Theme; use unicase::UniCase; use util::{ResultExt, maybe, post_inc}; #[derive( Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema, )] pub struct LanguageName(SharedString); impl LanguageName { pub fn new(s: &str) -> Self { Self(SharedString::new(s)) } pub fn from_proto(s: String) -> Self { Self(SharedString::from(s)) } pub fn to_proto(self) -> String { self.0.to_string() } pub fn lsp_id(&self) -> String { match self.0.as_ref() { "Plain Text" => "plaintext".to_string(), language_name => language_name.to_lowercase(), } } } impl From for SharedString { fn from(value: LanguageName) -> Self { value.0 } } impl AsRef for LanguageName { fn as_ref(&self) -> &str { self.0.as_ref() } } impl Borrow for LanguageName { fn borrow(&self) -> &str { self.0.as_ref() } } impl std::fmt::Display for LanguageName { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}", self.0) } } impl<'a> From<&'a str> for LanguageName { fn from(str: &'a str) -> LanguageName { LanguageName(SharedString::new(str)) } } impl From for String { fn from(value: LanguageName) -> Self { let value: &str = &value.0; Self::from(value) } } pub struct LanguageRegistry { state: RwLock, language_server_download_dir: Option>, executor: BackgroundExecutor, lsp_binary_status_tx: BinaryStatusSender, dap_binary_status_tx: BinaryStatusSender, } struct LanguageRegistryState { next_language_server_id: usize, languages: Vec>, language_settings: AllLanguageSettingsContent, available_languages: Vec, grammars: HashMap, AvailableGrammar>, lsp_adapters: HashMap>>, all_lsp_adapters: HashMap>, available_lsp_adapters: HashMap Arc + 'static + Send + Sync>>, loading_languages: HashMap>>>>, subscription: (watch::Sender<()>, watch::Receiver<()>), theme: Option>, version: usize, reload_count: usize, #[cfg(any(test, feature = "test-support"))] fake_server_entries: HashMap, } #[cfg(any(test, feature = "test-support"))] pub struct FakeLanguageServerEntry { pub capabilities: lsp::ServerCapabilities, pub initializer: Option>, pub tx: futures::channel::mpsc::UnboundedSender, pub _server: Option, } #[derive(Clone, Debug, PartialEq, Eq)] pub enum BinaryStatus { None, CheckingForUpdate, Downloading, Failed { error: String }, } #[derive(Clone)] pub struct AvailableLanguage { id: LanguageId, name: LanguageName, grammar: Option>, matcher: LanguageMatcher, hidden: bool, load: Arc Result + 'static + Send + Sync>, loaded: bool, } impl AvailableLanguage { pub fn name(&self) -> LanguageName { self.name.clone() } pub fn matcher(&self) -> &LanguageMatcher { &self.matcher } pub fn hidden(&self) -> bool { self.hidden } } enum AvailableGrammar { Native(tree_sitter::Language), Loaded(#[allow(unused)] PathBuf, tree_sitter::Language), Loading( #[allow(unused)] PathBuf, Vec>>>, ), Unloaded(PathBuf), LoadFailed(Arc), } #[derive(Debug)] pub struct LanguageNotFound; impl std::fmt::Display for LanguageNotFound { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "language not found") } } pub const QUERY_FILENAME_PREFIXES: &[( &str, fn(&mut LanguageQueries) -> &mut Option>, )] = &[ ("highlights", |q| &mut q.highlights), ("brackets", |q| &mut q.brackets), ("outline", |q| &mut q.outline), ("indents", |q| &mut q.indents), ("embedding", |q| &mut q.embedding), ("injections", |q| &mut q.injections), ("overrides", |q| &mut q.overrides), ("redactions", |q| &mut q.redactions), ("runnables", |q| &mut q.runnables), ("textobjects", |q| &mut q.text_objects), ]; /// Tree-sitter language queries for a given language. #[derive(Debug, Default)] pub struct LanguageQueries { pub highlights: Option>, pub brackets: Option>, pub indents: Option>, pub outline: Option>, pub embedding: Option>, pub injections: Option>, pub overrides: Option>, pub redactions: Option>, pub runnables: Option>, pub text_objects: Option>, } #[derive(Clone, Default)] struct BinaryStatusSender { txs: Arc>>>, } pub struct LoadedLanguage { pub config: LanguageConfig, pub queries: LanguageQueries, pub context_provider: Option>, pub toolchain_provider: Option>, } impl LanguageRegistry { pub fn new(executor: BackgroundExecutor) -> Self { let this = Self { state: RwLock::new(LanguageRegistryState { next_language_server_id: 0, languages: Vec::new(), available_languages: Vec::new(), grammars: Default::default(), language_settings: Default::default(), loading_languages: Default::default(), lsp_adapters: Default::default(), all_lsp_adapters: Default::default(), available_lsp_adapters: HashMap::default(), subscription: watch::channel(), theme: Default::default(), version: 0, reload_count: 0, #[cfg(any(test, feature = "test-support"))] fake_server_entries: Default::default(), }), language_server_download_dir: None, lsp_binary_status_tx: Default::default(), dap_binary_status_tx: Default::default(), executor, }; this.add(PLAIN_TEXT.clone()); this } #[cfg(any(test, feature = "test-support"))] pub fn test(executor: BackgroundExecutor) -> Self { let mut this = Self::new(executor); this.language_server_download_dir = Some(Path::new("/the-download-dir").into()); this } /// Clears out all of the loaded languages and reload them from scratch. pub fn reload(&self) { self.state.write().reload(); } /// Reorders the list of language servers for the given language. /// /// Uses the provided list of ordered [`CachedLspAdapters`] as the desired order. /// /// Any existing language servers not present in `ordered_lsp_adapters` will be /// appended to the end. pub fn reorder_language_servers( &self, language: &LanguageName, ordered_lsp_adapters: Vec>, ) { self.state .write() .reorder_language_servers(language, ordered_lsp_adapters); } /// Removes the specified languages and grammars from the registry. pub fn remove_languages( &self, languages_to_remove: &[LanguageName], grammars_to_remove: &[Arc], ) { self.state .write() .remove_languages(languages_to_remove, grammars_to_remove) } pub fn remove_lsp_adapter(&self, language_name: &LanguageName, name: &LanguageServerName) { let mut state = self.state.write(); if let Some(adapters) = state.lsp_adapters.get_mut(language_name) { adapters.retain(|adapter| &adapter.name != name) } state.version += 1; state.reload_count += 1; *state.subscription.0.borrow_mut() = (); } #[cfg(any(feature = "test-support", test))] pub fn register_test_language(&self, config: LanguageConfig) { self.register_language( config.name.clone(), config.grammar.clone(), config.matcher.clone(), config.hidden, Arc::new(move || { Ok(LoadedLanguage { config: config.clone(), queries: Default::default(), toolchain_provider: None, context_provider: None, }) }), ) } /// Registers an available language server adapter. /// /// The language server is registered under the language server name, but /// not bound to a particular language. /// /// When a language wants to load this particular language server, it will /// invoke the `load` function. pub fn register_available_lsp_adapter( &self, name: LanguageServerName, load: impl Fn() -> Arc + 'static + Send + Sync, ) { self.state.write().available_lsp_adapters.insert( name, Arc::new(move || { let lsp_adapter = load(); CachedLspAdapter::new(lsp_adapter) }), ); } /// Loads the language server adapter for the language server with the given name. pub fn load_available_lsp_adapter( &self, name: &LanguageServerName, ) -> Option> { let state = self.state.read(); let load_lsp_adapter = state.available_lsp_adapters.get(name)?; Some(load_lsp_adapter()) } pub fn register_lsp_adapter( &self, language_name: LanguageName, adapter: Arc, ) -> Arc { let cached = CachedLspAdapter::new(adapter); let mut state = self.state.write(); state .lsp_adapters .entry(language_name) .or_default() .push(cached.clone()); state .all_lsp_adapters .insert(cached.name.clone(), cached.clone()); cached } pub fn get_or_register_lsp_adapter( &self, language_name: LanguageName, server_name: LanguageServerName, build_adapter: impl FnOnce() -> Arc + 'static, ) -> Arc { let registered = self .state .write() .lsp_adapters .entry(language_name.clone()) .or_default() .iter() .find(|cached_adapter| cached_adapter.name == server_name) .cloned(); if let Some(found) = registered { found } else { let adapter = build_adapter(); self.register_lsp_adapter(language_name, adapter) } } /// Register a fake language server and adapter /// The returned channel receives a new instance of the language server every time it is started #[cfg(any(feature = "test-support", test))] pub fn register_fake_lsp( &self, language_name: impl Into, mut adapter: crate::FakeLspAdapter, ) -> futures::channel::mpsc::UnboundedReceiver { let language_name = language_name.into(); let adapter_name = LanguageServerName(adapter.name.into()); let capabilities = adapter.capabilities.clone(); let initializer = adapter.initializer.take(); let adapter = CachedLspAdapter::new(Arc::new(adapter)); { let mut state = self.state.write(); state .lsp_adapters .entry(language_name.clone()) .or_default() .push(adapter.clone()); state.all_lsp_adapters.insert(adapter.name(), adapter); } self.register_fake_language_server(adapter_name, capabilities, initializer) } /// Register a fake lsp adapter (without the language server) /// The returned channel receives a new instance of the language server every time it is started #[cfg(any(feature = "test-support", test))] pub fn register_fake_lsp_adapter( &self, language_name: impl Into, adapter: crate::FakeLspAdapter, ) { let language_name = language_name.into(); let mut state = self.state.write(); let cached_adapter = CachedLspAdapter::new(Arc::new(adapter)); state .lsp_adapters .entry(language_name.clone()) .or_default() .push(cached_adapter.clone()); state .all_lsp_adapters .insert(cached_adapter.name(), cached_adapter); } /// Register a fake language server (without the adapter) /// The returned channel receives a new instance of the language server every time it is started #[cfg(any(feature = "test-support", test))] pub fn register_fake_language_server( &self, lsp_name: LanguageServerName, capabilities: lsp::ServerCapabilities, initializer: Option>, ) -> futures::channel::mpsc::UnboundedReceiver { let (servers_tx, servers_rx) = futures::channel::mpsc::unbounded(); self.state.write().fake_server_entries.insert( lsp_name, FakeLanguageServerEntry { tx: servers_tx, capabilities, initializer, _server: None, }, ); servers_rx } /// Adds a language to the registry, which can be loaded if needed. pub fn register_language( &self, name: LanguageName, grammar_name: Option>, matcher: LanguageMatcher, hidden: bool, load: Arc Result + 'static + Send + Sync>, ) { let state = &mut *self.state.write(); for existing_language in &mut state.available_languages { if existing_language.name == name { existing_language.grammar = grammar_name; existing_language.matcher = matcher; existing_language.load = load; return; } } state.available_languages.push(AvailableLanguage { id: LanguageId::new(), name, grammar: grammar_name, matcher, load, hidden, loaded: false, }); state.version += 1; state.reload_count += 1; *state.subscription.0.borrow_mut() = (); } /// Adds grammars to the registry. Language configurations reference a grammar by name. The /// grammar controls how the source code is parsed. pub fn register_native_grammars( &self, grammars: impl IntoIterator>, impl Into)>, ) { self.state.write().grammars.extend( grammars .into_iter() .map(|(name, grammar)| (name.into(), AvailableGrammar::Native(grammar.into()))), ); } /// Adds paths to WASM grammar files, which can be loaded if needed. pub fn register_wasm_grammars( &self, grammars: impl IntoIterator>, PathBuf)>, ) { let mut state = self.state.write(); state.grammars.extend( grammars .into_iter() .map(|(name, path)| (name.into(), AvailableGrammar::Unloaded(path))), ); state.version += 1; state.reload_count += 1; *state.subscription.0.borrow_mut() = (); } pub fn language_settings(&self) -> AllLanguageSettingsContent { self.state.read().language_settings.clone() } pub fn language_names(&self) -> Vec { let state = self.state.read(); let mut result = state .available_languages .iter() .filter_map(|l| l.loaded.not().then_some(l.name.to_string())) .chain(state.languages.iter().map(|l| l.config.name.to_string())) .collect::>(); result.sort_unstable_by_key(|language_name| language_name.to_lowercase()); result } pub fn grammar_names(&self) -> Vec> { let state = self.state.read(); let mut result = state.grammars.keys().cloned().collect::>(); result.sort_unstable_by_key(|grammar_name| grammar_name.to_lowercase()); result } /// Add a pre-loaded language to the registry. pub fn add(&self, language: Arc) { let mut state = self.state.write(); state.available_languages.push(AvailableLanguage { id: language.id, name: language.name(), grammar: language.config.grammar.clone(), matcher: language.config.matcher.clone(), hidden: language.config.hidden, load: Arc::new(|| Err(anyhow!("already loaded"))), loaded: true, }); state.add(language); } pub fn subscribe(&self) -> watch::Receiver<()> { self.state.read().subscription.1.clone() } /// Returns the number of times that the registry has been changed, /// by adding languages or reloading. pub fn version(&self) -> usize { self.state.read().version } /// Returns the number of times that the registry has been reloaded. pub fn reload_count(&self) -> usize { self.state.read().reload_count } pub fn set_theme(&self, theme: Arc) { let mut state = self.state.write(); state.theme = Some(theme.clone()); for language in &state.languages { language.set_theme(theme.syntax()); } } pub fn set_language_server_download_dir(&mut self, path: impl Into>) { self.language_server_download_dir = Some(path.into()); } pub fn language_for_name( self: &Arc, name: &str, ) -> impl Future>> + use<> { let name = UniCase::new(name); let rx = self.get_or_load_language(|language_name, _| { if UniCase::new(&language_name.0) == name { 1 } else { 0 } }); async move { rx.await? } } pub fn language_for_name_or_extension( self: &Arc, string: &str, ) -> impl Future>> { let string = UniCase::new(string); let rx = self.get_or_load_language(|name, config| { if UniCase::new(&name.0) == string || config .path_suffixes .iter() .any(|suffix| UniCase::new(suffix) == string) { 1 } else { 0 } }); async move { rx.await? } } pub fn available_language_for_name(self: &Arc, name: &str) -> Option { let state = self.state.read(); state .available_languages .iter() .find(|l| l.name.0.as_ref() == name) .cloned() } pub fn language_for_file( self: &Arc, file: &Arc, content: Option<&Rope>, cx: &App, ) -> Option { let user_file_types = all_language_settings(Some(file), cx); self.language_for_file_internal( &file.full_path(cx), content, Some(&user_file_types.file_types), ) } pub fn language_for_file_path<'a>( self: &Arc, path: &'a Path, ) -> impl Future>> + 'a { let available_language = self.language_for_file_internal(path, None, None); let this = self.clone(); async move { if let Some(language) = available_language { this.load_language(&language).await? } else { Err(anyhow!(LanguageNotFound)) } } } fn language_for_file_internal( self: &Arc, path: &Path, content: Option<&Rope>, user_file_types: Option<&HashMap, GlobSet>>, ) -> Option { let filename = path.file_name().and_then(|name| name.to_str()); // `Path.extension()` returns None for files with a leading '.' // and no other extension which is not the desired behavior here, // as we want `.zshrc` to result in extension being `Some("zshrc")` let extension = filename.and_then(|filename| filename.split('.').next_back()); let path_suffixes = [extension, filename, path.to_str()]; let empty = GlobSet::empty(); self.find_matching_language(move |language_name, config| { let path_matches_default_suffix = config .path_suffixes .iter() .any(|suffix| path_suffixes.contains(&Some(suffix.as_str()))); let custom_suffixes = user_file_types .and_then(|types| types.get(language_name.as_ref())) .unwrap_or(&empty); let path_matches_custom_suffix = path_suffixes .iter() .map(|suffix| suffix.unwrap_or("")) .any(|suffix| custom_suffixes.is_match(suffix)); let content_matches = content.zip(config.first_line_pattern.as_ref()).map_or( false, |(content, pattern)| { let end = content.clip_point(Point::new(0, 256), Bias::Left); let end = content.point_to_offset(end); let text = content.chunks_in_range(0..end).collect::(); pattern.is_match(&text) }, ); if path_matches_custom_suffix { 2 } else if path_matches_default_suffix || content_matches { 1 } else { 0 } }) } fn find_matching_language( self: &Arc, callback: impl Fn(&LanguageName, &LanguageMatcher) -> usize, ) -> Option { let state = self.state.read(); let available_language = state .available_languages .iter() .filter_map(|language| { let score = callback(&language.name, &language.matcher); if score > 0 { Some((language.clone(), score)) } else { None } }) .max_by_key(|e| e.1) .clone() .map(|(available_language, _)| available_language); drop(state); available_language } pub fn load_language( self: &Arc, language: &AvailableLanguage, ) -> oneshot::Receiver>> { let (tx, rx) = oneshot::channel(); let mut state = self.state.write(); // If the language is already loaded, resolve with it immediately. for loaded_language in state.languages.iter() { if loaded_language.id == language.id { tx.send(Ok(loaded_language.clone())).unwrap(); return rx; } } match state.loading_languages.entry(language.id) { // If the language is already being loaded, then add this // channel to a list that will be sent to when the load completes. hash_map::Entry::Occupied(mut entry) => entry.get_mut().push(tx), // Otherwise, start loading the language. hash_map::Entry::Vacant(entry) => { let this = self.clone(); let id = language.id; let name = language.name.clone(); let language_load = language.load.clone(); self.executor .spawn(async move { let language = async { let loaded_language = (language_load)()?; if let Some(grammar) = loaded_language.config.grammar.clone() { let grammar = Some(this.get_or_load_grammar(grammar).await?); Language::new_with_id(id, loaded_language.config, grammar) .with_context_provider(loaded_language.context_provider) .with_toolchain_lister(loaded_language.toolchain_provider) .with_queries(loaded_language.queries) } else { Ok(Language::new_with_id(id, loaded_language.config, None) .with_context_provider(loaded_language.context_provider) .with_toolchain_lister(loaded_language.toolchain_provider)) } } .await; match language { Ok(language) => { let language = Arc::new(language); let mut state = this.state.write(); state.add(language.clone()); state.mark_language_loaded(id); if let Some(mut txs) = state.loading_languages.remove(&id) { for tx in txs.drain(..) { let _ = tx.send(Ok(language.clone())); } } } Err(e) => { log::error!("failed to load language {name}:\n{:?}", e); let mut state = this.state.write(); state.mark_language_loaded(id); if let Some(mut txs) = state.loading_languages.remove(&id) { for tx in txs.drain(..) { let _ = tx.send(Err(anyhow!( "failed to load language {}: {}", name, e ))); } } } }; }) .detach(); entry.insert(vec![tx]); } } drop(state); rx } fn get_or_load_language( self: &Arc, callback: impl Fn(&LanguageName, &LanguageMatcher) -> usize, ) -> oneshot::Receiver>> { let Some(language) = self.find_matching_language(callback) else { let (tx, rx) = oneshot::channel(); let _ = tx.send(Err(anyhow!(LanguageNotFound))); return rx; }; self.load_language(&language) } fn get_or_load_grammar( self: &Arc, name: Arc, ) -> impl Future> { let (tx, rx) = oneshot::channel(); let mut state = self.state.write(); if let Some(grammar) = state.grammars.get_mut(name.as_ref()) { match grammar { AvailableGrammar::LoadFailed(error) => { tx.send(Err(error.clone())).ok(); } AvailableGrammar::Native(grammar) | AvailableGrammar::Loaded(_, grammar) => { tx.send(Ok(grammar.clone())).ok(); } AvailableGrammar::Loading(_, txs) => { txs.push(tx); } AvailableGrammar::Unloaded(wasm_path) => { let this = self.clone(); let wasm_path = wasm_path.clone(); *grammar = AvailableGrammar::Loading(wasm_path.clone(), vec![tx]); self.executor .spawn(async move { let grammar_result = maybe!({ let wasm_bytes = std::fs::read(&wasm_path)?; let grammar_name = wasm_path .file_stem() .and_then(OsStr::to_str) .ok_or_else(|| anyhow!("invalid grammar filename"))?; anyhow::Ok(with_parser(|parser| { let mut store = parser.take_wasm_store().unwrap(); let grammar = store.load_language(grammar_name, &wasm_bytes); parser.set_wasm_store(store).unwrap(); grammar })?) }) .map_err(Arc::new); let value = match &grammar_result { Ok(grammar) => AvailableGrammar::Loaded(wasm_path, grammar.clone()), Err(error) => AvailableGrammar::LoadFailed(error.clone()), }; let old_value = this.state.write().grammars.insert(name, value); if let Some(AvailableGrammar::Loading(_, txs)) = old_value { for tx in txs { tx.send(grammar_result.clone()).ok(); } } }) .detach(); } } } else { tx.send(Err(Arc::new(anyhow!("no such grammar {}", name)))) .ok(); } async move { rx.await?.map_err(|e| anyhow!(e)) } } pub fn to_vec(&self) -> Vec> { self.state.read().languages.to_vec() } pub fn lsp_adapters(&self, language_name: &LanguageName) -> Vec> { self.state .read() .lsp_adapters .get(language_name) .cloned() .unwrap_or_default() } pub fn all_lsp_adapters(&self) -> Vec> { self.state .read() .all_lsp_adapters .values() .cloned() .collect() } pub fn adapter_for_name(&self, name: &LanguageServerName) -> Option> { self.state.read().all_lsp_adapters.get(name).cloned() } pub fn update_lsp_status(&self, server_name: LanguageServerName, status: BinaryStatus) { self.lsp_binary_status_tx.send(server_name.0, status); } pub fn update_dap_status(&self, server_name: LanguageServerName, status: BinaryStatus) { self.dap_binary_status_tx.send(server_name.0, status); } pub fn next_language_server_id(&self) -> LanguageServerId { self.state.write().next_language_server_id() } pub fn language_server_download_dir(&self, name: &LanguageServerName) -> Option> { self.language_server_download_dir .as_ref() .map(|dir| Arc::from(dir.join(name.0.as_ref()))) } #[cfg(any(test, feature = "test-support"))] pub fn create_fake_language_server( &self, server_id: LanguageServerId, name: &LanguageServerName, binary: lsp::LanguageServerBinary, cx: &mut gpui::AsyncApp, ) -> Option { use gpui::AppContext as _; let mut state = self.state.write(); let fake_entry = state.fake_server_entries.get_mut(&name)?; let (server, mut fake_server) = lsp::FakeLanguageServer::new( server_id, binary, name.0.to_string(), fake_entry.capabilities.clone(), cx, ); fake_entry._server = Some(fake_server.clone()); if let Some(initializer) = &fake_entry.initializer { initializer(&mut fake_server); } let tx = fake_entry.tx.clone(); cx.background_spawn(async move { if fake_server .try_receive_notification::() .await .is_some() { tx.unbounded_send(fake_server.clone()).ok(); } }) .detach(); Some(server) } pub fn language_server_binary_statuses( &self, ) -> mpsc::UnboundedReceiver<(SharedString, BinaryStatus)> { self.lsp_binary_status_tx.subscribe() } pub fn dap_server_binary_statuses( &self, ) -> mpsc::UnboundedReceiver<(SharedString, BinaryStatus)> { self.dap_binary_status_tx.subscribe() } pub async fn delete_server_container(&self, name: LanguageServerName) { log::info!("deleting server container"); let Some(dir) = self.language_server_download_dir(&name) else { return; }; smol::fs::remove_dir_all(dir) .await .context("server container removal") .log_err(); } } impl LanguageRegistryState { fn next_language_server_id(&mut self) -> LanguageServerId { LanguageServerId(post_inc(&mut self.next_language_server_id)) } fn add(&mut self, language: Arc) { if let Some(theme) = self.theme.as_ref() { language.set_theme(theme.syntax()); } self.language_settings.languages.insert( language.name(), LanguageSettingsContent { tab_size: language.config.tab_size, hard_tabs: language.config.hard_tabs, soft_wrap: language.config.soft_wrap, auto_indent_on_paste: language.config.auto_indent_on_paste, ..Default::default() } .clone(), ); self.languages.push(language); self.version += 1; *self.subscription.0.borrow_mut() = (); } fn reload(&mut self) { self.languages.clear(); self.version += 1; self.reload_count += 1; for language in &mut self.available_languages { language.loaded = false; } *self.subscription.0.borrow_mut() = (); } /// Reorders the list of language servers for the given language. /// /// Uses the provided list of ordered [`CachedLspAdapters`] as the desired order. /// /// Any existing language servers not present in `ordered_lsp_adapters` will be /// appended to the end. fn reorder_language_servers( &mut self, language_name: &LanguageName, ordered_lsp_adapters: Vec>, ) { let Some(lsp_adapters) = self.lsp_adapters.get_mut(language_name) else { return; }; let ordered_lsp_adapter_ids = ordered_lsp_adapters .iter() .map(|lsp_adapter| lsp_adapter.name.clone()) .collect::>(); let mut new_lsp_adapters = ordered_lsp_adapters; for adapter in lsp_adapters.iter() { if !ordered_lsp_adapter_ids.contains(&adapter.name) { new_lsp_adapters.push(adapter.clone()); } } *lsp_adapters = new_lsp_adapters; } fn remove_languages( &mut self, languages_to_remove: &[LanguageName], grammars_to_remove: &[Arc], ) { if languages_to_remove.is_empty() && grammars_to_remove.is_empty() { return; } self.languages .retain(|language| !languages_to_remove.contains(&language.name())); self.available_languages .retain(|language| !languages_to_remove.contains(&language.name)); self.grammars .retain(|name, _| !grammars_to_remove.contains(name)); self.version += 1; self.reload_count += 1; *self.subscription.0.borrow_mut() = (); } /// Mark the given language as having been loaded, so that the /// language registry won't try to load it again. fn mark_language_loaded(&mut self, id: LanguageId) { for language in &mut self.available_languages { if language.id == id { language.loaded = true; break; } } } } impl BinaryStatusSender { fn subscribe(&self) -> mpsc::UnboundedReceiver<(SharedString, BinaryStatus)> { let (tx, rx) = mpsc::unbounded(); self.txs.lock().push(tx); rx } fn send(&self, name: SharedString, status: BinaryStatus) { let mut txs = self.txs.lock(); txs.retain(|tx| tx.unbounded_send((name.clone(), status.clone())).is_ok()); } }