From b6eadc9af8b9c5a50be16d4b045b751f5f5f4031 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 30 Nov 2023 13:25:25 +0200 Subject: [PATCH] Fix TypeScript diagnostics (#3457) Deals with https://github.com/zed-industries/community/issues/2124 * sends more ClientCapabilities LSP data, diagnostics capabilities in particular: those are now required by typescript-language-server LSP to start publishing diagnostics * sends more parameters during eslint workspace initialization, so it is able to correctly look up project's typescript config Presumably, it's not enough and some convoluted project set ups may break still, but let's wait for examples and feedback. Release Notes: - Fixed typescript-language-server diagnostics not appearing for newer server versions; fixed eslint diagnostics using wrong directory for typescript config lookup --- crates/language/src/language.rs | 10 +++++--- crates/language2/src/language2.rs | 10 +++++--- crates/lsp/src/lsp.rs | 33 +++++++++++++++++++------ crates/lsp2/src/lsp2.rs | 33 +++++++++++++++++++------ crates/project/src/project.rs | 22 ++++++++++++----- crates/project2/src/project2.rs | 22 ++++++++++++----- crates/zed/src/languages/json.rs | 1 + crates/zed/src/languages/php.rs | 1 - crates/zed/src/languages/tailwind.rs | 6 ++++- crates/zed/src/languages/typescript.rs | 13 ++++++++-- crates/zed/src/languages/yaml.rs | 6 ++++- crates/zed2/src/languages/json.rs | 1 + crates/zed2/src/languages/php.rs | 1 - crates/zed2/src/languages/tailwind.rs | 6 ++++- crates/zed2/src/languages/typescript.rs | 13 ++++++++-- crates/zed2/src/languages/yaml.rs | 6 ++++- 16 files changed, 140 insertions(+), 44 deletions(-) diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index af7504529cefbee215fb96e53798242da340a4d6..811e54940672ee075993d8cd981c945199409675 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -197,8 +197,12 @@ impl CachedLspAdapter { self.adapter.code_action_kinds() } - pub fn workspace_configuration(&self, cx: &mut AppContext) -> BoxFuture<'static, Value> { - self.adapter.workspace_configuration(cx) + pub fn workspace_configuration( + &self, + workspace_root: &Path, + cx: &mut AppContext, + ) -> BoxFuture<'static, Value> { + self.adapter.workspace_configuration(workspace_root, cx) } pub fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) { @@ -312,7 +316,7 @@ pub trait LspAdapter: 'static + Send + Sync { None } - fn workspace_configuration(&self, _: &mut AppContext) -> BoxFuture<'static, Value> { + fn workspace_configuration(&self, _: &Path, _: &mut AppContext) -> BoxFuture<'static, Value> { futures::future::ready(serde_json::json!({})).boxed() } diff --git a/crates/language2/src/language2.rs b/crates/language2/src/language2.rs index 5c17592f0ca3ff2a1a352954b79e2ed0a937079e..8fdf524f69e16a219bb264504051bb96f7e565ad 100644 --- a/crates/language2/src/language2.rs +++ b/crates/language2/src/language2.rs @@ -200,8 +200,12 @@ impl CachedLspAdapter { self.adapter.code_action_kinds() } - pub fn workspace_configuration(&self, cx: &mut AppContext) -> BoxFuture<'static, Value> { - self.adapter.workspace_configuration(cx) + pub fn workspace_configuration( + &self, + workspace_root: &Path, + cx: &mut AppContext, + ) -> BoxFuture<'static, Value> { + self.adapter.workspace_configuration(workspace_root, cx) } pub fn process_diagnostics(&self, params: &mut lsp::PublishDiagnosticsParams) { @@ -315,7 +319,7 @@ pub trait LspAdapter: 'static + Send + Sync { None } - fn workspace_configuration(&self, _: &mut AppContext) -> BoxFuture<'static, Value> { + fn workspace_configuration(&self, _: &Path, _: &mut AppContext) -> BoxFuture<'static, Value> { futures::future::ready(serde_json::json!({})).boxed() } diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 98fd81f012f163402af47a840ae765e8760b062b..dc5b63d222d0f1545dfce7eab0748f5fd9c23e2d 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -429,8 +429,8 @@ impl LanguageServer { let root_uri = Url::from_file_path(&self.root_path).unwrap(); #[allow(deprecated)] let params = InitializeParams { - process_id: Default::default(), - root_path: Default::default(), + process_id: None, + root_path: None, root_uri: Some(root_uri.clone()), initialization_options: options, capabilities: ClientCapabilities { @@ -451,12 +451,15 @@ impl LanguageServer { inlay_hint: Some(InlayHintWorkspaceClientCapabilities { refresh_support: Some(true), }), + diagnostic: Some(DiagnosticWorkspaceClientCapabilities { + refresh_support: None, + }), ..Default::default() }), text_document: Some(TextDocumentClientCapabilities { definition: Some(GotoCapability { link_support: Some(true), - ..Default::default() + dynamic_registration: None, }), code_action: Some(CodeActionClientCapabilities { code_action_literal_support: Some(CodeActionLiteralSupport { @@ -501,7 +504,7 @@ impl LanguageServer { }), hover: Some(HoverClientCapabilities { content_format: Some(vec![MarkupKind::Markdown]), - ..Default::default() + dynamic_registration: None, }), inlay_hint: Some(InlayHintClientCapabilities { resolve_support: Some(InlayHintResolveClientCapabilities { @@ -515,6 +518,20 @@ impl LanguageServer { }), dynamic_registration: Some(false), }), + publish_diagnostics: Some(PublishDiagnosticsClientCapabilities { + related_information: Some(true), + ..Default::default() + }), + formatting: Some(DynamicRegistrationClientCapabilities { + dynamic_registration: None, + }), + on_type_formatting: Some(DynamicRegistrationClientCapabilities { + dynamic_registration: None, + }), + diagnostic: Some(DiagnosticClientCapabilities { + related_document_support: Some(true), + dynamic_registration: None, + }), ..Default::default() }), experimental: Some(json!({ @@ -524,15 +541,15 @@ impl LanguageServer { work_done_progress: Some(true), ..Default::default() }), - ..Default::default() + general: None, }, - trace: Default::default(), + trace: None, workspace_folders: Some(vec![WorkspaceFolder { uri: root_uri, name: Default::default(), }]), - client_info: Default::default(), - locale: Default::default(), + client_info: None, + locale: None, }; let response = self.request::(params).await?; diff --git a/crates/lsp2/src/lsp2.rs b/crates/lsp2/src/lsp2.rs index 356d029c587b57aaaf182bb090d82d42f14123d9..788c424373deca7c1490dd954fa005e0943d8a99 100644 --- a/crates/lsp2/src/lsp2.rs +++ b/crates/lsp2/src/lsp2.rs @@ -434,8 +434,8 @@ impl LanguageServer { let root_uri = Url::from_file_path(&self.root_path).unwrap(); #[allow(deprecated)] let params = InitializeParams { - process_id: Default::default(), - root_path: Default::default(), + process_id: None, + root_path: None, root_uri: Some(root_uri.clone()), initialization_options: options, capabilities: ClientCapabilities { @@ -456,12 +456,15 @@ impl LanguageServer { inlay_hint: Some(InlayHintWorkspaceClientCapabilities { refresh_support: Some(true), }), + diagnostic: Some(DiagnosticWorkspaceClientCapabilities { + refresh_support: None, + }), ..Default::default() }), text_document: Some(TextDocumentClientCapabilities { definition: Some(GotoCapability { link_support: Some(true), - ..Default::default() + dynamic_registration: None, }), code_action: Some(CodeActionClientCapabilities { code_action_literal_support: Some(CodeActionLiteralSupport { @@ -503,7 +506,7 @@ impl LanguageServer { }), hover: Some(HoverClientCapabilities { content_format: Some(vec![MarkupKind::Markdown]), - ..Default::default() + dynamic_registration: None, }), inlay_hint: Some(InlayHintClientCapabilities { resolve_support: Some(InlayHintResolveClientCapabilities { @@ -517,6 +520,20 @@ impl LanguageServer { }), dynamic_registration: Some(false), }), + publish_diagnostics: Some(PublishDiagnosticsClientCapabilities { + related_information: Some(true), + ..Default::default() + }), + formatting: Some(DynamicRegistrationClientCapabilities { + dynamic_registration: None, + }), + on_type_formatting: Some(DynamicRegistrationClientCapabilities { + dynamic_registration: None, + }), + diagnostic: Some(DiagnosticClientCapabilities { + related_document_support: Some(true), + dynamic_registration: None, + }), ..Default::default() }), experimental: Some(json!({ @@ -526,15 +543,15 @@ impl LanguageServer { work_done_progress: Some(true), ..Default::default() }), - ..Default::default() + general: None, }, - trace: Default::default(), + trace: None, workspace_folders: Some(vec![WorkspaceFolder { uri: root_uri, name: Default::default(), }]), - client_info: Default::default(), - locale: Default::default(), + client_info: None, + locale: None, }; let response = self.request::(params).await?; diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c24fb5eea1f620b43920d249e6d67b308549ae41..776993223549921c08384619681533b2e7b5f137 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2629,8 +2629,9 @@ impl Project { }); for (adapter, server) in servers { - let workspace_config = - cx.update(|cx| adapter.workspace_configuration(cx)).await; + let workspace_config = cx + .update(|cx| adapter.workspace_configuration(server.root_path(), cx)) + .await; server .notify::( lsp::DidChangeConfigurationParams { @@ -2738,7 +2739,7 @@ impl Project { stderr_capture.clone(), language.clone(), adapter.clone(), - worktree_path, + Arc::clone(&worktree_path), ProjectLspAdapterDelegate::new(self, cx), cx, ) { @@ -2761,6 +2762,7 @@ impl Project { cx.spawn_weak(|this, mut cx| async move { let result = Self::setup_and_insert_language_server( this, + &worktree_path, override_options, pending_server, adapter.clone(), @@ -2876,6 +2878,7 @@ impl Project { async fn setup_and_insert_language_server( this: WeakModelHandle, + worktree_path: &Path, override_initialization_options: Option, pending_server: PendingLanguageServer, adapter: Arc, @@ -2888,6 +2891,7 @@ impl Project { this, override_initialization_options, pending_server, + worktree_path, adapter.clone(), server_id, cx, @@ -2917,11 +2921,14 @@ impl Project { this: WeakModelHandle, override_options: Option, pending_server: PendingLanguageServer, + worktree_path: &Path, adapter: Arc, server_id: LanguageServerId, cx: &mut AsyncAppContext, ) -> Result> { - let workspace_config = cx.update(|cx| adapter.workspace_configuration(cx)).await; + let workspace_config = cx + .update(|cx| adapter.workspace_configuration(worktree_path, cx)) + .await; let language_server = pending_server.task.await?; language_server @@ -2949,11 +2956,14 @@ impl Project { language_server .on_request::({ let adapter = adapter.clone(); + let worktree_path = worktree_path.to_path_buf(); move |params, mut cx| { let adapter = adapter.clone(); + let worktree_path = worktree_path.clone(); async move { - let workspace_config = - cx.update(|cx| adapter.workspace_configuration(cx)).await; + let workspace_config = cx + .update(|cx| adapter.workspace_configuration(&worktree_path, cx)) + .await; Ok(params .items .into_iter() diff --git a/crates/project2/src/project2.rs b/crates/project2/src/project2.rs index 3f7c9b7188bcbca247839cf70fe9bb2568e4461d..735eea6d753da9d430158600e7c1c1f6e96f8809 100644 --- a/crates/project2/src/project2.rs +++ b/crates/project2/src/project2.rs @@ -2667,8 +2667,9 @@ impl Project { })?; for (adapter, server) in servers { - let workspace_config = - cx.update(|cx| adapter.workspace_configuration(cx))?.await; + let workspace_config = cx + .update(|cx| adapter.workspace_configuration(server.root_path(), cx))? + .await; server .notify::( lsp::DidChangeConfigurationParams { @@ -2777,7 +2778,7 @@ impl Project { stderr_capture.clone(), language.clone(), adapter.clone(), - worktree_path, + Arc::clone(&worktree_path), ProjectLspAdapterDelegate::new(self, cx), cx, ) { @@ -2809,6 +2810,7 @@ impl Project { cx.spawn(move |this, mut cx| async move { let result = Self::setup_and_insert_language_server( this.clone(), + &worktree_path, initialization_options, pending_server, adapter.clone(), @@ -2929,6 +2931,7 @@ impl Project { async fn setup_and_insert_language_server( this: WeakModel, + worktree_path: &Path, initialization_options: Option, pending_server: PendingLanguageServer, adapter: Arc, @@ -2941,6 +2944,7 @@ impl Project { this.clone(), initialization_options, pending_server, + worktree_path, adapter.clone(), server_id, cx, @@ -2970,11 +2974,14 @@ impl Project { this: WeakModel, initialization_options: Option, pending_server: PendingLanguageServer, + worktree_path: &Path, adapter: Arc, server_id: LanguageServerId, cx: &mut AsyncAppContext, ) -> Result> { - let workspace_config = cx.update(|cx| adapter.workspace_configuration(cx))?.await; + let workspace_config = cx + .update(|cx| adapter.workspace_configuration(worktree_path, cx))? + .await; let language_server = pending_server.task.await?; language_server @@ -3003,11 +3010,14 @@ impl Project { language_server .on_request::({ let adapter = adapter.clone(); + let worktree_path = worktree_path.to_path_buf(); move |params, cx| { let adapter = adapter.clone(); + let worktree_path = worktree_path.clone(); async move { - let workspace_config = - cx.update(|cx| adapter.workspace_configuration(cx))?.await; + let workspace_config = cx + .update(|cx| adapter.workspace_configuration(&worktree_path, cx))? + .await; Ok(params .items .into_iter() diff --git a/crates/zed/src/languages/json.rs b/crates/zed/src/languages/json.rs index 63f909ae2a2e264ea672dee48e305ba1be82e066..891c25c31f1675f175b032f38d9b82a94df0aeaf 100644 --- a/crates/zed/src/languages/json.rs +++ b/crates/zed/src/languages/json.rs @@ -105,6 +105,7 @@ impl LspAdapter for JsonLspAdapter { fn workspace_configuration( &self, + _workspace_root: &Path, cx: &mut AppContext, ) -> BoxFuture<'static, serde_json::Value> { let action_names = cx.all_action_names().collect::>(); diff --git a/crates/zed/src/languages/php.rs b/crates/zed/src/languages/php.rs index 3096fd16e6b87764a0f9dd127a0dec6eaba0a77a..e3d0f1c6903c58134a0c7ff04742020fedc5892d 100644 --- a/crates/zed/src/languages/php.rs +++ b/crates/zed/src/languages/php.rs @@ -29,7 +29,6 @@ pub struct IntelephenseLspAdapter { impl IntelephenseLspAdapter { const SERVER_PATH: &'static str = "node_modules/intelephense/lib/intelephense.js"; - #[allow(unused)] pub fn new(node: Arc) -> Self { Self { node } } diff --git a/crates/zed/src/languages/tailwind.rs b/crates/zed/src/languages/tailwind.rs index 6d6006dbd48c3d4ea065e12e909fdbd3cf775e7f..0dfa700b01767745711de9b2b7384a81e1af2ea9 100644 --- a/crates/zed/src/languages/tailwind.rs +++ b/crates/zed/src/languages/tailwind.rs @@ -107,7 +107,11 @@ impl LspAdapter for TailwindLspAdapter { })) } - fn workspace_configuration(&self, _: &mut AppContext) -> BoxFuture<'static, Value> { + fn workspace_configuration( + &self, + _workspace_root: &Path, + _: &mut AppContext, + ) -> BoxFuture<'static, Value> { future::ready(json!({ "tailwindCSS": { "emmetCompletions": true, diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index d259afb05debea950bd767b407d8f77a1a2a2de9..fbb14930fc8e79ee888f58fb804fbff5846886e9 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -205,7 +205,6 @@ pub struct EsLintLspAdapter { impl EsLintLspAdapter { const SERVER_PATH: &'static str = "vscode-eslint/server/out/eslintServer.js"; - #[allow(unused)] pub fn new(node: Arc) -> Self { EsLintLspAdapter { node } } @@ -213,13 +212,23 @@ impl EsLintLspAdapter { #[async_trait] impl LspAdapter for EsLintLspAdapter { - fn workspace_configuration(&self, _: &mut AppContext) -> BoxFuture<'static, Value> { + fn workspace_configuration( + &self, + workspace_root: &Path, + _: &mut AppContext, + ) -> BoxFuture<'static, Value> { future::ready(json!({ "": { "validate": "on", "rulesCustomizations": [], "run": "onType", "nodePath": null, + "workingDirectory": {"mode": "auto"}, + "workspaceFolder": { + "uri": workspace_root, + "name": workspace_root.file_name() + .unwrap_or_else(|| workspace_root.as_os_str()), + }, } })) .boxed() diff --git a/crates/zed/src/languages/yaml.rs b/crates/zed/src/languages/yaml.rs index 8b438d0949dc0ef1f514f3c315c3eab98174d506..fbed9ba78f0f093145ebfd3b12227879ab8d5617 100644 --- a/crates/zed/src/languages/yaml.rs +++ b/crates/zed/src/languages/yaml.rs @@ -93,7 +93,11 @@ impl LspAdapter for YamlLspAdapter { ) -> Option { get_cached_server_binary(container_dir, &*self.node).await } - fn workspace_configuration(&self, cx: &mut AppContext) -> BoxFuture<'static, Value> { + fn workspace_configuration( + &self, + _workspace_root: &Path, + cx: &mut AppContext, + ) -> BoxFuture<'static, Value> { let tab_size = all_language_settings(None, cx) .language(Some("YAML")) .tab_size; diff --git a/crates/zed2/src/languages/json.rs b/crates/zed2/src/languages/json.rs index f04f59cf6d2e23ed7d594636e41b7e173d5d4081..162d4c9fdb9c5e4bbd5c39230764e40c5d0e95c7 100644 --- a/crates/zed2/src/languages/json.rs +++ b/crates/zed2/src/languages/json.rs @@ -105,6 +105,7 @@ impl LspAdapter for JsonLspAdapter { fn workspace_configuration( &self, + _workspace_root: &Path, cx: &mut AppContext, ) -> BoxFuture<'static, serde_json::Value> { let action_names = cx.all_action_names(); diff --git a/crates/zed2/src/languages/php.rs b/crates/zed2/src/languages/php.rs index 3096fd16e6b87764a0f9dd127a0dec6eaba0a77a..e3d0f1c6903c58134a0c7ff04742020fedc5892d 100644 --- a/crates/zed2/src/languages/php.rs +++ b/crates/zed2/src/languages/php.rs @@ -29,7 +29,6 @@ pub struct IntelephenseLspAdapter { impl IntelephenseLspAdapter { const SERVER_PATH: &'static str = "node_modules/intelephense/lib/intelephense.js"; - #[allow(unused)] pub fn new(node: Arc) -> Self { Self { node } } diff --git a/crates/zed2/src/languages/tailwind.rs b/crates/zed2/src/languages/tailwind.rs index 6d6006dbd48c3d4ea065e12e909fdbd3cf775e7f..0dfa700b01767745711de9b2b7384a81e1af2ea9 100644 --- a/crates/zed2/src/languages/tailwind.rs +++ b/crates/zed2/src/languages/tailwind.rs @@ -107,7 +107,11 @@ impl LspAdapter for TailwindLspAdapter { })) } - fn workspace_configuration(&self, _: &mut AppContext) -> BoxFuture<'static, Value> { + fn workspace_configuration( + &self, + _workspace_root: &Path, + _: &mut AppContext, + ) -> BoxFuture<'static, Value> { future::ready(json!({ "tailwindCSS": { "emmetCompletions": true, diff --git a/crates/zed2/src/languages/typescript.rs b/crates/zed2/src/languages/typescript.rs index 1b96e7e7022d56cf8947906fda67df2604c07471..26ad33792095976b39a6e6b25f6d1eae22756f4e 100644 --- a/crates/zed2/src/languages/typescript.rs +++ b/crates/zed2/src/languages/typescript.rs @@ -205,7 +205,6 @@ pub struct EsLintLspAdapter { impl EsLintLspAdapter { const SERVER_PATH: &'static str = "vscode-eslint/server/out/eslintServer.js"; - #[allow(unused)] pub fn new(node: Arc) -> Self { EsLintLspAdapter { node } } @@ -213,13 +212,23 @@ impl EsLintLspAdapter { #[async_trait] impl LspAdapter for EsLintLspAdapter { - fn workspace_configuration(&self, _: &mut AppContext) -> BoxFuture<'static, Value> { + fn workspace_configuration( + &self, + workspace_root: &Path, + _: &mut AppContext, + ) -> BoxFuture<'static, Value> { future::ready(json!({ "": { "validate": "on", "rulesCustomizations": [], "run": "onType", "nodePath": null, + "workingDirectory": {"mode": "auto"}, + "workspaceFolder": { + "uri": workspace_root, + "name": workspace_root.file_name() + .unwrap_or_else(|| workspace_root.as_os_str()), + }, } })) .boxed() diff --git a/crates/zed2/src/languages/yaml.rs b/crates/zed2/src/languages/yaml.rs index 8b438d0949dc0ef1f514f3c315c3eab98174d506..fbed9ba78f0f093145ebfd3b12227879ab8d5617 100644 --- a/crates/zed2/src/languages/yaml.rs +++ b/crates/zed2/src/languages/yaml.rs @@ -93,7 +93,11 @@ impl LspAdapter for YamlLspAdapter { ) -> Option { get_cached_server_binary(container_dir, &*self.node).await } - fn workspace_configuration(&self, cx: &mut AppContext) -> BoxFuture<'static, Value> { + fn workspace_configuration( + &self, + _workspace_root: &Path, + cx: &mut AppContext, + ) -> BoxFuture<'static, Value> { let tab_size = all_language_settings(None, cx) .language(Some("YAML")) .tab_size;