diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ccb9069b0a6370..9f4b62a598ce61 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -126,7 +126,7 @@ use project::{ lsp_store::{FormatTarget, FormatTrigger}, project_settings::{GitGutterSetting, ProjectSettings}, CodeAction, Completion, CompletionIntent, DocumentHighlight, InlayHint, Location, LocationLink, - Project, ProjectItem, ProjectTransaction, TaskSourceKind, + LspDiagnostics, Project, ProjectItem, ProjectTransaction, TaskSourceKind, }; use rand::prelude::*; use rpc::{proto::*, ErrorExt}; @@ -10909,7 +10909,7 @@ impl Editor { editor .update(&mut cx, |_, cx| { - project.diagnostics(&start_buffer, start, cx); + project.diagnostics(&start_buffer, start, cx) }) .ok(); }); @@ -13789,7 +13789,7 @@ pub trait DiagnosticsProvider { buffer: &Model, position: text::Anchor, cx: &mut WindowContext, - ) -> Option>>>; + ) -> Option>>>; } impl CodeActionProvider for Model { @@ -14080,7 +14080,7 @@ impl DiagnosticsProvider for Model { buffer: &Model, position: text::Anchor, cx: &mut WindowContext, - ) -> Option>>> { + ) -> Option>>> { Some(self.update(cx, |project, cx| { project.document_diagnostics(buffer, position, cx) })) diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index f95e6be4456627..5b7cbf73270e69 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -3,7 +3,8 @@ mod signature_help; use crate::{ lsp_store::LspStore, CodeAction, CoreCompletion, DocumentHighlight, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, - InlayHintTooltip, Location, LocationLink, MarkupContent, ProjectTransaction, ResolveState, + InlayHintTooltip, Location, LocationLink, LspDiagnostics, MarkupContent, ProjectTransaction, + ResolveState, }; use anyhow::{anyhow, Context, Result}; use async_trait::async_trait; @@ -21,12 +22,13 @@ use language::{ }; use lsp::{ AdapterServerCapabilities, CodeActionKind, CodeActionOptions, CompletionContext, - CompletionListItemDefaultsEditRange, CompletionTriggerKind, Diagnostic, DocumentHighlightKind, + CompletionListItemDefaultsEditRange, CompletionTriggerKind, DocumentHighlightKind, LanguageServer, LanguageServerId, LinkedEditingRangeServerCapabilities, OneOf, ServerCapabilities, }; +use serde_json::Value; use signature_help::{lsp_to_proto_signature, proto_to_lsp_signature}; -use std::{cmp::Reverse, ops::Range, path::Path, sync::Arc}; +use std::{cmp::Reverse, ops::Range, path::Path, str::FromStr, sync::Arc}; use text::{BufferId, LineEnding}; pub use signature_help::{ @@ -3075,9 +3077,92 @@ impl LspCommand for LinkedEditingRange { } } +impl GetDocumentDiagnostics { + pub fn deserialize_lsp_diagnostic(diagnostic: proto::LspDiagnostic) -> lsp::Diagnostic { + let start = diagnostic.start.unwrap(); + let end = diagnostic.end.unwrap(); + + let range = Range:: { + start: PointUtf16 { + row: start.row, + column: start.column, + }, + end: PointUtf16 { + row: end.row, + column: end.column, + }, + }; + + let data = if let Some(data) = diagnostic.data { + Value::from_str(&data).ok() + } else { + None + }; + + let code = if let Some(code) = diagnostic.code { + Some(lsp::NumberOrString::String(code)) + } else { + None + }; + + lsp::Diagnostic { + range: language::range_to_lsp(range), + severity: match proto::lsp_diagnostic::Severity::from_i32(diagnostic.severity).unwrap() + { + proto::lsp_diagnostic::Severity::Error => Some(lsp::DiagnosticSeverity::ERROR), + proto::lsp_diagnostic::Severity::Warning => Some(lsp::DiagnosticSeverity::WARNING), + proto::lsp_diagnostic::Severity::Information => { + Some(lsp::DiagnosticSeverity::INFORMATION) + } + proto::lsp_diagnostic::Severity::Hint => Some(lsp::DiagnosticSeverity::HINT), + _ => None, + }, + code, + // FIXME + code_description: None, + related_information: Some(vec![]), + tags: Some(vec![]), + source: diagnostic.source.clone(), + message: diagnostic.message, + data, + } + } + + pub fn serialize_lsp_diagnostic(diagnostic: lsp::Diagnostic) -> proto::LspDiagnostic { + let range = language::range_from_lsp(diagnostic.range); + + proto::LspDiagnostic { + start: Some(proto::PointUtf16 { + row: range.start.0.row, + column: range.start.0.column, + }), + end: Some(proto::PointUtf16 { + row: range.end.0.row, + column: range.end.0.column, + }), + severity: match diagnostic.severity { + Some(lsp::DiagnosticSeverity::ERROR) => proto::lsp_diagnostic::Severity::Error, + Some(lsp::DiagnosticSeverity::WARNING) => proto::lsp_diagnostic::Severity::Warning, + Some(lsp::DiagnosticSeverity::INFORMATION) => { + proto::lsp_diagnostic::Severity::Information + } + Some(lsp::DiagnosticSeverity::HINT) => proto::lsp_diagnostic::Severity::Hint, + _ => proto::lsp_diagnostic::Severity::None, + } as i32, + code: diagnostic.code.as_ref().map(|code| match code { + lsp::NumberOrString::Number(code) => code.to_string(), + lsp::NumberOrString::String(code) => code.clone(), + }), + source: diagnostic.source.clone(), + message: diagnostic.message, + data: diagnostic.data.as_ref().map(|data| data.to_string()), + } + } +} + #[async_trait(?Send)] impl LspCommand for GetDocumentDiagnostics { - type Response = Option>; + type Response = LspDiagnostics; type LspRequest = lsp::request::DocumentDiagnosticRequest; type ProtoRequest = proto::GetDocumentDiagnostics; @@ -3119,17 +3204,24 @@ impl LspCommand for GetDocumentDiagnostics { message: lsp::DocumentDiagnosticReportResult, _: Model, _: Model, - _: LanguageServerId, + server_id: LanguageServerId, _: AsyncAppContext, ) -> Result { match message { lsp::DocumentDiagnosticReportResult::Report(report) => match report { - lsp::DocumentDiagnosticReport::Full(report) => { - Ok(Some(report.full_document_diagnostic_report.items.clone())) - } - lsp::DocumentDiagnosticReport::Unchanged(_) => Ok(None), + lsp::DocumentDiagnosticReport::Full(report) => Ok(LspDiagnostics { + server_id, + diagnostics: Some(report.full_document_diagnostic_report.items.clone()), + }), + lsp::DocumentDiagnosticReport::Unchanged(_) => Ok(LspDiagnostics { + server_id, + diagnostics: None, + }), }, - lsp::DocumentDiagnosticReportResult::Partial(_) => Ok(None), + lsp::DocumentDiagnosticReportResult::Partial(_) => Ok(LspDiagnostics { + server_id, + diagnostics: None, + }), } } diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index ac38159ae846f7..4cebd971e6f59b 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -10,8 +10,9 @@ use crate::{ toolchain_store::{EmptyToolchainStore, ToolchainStoreEvent}, worktree_store::{WorktreeStore, WorktreeStoreEvent}, yarn::YarnPathStore, - CodeAction, Completion, CoreCompletion, Hover, InlayHint, ProjectItem as _, ProjectPath, - ProjectTransaction, ResolveState, Symbol, ToolchainStore, + CodeAction, Completion, CoreCompletion, Hover, InlayHint, ProjectItem as _, + LspDiagnostics, ProjectPath, ProjectTransaction, ResolveState, Symbol, + ToolchainStore, }; use anyhow::{anyhow, Context as _, Result}; use async_trait::async_trait; @@ -3163,7 +3164,7 @@ impl LspStore { buffer_handle: &Model, position: Anchor, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { let buffer = buffer_handle.read(cx); let buffer_id = buffer.remote_id(); @@ -3213,7 +3214,6 @@ impl LspStore { .into_iter() .collect::>>()? .into_iter() - .flat_map(|diagnostics| diagnostics.into_iter().flatten()) .collect()) }) } else { @@ -3223,13 +3223,7 @@ impl LspStore { GetDocumentDiagnostics { position }, cx, ); - cx.spawn(|_, _| async move { - Ok(all_actions_task - .await - .into_iter() - .flat_map(|diagnostics| diagnostics.into_iter().flatten()) - .collect()) - }) + cx.spawn(|_, _| async move { Ok(all_actions_task.await.into_iter().collect()) }) } } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 66c5f6160ca8fc..2eeda5f94d8def 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -557,6 +557,24 @@ pub const DEFAULT_COMPLETION_CONTEXT: CompletionContext = CompletionContext { trigger_character: None, }; +/// An LSP diagnostics associated with a certain language server. +#[derive(Clone, Debug)] +pub struct LspDiagnostics { + /// The id of the language server that produced this code action. + pub server_id: LanguageServerId, + /// The diagnostics produced by this language server. + pub diagnostics: Option>, +} + +impl std::default::Default for LspDiagnostics { + fn default() -> Self { + Self { + server_id: LanguageServerId(0), + diagnostics: None, + } + } +} + impl Project { pub fn init_settings(cx: &mut AppContext) { WorktreeSettings::register(cx); @@ -2964,7 +2982,7 @@ impl Project { buffer_handle: &Model, position: Anchor, cx: &mut ModelContext, - ) -> Task>> { + ) -> Task>> { self.lsp_store.update(cx, |lsp_store, cx| { lsp_store.document_diagnostic(buffer_handle, position, cx) }) diff --git a/crates/proto/proto/zed.proto b/crates/proto/proto/zed.proto index 7e9de4c8d71334..2f8c18fdc6bd80 100644 --- a/crates/proto/proto/zed.proto +++ b/crates/proto/proto/zed.proto @@ -2575,6 +2575,37 @@ message InstallExtension { string tmp_dir = 2; } +message LspDiagnosticRelatedInformation { + string location = 1; + string message = 2; +} + +message LspDiagnosticTag { + string name = 1; + optional string value = 2; +} + +message LspDiagnostic { + PointUtf16 start = 1; + PointUtf16 end = 2; + Severity severity = 3; + optional string code = 4; + // optional string code_description = 5; + optional string source = 6; + string message = 7; + // repeated LspDiagnosticRelatedInformation related_information = 8; + // repeated LspDiagnosticTag tags = 9; + optional string data = 10; + + enum Severity { + None = 0; + Error = 1; + Warning = 2; + Information = 3; + Hint = 4; + } +} + message GetDocumentDiagnostics { uint64 project_id = 1; uint64 buffer_id = 2; @@ -2583,5 +2614,6 @@ message GetDocumentDiagnostics { } message GetDocumentDiagnosticsResponse { - repeated Diagnostic diagnostics = 1; + uint64 server_id = 1; + repeated LspDiagnostic diagnostics = 2; }