Skip to content

Commit

Permalink
lsp: add struct for LSP pull-based diagnostics
Browse files Browse the repository at this point in the history
  • Loading branch information
vitallium committed Dec 7, 2024
1 parent 62ddf27 commit 9ddc70c
Show file tree
Hide file tree
Showing 5 changed files with 228 additions and 40 deletions.
68 changes: 53 additions & 15 deletions crates/editor/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -663,7 +663,7 @@ pub struct Editor {
next_scroll_position: NextScrollCursorCenterTopBottom,
addons: HashMap<TypeId, Box<dyn Addon>>,
_scroll_cursor_center_top_bottom_task: Task<()>,
_pull_document_diagnostics_task: Task<()>,
_pull_document_diagnostics_task: Task<Result<(), anyhow::Error>>,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, Default)]
Expand Down Expand Up @@ -2161,7 +2161,7 @@ impl Editor {
addons: HashMap::default(),
_scroll_cursor_center_top_bottom_task: Task::ready(()),
text_style_refinement: None,
_pull_document_diagnostics_task: Task::ready(()),
_pull_document_diagnostics_task: Task::ready(Ok(())),
};
this.tasks_update_task = Some(this.refresh_runnables(cx));
this._subscriptions.extend(project_subscriptions);
Expand Down Expand Up @@ -10911,16 +10911,24 @@ impl Editor {
return None;
}

