1use std::{collections::HashMap, path::PathBuf, str::Utf8Error};
20
21use ariadne::{ColorGenerator, Fmt, Label, ReportBuilder, Source};
22use lsp_types::Url;
23use thiserror::Error;
24
25#[derive(Error, Clone, Debug, PartialEq, Eq)]
33pub enum ParseError {
34 #[error("{error}")]
35 LexerError {
36 range: lsp_types::Range,
37 #[source]
38 error: LexerError,
39 },
40 #[error("{error}")]
41 AstError {
42 range: lsp_types::Range,
43 #[source]
44 error: AstError,
45 },
46}
47
48impl From<ParseError> for lsp_types::Diagnostic {
49 fn from(error: ParseError) -> Self {
50 (&error).into()
51 }
52}
53
54impl From<&ParseError> for lsp_types::Diagnostic {
55 fn from(error: &ParseError) -> Self {
56 let (range, message) = match error {
57 ParseError::AstError { range, error } => (*range, error.to_string()),
58 ParseError::LexerError { range, error } => (*range, error.to_string()),
59 };
60 lsp_types::Diagnostic {
61 range,
62 severity: Some(lsp_types::DiagnosticSeverity::ERROR),
63 message,
64 code: Some(lsp_types::NumberOrString::String("AUTO_LSP".into())),
65 ..Default::default()
66 }
67 }
68}
69
70impl ParseError {
71 pub fn to_label(
73 &self,
74 source: &Source<&str>,
75 colors: &mut ColorGenerator,
76 report: &mut ReportBuilder<'_, std::ops::Range<usize>>,
77 ) {
78 let range = match self {
79 ParseError::LexerError { range, .. } => range,
80 ParseError::AstError { range, .. } => range,
81 };
82 let start_line = source.line(range.start.line as usize).unwrap().offset();
83 let end_line = source.line(range.end.line as usize).unwrap().offset();
84 let start = start_line + range.start.character as usize;
85 let end = end_line + range.end.character as usize;
86 let curr_color = colors.next();
87
88 report.add_label(
89 Label::new(start..end)
90 .with_message(format!("{}", self.to_string().fg(curr_color)))
91 .with_color(curr_color),
92 );
93 }
94}
95
96#[derive(Error, Clone, Debug, PartialEq, Eq)]
98pub enum AstError {
99 #[error("Unexpected {symbol} in {parent_name}")]
100 UnexpectedSymbol {
101 range: tree_sitter::Range,
102 symbol: &'static str,
103 parent_name: &'static str,
104 },
105}
106
107impl From<AstError> for ParseError {
108 fn from(error: AstError) -> Self {
109 let range = match &error {
110 AstError::UnexpectedSymbol { range, .. } => lsp_types::Range {
111 start: lsp_types::Position {
112 line: range.start_point.row as u32,
113 character: range.start_point.column as u32,
114 },
115 end: lsp_types::Position {
116 line: range.end_point.row as u32,
117 character: range.end_point.column as u32,
118 },
119 },
120 };
121 Self::AstError { range, error }
122 }
123}
124
125#[derive(Error, Clone, Debug, PartialEq, Eq)]
129pub enum LexerError {
130 #[error("{error}")]
131 Missing {
132 range: lsp_types::Range,
133 error: String,
134 grammar_name: &'static str,
136 },
137 #[error("{error}")]
138 Syntax {
139 range: lsp_types::Range,
140 error: String,
141 affected: String,
142 },
143}
144
145impl From<LexerError> for ParseError {
146 fn from(error: LexerError) -> Self {
147 let range = match &error {
148 LexerError::Missing { range, .. } => *range,
149 LexerError::Syntax { range, .. } => *range,
150 };
151 Self::LexerError { range, error }
152 }
153}
154
155#[derive(Debug)]
159#[salsa::accumulator]
160pub struct ParseErrorAccumulator(pub ParseError);
161
162impl ParseErrorAccumulator {
163 pub fn to_label(
164 &self,
165 source: &Source<&str>,
166 colors: &mut ColorGenerator,
167 report: &mut ReportBuilder<'_, std::ops::Range<usize>>,
168 ) {
169 self.0.to_label(source, colors, report);
170 }
171}
172
173impl From<&ParseErrorAccumulator> for lsp_types::Diagnostic {
174 fn from(error: &ParseErrorAccumulator) -> Self {
175 Self::from(&error.0)
176 }
177}
178
179impl From<&ParseError> for ParseErrorAccumulator {
180 fn from(diagnostic: &ParseError) -> Self {
181 Self(diagnostic.clone())
182 }
183}
184
185impl From<ParseError> for ParseErrorAccumulator {
186 fn from(diagnostic: ParseError) -> Self {
187 Self(diagnostic)
188 }
189}
190
191impl From<&ParseErrorAccumulator> for ParseError {
192 fn from(diagnostic: &ParseErrorAccumulator) -> Self {
193 diagnostic.0.clone()
194 }
195}
196
197impl From<LexerError> for ParseErrorAccumulator {
198 fn from(error: LexerError) -> Self {
199 Self(error.into())
200 }
201}
202
203impl From<AstError> for ParseErrorAccumulator {
204 fn from(error: AstError) -> Self {
205 Self(ParseError::from(error))
206 }
207}
208
209#[derive(Error, Clone, Debug, PartialEq, Eq)]
213pub enum PositionError {
214 #[error("Failed to find position of offset {offset}, max line length is {length}")]
215 LineOutOfBound { offset: usize, length: usize },
216 #[error("Failed to get position of offset {offset}")]
217 WrongPosition { offset: usize },
218 #[error("Failed to get range of {range:?}: {position_error}")]
219 WrongRange {
220 range: std::ops::Range<usize>,
221 #[source]
222 position_error: Box<PositionError>,
223 },
224 #[error("Failed to get text in {range:?}")]
225 WrongTextRange { range: std::ops::Range<usize> },
226 #[error("Failed to get text in {range:?}: Encountered UTF-8 error {utf8_error}")]
227 UTF8Error {
228 range: std::ops::Range<usize>,
229 utf8_error: Utf8Error,
230 },
231}
232
233#[derive(Error, Clone, Debug, PartialEq, Eq)]
235pub enum RuntimeError {
236 #[error("Document error in {uri}: {error}")]
237 DocumentError {
238 uri: Url,
239 #[source]
240 error: DocumentError,
241 },
242 #[error("Missing initialization options from client")]
243 MissingOptions,
244 #[error("Missing perFileParser object from initialization options")]
245 MissingPerFileParser,
246 #[error(transparent)]
247 DataBaseError(#[from] DataBaseError),
248 #[error(transparent)]
249 FileSystemError(#[from] FileSystemError),
250 #[error(transparent)]
251 ExtensionError(#[from] ExtensionError),
252}
253
254impl From<(&Url, DocumentError)> for RuntimeError {
255 fn from((uri, error): (&Url, DocumentError)) -> Self {
256 RuntimeError::DocumentError {
257 uri: uri.clone(),
258 error,
259 }
260 }
261}
262
263impl From<(&Url, TreeSitterError)> for RuntimeError {
264 fn from((uri, error): (&Url, TreeSitterError)) -> Self {
265 RuntimeError::DocumentError {
266 uri: uri.clone(),
267 error: DocumentError::TreeSitter(error),
268 }
269 }
270}
271
272impl From<(&Url, TexterError)> for RuntimeError {
273 fn from((uri, error): (&Url, TexterError)) -> Self {
274 RuntimeError::DocumentError {
275 uri: uri.clone(),
276 error: DocumentError::Texter(error),
277 }
278 }
279}
280
281#[derive(Error, Clone, Debug, PartialEq, Eq)]
283pub enum FileSystemError {
284 #[cfg(windows)]
285 #[error("Invalid host '{host}' for file path: {path}")]
286 FileUrlHost { host: String, path: Url },
287 #[error("Failed to convert url {path} to file path")]
288 FileUrlToFilePath { path: Url },
289 #[error("Failed to convert file path {path} to url")]
290 FilePathToUrl { path: PathBuf },
291 #[error("Failed to get extension of file {path}")]
292 FileExtension { path: Url },
293 #[error("Failed to open file {path}: {error}")]
294 FileOpen { path: Url, error: String },
295 #[error("Failed to read file {path}: {error}")]
296 FileRead { path: Url, error: String },
297 #[error(transparent)]
298 ExtensionError(#[from] ExtensionError),
299}
300
301#[derive(Error, Clone, Debug, PartialEq, Eq)]
303pub enum ExtensionError {
304 #[error("Unknown file extension {extension}, available extensions are: {available:?}")]
305 UnknownExtension {
306 extension: String,
307 available: HashMap<String, String>,
308 },
309 #[error("No parser found for extension {extension}, available parsers are: {available:?}")]
310 UnknownParser {
311 extension: String,
312 available: Vec<&'static str>,
313 },
314}
315
316#[derive(Error, Clone, Debug, PartialEq, Eq)]
318pub enum DataBaseError {
319 #[error("Failed to get file {uri}")]
320 FileNotFound { uri: Url },
321 #[error("File {uri} already exists")]
322 FileAlreadyExists { uri: Url },
323 #[error("Document error in {uri}: {error}")]
324 DocumentError {
325 uri: Url,
326 #[source]
327 error: DocumentError,
328 },
329}
330
331impl From<(&Url, DocumentError)> for DataBaseError {
332 fn from((uri, error): (&Url, DocumentError)) -> Self {
333 DataBaseError::DocumentError {
334 uri: uri.clone(),
335 error,
336 }
337 }
338}
339
340impl From<(&Url, TreeSitterError)> for DataBaseError {
341 fn from((uri, error): (&Url, TreeSitterError)) -> Self {
342 DataBaseError::DocumentError {
343 uri: uri.clone(),
344 error: DocumentError::TreeSitter(error),
345 }
346 }
347}
348
349#[derive(Error, Clone, Debug, PartialEq, Eq)]
353pub enum DocumentError {
354 #[error(transparent)]
355 TreeSitter(#[from] TreeSitterError),
356 #[error(transparent)]
357 Texter(#[from] TexterError),
358}
359
360#[derive(Error, Clone, Debug, PartialEq, Eq)]
361pub enum TreeSitterError {
362 #[error("Tree sitter failed to parse tree")]
363 TreeSitterParser,
364}
365
366#[derive(Error, Clone, Debug, PartialEq, Eq)]
367pub enum TexterError {
368 #[error("Texter failed to handle document")]
369 TexterError(#[from] texter::error::Error),
370}