coderlib/lsp/
types.rs

1//! LSP types and error definitions
2
3use std::path::PathBuf;
4use serde::{Deserialize, Serialize};
5
6/// LSP-specific errors
7#[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/// Simplified diagnostic information from LSP
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct LspDiagnostic {
43    /// File path
44    pub file_path: PathBuf,
45    
46    /// Line number (0-based)
47    pub line: u32,
48    
49    /// Column number (0-based)
50    pub column: u32,
51    
52    /// End line (0-based)
53    pub end_line: u32,
54    
55    /// End column (0-based)
56    pub end_column: u32,
57    
58    /// Diagnostic message
59    pub message: String,
60    
61    /// Severity level
62    pub severity: DiagnosticSeverity,
63    
64    /// Diagnostic source (e.g., "rust-analyzer", "gopls")
65    pub source: Option<String>,
66    
67    /// Error code if available
68    pub code: Option<String>,
69}
70
71/// Diagnostic severity levels
72#[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, // Default to error for unknown severities
99        }
100    }
101}
102
103/// Position in a text document
104#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
105pub struct Position {
106    pub line: u32,
107    pub character: u32,
108}
109
110/// Range in a text document
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
112pub struct Range {
113    pub start: Position,
114    pub end: Position,
115}
116
117/// Location in a text document
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct Location {
120    pub file_path: PathBuf,
121    pub range: Range,
122}
123
124/// Completion item from LSP
125#[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/// Completion item kind
137#[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/// Hover information
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct Hover {
169    pub contents: String,
170    pub range: Option<Range>,
171}
172
173/// Code action from LSP
174#[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/// Symbol information
184#[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/// Symbol kind
193#[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
223// Conversion implementations
224impl 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, // Default fallback
289        }
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, // Default fallback
323        }
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(), // Will be set by caller
331            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    /// Format diagnostic for display
350    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    /// Check if this is an error-level diagnostic
372    pub fn is_error(&self) -> bool {
373        self.severity == DiagnosticSeverity::Error
374    }
375    
376    /// Check if this is a warning-level diagnostic
377    pub fn is_warning(&self) -> bool {
378        self.severity == DiagnosticSeverity::Warning
379    }
380}