Improve support for multiple registrations of textDocument/diagnostic (#43703)
Closes https://github.com/zed-industries/zed/issues/41935 The registration ID responsible for generating each diagnostic is now tracked. This allows us to replace only the diagnostics from the same registration ID when a pull diagnostics report is applied. Additionally, various deficiencies in our support for pull diagnostics have been fixed: - Document pulls are issued for all open buffers, not just the edited one. A shorter debounce is used for the edited buffer. Workspace diagnostics are also now ignored for open buffers. - Tracking of `lastResultId` is improved. - Stored pull diagnostics are discarded when the corresponding buffer is closed. Release Notes: - Improved compatibility with language servers that use the "pull diagnostics" feature of Language Server Protocol. --------- Co-authored-by: Kirill Bulatov <mail4score@gmail.com> Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
co-authored by
Kirill Bulatov
Kirill Bulatov
parent
493cfadb42
commit
a51e975b81
@@ -14,7 +14,7 @@ use client::proto::{self, PeerId};
|
||||
use clock::Global;
|
||||
use collections::{HashMap, HashSet};
|
||||
use futures::future;
|
||||
use gpui::{App, AsyncApp, Entity, Task};
|
||||
use gpui::{App, AsyncApp, Entity, SharedString, Task};
|
||||
use language::{
|
||||
Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind, CharScopeContext,
|
||||
OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction, Unclipped,
|
||||
@@ -26,8 +26,8 @@ use language::{
|
||||
use lsp::{
|
||||
AdapterServerCapabilities, CodeActionKind, CodeActionOptions, CodeDescription,
|
||||
CompletionContext, CompletionListItemDefaultsEditRange, CompletionTriggerKind,
|
||||
DiagnosticServerCapabilities, DocumentHighlightKind, LanguageServer, LanguageServerId,
|
||||
LinkedEditingRangeServerCapabilities, OneOf, RenameOptions, ServerCapabilities,
|
||||
DocumentHighlightKind, LanguageServer, LanguageServerId, LinkedEditingRangeServerCapabilities,
|
||||
OneOf, RenameOptions, ServerCapabilities,
|
||||
};
|
||||
use serde_json::Value;
|
||||
use signature_help::{lsp_to_proto_signature, proto_to_lsp_signature};
|
||||
@@ -265,8 +265,9 @@ pub(crate) struct LinkedEditingRange {
|
||||
pub(crate) struct GetDocumentDiagnostics {
|
||||
/// We cannot blindly rely on server's capabilities.diagnostic_provider, as they're a singular field, whereas
|
||||
/// a server can register multiple diagnostic providers post-mortem.
|
||||
pub dynamic_caps: DiagnosticServerCapabilities,
|
||||
pub previous_result_id: Option<String>,
|
||||
pub registration_id: Option<SharedString>,
|
||||
pub identifier: Option<String>,
|
||||
pub previous_result_id: Option<SharedString>,
|
||||
}
|
||||
|
||||
#[async_trait(?Send)]
|
||||
@@ -3755,15 +3756,16 @@ impl GetDocumentDiagnostics {
|
||||
.into_iter()
|
||||
.filter_map(|diagnostics| {
|
||||
Some(LspPullDiagnostics::Response {
|
||||
registration_id: diagnostics.registration_id.map(SharedString::from),
|
||||
server_id: LanguageServerId::from_proto(diagnostics.server_id),
|
||||
uri: lsp::Uri::from_str(diagnostics.uri.as_str()).log_err()?,
|
||||
diagnostics: if diagnostics.changed {
|
||||
PulledDiagnostics::Unchanged {
|
||||
result_id: diagnostics.result_id?,
|
||||
result_id: SharedString::new(diagnostics.result_id?),
|
||||
}
|
||||
} else {
|
||||
PulledDiagnostics::Changed {
|
||||
result_id: diagnostics.result_id,
|
||||
result_id: diagnostics.result_id.map(SharedString::new),
|
||||
diagnostics: diagnostics
|
||||
.diagnostics
|
||||
.into_iter()
|
||||
@@ -3927,6 +3929,7 @@ impl GetDocumentDiagnostics {
|
||||
pub fn deserialize_workspace_diagnostics_report(
|
||||
report: lsp::WorkspaceDiagnosticReportResult,
|
||||
server_id: LanguageServerId,
|
||||
registration_id: Option<SharedString>,
|
||||
) -> Vec<WorkspaceLspPullDiagnostics> {
|
||||
let mut pulled_diagnostics = HashMap::default();
|
||||
match report {
|
||||
@@ -3938,6 +3941,7 @@ impl GetDocumentDiagnostics {
|
||||
&mut pulled_diagnostics,
|
||||
server_id,
|
||||
report,
|
||||
registration_id.clone(),
|
||||
)
|
||||
}
|
||||
lsp::WorkspaceDocumentDiagnosticReport::Unchanged(report) => {
|
||||
@@ -3945,6 +3949,7 @@ impl GetDocumentDiagnostics {
|
||||
&mut pulled_diagnostics,
|
||||
server_id,
|
||||
report,
|
||||
registration_id.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -3960,6 +3965,7 @@ impl GetDocumentDiagnostics {
|
||||
&mut pulled_diagnostics,
|
||||
server_id,
|
||||
report,
|
||||
registration_id.clone(),
|
||||
)
|
||||
}
|
||||
lsp::WorkspaceDocumentDiagnosticReport::Unchanged(report) => {
|
||||
@@ -3967,6 +3973,7 @@ impl GetDocumentDiagnostics {
|
||||
&mut pulled_diagnostics,
|
||||
server_id,
|
||||
report,
|
||||
registration_id.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -3987,6 +3994,7 @@ fn process_full_workspace_diagnostics_report(
|
||||
diagnostics: &mut HashMap<lsp::Uri, WorkspaceLspPullDiagnostics>,
|
||||
server_id: LanguageServerId,
|
||||
report: lsp::WorkspaceFullDocumentDiagnosticReport,
|
||||
registration_id: Option<SharedString>,
|
||||
) {
|
||||
let mut new_diagnostics = HashMap::default();
|
||||
process_full_diagnostics_report(
|
||||
@@ -3994,6 +4002,7 @@ fn process_full_workspace_diagnostics_report(
|
||||
server_id,
|
||||
report.uri,
|
||||
report.full_document_diagnostic_report,
|
||||
registration_id,
|
||||
);
|
||||
diagnostics.extend(new_diagnostics.into_iter().map(|(uri, diagnostics)| {
|
||||
(
|
||||
@@ -4010,6 +4019,7 @@ fn process_unchanged_workspace_diagnostics_report(
|
||||
diagnostics: &mut HashMap<lsp::Uri, WorkspaceLspPullDiagnostics>,
|
||||
server_id: LanguageServerId,
|
||||
report: lsp::WorkspaceUnchangedDocumentDiagnosticReport,
|
||||
registration_id: Option<SharedString>,
|
||||
) {
|
||||
let mut new_diagnostics = HashMap::default();
|
||||
process_unchanged_diagnostics_report(
|
||||
@@ -4017,6 +4027,7 @@ fn process_unchanged_workspace_diagnostics_report(
|
||||
server_id,
|
||||
report.uri,
|
||||
report.unchanged_document_diagnostic_report,
|
||||
registration_id,
|
||||
);
|
||||
diagnostics.extend(new_diagnostics.into_iter().map(|(uri, diagnostics)| {
|
||||
(
|
||||
@@ -4050,19 +4061,12 @@ impl LspCommand for GetDocumentDiagnostics {
|
||||
_: &Arc<LanguageServer>,
|
||||
_: &App,
|
||||
) -> Result<lsp::DocumentDiagnosticParams> {
|
||||
let identifier = match &self.dynamic_caps {
|
||||
lsp::DiagnosticServerCapabilities::Options(options) => options.identifier.clone(),
|
||||
lsp::DiagnosticServerCapabilities::RegistrationOptions(options) => {
|
||||
options.diagnostic_options.identifier.clone()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(lsp::DocumentDiagnosticParams {
|
||||
text_document: lsp::TextDocumentIdentifier {
|
||||
uri: file_path_to_lsp_url(path)?,
|
||||
},
|
||||
identifier,
|
||||
previous_result_id: self.previous_result_id.clone(),
|
||||
identifier: self.identifier.clone(),
|
||||
previous_result_id: self.previous_result_id.clone().map(|id| id.to_string()),
|
||||
partial_result_params: Default::default(),
|
||||
work_done_progress_params: Default::default(),
|
||||
})
|
||||
@@ -4097,6 +4101,7 @@ impl LspCommand for GetDocumentDiagnostics {
|
||||
&mut pulled_diagnostics,
|
||||
server_id,
|
||||
related_documents,
|
||||
self.registration_id.clone(),
|
||||
);
|
||||
}
|
||||
process_full_diagnostics_report(
|
||||
@@ -4104,6 +4109,7 @@ impl LspCommand for GetDocumentDiagnostics {
|
||||
server_id,
|
||||
url,
|
||||
report.full_document_diagnostic_report,
|
||||
self.registration_id,
|
||||
);
|
||||
}
|
||||
lsp::DocumentDiagnosticReport::Unchanged(report) => {
|
||||
@@ -4112,6 +4118,7 @@ impl LspCommand for GetDocumentDiagnostics {
|
||||
&mut pulled_diagnostics,
|
||||
server_id,
|
||||
related_documents,
|
||||
self.registration_id.clone(),
|
||||
);
|
||||
}
|
||||
process_unchanged_diagnostics_report(
|
||||
@@ -4119,6 +4126,7 @@ impl LspCommand for GetDocumentDiagnostics {
|
||||
server_id,
|
||||
url,
|
||||
report.unchanged_document_diagnostic_report,
|
||||
self.registration_id,
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -4128,6 +4136,7 @@ impl LspCommand for GetDocumentDiagnostics {
|
||||
&mut pulled_diagnostics,
|
||||
server_id,
|
||||
related_documents,
|
||||
self.registration_id,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4170,6 +4179,7 @@ impl LspCommand for GetDocumentDiagnostics {
|
||||
server_id,
|
||||
uri,
|
||||
diagnostics,
|
||||
registration_id,
|
||||
} => {
|
||||
let mut changed = false;
|
||||
let (diagnostics, result_id) = match diagnostics {
|
||||
@@ -4184,7 +4194,7 @@ impl LspCommand for GetDocumentDiagnostics {
|
||||
};
|
||||
Some(proto::PulledDiagnostics {
|
||||
changed,
|
||||
result_id,
|
||||
result_id: result_id.map(|id| id.to_string()),
|
||||
uri: uri.to_string(),
|
||||
server_id: server_id.to_proto(),
|
||||
diagnostics: diagnostics
|
||||
@@ -4195,6 +4205,7 @@ impl LspCommand for GetDocumentDiagnostics {
|
||||
.log_err()
|
||||
})
|
||||
.collect(),
|
||||
registration_id: registration_id.as_ref().map(ToString::to_string),
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -4365,14 +4376,25 @@ fn process_related_documents(
|
||||
diagnostics: &mut HashMap<lsp::Uri, LspPullDiagnostics>,
|
||||
server_id: LanguageServerId,
|
||||
documents: impl IntoIterator<Item = (lsp::Uri, lsp::DocumentDiagnosticReportKind)>,
|
||||
registration_id: Option<SharedString>,
|
||||
) {
|
||||
for (url, report_kind) in documents {
|
||||
match report_kind {
|
||||
lsp::DocumentDiagnosticReportKind::Full(report) => {
|
||||
process_full_diagnostics_report(diagnostics, server_id, url, report)
|
||||
}
|
||||
lsp::DocumentDiagnosticReportKind::Full(report) => process_full_diagnostics_report(
|
||||
diagnostics,
|
||||
server_id,
|
||||
url,
|
||||
report,
|
||||
registration_id.clone(),
|
||||
),
|
||||
lsp::DocumentDiagnosticReportKind::Unchanged(report) => {
|
||||
process_unchanged_diagnostics_report(diagnostics, server_id, url, report)
|
||||
process_unchanged_diagnostics_report(
|
||||
diagnostics,
|
||||
server_id,
|
||||
url,
|
||||
report,
|
||||
registration_id.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4383,8 +4405,9 @@ fn process_unchanged_diagnostics_report(
|
||||
server_id: LanguageServerId,
|
||||
uri: lsp::Uri,
|
||||
report: lsp::UnchangedDocumentDiagnosticReport,
|
||||
registration_id: Option<SharedString>,
|
||||
) {
|
||||
let result_id = report.result_id;
|
||||
let result_id = SharedString::new(report.result_id);
|
||||
match diagnostics.entry(uri.clone()) {
|
||||
hash_map::Entry::Occupied(mut o) => match o.get_mut() {
|
||||
LspPullDiagnostics::Default => {
|
||||
@@ -4392,12 +4415,14 @@ fn process_unchanged_diagnostics_report(
|
||||
server_id,
|
||||
uri,
|
||||
diagnostics: PulledDiagnostics::Unchanged { result_id },
|
||||
registration_id,
|
||||
});
|
||||
}
|
||||
LspPullDiagnostics::Response {
|
||||
server_id: existing_server_id,
|
||||
uri: existing_uri,
|
||||
diagnostics: existing_diagnostics,
|
||||
..
|
||||
} => {
|
||||
if server_id != *existing_server_id || &uri != existing_uri {
|
||||
debug_panic!(
|
||||
@@ -4417,6 +4442,7 @@ fn process_unchanged_diagnostics_report(
|
||||
server_id,
|
||||
uri,
|
||||
diagnostics: PulledDiagnostics::Unchanged { result_id },
|
||||
registration_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4427,8 +4453,9 @@ fn process_full_diagnostics_report(
|
||||
server_id: LanguageServerId,
|
||||
uri: lsp::Uri,
|
||||
report: lsp::FullDocumentDiagnosticReport,
|
||||
registration_id: Option<SharedString>,
|
||||
) {
|
||||
let result_id = report.result_id;
|
||||
let result_id = report.result_id.map(SharedString::new);
|
||||
match diagnostics.entry(uri.clone()) {
|
||||
hash_map::Entry::Occupied(mut o) => match o.get_mut() {
|
||||
LspPullDiagnostics::Default => {
|
||||
@@ -4439,12 +4466,14 @@ fn process_full_diagnostics_report(
|
||||
result_id,
|
||||
diagnostics: report.items,
|
||||
},
|
||||
registration_id,
|
||||
});
|
||||
}
|
||||
LspPullDiagnostics::Response {
|
||||
server_id: existing_server_id,
|
||||
uri: existing_uri,
|
||||
diagnostics: existing_diagnostics,
|
||||
..
|
||||
} => {
|
||||
if server_id != *existing_server_id || &uri != existing_uri {
|
||||
debug_panic!(
|
||||
@@ -4478,6 +4507,7 @@ fn process_full_diagnostics_report(
|
||||
result_id,
|
||||
diagnostics: report.items,
|
||||
},
|
||||
registration_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user