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
This commit is contained in:
Kirill Bulatov 2023-11-30 13:25:25 +02:00 committed by GitHub
commit 7b76db4b50
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 140 additions and 44 deletions

View file

@ -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()
}

View file

@ -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()
}

View file

@ -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::<request::Initialize>(params).await?;

View file

@ -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::<request::Initialize>(params).await?;

View file

@ -2641,8 +2641,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::notification::DidChangeConfiguration>(
lsp::DidChangeConfigurationParams {
@ -2753,7 +2754,7 @@ impl Project {
stderr_capture.clone(),
language.clone(),
adapter.clone(),
worktree_path,
Arc::clone(&worktree_path),
ProjectLspAdapterDelegate::new(self, cx),
cx,
) {
@ -2776,6 +2777,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(),
@ -2891,6 +2893,7 @@ impl Project {
async fn setup_and_insert_language_server(
this: WeakModelHandle<Self>,
worktree_path: &Path,
override_initialization_options: Option<serde_json::Value>,
pending_server: PendingLanguageServer,
adapter: Arc<CachedLspAdapter>,
@ -2903,6 +2906,7 @@ impl Project {
this,
override_initialization_options,
pending_server,
worktree_path,
adapter.clone(),
server_id,
cx,
@ -2932,11 +2936,14 @@ impl Project {
this: WeakModelHandle<Self>,
override_options: Option<serde_json::Value>,
pending_server: PendingLanguageServer,
worktree_path: &Path,
adapter: Arc<CachedLspAdapter>,
server_id: LanguageServerId,
cx: &mut AsyncAppContext,
) -> Result<Arc<LanguageServer>> {
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
@ -2964,11 +2971,14 @@ impl Project {
language_server
.on_request::<lsp::request::WorkspaceConfiguration, _, _>({
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()

View file

@ -2677,8 +2677,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::notification::DidChangeConfiguration>(
lsp::DidChangeConfigurationParams {
@ -2790,7 +2791,7 @@ impl Project {
stderr_capture.clone(),
language.clone(),
adapter.clone(),
worktree_path,
Arc::clone(&worktree_path),
ProjectLspAdapterDelegate::new(self, cx),
cx,
) {
@ -2822,6 +2823,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(),
@ -2942,6 +2944,7 @@ impl Project {
async fn setup_and_insert_language_server(
this: WeakModel<Self>,
worktree_path: &Path,
initialization_options: Option<serde_json::Value>,
pending_server: PendingLanguageServer,
adapter: Arc<CachedLspAdapter>,
@ -2954,6 +2957,7 @@ impl Project {
this.clone(),
initialization_options,
pending_server,
worktree_path,
adapter.clone(),
server_id,
cx,
@ -2983,11 +2987,14 @@ impl Project {
this: WeakModel<Self>,
initialization_options: Option<serde_json::Value>,
pending_server: PendingLanguageServer,
worktree_path: &Path,
adapter: Arc<CachedLspAdapter>,
server_id: LanguageServerId,
cx: &mut AsyncAppContext,
) -> Result<Arc<LanguageServer>> {
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
@ -3016,11 +3023,14 @@ impl Project {
language_server
.on_request::<lsp::request::WorkspaceConfiguration, _, _>({
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()

View file

@ -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::<Vec<_>>();

View file

@ -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<dyn NodeRuntime>) -> Self {
Self { node }
}

View file

@ -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,

View file

@ -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<dyn NodeRuntime>) -> 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()

View file

@ -93,7 +93,11 @@ impl LspAdapter for YamlLspAdapter {
) -> Option<LanguageServerBinary> {
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;

View file

@ -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();

View file

@ -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<dyn NodeRuntime>) -> Self {
Self { node }
}

View file

@ -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,

View file

@ -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<dyn NodeRuntime>) -> 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()

View file

@ -93,7 +93,11 @@ impl LspAdapter for YamlLspAdapter {
) -> Option<LanguageServerBinary> {
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;