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::{FxHashMap, 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 smallvec::SmallVec;use std::{ borrow::{Borrow, Cow}, cell::LazyCell, 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<LanguageName> for SharedString { fn from(value: LanguageName) -> Self { value.0 }} impl From<SharedString> for LanguageName { fn from(value: SharedString) -> Self { LanguageName(value) }} impl AsRef<str> for LanguageName { fn as_ref(&self) -> &str { self.0.as_ref() }} impl Borrow<str> 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<LanguageName> for String { fn from(value: LanguageName) -> Self { let value: &str = &value.0; Self::from(value) }} pub struct LanguageRegistry { state: RwLock<LanguageRegistryState>, language_server_download_dir: Option<Arc<Path>>, executor: BackgroundExecutor, lsp_binary_status_tx: BinaryStatusSender,} struct LanguageRegistryState { next_language_server_id: usize, languages: Vec<Arc<Language>>, language_settings: AllLanguageSettingsContent, available_languages: Vec<AvailableLanguage>, grammars: HashMap<Arc<str>, AvailableGrammar>, lsp_adapters: HashMap<LanguageName, Vec<Arc<CachedLspAdapter>>>, all_lsp_adapters: HashMap<LanguageServerName, Arc<CachedLspAdapter>>, available_lsp_adapters: HashMap<LanguageServerName, Arc<dyn Fn() -> Arc<CachedLspAdapter> + 'static + Send + Sync>>, loading_languages: HashMap<LanguageId, Vec<oneshot::Sender<Result<Arc<Language>>>>>, subscription: (watch::Sender<()>, watch::Receiver<()>), theme: Option<Arc<Theme>>, version: usize, reload_count: usize, #[cfg(any(test, feature = "test-support"))] fake_server_entries: HashMap<LanguageServerName, FakeLanguageServerEntry>,} #[cfg(any(test, feature = "test-support"))]pub struct FakeLanguageServerEntry { pub capabilities: lsp::ServerCapabilities, pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>, pub tx: futures::channel::mpsc::UnboundedSender<lsp::FakeLanguageServer>, pub _server: Option<lsp::FakeLanguageServer>,} #[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<Arc<str>>, matcher: LanguageMatcher, hidden: bool, load: Arc<dyn Fn() -> Result<LoadedLanguage> + '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 }} #[derive(Copy, Clone, Default)]enum LanguageMatchPrecedence { #[default] Undetermined, PathOrContent(usize), UserConfigured(usize),} enum AvailableGrammar { Native(tree_sitter::Language), Loaded(#[allow(unused)] PathBuf, tree_sitter::Language), Loading( #[allow(unused)] PathBuf, Vec<oneshot::Sender<Result<tree_sitter::Language, Arc<anyhow::Error>>>>, ), Unloaded(PathBuf), LoadFailed(Arc<anyhow::Error>),} #[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<Cow<'static, str>>,)] = &[ ("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), ("debug_variables", |q| &mut q.debug_variables), ("textobjects", |q| &mut q.text_objects),]; /// Tree-sitter language queries for a given language.#[derive(Debug, Default)]pub struct LanguageQueries { pub highlights: Option<Cow<'static, str>>, pub brackets: Option<Cow<'static, str>>, pub indents: Option<Cow<'static, str>>, pub outline: Option<Cow<'static, str>>, pub embedding: Option<Cow<'static, str>>, pub injections: Option<Cow<'static, str>>, pub overrides: Option<Cow<'static, str>>, pub redactions: Option<Cow<'static, str>>, pub runnables: Option<Cow<'static, str>>, pub text_objects: Option<Cow<'static, str>>, pub debug_variables: Option<Cow<'static, str>>,} #[derive(Clone, Default)]struct BinaryStatusSender { txs: Arc<Mutex<Vec<mpsc::UnboundedSender<(SharedString, BinaryStatus)>>>>,} pub struct LoadedLanguage { pub config: LanguageConfig, pub queries: LanguageQueries, pub context_provider: Option<Arc<dyn ContextProvider>>, pub toolchain_provider: Option<Arc<dyn ToolchainLister>>,} 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(), 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<Arc<CachedLspAdapter>>, ) { 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<str>], ) { 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<dyn LspAdapter> + '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<Arc<CachedLspAdapter>> { 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<dyn LspAdapter>, ) -> Arc<CachedLspAdapter> { 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<dyn LspAdapter> + 'static, ) -> Arc<CachedLspAdapter> { 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<LanguageName>, mut adapter: crate::FakeLspAdapter, ) -> futures::channel::mpsc::UnboundedReceiver<lsp::FakeLanguageServer> { 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<LanguageName>, 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<Box<dyn Fn(&mut lsp::FakeLanguageServer) + Send + Sync>>, ) -> futures::channel::mpsc::UnboundedReceiver<lsp::FakeLanguageServer> { 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<Arc<str>>, matcher: LanguageMatcher, hidden: bool, load: Arc<dyn Fn() -> Result<LoadedLanguage> + '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;Lines 1–500 of 1253. Load more
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::{FxHashMap, 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 smallvec::SmallVec; use std::{ borrow::{Borrow, Cow}, cell::LazyCell, 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<LanguageName> for SharedString { fn from(value: LanguageName) -> Self { value.0 } } impl From<SharedString> for LanguageName { fn from(value: SharedString) -> Self { LanguageName(value) } } impl AsRef<str> for LanguageName { fn as_ref(&self) -> &str { self.0.as_ref() } } impl Borrow<str> 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<LanguageName> for String { fn from(value: LanguageName) -> Self { let value: &str = &value.0; Self::from(value) } } pub struct LanguageRegistry { state: RwLock<LanguageRegistryState>, language_server_download_dir: Option<Arc<Path>>, executor: BackgroundExecutor, lsp_binary_status_tx: BinaryStatusSender, } struct LanguageRegistryState { next_language_server_id: usize, languages: Vec<Arc<Language>>, language_settings: AllLanguageSettingsContent, available_languages: Vec<AvailableLanguage>, grammars: HashMap<Arc<str>, AvailableGrammar>, lsp_adapters: HashMap<LanguageName, Vec<Arc<CachedLspAdapter>>>, all_lsp_adapters: HashMap<LanguageServerName, Arc<CachedLspAdapter>>, available_lsp_adapters: HashMap<LanguageServerName, Arc<dyn Fn() -> Arc<CachedLspAdapter> + 'static + Send + Sync>>, loading_languages: HashMap<LanguageId, Vec<oneshot::Sender<Result<Arc<Language>>>>>, subscription: (watch::Sender<()>, watch::Receiver<()>), theme: Option<Arc<Theme>>, version: usize, reload_count: usize, #[cfg(any(test, feature = "test-support"))] fake_server_entries: HashMap<LanguageServerName, FakeLanguageServerEntry>, } #[cfg(any(test, feature = "test-support"))] pub struct FakeLanguageServerEntry { pub capabilities: lsp::ServerCapabilities, pub initializer: Option<Box<dyn 'static + Send + Sync + Fn(&mut lsp::FakeLanguageServer)>>, pub tx: futures::channel::mpsc::UnboundedSender<lsp::FakeLanguageServer>, pub _server: Option<lsp::FakeLanguageServer>, } #[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<Arc<str>>, matcher: LanguageMatcher, hidden: bool, load: Arc<dyn Fn() -> Result<LoadedLanguage> + '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 } } #[derive(Copy, Clone, Default)] enum LanguageMatchPrecedence { #[default] Undetermined, PathOrContent(usize), UserConfigured(usize), } enum AvailableGrammar { Native(tree_sitter::Language), Loaded(#[allow(unused)] PathBuf, tree_sitter::Language), Loading( #[allow(unused)] PathBuf, Vec<oneshot::Sender<Result<tree_sitter::Language, Arc<anyhow::Error>>>>, ), Unloaded(PathBuf), LoadFailed(Arc<anyhow::Error>), } #[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<Cow<'static, str>>, )] = &[ ("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), ("debug_variables", |q| &mut q.debug_variables), ("textobjects", |q| &mut q.text_objects), ]; /// Tree-sitter language queries for a given language. #[derive(Debug, Default)] pub struct LanguageQueries { pub highlights: Option<Cow<'static, str>>, pub brackets: Option<Cow<'static, str>>, pub indents: Option<Cow<'static, str>>, pub outline: Option<Cow<'static, str>>, pub embedding: Option<Cow<'static, str>>, pub injections: Option<Cow<'static, str>>, pub overrides: Option<Cow<'static, str>>, pub redactions: Option<Cow<'static, str>>, pub runnables: Option<Cow<'static, str>>, pub text_objects: Option<Cow<'static, str>>, pub debug_variables: Option<Cow<'static, str>>, } #[derive(Clone, Default)] struct BinaryStatusSender { txs: Arc<Mutex<Vec<mpsc::UnboundedSender<(SharedString, BinaryStatus)>>>>, } pub struct LoadedLanguage { pub config: LanguageConfig, pub queries: LanguageQueries, pub context_provider: Option<Arc<dyn ContextProvider>>, pub toolchain_provider: Option<Arc<dyn ToolchainLister>>, } 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(), 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<Arc<CachedLspAdapter>>, ) { 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<str>], ) { 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<dyn LspAdapter> + '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<Arc<CachedLspAdapter>> { 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<dyn LspAdapter>, ) -> Arc<CachedLspAdapter> { 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<dyn LspAdapter> + 'static, ) -> Arc<CachedLspAdapter> { 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<LanguageName>, mut adapter: crate::FakeLspAdapter, ) -> futures::channel::mpsc::UnboundedReceiver<lsp::FakeLanguageServer> { 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<LanguageName>, 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<Box<dyn Fn(&mut lsp::FakeLanguageServer) + Send + Sync>>, ) -> futures::channel::mpsc::UnboundedReceiver<lsp::FakeLanguageServer> { 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<Arc<str>>, matcher: LanguageMatcher, hidden: bool, load: Arc<dyn Fn() -> Result<LoadedLanguage> + '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;