1use crate::ast::expressions::{
2 DatexExpression, DatexExpressionData, List, Map, Statements,
3 VariableAccess, VariableAssignment, VariableDeclaration,
4};
5use crate::compiler::error::DetailedCompilerErrors;
6use crate::compiler::precompiler::precompiled_ast::VariableMetadata;
7use crate::lsp::LanguageServerBackend;
8use crate::lsp::errors::SpannedLSPCompilerError;
9use crate::lsp::type_hint_collector::TypeHintCollector;
10use crate::values::core_values::decimal::Decimal;
11use crate::values::core_values::decimal::typed_decimal::TypedDecimal;
12use crate::values::core_values::endpoint::Endpoint;
13use crate::values::core_values::integer::Integer;
14use crate::values::core_values::integer::typed_integer::TypedInteger;
15use crate::values::core_values::r#type::Type;
16use crate::visitor::VisitAction;
17use crate::visitor::expression::ExpressionVisitor;
18use crate::visitor::type_expression::TypeExpressionVisitor;
19use realhydroper_lsp::lsp_types::{
20 MessageType, Position, Range, TextDocumentPositionParams,
21};
22use url::Url;
23
24impl LanguageServerBackend {
25 pub async fn update_file_contents(&self, url: Url, content: String) {
26 let mut compiler_workspace = self.compiler_workspace.borrow_mut();
27 let file = compiler_workspace.load_file(url.clone(), content.clone());
28 self.clear_compiler_errors(&url);
30 if let Some(errors) = &file.errors {
31 self.client
32 .log_message(
33 MessageType::ERROR,
34 format!("Failed to compile file {}: {}", url, errors,),
35 )
36 .await;
37 self.collect_compiler_errors(errors, url, &content)
38 }
39 if let Some(rich_ast) = &file.rich_ast {
40 self.client
41 .log_message(
42 MessageType::INFO,
43 format!("AST: {:#?}", rich_ast.ast),
44 )
45 .await;
46 self.client
47 .log_message(
48 MessageType::INFO,
49 format!("AST metadata: {:#?}", *rich_ast.metadata.borrow()),
50 )
51 .await;
52 }
53 }
54
55 pub(crate) fn get_type_hints(
56 &self,
57 url: Url,
58 ) -> Option<Vec<(Position, Option<Type>)>> {
59 let mut workspace = self.compiler_workspace.borrow_mut();
60 let file = workspace.get_file_mut(&url).unwrap();
61 if let Some(rich_ast) = &mut file.rich_ast {
62 let ast = &mut rich_ast.ast;
63 let mut collector = TypeHintCollector::default();
64 collector.visit_datex_expression(ast);
65 Some(
66 collector
67 .type_hints
68 .into_iter()
69 .map(|hint| {
70 (
71 self.byte_offset_to_position(hint.0, &file.content)
72 .unwrap(),
73 rich_ast
74 .metadata
75 .borrow()
76 .variables
77 .get(hint.1)
78 .unwrap()
79 .var_type
80 .clone(),
81 )
82 })
83 .collect(),
84 )
85 } else {
86 None
87 }
88 }
89
90 fn clear_compiler_errors(&self, url: &Url) {
92 let mut spanned_compiler_errors =
93 self.spanned_compiler_errors.borrow_mut();
94 spanned_compiler_errors.remove(url);
95 }
96
97 fn collect_compiler_errors(
99 &self,
100 errors: &DetailedCompilerErrors,
101 url: Url,
102 file_content: &String,
103 ) {
104 let mut spanned_compiler_errors =
105 self.spanned_compiler_errors.borrow_mut();
106 let file_errors =
107 spanned_compiler_errors.entry(url.clone()).or_default();
108
109 for error in &errors.errors {
110 let span = error
111 .span
112 .as_ref()
113 .map(|span| {
114 self.convert_byte_range_to_document_range(
115 span,
116 file_content,
117 )
118 })
119 .unwrap_or_else(|| {
120 self.convert_byte_range_to_document_range(
121 &(0..file_content.len()),
122 file_content,
123 )
124 });
125 file_errors.push(SpannedLSPCompilerError {
126 span,
127 error: error.error.clone(),
128 });
129 }
130 }
131
132 pub fn find_variable_starting_with(
134 &self,
135 prefix: &str,
136 ) -> Vec<VariableMetadata> {
137 let compiler_workspace = self.compiler_workspace.borrow();
138 let mut results = Vec::new();
139 for file in compiler_workspace.files().values() {
140 if let Some(rich_ast) = &file.rich_ast {
141 let metadata = rich_ast.metadata.borrow();
142 for var in metadata.variables.iter() {
143 if var.name.starts_with(prefix) {
144 results.push(var.clone());
145 }
146 }
147 }
148 }
149 results
150 }
151
152 pub fn get_variable_by_id(&self, id: usize) -> Option<VariableMetadata> {
154 let compiler_workspace = self.compiler_workspace.borrow();
155 for file in compiler_workspace.files().values() {
156 if let Some(rich_ast) = &file.rich_ast {
157 let metadata = rich_ast.metadata.borrow();
158 if let Some(v) = metadata.variables.get(id).cloned() {
159 return Some(v);
160 }
161 }
162 }
163 None
164 }
165
166 fn position_to_byte_offset(
168 &self,
169 position: &TextDocumentPositionParams,
170 ) -> usize {
171 let workspace = self.compiler_workspace.borrow();
172 let file_content = &workspace
175 .get_file(&position.text_document.uri)
176 .unwrap()
177 .content;
178
179 Self::line_char_to_byte_index(
180 file_content,
181 position.position.line as usize,
182 position.position.character as usize,
183 )
184 .unwrap_or(0)
185 }
186
187 pub fn convert_byte_range_to_document_range(
189 &self,
190 span: &core::ops::Range<usize>,
191 file_content: &String,
192 ) -> Range {
193 let start = self
194 .byte_offset_to_position(span.start, file_content)
195 .unwrap_or(Position {
196 line: 0,
197 character: 0,
198 });
199 let end = self
200 .byte_offset_to_position(span.end, file_content)
201 .unwrap_or(Position {
202 line: 0,
203 character: 0,
204 });
205 Range { start, end }
206 }
207
208 pub fn byte_offset_to_position(
211 &self,
212 byte_offset: usize,
213 file_content: &String,
214 ) -> Option<Position> {
215 let mut current_offset = 0;
216 for (line_idx, line) in file_content.lines().enumerate() {
217 let line_length = line.len() + 1; if current_offset + line_length > byte_offset {
219 let char_offset = line
221 .char_indices()
222 .find(|(i, _)| current_offset + i >= byte_offset)
223 .map(|(i, _)| i)
224 .unwrap_or(line.len());
225 return Some(Position {
226 line: line_idx as u32,
227 character: char_offset as u32,
228 });
229 }
230 current_offset += line_length;
231 }
232 None
233 }
234
235 pub fn get_previous_text_at_position(
238 &self,
239 position: &TextDocumentPositionParams,
240 ) -> String {
241 let byte_offset = self.position_to_byte_offset(position);
242 let workspace = self.compiler_workspace.borrow();
243 let file_content = &workspace
244 .get_file(&position.text_document.uri)
245 .unwrap()
246 .content;
247 let previous_text = &file_content[..byte_offset];
249 let last_word = previous_text
250 .rsplit(|c: char| !c.is_alphanumeric() && c != '_')
251 .next()
252 .unwrap_or("");
253 last_word.to_string()
254 }
255
256 pub fn get_expression_at_position(
258 &self,
259 position: &TextDocumentPositionParams,
260 ) -> Option<DatexExpression> {
261 let byte_offset = self.position_to_byte_offset(position);
262 let mut workspace = self.compiler_workspace.borrow_mut();
263 if let Some(rich_ast) = &mut workspace
264 .get_file_mut(&position.text_document.uri)
265 .unwrap()
266 .rich_ast
267 {
268 let ast = &mut rich_ast.ast;
269 let mut finder = ExpressionFinder::new(byte_offset);
270 finder.visit_datex_expression(ast);
271 finder.found_expr.map(|e| DatexExpression {
272 span: e.1,
273 data: e.0,
274 ty: None,
275 })
276 } else {
277 None
278 }
279 }
280
281 pub fn line_char_to_byte_index(
285 text: &str,
286 line: usize,
287 character: usize,
288 ) -> Option<usize> {
289 let mut lines = text.split('\n');
290
291 let line_text = lines.nth(line)?;
293
294 let byte_offset_to_line_start = text
296 .lines()
297 .take(line)
298 .map(|l| l.len() + 1) .sum::<usize>();
300
301 let byte_offset_within_line = line_text
303 .char_indices()
304 .nth(character)
305 .map(|(i, _)| i)
306 .unwrap_or_else(|| line_text.len());
307
308 Some(byte_offset_to_line_start + byte_offset_within_line)
309 }
310}
311
312struct ExpressionFinder {
315 pub search_pos: usize,
316 pub found_expr: Option<(DatexExpressionData, core::ops::Range<usize>)>,
317}
318
319impl ExpressionFinder {
320 pub fn new(search_pos: usize) -> Self {
321 Self {
322 search_pos,
323 found_expr: None,
324 }
325 }
326
327 fn match_span(
331 &mut self,
332 span: &core::ops::Range<usize>,
333 expr_data: DatexExpressionData,
334 ) -> Result<VisitAction<DatexExpression>, ()> {
335 if span.start <= self.search_pos && self.search_pos <= span.end {
336 if let Some((_, existing_expr_span)) = &self.found_expr {
338 if (span.end - span.start)
339 < (existing_expr_span.end - existing_expr_span.start)
340 {
341 self.found_expr = Some((expr_data, span.clone()));
342 }
343 } else {
344 self.found_expr = Some((expr_data, span.clone()));
345 }
346 Ok(VisitAction::VisitChildren)
347 } else {
348 Ok(VisitAction::SkipChildren)
349 }
350 }
351}
352
353impl TypeExpressionVisitor<()> for ExpressionFinder {}
354
355impl ExpressionVisitor<()> for ExpressionFinder {
356 fn visit_statements(
357 &mut self,
358 stmts: &mut Statements,
359 span: &core::ops::Range<usize>,
360 ) -> Result<VisitAction<DatexExpression>, ()> {
361 self.match_span(span, DatexExpressionData::Statements(stmts.clone()))
362 }
363
364 fn visit_variable_declaration(
365 &mut self,
366 var_decl: &mut VariableDeclaration,
367 span: &core::ops::Range<usize>,
368 ) -> Result<VisitAction<DatexExpression>, ()> {
369 self.match_span(
370 span,
371 DatexExpressionData::VariableDeclaration(var_decl.clone()),
372 )
373 }
374
375 fn visit_variable_assignment(
376 &mut self,
377 var_assign: &mut VariableAssignment,
378 span: &core::ops::Range<usize>,
379 ) -> Result<VisitAction<DatexExpression>, ()> {
380 self.match_span(
381 span,
382 DatexExpressionData::VariableAssignment(var_assign.clone()),
383 )
384 }
385
386 fn visit_variable_access(
387 &mut self,
388 var_access: &mut VariableAccess,
389 span: &core::ops::Range<usize>,
390 ) -> Result<VisitAction<DatexExpression>, ()> {
391 self.match_span(
392 span,
393 DatexExpressionData::VariableAccess(var_access.clone()),
394 )
395 }
396
397 fn visit_list(
398 &mut self,
399 list: &mut List,
400 span: &core::ops::Range<usize>,
401 ) -> Result<VisitAction<DatexExpression>, ()> {
402 self.match_span(span, DatexExpressionData::List(list.clone()))
403 }
404
405 fn visit_map(
406 &mut self,
407 map: &mut Map,
408 span: &core::ops::Range<usize>,
409 ) -> Result<VisitAction<DatexExpression>, ()> {
410 self.match_span(span, DatexExpressionData::Map(map.clone()))
411 }
412
413 fn visit_integer(
414 &mut self,
415 value: &mut Integer,
416 span: &core::ops::Range<usize>,
417 ) -> Result<VisitAction<DatexExpression>, ()> {
418 self.match_span(span, DatexExpressionData::Integer(value.clone()))
419 }
420
421 fn visit_typed_integer(
422 &mut self,
423 value: &mut TypedInteger,
424 span: &core::ops::Range<usize>,
425 ) -> Result<VisitAction<DatexExpression>, ()> {
426 self.match_span(span, DatexExpressionData::TypedInteger(value.clone()))
427 }
428
429 fn visit_decimal(
430 &mut self,
431 value: &mut Decimal,
432 span: &core::ops::Range<usize>,
433 ) -> Result<VisitAction<DatexExpression>, ()> {
434 self.match_span(span, DatexExpressionData::Decimal(value.clone()))
435 }
436
437 fn visit_typed_decimal(
438 &mut self,
439 value: &mut TypedDecimal,
440 span: &core::ops::Range<usize>,
441 ) -> Result<VisitAction<DatexExpression>, ()> {
442 self.match_span(span, DatexExpressionData::TypedDecimal(value.clone()))
443 }
444
445 fn visit_text(
446 &mut self,
447 value: &mut String,
448 span: &core::ops::Range<usize>,
449 ) -> Result<VisitAction<DatexExpression>, ()> {
450 self.match_span(span, DatexExpressionData::Text(value.clone()))
451 }
452
453 fn visit_boolean(
454 &mut self,
455 value: &mut bool,
456 span: &core::ops::Range<usize>,
457 ) -> Result<VisitAction<DatexExpression>, ()> {
458 self.match_span(span, DatexExpressionData::Boolean(*value))
459 }
460
461 fn visit_endpoint(
462 &mut self,
463 value: &mut Endpoint,
464 span: &core::ops::Range<usize>,
465 ) -> Result<VisitAction<DatexExpression>, ()> {
466 self.match_span(span, DatexExpressionData::Endpoint(value.clone()))
467 }
468
469 fn visit_null(
470 &mut self,
471 span: &core::ops::Range<usize>,
472 ) -> Result<VisitAction<DatexExpression>, ()> {
473 self.match_span(span, DatexExpressionData::Null)
474 }
475}