self._pull_document_diagnostics_task = cx.spawn(|editor, mut cx| async move {
self._pull_document_diagnostics_task = cx.spawn(|this, mut cx| async move {
cx.background_executor()
.timer(DOCUMENT_DIAGNOSTICS_DEBOUNCE_TIMEOUT)
.await;

editor
.update(&mut cx, |_, cx| {
project.diagnostics(&start_buffer, start, cx);
})
.ok();
let pull_diagnostics_task = this.update(&mut cx, |_, cx| {
project.pull_diagnostics(&start_buffer, start, cx)
})?;

if let Some(pull_diagnostics_task) = pull_diagnostics_task {
let diagnostics = pull_diagnostics_task
.await
.context("Pull diagnostics task")?;

let _ = this.update(&mut cx, |_, cx| project.update_diagnostics(diagnostics, cx));
}

anyhow::Ok(())
});
None
}
Expand Down Expand Up @@ -13886,12 +13894,18 @@ pub trait CodeActionProvider {
}

pub trait DiagnosticsProvider {
fn diagnostics(
fn pull_diagnostics(
&self,
buffer: &Model<Buffer>,
position: text::Anchor,
cx: &mut WindowContext,
) -> Option<Task<Result<Vec<lsp::Diagnostic>>>>;
cx: &mut AppContext,
) -> Option<Task<Result<Vec<LspDiagnostics>>>>;

fn update_diagnostics(
&self,
diagnostics: Vec<LspDiagnostics>,
cx: &mut AppContext,
) -> Result<()>;
}

impl CodeActionProvider for Model<Project> {
Expand Down Expand Up @@ -14232,16 +14246,40 @@ impl SemanticsProvider for Model<Project> {
}

impl DiagnosticsProvider for Model<Project> {
fn diagnostics(
fn pull_diagnostics(
&self,
buffer: &Model<Buffer>,
position: text::Anchor,
cx: &mut WindowContext,
) -> Option<Task<Result<Vec<lsp::Diagnostic>>>> {
cx: &mut AppContext,
) -> Option<Task<Result<Vec<LspDiagnostics>>>> {
Some(self.update(cx, |project, cx| {
project.document_diagnostics(buffer, position, cx)
}))
}

fn update_diagnostics(
&self,
diagnostics: Vec<LspDiagnostics>,
cx: &mut AppContext,
) -> Result<()> {
self.update(cx, |project, cx| {
diagnostics
.into_iter()
.map(|diagnostic_set| {
project.update_diagnostics(
diagnostic_set.server_id,
lsp::PublishDiagnosticsParams {
uri: diagnostic_set.uri.unwrap(),
diagnostics: diagnostic_set.diagnostics.unwrap_or_else(|| vec![]),
version: None,
},
&[],
cx,
)
})
.collect()
})
}
}

fn inlay_hint_settings(
Expand Down
126 changes: 114 additions & 12 deletions crates/project/src/lsp_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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::{
Expand Down Expand Up @@ -3075,9 +3077,91 @@ 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::<PointUtf16> {
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,
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<Vec<Diagnostic>>;
type Response = LspDiagnostics;
type LspRequest = lsp::request::DocumentDiagnosticRequest;
type ProtoRequest = proto::GetDocumentDiagnostics;

Expand Down Expand Up @@ -3118,18 +3202,34 @@ impl LspCommand for GetDocumentDiagnostics {
self,
message: lsp::DocumentDiagnosticReportResult,
_: Model<LspStore>,
_: Model<Buffer>,
_: LanguageServerId,
_: AsyncAppContext,
buffer: Model<Buffer>,
server_id: LanguageServerId,
cx: AsyncAppContext,
) -> Result<Self::Response> {
let uri = buffer.read_with(&cx, |buffer, cx| {
let file = buffer.file().and_then(|file| file.as_local())?;
let uri = lsp::Url::from_file_path(file.abs_path(cx).clone()).unwrap();
Some(uri)
})?;

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,
uri,
diagnostics: Some(report.full_document_diagnostic_report.items.clone()),
}),
lsp::DocumentDiagnosticReport::Unchanged(_) => Ok(LspDiagnostics {
server_id,
uri,
diagnostics: None,
}),
},
lsp::DocumentDiagnosticReportResult::Partial(_) => Ok(None),
lsp::DocumentDiagnosticReportResult::Partial(_) => Ok(LspDiagnostics {
server_id,
uri,
diagnostics: None,
}),
}
}

Expand Down Expand Up @@ -3180,6 +3280,7 @@ impl LspCommand for GetDocumentDiagnostics {

proto::GetDocumentDiagnosticsResponse {
server_id: LanguageServerId::to_proto(response.server_id),
uri: response.uri.unwrap().to_string(),
diagnostics,
}
}
Expand All @@ -3199,6 +3300,7 @@ impl LspCommand for GetDocumentDiagnostics {

Ok(LspDiagnostics {
server_id: LanguageServerId::from_proto(response.server_id),
uri: Some(lsp::Url::from_str(response.uri.as_str()).unwrap()),
diagnostics: Some(diagnostics),
})
}
Expand Down
16 changes: 5 additions & 11 deletions crates/project/src/lsp_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -3194,7 +3195,7 @@ impl LspStore {
buffer_handle: &Model<Buffer>,
position: Anchor,
cx: &mut ModelContext<Self>,
) -> Task<Result<Vec<lsp::Diagnostic>>> {
) -> Task<Result<Vec<LspDiagnostics>>> {
let buffer = buffer_handle.read(cx);
let buffer_id = buffer.remote_id();

Expand Down Expand Up @@ -3244,7 +3245,6 @@ impl LspStore {
.into_iter()
.collect::<Result<Vec<_>>>()?
.into_iter()
.flat_map(|diagnostics| diagnostics.into_iter().flatten())
.collect())
})
} else {
Expand All @@ -3254,13 +3254,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()) })
}
}

Expand Down
23 changes: 22 additions & 1 deletion crates/project/src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,27 @@ 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 diagnostics.
pub server_id: LanguageServerId,
/// URI of thr resource,
pub uri: Option<lsp::Url>,
/// The diagnostics produced by this language server.
pub diagnostics: Option<Vec<lsp::Diagnostic>>,
}

impl std::default::Default for LspDiagnostics {
fn default() -> Self {
Self {
server_id: LanguageServerId(0),
uri: None,
diagnostics: None,
}
}
}

impl Project {
pub fn init_settings(cx: &mut AppContext) {
WorktreeSettings::register(cx);
Expand Down Expand Up @@ -3012,7 +3033,7 @@ impl Project {
buffer_handle: &Model<Buffer>,
position: Anchor,
cx: &mut ModelContext<Self>,
) -> Task<anyhow::Result<Vec<lsp::Diagnostic>>> {
) -> Task<Result<Vec<LspDiagnostics>>> {
self.lsp_store.update(cx, |lsp_store, cx| {
lsp_store.document_diagnostic(buffer_handle, position, cx)
})
Expand Down
Loading

0 comments on commit 9ddc70c

Please sign in to comment.