use lsp_types::{Diagnostic, DiagnosticSeverity, NumberOrString, PublishDiagnosticsParams, Uri};
use crate::App;
pub trait DiagnosticsProvider {
fn diagnostics(
&self,
uri: &Uri,
window: &mut crate::Window,
_cx: &mut App,
) -> crate::Task<anyhow::Result<Vec<DiagnosticEntry>>> {
let _ = (uri, window);
crate::Task::ready(Ok(vec![]))
}
fn on_diagnostics(&self, callback: Box<dyn Fn(PublishDiagnosticsParams)>, cx: &mut App) {
let _ = (callback, cx);
}
}
#[derive(Debug, Clone)]
pub struct DiagnosticEntry {
pub range: lsp_types::Range,
pub severity: DiagnosticSeverity,
pub source: Option<String>,
pub message: String,
pub related_information: Vec<RelatedInformation>,
pub code: Option<String>,
pub tags: Vec<DiagnosticTag>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiagnosticTag {
Unnecessary,
Deprecated,
}
#[derive(Debug, Clone)]
pub struct RelatedInformation {
pub location: lsp_types::Location,
pub message: String,
}
impl DiagnosticEntry {
pub fn from_diagnostic(diagnostic: Diagnostic) -> Self {
let severity = diagnostic.severity.unwrap_or(DiagnosticSeverity::WARNING);
let code = diagnostic.code.as_ref().map(|c| match c {
NumberOrString::Number(n) => n.to_string(),
NumberOrString::String(s) => s.clone(),
});
let tags = diagnostic
.tags
.unwrap_or_default()
.into_iter()
.filter_map(|t| match t {
lsp_types::DiagnosticTag::UNNECESSARY => Some(DiagnosticTag::Unnecessary),
lsp_types::DiagnosticTag::DEPRECATED => Some(DiagnosticTag::Deprecated),
_ => None,
})
.collect();
let related_information = diagnostic
.related_information
.unwrap_or_default()
.into_iter()
.map(|info| RelatedInformation {
location: info.location,
message: info.message,
})
.collect();
Self {
range: diagnostic.range,
severity,
source: diagnostic.source,
message: diagnostic.message,
related_information,
code,
tags,
}
}
pub fn is_error(&self) -> bool {
self.severity == DiagnosticSeverity::ERROR
}
pub fn is_warning(&self) -> bool {
self.severity == DiagnosticSeverity::WARNING
}
pub fn is_info(&self) -> bool {
self.severity == DiagnosticSeverity::INFORMATION
}
pub fn is_hint(&self) -> bool {
self.severity == DiagnosticSeverity::HINT
}
}
#[derive(Default)]
pub struct DiagnosticsState {
pub diagnostics: std::collections::HashMap<Uri, Vec<DiagnosticEntry>>,
}
impl DiagnosticsState {
pub fn update(&mut self, params: PublishDiagnosticsParams) {
let diagnostics: Vec<DiagnosticEntry> = params
.diagnostics
.into_iter()
.map(DiagnosticEntry::from_diagnostic)
.collect();
self.diagnostics.insert(params.uri, diagnostics);
}
pub fn get(&self, uri: &Uri) -> &[DiagnosticEntry] {
self.diagnostics.get(uri).map_or(&[], |v| v.as_slice())
}
pub fn clear(&mut self, uri: &Uri) {
self.diagnostics.remove(uri);
}
pub fn clear_all(&mut self) {
self.diagnostics.clear();
}
pub fn error_count(&self, uri: &Uri) -> usize {
self.get(uri).iter().filter(|d| d.is_error()).count()
}
pub fn warning_count(&self, uri: &Uri) -> usize {
self.get(uri).iter().filter(|d| d.is_warning()).count()
}
}