1use std::path::PathBuf;
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, thiserror::Error)]
8pub enum LspError {
9 #[error("LSP server not found for file type: {0}")]
10 ServerNotFound(String),
11
12 #[error("LSP server failed to start: {0}")]
13 ServerStartFailed(String),
14
15 #[error("LSP communication error: {0}")]
16 CommunicationError(String),
17
18 #[error("LSP server timeout: {0}")]
19 Timeout(String),
20
21 #[error("LSP server not initialized")]
22 NotInitialized,
23
24 #[error("Invalid LSP response: {0}")]
25 InvalidResponse(String),
26
27 #[error("IO error: {0}")]
28 Io(#[from] std::io::Error),
29
30 #[error("JSON error: {0}")]
31 Json(#[from] serde_json::Error),
32
33 #[error("Tower LSP error: {0}")]
34 TowerLsp(String),
35
36 #[error("File not open: {0}")]
37 FileNotOpen(PathBuf),
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct LspDiagnostic {
43 pub file_path: PathBuf,
45
46 pub line: u32,
48
49 pub column: u32,
51
52 pub end_line: u32,
54
55 pub end_column: u32,
57
58 pub message: String,
60
61 pub severity: DiagnosticSeverity,
63
64 pub source: Option<String>,
66
67 pub code: Option<String>,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
73pub enum DiagnosticSeverity {
74 Error,
75 Warning,
76 Information,
77 Hint,
78}
79
80impl std::fmt::Display for DiagnosticSeverity {
81 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82 match self {
83 DiagnosticSeverity::Error => write!(f, "ERROR"),
84 DiagnosticSeverity::Warning => write!(f, "WARNING"),
85 DiagnosticSeverity::Information => write!(f, "INFO"),
86 DiagnosticSeverity::Hint => write!(f, "HINT"),
87 }
88 }
89}
90
91impl From<lsp_types::DiagnosticSeverity> for DiagnosticSeverity {
92 fn from(severity: lsp_types::DiagnosticSeverity) -> Self {
93 match severity {
94 lsp_types::DiagnosticSeverity::ERROR => DiagnosticSeverity::Error,
95 lsp_types::DiagnosticSeverity::WARNING => DiagnosticSeverity::Warning,
96 lsp_types::DiagnosticSeverity::INFORMATION => DiagnosticSeverity::Information,
97 lsp_types::DiagnosticSeverity::HINT => DiagnosticSeverity::Hint,
98 _ => DiagnosticSeverity::Error, }
100 }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
105pub struct Position {
106 pub line: u32,
107 pub character: u32,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
112pub struct Range {
113 pub start: Position,
114 pub end: Position,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct Location {
120 pub file_path: PathBuf,
121 pub range: Range,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct CompletionItem {
127 pub label: String,
128 pub kind: Option<CompletionItemKind>,
129 pub detail: Option<String>,
130 pub documentation: Option<String>,
131 pub insert_text: Option<String>,
132 pub filter_text: Option<String>,
133 pub sort_text: Option<String>,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub enum CompletionItemKind {
139 Text,
140 Method,
141 Function,
142 Constructor,
143 Field,
144 Variable,
145 Class,
146 Interface,
147 Module,
148 Property,
149 Unit,
150 Value,
151 Enum,
152 Keyword,
153 Snippet,
154 Color,
155 File,
156 Reference,
157 Folder,
158 EnumMember,
159 Constant,
160 Struct,
161 Event,
162 Operator,
163 TypeParameter,
164}
165
166#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct Hover {
169 pub contents: String,
170 pub range: Option<Range>,
171}
172
173#[derive(Debug, Clone, Serialize, Deserialize)]
175pub struct CodeAction {
176 pub title: String,
177 pub kind: Option<String>,
178 pub diagnostics: Vec<LspDiagnostic>,
179 pub is_preferred: bool,
180 pub disabled: Option<String>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct SymbolInformation {
186 pub name: String,
187 pub kind: SymbolKind,
188 pub location: Location,
189 pub container_name: Option<String>,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
194pub enum SymbolKind {
195 File,
196 Module,
197 Namespace,
198 Package,
199 Class,
200 Method,
201 Property,
202 Field,
203 Constructor,
204 Enum,
205 Interface,
206 Function,
207 Variable,
208 Constant,
209 String,
210 Number,
211 Boolean,
212 Array,
213 Object,
214 Key,
215 Null,
216 EnumMember,
217 Struct,
218 Event,
219 Operator,
220 TypeParameter,
221}
222
223impl From<lsp_types::Position> for Position {
225 fn from(pos: lsp_types::Position) -> Self {
226 Self {
227 line: pos.line,
228 character: pos.character,
229 }
230 }
231}
232
233impl From<Position> for lsp_types::Position {
234 fn from(pos: Position) -> Self {
235 Self {
236 line: pos.line,
237 character: pos.character,
238 }
239 }
240}
241
242impl From<lsp_types::Range> for Range {
243 fn from(range: lsp_types::Range) -> Self {
244 Self {
245 start: range.start.into(),
246 end: range.end.into(),
247 }
248 }
249}
250
251impl From<Range> for lsp_types::Range {
252 fn from(range: Range) -> Self {
253 Self {
254 start: range.start.into(),
255 end: range.end.into(),
256 }
257 }
258}
259
260impl From<lsp_types::CompletionItemKind> for CompletionItemKind {
261 fn from(kind: lsp_types::CompletionItemKind) -> Self {
262 match kind {
263 lsp_types::CompletionItemKind::TEXT => CompletionItemKind::Text,
264 lsp_types::CompletionItemKind::METHOD => CompletionItemKind::Method,
265 lsp_types::CompletionItemKind::FUNCTION => CompletionItemKind::Function,
266 lsp_types::CompletionItemKind::CONSTRUCTOR => CompletionItemKind::Constructor,
267 lsp_types::CompletionItemKind::FIELD => CompletionItemKind::Field,
268 lsp_types::CompletionItemKind::VARIABLE => CompletionItemKind::Variable,
269 lsp_types::CompletionItemKind::CLASS => CompletionItemKind::Class,
270 lsp_types::CompletionItemKind::INTERFACE => CompletionItemKind::Interface,
271 lsp_types::CompletionItemKind::MODULE => CompletionItemKind::Module,
272 lsp_types::CompletionItemKind::PROPERTY => CompletionItemKind::Property,
273 lsp_types::CompletionItemKind::UNIT => CompletionItemKind::Unit,
274 lsp_types::CompletionItemKind::VALUE => CompletionItemKind::Value,
275 lsp_types::CompletionItemKind::ENUM => CompletionItemKind::Enum,
276 lsp_types::CompletionItemKind::KEYWORD => CompletionItemKind::Keyword,
277 lsp_types::CompletionItemKind::SNIPPET => CompletionItemKind::Snippet,
278 lsp_types::CompletionItemKind::COLOR => CompletionItemKind::Color,
279 lsp_types::CompletionItemKind::FILE => CompletionItemKind::File,
280 lsp_types::CompletionItemKind::REFERENCE => CompletionItemKind::Reference,
281 lsp_types::CompletionItemKind::FOLDER => CompletionItemKind::Folder,
282 lsp_types::CompletionItemKind::ENUM_MEMBER => CompletionItemKind::EnumMember,
283 lsp_types::CompletionItemKind::CONSTANT => CompletionItemKind::Constant,
284 lsp_types::CompletionItemKind::STRUCT => CompletionItemKind::Struct,
285 lsp_types::CompletionItemKind::EVENT => CompletionItemKind::Event,
286 lsp_types::CompletionItemKind::OPERATOR => CompletionItemKind::Operator,
287 lsp_types::CompletionItemKind::TYPE_PARAMETER => CompletionItemKind::TypeParameter,
288 _ => CompletionItemKind::Text, }
290 }
291}
292
293impl From<lsp_types::SymbolKind> for SymbolKind {
294 fn from(kind: lsp_types::SymbolKind) -> Self {
295 match kind {
296 lsp_types::SymbolKind::FILE => SymbolKind::File,
297 lsp_types::SymbolKind::MODULE => SymbolKind::Module,
298 lsp_types::SymbolKind::NAMESPACE => SymbolKind::Namespace,
299 lsp_types::SymbolKind::PACKAGE => SymbolKind::Package,
300 lsp_types::SymbolKind::CLASS => SymbolKind::Class,
301 lsp_types::SymbolKind::METHOD => SymbolKind::Method,
302 lsp_types::SymbolKind::PROPERTY => SymbolKind::Property,
303 lsp_types::SymbolKind::FIELD => SymbolKind::Field,
304 lsp_types::SymbolKind::CONSTRUCTOR => SymbolKind::Constructor,
305 lsp_types::SymbolKind::ENUM => SymbolKind::Enum,
306 lsp_types::SymbolKind::INTERFACE => SymbolKind::Interface,
307 lsp_types::SymbolKind::FUNCTION => SymbolKind::Function,
308 lsp_types::SymbolKind::VARIABLE => SymbolKind::Variable,
309 lsp_types::SymbolKind::CONSTANT => SymbolKind::Constant,
310 lsp_types::SymbolKind::STRING => SymbolKind::String,
311 lsp_types::SymbolKind::NUMBER => SymbolKind::Number,
312 lsp_types::SymbolKind::BOOLEAN => SymbolKind::Boolean,
313 lsp_types::SymbolKind::ARRAY => SymbolKind::Array,
314 lsp_types::SymbolKind::OBJECT => SymbolKind::Object,
315 lsp_types::SymbolKind::KEY => SymbolKind::Key,
316 lsp_types::SymbolKind::NULL => SymbolKind::Null,
317 lsp_types::SymbolKind::ENUM_MEMBER => SymbolKind::EnumMember,
318 lsp_types::SymbolKind::STRUCT => SymbolKind::Struct,
319 lsp_types::SymbolKind::EVENT => SymbolKind::Event,
320 lsp_types::SymbolKind::OPERATOR => SymbolKind::Operator,
321 lsp_types::SymbolKind::TYPE_PARAMETER => SymbolKind::TypeParameter,
322 _ => SymbolKind::File, }
324 }
325}
326
327impl From<lsp_types::Diagnostic> for LspDiagnostic {
328 fn from(diagnostic: lsp_types::Diagnostic) -> Self {
329 Self {
330 file_path: PathBuf::new(), line: diagnostic.range.start.line,
332 column: diagnostic.range.start.character,
333 end_line: diagnostic.range.end.line,
334 end_column: diagnostic.range.end.character,
335 message: diagnostic.message,
336 severity: diagnostic.severity
337 .map(DiagnosticSeverity::from)
338 .unwrap_or(DiagnosticSeverity::Error),
339 source: diagnostic.source,
340 code: diagnostic.code.and_then(|c| match c {
341 lsp_types::NumberOrString::Number(n) => Some(n.to_string()),
342 lsp_types::NumberOrString::String(s) => Some(s),
343 }),
344 }
345 }
346}
347
348impl LspDiagnostic {
349 pub fn format(&self) -> String {
351 let severity_str = match self.severity {
352 DiagnosticSeverity::Error => "ERROR",
353 DiagnosticSeverity::Warning => "WARNING",
354 DiagnosticSeverity::Information => "INFO",
355 DiagnosticSeverity::Hint => "HINT",
356 };
357
358 let location = format!("{}:{}:{}",
359 self.file_path.display(),
360 self.line + 1,
361 self.column + 1
362 );
363
364 let source_info = self.source.as_ref()
365 .map(|s| format!(" [{}]", s))
366 .unwrap_or_default();
367
368 format!("{} {}{}: {}", severity_str, location, source_info, self.message)
369 }
370
371 pub fn is_error(&self) -> bool {
373 self.severity == DiagnosticSeverity::Error
374 }
375
376 pub fn is_warning(&self) -> bool {
378 self.severity == DiagnosticSeverity::Warning
379 }
380}