lspkit_server/
diagnostics.rs1use std::sync::{Arc, Mutex};
8
9use async_trait::async_trait;
10use lspkit::Generation;
11use lspkit_vfs::DocumentUri;
12
13use crate::uri::path_to_uri;
14
15#[non_exhaustive]
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum Severity {
19 Error,
21 Warning,
23 Information,
25 Hint,
27}
28
29#[non_exhaustive]
31#[derive(Debug, Clone)]
32pub struct Diagnostic {
33 pub severity: Severity,
35 pub message: String,
37 pub range: (u32, u32, u32, u32),
39 pub code: Option<String>,
41 pub source: Option<String>,
43}
44
45impl Diagnostic {
46 #[must_use]
48 pub fn new(
49 severity: Severity,
50 message: impl Into<String>,
51 range: (u32, u32, u32, u32),
52 ) -> Self {
53 Self {
54 severity,
55 message: message.into(),
56 range,
57 code: None,
58 source: None,
59 }
60 }
61
62 #[must_use]
64 pub fn with_code(mut self, code: impl Into<String>) -> Self {
65 self.code = Some(code.into());
66 self
67 }
68
69 #[must_use]
71 pub fn with_source(mut self, source: impl Into<String>) -> Self {
72 self.source = Some(source.into());
73 self
74 }
75}
76
77#[non_exhaustive]
79#[derive(Debug, Clone)]
80pub struct DiagnosticsBatch {
81 pub uri: DocumentUri,
83 pub generation: Generation,
85 pub diagnostics: Vec<Diagnostic>,
87}
88
89impl DiagnosticsBatch {
90 #[must_use]
92 pub fn new(uri: DocumentUri, generation: Generation, diagnostics: Vec<Diagnostic>) -> Self {
93 Self {
94 uri,
95 generation,
96 diagnostics,
97 }
98 }
99}
100
101#[async_trait]
103pub trait DiagnosticsSink: Send + Sync + 'static {
104 async fn publish(&self, batch: DiagnosticsBatch);
106}
107
108#[derive(Clone, Default)]
110pub struct DiagnosticsBus {
111 sinks: Arc<Mutex<Vec<Arc<dyn DiagnosticsSink>>>>,
112}
113
114impl DiagnosticsBus {
115 #[must_use]
117 pub fn new() -> Self {
118 Self::default()
119 }
120
121 pub fn attach(&self, sink: Arc<dyn DiagnosticsSink>) -> usize {
123 if let Ok(mut guard) = self.sinks.lock() {
124 guard.push(sink);
125 guard.len()
126 } else {
127 0
128 }
129 }
130
131 pub async fn publish(&self, batch: DiagnosticsBatch) {
133 let snapshot: Vec<Arc<dyn DiagnosticsSink>> = self
134 .sinks
135 .lock()
136 .map(|guard| guard.clone())
137 .unwrap_or_default();
138 for sink in snapshot {
139 let sink_batch = batch.clone();
140 sink.publish(sink_batch).await;
141 }
142 }
143}
144
145impl std::fmt::Debug for DiagnosticsBus {
146 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147 let count = self.sinks.lock().map_or(0, |g| g.len());
148 f.debug_struct("DiagnosticsBus")
149 .field("sinks", &count)
150 .finish()
151 }
152}
153
154pub fn document_uri_from_path(path: &std::path::Path) -> Result<DocumentUri, crate::uri::UriError> {
159 Ok(DocumentUri::new(path_to_uri(path)?))
160}