Detailed changes
@@ -46,6 +46,8 @@ struct LanguageRegistryState {
available_languages: Vec<AvailableLanguage>,
grammars: HashMap<Arc<str>, AvailableGrammar>,
lsp_adapters: HashMap<Arc<str>, Vec<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>>,
@@ -153,6 +155,7 @@ impl LanguageRegistry {
language_settings: Default::default(),
loading_languages: Default::default(),
lsp_adapters: Default::default(),
+ available_lsp_adapters: HashMap::default(),
subscription: watch::channel(),
theme: Default::default(),
version: 0,
@@ -213,6 +216,38 @@ impl LanguageRegistry {
)
}
+ /// 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, true)
+ }),
+ );
+ }
+
+ /// 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: Arc<str>, adapter: Arc<dyn LspAdapter>) {
self.state
.write()
@@ -789,5 +789,24 @@ mod tests {
),
language_server_names(&["deno", "eslint", "tailwind"])
);
+
+ // Adding a language server not in the list of available languages servers adds it to the list.
+ assert_eq!(
+ LanguageSettings::resolve_language_servers(
+ &[
+ "my-cool-language-server".into(),
+ LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()
+ ],
+ &available_language_servers
+ ),
+ language_server_names(&[
+ "my-cool-language-server",
+ "typescript-language-server",
+ "biome",
+ "deno",
+ "eslint",
+ "tailwind",
+ ])
+ );
}
}
@@ -107,10 +107,7 @@ pub fn init(
language!("cpp", vec![Arc::new(c::CLspAdapter)]);
language!(
"css",
- vec![
- Arc::new(css::CssLspAdapter::new(node_runtime.clone())),
- Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
- ]
+ vec![Arc::new(css::CssLspAdapter::new(node_runtime.clone())),]
);
language!("go", vec![Arc::new(go::GoLspAdapter)]);
language!("gomod");
@@ -160,13 +157,7 @@ pub fn init(
))]
);
language!("ruby", vec![Arc::new(ruby::RubyLanguageServer)]);
- language!(
- "erb",
- vec![
- Arc::new(ruby::RubyLanguageServer),
- Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone())),
- ]
- );
+ language!("erb", vec![Arc::new(ruby::RubyLanguageServer),]);
language!("regex");
language!(
"yaml",
@@ -174,14 +165,42 @@ pub fn init(
);
language!("proto");
+ // Register Tailwind globally as an available language server.
+ //
+ // This will allow users to add Tailwind support for a given language via
+ // the `language_servers` setting:
+ //
+ // ```json
+ // {
+ // "languages": {
+ // "My Language": {
+ // "language_servers": ["tailwindcss-language-server", "..."]
+ // }
+ // }
+ // }
+ // ```
+ languages.register_available_lsp_adapter(
+ LanguageServerName("tailwindcss-language-server".into()),
+ {
+ let node_runtime = node_runtime.clone();
+ move || Arc::new(tailwind::TailwindLspAdapter::new(node_runtime.clone()))
+ },
+ );
+
+ // Register Tailwind for the existing languages that should have it by default.
+ //
+ // This can be driven by the `language_servers` setting once we have a way for
+ // extensions to provide their own default value for that setting.
let tailwind_languages = [
"Astro",
+ "CSS",
+ "ERB",
"HEEX",
"HTML",
+ "JavaScript",
"PHP",
"Svelte",
"TSX",
- "JavaScript",
"Vue.js",
];
@@ -3066,12 +3066,34 @@ impl Project {
.map(|lsp_adapter| lsp_adapter.name.clone())
.collect::<Vec<_>>();
- let enabled_language_servers =
+ let desired_language_servers =
settings.customized_language_servers(&available_language_servers);
- let enabled_lsp_adapters = available_lsp_adapters
- .into_iter()
- .filter(|adapter| enabled_language_servers.contains(&adapter.name))
- .collect::<Vec<_>>();
+
+ let mut enabled_lsp_adapters: Vec<Arc<CachedLspAdapter>> = Vec::new();
+ for desired_language_server in desired_language_servers {
+ if let Some(adapter) = available_lsp_adapters
+ .iter()
+ .find(|adapter| adapter.name == desired_language_server)
+ {
+ enabled_lsp_adapters.push(adapter.clone());
+ continue;
+ }
+
+ if let Some(adapter) = self
+ .languages
+ .load_available_lsp_adapter(&desired_language_server)
+ {
+ self.languages()
+ .register_lsp_adapter(language.name(), adapter.adapter.clone());
+ enabled_lsp_adapters.push(adapter);
+ continue;
+ }
+
+ log::warn!(
+ "no language server found matching '{}'",
+ desired_language_server.0
+ );
+ }
log::info!(
"starting language servers for {language}: {adapters}",
@@ -3083,7 +3105,7 @@ impl Project {
);
for adapter in enabled_lsp_adapters {
- self.start_language_server(worktree, adapter.clone(), language.clone(), cx);
+ self.start_language_server(worktree, adapter, language.clone(), cx);
}
}