Add language_server_workspace_configuration
to extension API (#10212)
This PR adds the ability for extensions to implement `language_server_workspace_configuration` to provide workspace configuration to the language server. We've used the Dart extension as a motivating example for this, pulling it out into an extension in the process. Release Notes: - Removed built-in support for Dart, in favor of making it available as an extension. The Dart extension will be suggested for download when you open a `.dart` file. --------- Co-authored-by: Max <max@zed.dev> Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
This commit is contained in:
parent
4aaf3459c4
commit
c851e6edba
36 changed files with 586 additions and 187 deletions
|
@ -40,7 +40,6 @@ tree-sitter-bash.workspace = true
|
|||
tree-sitter-c.workspace = true
|
||||
tree-sitter-cpp.workspace = true
|
||||
tree-sitter-css.workspace = true
|
||||
tree-sitter-dart.workspace = true
|
||||
tree-sitter-elixir.workspace = true
|
||||
tree-sitter-elm.workspace = true
|
||||
tree-sitter-embedded-template.workspace = true
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use gpui::AppContext;
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use project::project_settings::ProjectSettings;
|
||||
use serde_json::Value;
|
||||
use settings::Settings;
|
||||
use std::{
|
||||
any::Any,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
pub struct DartLanguageServer;
|
||||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for DartLanguageServer {
|
||||
fn name(&self) -> LanguageServerName {
|
||||
LanguageServerName("dart".into())
|
||||
}
|
||||
|
||||
async fn fetch_latest_server_version(
|
||||
&self,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<Box<dyn 'static + Send + Any>> {
|
||||
Ok(Box::new(()))
|
||||
}
|
||||
|
||||
async fn fetch_server_binary(
|
||||
&self,
|
||||
_: Box<dyn 'static + Send + Any>,
|
||||
_: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Result<LanguageServerBinary> {
|
||||
Err(anyhow!("dart must me installed from dart.dev/get-dart"))
|
||||
}
|
||||
|
||||
async fn cached_server_binary(
|
||||
&self,
|
||||
_: PathBuf,
|
||||
_: &dyn LspAdapterDelegate,
|
||||
) -> Option<LanguageServerBinary> {
|
||||
Some(LanguageServerBinary {
|
||||
path: "dart".into(),
|
||||
env: None,
|
||||
arguments: vec!["language-server".into(), "--protocol=lsp".into()],
|
||||
})
|
||||
}
|
||||
|
||||
fn can_be_reinstalled(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
async fn installation_test_binary(&self, _: PathBuf) -> Option<LanguageServerBinary> {
|
||||
None
|
||||
}
|
||||
|
||||
fn workspace_configuration(&self, _workspace_root: &Path, cx: &mut AppContext) -> Value {
|
||||
let settings = ProjectSettings::get_global(cx)
|
||||
.lsp
|
||||
.get("dart")
|
||||
.and_then(|s| s.settings.clone())
|
||||
.unwrap_or_default();
|
||||
|
||||
serde_json::json!({
|
||||
"dart": settings
|
||||
})
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
("(" @open ")" @close)
|
||||
("[" @open "]" @close)
|
||||
("{" @open "}" @close)
|
||||
("<" @open ">" @close)
|
||||
("\"" @open "\"" @close)
|
||||
("'" @open "'" @close)
|
|
@ -1,13 +0,0 @@
|
|||
name = "Dart"
|
||||
grammar = "dart"
|
||||
path_suffixes = ["dart"]
|
||||
line_comments = ["// "]
|
||||
autoclose_before = ";:.,=}])>"
|
||||
brackets = [
|
||||
{ start = "{", end = "}", close = true, newline = true },
|
||||
{ start = "[", end = "]", close = true, newline = true },
|
||||
{ start = "(", end = ")", close = true, newline = true },
|
||||
{ start = "\"", end = "\"", close = true, newline = false, not_in = ["string"] },
|
||||
{ start = "'", end = "'", close = true, newline = false, not_in = ["string"] },
|
||||
{ start = "/*", end = " */", close = true, newline = false, not_in = ["string", "comment"] },
|
||||
]
|
|
@ -1,209 +0,0 @@
|
|||
(dotted_identifier_list) @string
|
||||
|
||||
; Methods
|
||||
; --------------------
|
||||
(function_type
|
||||
name: (identifier) @function)
|
||||
(super) @function
|
||||
|
||||
; Annotations
|
||||
; --------------------
|
||||
(annotation
|
||||
name: (identifier) @attribute)
|
||||
|
||||
; Operators and Tokens
|
||||
; --------------------
|
||||
(template_substitution
|
||||
"$" @punctuation.special
|
||||
"{" @punctuation.special
|
||||
"}" @punctuation.special
|
||||
) @none
|
||||
|
||||
(template_substitution
|
||||
"$" @punctuation.special
|
||||
(identifier_dollar_escaped) @variable
|
||||
) @none
|
||||
|
||||
(escape_sequence) @string.escape
|
||||
|
||||
[
|
||||
"@"
|
||||
"=>"
|
||||
".."
|
||||
"??"
|
||||
"=="
|
||||
"?"
|
||||
":"
|
||||
"&&"
|
||||
"%"
|
||||
"<"
|
||||
">"
|
||||
"="
|
||||
">="
|
||||
"<="
|
||||
"||"
|
||||
(increment_operator)
|
||||
(is_operator)
|
||||
(prefix_operator)
|
||||
(equality_operator)
|
||||
(additive_operator)
|
||||
] @operator
|
||||
|
||||
[
|
||||
"("
|
||||
")"
|
||||
"["
|
||||
"]"
|
||||
"{"
|
||||
"}"
|
||||
"<"
|
||||
">"
|
||||
] @punctuation.bracket
|
||||
|
||||
; Delimiters
|
||||
; --------------------
|
||||
[
|
||||
";"
|
||||
"."
|
||||
","
|
||||
] @punctuation.delimiter
|
||||
|
||||
; Types
|
||||
; --------------------
|
||||
(class_definition
|
||||
name: (identifier) @type)
|
||||
(constructor_signature
|
||||
name: (identifier) @type)
|
||||
(scoped_identifier
|
||||
scope: (identifier) @type)
|
||||
(function_signature
|
||||
name: (identifier) @function)
|
||||
(getter_signature
|
||||
(identifier) @function)
|
||||
(setter_signature
|
||||
name: (identifier) @function)
|
||||
(enum_declaration
|
||||
name: (identifier) @type)
|
||||
(enum_constant
|
||||
name: (identifier) @type)
|
||||
(type_identifier) @type
|
||||
(void_type) @type
|
||||
|
||||
((scoped_identifier
|
||||
scope: (identifier) @type
|
||||
name: (identifier) @type)
|
||||
(#match? @type "^[a-zA-Z]"))
|
||||
|
||||
(type_identifier) @type
|
||||
|
||||
; Variables
|
||||
; --------------------
|
||||
; var keyword
|
||||
(inferred_type) @keyword
|
||||
|
||||
(const_builtin) @constant.builtin
|
||||
(final_builtin) @constant.builtin
|
||||
|
||||
((identifier) @type
|
||||
(#match? @type "^_?[A-Z]"))
|
||||
|
||||
("Function" @type)
|
||||
|
||||
; properties
|
||||
; TODO: add method/call_expression to grammar and
|
||||
; distinguish method call from variable access
|
||||
(unconditional_assignable_selector
|
||||
(identifier) @property)
|
||||
|
||||
; assignments
|
||||
(assignment_expression
|
||||
left: (assignable_expression) @variable)
|
||||
|
||||
(this) @variable.builtin
|
||||
|
||||
; Literals
|
||||
; --------------------
|
||||
[
|
||||
(hex_integer_literal)
|
||||
(decimal_integer_literal)
|
||||
(decimal_floating_point_literal)
|
||||
; TODO: inaccessible nodes
|
||||
; (octal_integer_literal)
|
||||
; (hex_floating_point_literal)
|
||||
] @number
|
||||
|
||||
(symbol_literal) @symbol
|
||||
(string_literal) @string
|
||||
(true) @boolean
|
||||
(false) @boolean
|
||||
(null_literal) @constant.builtin
|
||||
|
||||
(documentation_comment) @comment
|
||||
(comment) @comment
|
||||
|
||||
; Keywords
|
||||
; --------------------
|
||||
["import" "library" "export"] @keyword.include
|
||||
|
||||
; Reserved words (cannot be used as identifiers)
|
||||
; TODO: "rethrow" @keyword
|
||||
[
|
||||
; "assert"
|
||||
(case_builtin)
|
||||
"extension"
|
||||
"on"
|
||||
"class"
|
||||
"enum"
|
||||
"extends"
|
||||
"in"
|
||||
"is"
|
||||
"new"
|
||||
"return"
|
||||
"super"
|
||||
"with"
|
||||
] @keyword
|
||||
|
||||
|
||||
; Built in identifiers:
|
||||
; alone these are marked as keywords
|
||||
[
|
||||
"abstract"
|
||||
"as"
|
||||
"async"
|
||||
"async*"
|
||||
"yield"
|
||||
"sync*"
|
||||
"await"
|
||||
"covariant"
|
||||
"deferred"
|
||||
"dynamic"
|
||||
"external"
|
||||
"factory"
|
||||
"get"
|
||||
"implements"
|
||||
"interface"
|
||||
"library"
|
||||
"operator"
|
||||
"mixin"
|
||||
"part"
|
||||
"set"
|
||||
"show"
|
||||
"static"
|
||||
"typedef"
|
||||
] @keyword
|
||||
|
||||
; when used as an identifier:
|
||||
((identifier) @variable.builtin
|
||||
(#vim-match? @variable.builtin "^(abstract|as|covariant|deferred|dynamic|export|external|factory|Function|get|implements|import|interface|library|operator|mixin|part|set|static|typedef)$"))
|
||||
|
||||
["if" "else" "switch" "default"] @keyword
|
||||
|
||||
[
|
||||
"try"
|
||||
"throw"
|
||||
"catch"
|
||||
"finally"
|
||||
(break_statement)
|
||||
] @keyword
|
||||
|
||||
["do" "while" "continue" "for"] @keyword
|
|
@ -1,7 +0,0 @@
|
|||
[
|
||||
(if_statement)
|
||||
(for_statement)
|
||||
] @indent
|
||||
|
||||
(_ "{" "}" @end) @indent
|
||||
(_ "(" ")" @end) @indent
|
|
@ -1,18 +0,0 @@
|
|||
(class_definition
|
||||
"class" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
(function_signature
|
||||
name: (_) @name) @item
|
||||
|
||||
(getter_signature
|
||||
"get" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
(setter_signature
|
||||
"set" @context
|
||||
name: (_) @name) @item
|
||||
|
||||
(enum_declaration
|
||||
"enum" @context
|
||||
name: (_) @name) @item
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use gpui::{AppContext, AsyncAppContext, Task};
|
||||
use gpui::{AsyncAppContext, Task};
|
||||
pub use language::*;
|
||||
use lsp::{CompletionItemKind, LanguageServerBinary, SymbolKind};
|
||||
use project::project_settings::ProjectSettings;
|
||||
|
@ -14,7 +14,7 @@ use std::{
|
|||
any::Any,
|
||||
env::consts,
|
||||
ops::Deref,
|
||||
path::{Path, PathBuf},
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering::SeqCst},
|
||||
Arc,
|
||||
|
@ -278,16 +278,22 @@ impl LspAdapter for ElixirLspAdapter {
|
|||
})
|
||||
}
|
||||
|
||||
fn workspace_configuration(&self, _workspace_root: &Path, cx: &mut AppContext) -> Value {
|
||||
let settings = ProjectSettings::get_global(cx)
|
||||
.lsp
|
||||
.get("elixir-ls")
|
||||
.and_then(|s| s.settings.clone())
|
||||
.unwrap_or_default();
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Value> {
|
||||
let settings = cx.update(|cx| {
|
||||
ProjectSettings::get_global(cx)
|
||||
.lsp
|
||||
.get("elixir-ls")
|
||||
.and_then(|s| s.settings.clone())
|
||||
.unwrap_or_default()
|
||||
})?;
|
||||
|
||||
serde_json::json!({
|
||||
Ok(serde_json::json!({
|
||||
"elixirLS": settings
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use gpui::AppContext;
|
||||
use gpui::AsyncAppContext;
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use node_runtime::NodeRuntime;
|
||||
|
@ -94,16 +94,22 @@ impl LspAdapter for ElmLspAdapter {
|
|||
get_cached_server_binary(container_dir, &*self.node).await
|
||||
}
|
||||
|
||||
fn workspace_configuration(&self, _workspace_root: &Path, cx: &mut AppContext) -> Value {
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Value> {
|
||||
// elm-language-server expects workspace didChangeConfiguration notification
|
||||
// params to be the same as lsp initialization_options
|
||||
let override_options = ProjectSettings::get_global(cx)
|
||||
.lsp
|
||||
.get(SERVER_NAME)
|
||||
.and_then(|s| s.initialization_options.clone())
|
||||
.unwrap_or_default();
|
||||
let override_options = cx.update(|cx| {
|
||||
ProjectSettings::get_global(cx)
|
||||
.lsp
|
||||
.get(SERVER_NAME)
|
||||
.and_then(|s| s.initialization_options.clone())
|
||||
.unwrap_or_default()
|
||||
})?;
|
||||
|
||||
match override_options.clone().as_object_mut() {
|
||||
Ok(match override_options.clone().as_object_mut() {
|
||||
Some(op) => {
|
||||
// elm-language-server requests workspace configuration
|
||||
// for the `elmLS` section, so we have to nest
|
||||
|
@ -112,7 +118,7 @@ impl LspAdapter for ElmLspAdapter {
|
|||
serde_json::to_value(op).unwrap_or_default()
|
||||
}
|
||||
None => override_options,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use async_trait::async_trait;
|
|||
use collections::HashMap;
|
||||
use feature_flags::FeatureFlagAppExt;
|
||||
use futures::StreamExt;
|
||||
use gpui::AppContext;
|
||||
use gpui::{AppContext, AsyncAppContext};
|
||||
use language::{LanguageRegistry, LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use node_runtime::NodeRuntime;
|
||||
|
@ -152,10 +152,16 @@ impl LspAdapter for JsonLspAdapter {
|
|||
})))
|
||||
}
|
||||
|
||||
fn workspace_configuration(&self, _workspace_root: &Path, cx: &mut AppContext) -> Value {
|
||||
self.workspace_config
|
||||
.get_or_init(|| Self::get_workspace_config(self.languages.language_names(), cx))
|
||||
.clone()
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Value> {
|
||||
cx.update(|cx| {
|
||||
self.workspace_config
|
||||
.get_or_init(|| Self::get_workspace_config(self.languages.language_names(), cx))
|
||||
.clone()
|
||||
})
|
||||
}
|
||||
|
||||
fn language_ids(&self) -> HashMap<String, String> {
|
||||
|
|
|
@ -13,7 +13,6 @@ use self::{deno::DenoSettings, elixir::ElixirSettings};
|
|||
|
||||
mod c;
|
||||
mod css;
|
||||
mod dart;
|
||||
mod deno;
|
||||
mod elixir;
|
||||
mod elm;
|
||||
|
@ -92,7 +91,6 @@ pub fn init(
|
|||
("typescript", tree_sitter_typescript::language_typescript()),
|
||||
("vue", tree_sitter_vue::language()),
|
||||
("yaml", tree_sitter_yaml::language()),
|
||||
("dart", tree_sitter_dart::language()),
|
||||
]);
|
||||
|
||||
macro_rules! language {
|
||||
|
@ -312,7 +310,6 @@ pub fn init(
|
|||
vec![Arc::new(terraform::TerraformLspAdapter)]
|
||||
);
|
||||
language!("hcl", vec![]);
|
||||
language!("dart", vec![Arc::new(dart::DartLanguageServer {})]);
|
||||
|
||||
languages.register_secondary_lsp_adapter(
|
||||
"Astro".into(),
|
||||
|
|
|
@ -2,7 +2,7 @@ use anyhow::{anyhow, Result};
|
|||
use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use futures::StreamExt;
|
||||
use gpui::AppContext;
|
||||
use gpui::AsyncAppContext;
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::LanguageServerBinary;
|
||||
use node_runtime::NodeRuntime;
|
||||
|
@ -107,12 +107,16 @@ impl LspAdapter for TailwindLspAdapter {
|
|||
})))
|
||||
}
|
||||
|
||||
fn workspace_configuration(&self, _workspace_root: &Path, _: &mut AppContext) -> Value {
|
||||
json!({
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
_cx: &mut AsyncAppContext,
|
||||
) -> Result<Value> {
|
||||
Ok(json!({
|
||||
"tailwindCSS": {
|
||||
"emmetCompletions": true,
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn language_ids(&self) -> HashMap<String, String> {
|
||||
|
|
|
@ -3,7 +3,7 @@ use async_compression::futures::bufread::GzipDecoder;
|
|||
use async_tar::Archive;
|
||||
use async_trait::async_trait;
|
||||
use collections::HashMap;
|
||||
use gpui::AppContext;
|
||||
use gpui::AsyncAppContext;
|
||||
use language::{LanguageServerName, LspAdapter, LspAdapterDelegate};
|
||||
use lsp::{CodeActionKind, LanguageServerBinary};
|
||||
use node_runtime::NodeRuntime;
|
||||
|
@ -245,12 +245,20 @@ impl EsLintLspAdapter {
|
|||
|
||||
#[async_trait(?Send)]
|
||||
impl LspAdapter for EsLintLspAdapter {
|
||||
fn workspace_configuration(&self, workspace_root: &Path, cx: &mut AppContext) -> Value {
|
||||
let eslint_user_settings = ProjectSettings::get_global(cx)
|
||||
.lsp
|
||||
.get(Self::SERVER_NAME)
|
||||
.and_then(|s| s.settings.clone())
|
||||
.unwrap_or_default();
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
delegate: &Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Value> {
|
||||
let workspace_root = delegate.worktree_root_path();
|
||||
|
||||
let eslint_user_settings = cx.update(|cx| {
|
||||
ProjectSettings::get_global(cx)
|
||||
.lsp
|
||||
.get(Self::SERVER_NAME)
|
||||
.and_then(|s| s.settings.clone())
|
||||
.unwrap_or_default()
|
||||
})?;
|
||||
|
||||
let mut code_action_on_save = json!({
|
||||
// We enable this, but without also configuring `code_actions_on_format`
|
||||
|
@ -283,7 +291,7 @@ impl LspAdapter for EsLintLspAdapter {
|
|||
.iter()
|
||||
.any(|file| workspace_root.join(file).is_file());
|
||||
|
||||
json!({
|
||||
Ok(json!({
|
||||
"": {
|
||||
"validate": "on",
|
||||
"rulesCustomizations": [],
|
||||
|
@ -301,7 +309,7 @@ impl LspAdapter for EsLintLspAdapter {
|
|||
"useFlatConfig": use_flat_config,
|
||||
},
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
fn name(&self) -> LanguageServerName {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use anyhow::{anyhow, Result};
|
||||
use async_trait::async_trait;
|
||||
use futures::StreamExt;
|
||||
use gpui::AppContext;
|
||||
use gpui::AsyncAppContext;
|
||||
use language::{
|
||||
language_settings::all_language_settings, LanguageServerName, LspAdapter, LspAdapterDelegate,
|
||||
};
|
||||
|
@ -92,17 +92,26 @@ impl LspAdapter for YamlLspAdapter {
|
|||
) -> Option<LanguageServerBinary> {
|
||||
get_cached_server_binary(container_dir, &*self.node).await
|
||||
}
|
||||
fn workspace_configuration(&self, _workspace_root: &Path, cx: &mut AppContext) -> Value {
|
||||
serde_json::json!({
|
||||
|
||||
async fn workspace_configuration(
|
||||
self: Arc<Self>,
|
||||
_: &Arc<dyn LspAdapterDelegate>,
|
||||
cx: &mut AsyncAppContext,
|
||||
) -> Result<Value> {
|
||||
let tab_size = cx.update(|cx| {
|
||||
all_language_settings(None, cx)
|
||||
.language(Some("YAML"))
|
||||
.tab_size
|
||||
})?;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"yaml": {
|
||||
"keyOrdering": false
|
||||
},
|
||||
"[yaml]": {
|
||||
"editor.tabSize": all_language_settings(None, cx)
|
||||
.language(Some("YAML"))
|
||||
.tab_size,
|
||||
"editor.tabSize": tab_size
|
||||
}
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue