bend_language_server/core/
diagnostics.rs1use std::path::Path;
2
3pub use bend::diagnostics::*;
4use bend::{check_book, imports::DefaultLoader, CompileOpts};
5use tower_lsp::lsp_types::{self as lsp, Position};
6use tree_sitter as ts;
7
8use super::document::Document;
9use crate::utils::color_wrapper::treat_colors;
10
11pub fn check(doc: &Document) -> Diagnostics {
13 let path = Path::new(doc.url.path());
14 let diagnostics_config = DiagnosticsConfig::new(Severity::Warning, true);
15 let compile_opts = CompileOpts::default();
16
17 let package_loader = DefaultLoader::new(path);
18
19 let diagnostics = bend::load_file_to_book(path, package_loader, diagnostics_config)
20 .and_then(|mut book| check_book(&mut book, diagnostics_config, compile_opts));
21
22 match diagnostics {
23 Ok(d) | Err(d) => d,
24 }
25}
26
27pub fn lsp_diagnostics(doc: &Document, diagnostics: &Diagnostics) -> Vec<lsp::Diagnostic> {
28 diagnostics
29 .diagnostics
30 .iter()
32 .flat_map(|(key, vals)| vals.iter().map(move |val| (key, val)))
34 .filter_map(|(origin, diagnostic)| treat_diagnostic(doc, origin, diagnostic))
36 .collect()
37}
38
39fn treat_diagnostic(
40 doc: &Document,
41 origin: &DiagnosticOrigin,
42 diag: &Diagnostic,
43) -> Option<lsp::Diagnostic> {
44 Some(lsp::Diagnostic {
45 message: treat_colors(&diag.display_with_origin(origin).to_string()),
46 severity: match diag.severity {
47 Severity::Allow => Some(lsp::DiagnosticSeverity::HINT),
48 Severity::Warning => Some(lsp::DiagnosticSeverity::WARNING),
49 Severity::Error => Some(lsp::DiagnosticSeverity::ERROR),
50 },
51 range: match origin {
52 DiagnosticOrigin::Function(name) => find_def(doc, name.as_ref())?,
53 DiagnosticOrigin::Inet(name) => find_def(doc, name.as_ref())?,
54 _ => span_to_range(&diag.source.span),
55 },
56 code: None,
57 code_description: None,
58 source: Some("bend".into()),
59 related_information: None,
60 tags: None,
61 data: None,
62 })
63}
64
65fn find_def(doc: &Document, name: &str) -> Option<lsp::Range> {
69 let query = format!(
70 r#"
71 (fun_function_definition
72 name: (identifier) @name
73 (#eq? @name "{name}"))
74 (imp_function_definition
75 name: (identifier) @name
76 (#eq? @name "{name}"))
77 "#
78 );
79
80 doc.find_one(&query)
81 .map(|node| ts_range_to_lsp(node.range()))
82}
83
84fn span_to_range(span: &Option<TextSpan>) -> lsp::Range {
85 span.as_ref()
86 .map(|span| lsp::Range {
87 start: Position {
88 line: span.start.line as u32,
89 character: span.start.char as u32,
90 },
91 end: Position {
92 line: span.end.line as u32,
93 character: span.end.char as u32,
94 },
95 })
96 .unwrap_or_default()
97}
98
99pub fn ts_range_to_lsp(range: ts::Range) -> lsp::Range {
100 lsp::Range {
101 start: lsp::Position {
102 line: range.start_point.row as u32,
103 character: range.start_point.column as u32,
104 },
105 end: lsp::Position {
106 line: range.end_point.row as u32,
107 character: range.end_point.column as u32,
108 },
109 }
110}