solidity_language_server/
solar_runner.rs1use crate::runner::{Runner, RunnerError};
2use solar::{
3 interface::{
4 Session, SourceMap,
5 diagnostics::{Diag, DiagCtxt, InMemoryEmitter},
6 },
7 sema::Compiler,
8};
9use std::{io::Error, path::Path};
10use tokio::task;
11use tower_lsp::async_trait;
12use tower_lsp::lsp_types::{Diagnostic, Position, Url};
13
14pub struct SolarRunner;
15
16fn solar_diag_to_lsp(
17 diag: &Diag,
18 target_file: &str,
19 source_map: &SourceMap,
20) -> Option<tower_lsp::lsp_types::Diagnostic> {
21 use tower_lsp::lsp_types::NumberOrString;
22
23 let primary_span = diag.span.primary_span()?;
24 let _uri = Url::from_file_path(target_file).ok()?;
25 let range = span_to_range(source_map, primary_span);
26
27 Some(tower_lsp::lsp_types::Diagnostic {
28 range,
29 severity: Some(severity(diag.level())),
30 code: diag
31 .code
32 .as_ref()
33 .map(|id| NumberOrString::String(id.as_string())),
34 code_description: None,
35 source: Some("solar".into()),
36 message: diag.label().into_owned(),
37 related_information: None, tags: None,
39 data: None,
40 })
41}
42
43fn span_to_range(
44 source_map: &SourceMap,
45 span: solar::interface::Span,
46) -> tower_lsp::lsp_types::Range {
47 let start_loc = source_map.lookup_char_pos(span.lo());
48 let end_loc = source_map.lookup_char_pos(span.hi());
49 tower_lsp::lsp_types::Range {
50 start: Position {
51 line: start_loc.data.line as u32 - 1,
52 character: start_loc.data.col.0 as u32 - 1,
53 },
54 end: Position {
55 line: end_loc.data.line as u32 - 1,
56 character: end_loc.data.col.0 as u32 - 1,
57 },
58 }
59}
60
61fn severity(
62 level: solar::interface::diagnostics::Level,
63) -> tower_lsp::lsp_types::DiagnosticSeverity {
64 use solar::interface::diagnostics::Level::*;
65 match level {
66 Error | Fatal | Bug => tower_lsp::lsp_types::DiagnosticSeverity::ERROR,
67 Warning => tower_lsp::lsp_types::DiagnosticSeverity::WARNING,
68 Note | OnceNote => tower_lsp::lsp_types::DiagnosticSeverity::INFORMATION,
69 Help | OnceHelp => tower_lsp::lsp_types::DiagnosticSeverity::HINT,
70 _ => tower_lsp::lsp_types::DiagnosticSeverity::INFORMATION,
71 }
72}
73
74#[async_trait]
75impl Runner for SolarRunner {
76 async fn build(&self, _file: &str) -> Result<serde_json::Value, RunnerError> {
77 Ok(serde_json::Value::Object(serde_json::Map::new()))
80 }
81
82 async fn lint(&self, _file: &str) -> Result<serde_json::Value, RunnerError> {
83 Ok(serde_json::Value::Array(Vec::new()))
86 }
87
88 async fn format(&self, file: &str) -> Result<String, RunnerError> {
89 tokio::fs::read_to_string(file)
91 .await
92 .map_err(|_| RunnerError::ReadError)
93 }
94
95 async fn ast(&self, file: &str) -> Result<serde_json::Value, RunnerError> {
96 let file = file.to_string();
99
100 task::spawn_blocking(move || {
101 let paths = [Path::new(&file)];
102 let sess = Session::builder()
103 .with_buffer_emitter(solar::interface::ColorChoice::Auto)
104 .build();
105 let mut compiler = Compiler::new(sess);
106
107 let _ = compiler.enter_mut(|compiler| -> solar::interface::Result<_> {
108 let mut parsing_context = compiler.parse();
109 parsing_context.load_files(paths)?;
110 parsing_context.parse();
111 Ok(())
112 });
113
114 Ok(serde_json::Value::Object(serde_json::Map::new()))
117 })
118 .await
119 .map_err(|_| RunnerError::CommandError(Error::other("Task panicked")))?
120 }
121
122 async fn get_build_diagnostics(&self, file: &Url) -> Result<Vec<Diagnostic>, RunnerError> {
123 let path = file.to_file_path().map_err(|_| RunnerError::InvalidUrl)?;
124 let path_str = path.to_str().ok_or(RunnerError::InvalidUrl)?.to_string();
125
126 let content = tokio::fs::read_to_string(&path)
128 .await
129 .map_err(|_| RunnerError::ReadError)?;
130
131 task::spawn_blocking(move || {
132 let (emitter, diag_buffer) = InMemoryEmitter::new();
133 let sess = Session::builder()
134 .dcx(DiagCtxt::new(Box::new(emitter)))
135 .build();
136 let mut compiler = Compiler::new(sess);
137
138 let _ = compiler.enter_mut(|compiler| -> solar::interface::Result<_> {
139 let mut parsing_context = compiler.parse();
140 parsing_context.add_files(vec![
142 compiler
143 .sess()
144 .source_map()
145 .new_source_file(path_str.clone(), content)
146 .unwrap(),
147 ]);
148 parsing_context.parse();
149 Ok(())
150 });
151
152 let _ = compiler.enter_mut(|compiler| -> solar::interface::Result<_> {
153 let _ = compiler.lower_asts();
154 let _ = compiler.analysis();
155 Ok(())
156 });
157
158 let mut diagnostics = Vec::new();
159 println!("Diag buffer has {} items", diag_buffer.read().len());
160 for diag in diag_buffer.read().iter() {
161 if let Some(lsp_diag) =
163 solar_diag_to_lsp(diag, &path_str, compiler.sess().source_map())
164 {
165 diagnostics.push(lsp_diag);
166 }
167 }
168
169 Ok(diagnostics)
170 })
171 .await
172 .map_err(|_| RunnerError::CommandError(Error::other("Task panicked")))?
173 }
174
175 async fn get_lint_diagnostics(&self, _file: &Url) -> Result<Vec<Diagnostic>, RunnerError> {
176 let diagnostics = Vec::new();
177 Ok(diagnostics)
180 }
181}