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