diff --git a/server/src/config.rs b/server/src/config.rs index 3a7d8bc..288a339 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -1,5 +1,6 @@ // Copyright (c) ZeroC, Inc. +use slicec::slice_options::SliceOptions; use tower_lsp::{ lsp_types::{ConfigurationItem, DidChangeConfigurationParams, Url}, Client, @@ -7,22 +8,26 @@ use tower_lsp::{ #[derive(Default, Debug)] pub struct SliceConfig { - built_in_slice_path: String, references: Option>, + built_in_slice_path: String, root_uri: Option, + cached_slice_options: SliceOptions, } impl SliceConfig { pub fn set_root_uri(&mut self, root: Url) { self.root_uri = Some(root); + self.refresh_reference_paths(); } - pub fn set_built_in_reference(&mut self, path: impl Into) { - self.built_in_slice_path = path.into(); + pub fn set_built_in_reference(&mut self, path: String) { + self.built_in_slice_path = path; + self.refresh_reference_paths(); } - pub fn try_update_from_params(&mut self, params: &DidChangeConfigurationParams) { + pub fn update_from_params(&mut self, params: &DidChangeConfigurationParams) { self.references = Self::parse_reference_directories(params); + self.refresh_reference_paths(); } pub async fn try_update_from_client( @@ -30,6 +35,7 @@ impl SliceConfig { client: &Client, ) -> tower_lsp::jsonrpc::Result<()> { self.references = Self::fetch_reference_directories(client).await?; + self.refresh_reference_paths(); Ok(()) } @@ -70,8 +76,13 @@ impl SliceConfig { }) } + // This function should be called whenever the configuration changes. + fn refresh_reference_paths(&mut self) { + self.cached_slice_options.references = self.resolve_reference_paths(); + } + // Resolve reference URIs to file paths to be used by the Slice compiler. - pub fn resolve_reference_paths(&self) -> Vec { + fn resolve_reference_paths(&self) -> Vec { // If `root_uri` isn't set, or doesn't represent a valid file path, path resolution is impossible, so we return. let Some(Ok(root_path)) = self.root_uri.as_ref().map(Url::to_file_path) else { return vec![]; @@ -119,4 +130,8 @@ impl SliceConfig { paths.push(self.built_in_slice_path.clone()); paths } + + pub fn as_slice_options(&self) -> &SliceOptions { + &self.cached_slice_options + } } diff --git a/server/src/main.rs b/server/src/main.rs index e91aed7..778e9fc 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -4,8 +4,7 @@ use config::SliceConfig; use diagnostic_ext::try_into_lsp_diagnostic; use hover::get_hover_info; use jump_definition::get_definition_span; -use shared_state::SharedState; -use slicec::{compilation_state::CompilationState, slice_options::SliceOptions}; +use slicec::compilation_state::CompilationState; use std::{ collections::{HashMap, HashSet}, sync::Arc, @@ -18,7 +17,6 @@ mod config; mod diagnostic_ext; mod hover; mod jump_definition; -mod shared_state; mod utils; #[tokio::main] @@ -27,9 +25,9 @@ async fn main() { let stdout = tokio::io::stdout(); let (service, socket) = LspService::new(|client| Backend { - slice_config: Arc::new(Mutex::new(SliceConfig::default())), client, - shared_state: Arc::new(Mutex::new(SharedState::new())), + slice_config: Arc::new(Mutex::new(SliceConfig::default())), + compilation_state: Arc::new(Mutex::new(CompilationState::create())), }); Server::new(stdin, stdout, socket).serve(service).await; @@ -38,7 +36,7 @@ async fn main() { struct Backend { client: Client, slice_config: Arc>, - shared_state: Arc>, + compilation_state: Arc>, } #[tower_lsp::async_trait] @@ -65,7 +63,7 @@ impl LanguageServer for Backend { options.get("builtInSlicePath").and_then(|v| v.as_str()) { let mut slice_config = self.slice_config.lock().await; - slice_config.set_built_in_reference(built_in_slice_path); + slice_config.set_built_in_reference(built_in_slice_path.to_owned()); } } @@ -109,19 +107,15 @@ impl LanguageServer for Backend { .await; } - let mut shared_state_lock = self.shared_state.lock().await; - // Compile the Slice files and publish diagnostics - let (updated_state, options) = self.compile_slice_files().await; - - shared_state_lock.compilation_state = updated_state; - shared_state_lock.compilation_options = options; + let mut compilation_state_lock = self.compilation_state.lock().await; + *compilation_state_lock = self.compile_slice_files().await; self.client .log_message(MessageType::INFO, "Slice Language Server initialized") .await; - self.publish_diagnostics_for_all_files(&mut shared_state_lock) + self.publish_diagnostics_for_all_files(&mut compilation_state_lock) .await; } @@ -137,22 +131,21 @@ impl LanguageServer for Backend { // Update the slice configuration { let mut slice_config = self.slice_config.lock().await; - slice_config.try_update_from_params(¶ms); + slice_config.update_from_params(¶ms); } // Store the current files in the compilation state before re-compiling let current_files = &self - .shared_state + .compilation_state .lock() .await - .compilation_state .files .keys() .cloned() .collect::>(); // Re-compile the Slice files considering the updated references - let (updated_state, options) = self.compile_slice_files().await; + let updated_state = self.compile_slice_files().await; // Clear the diagnostics from files that are no longer in the compilation state let new_files = &updated_state.files.keys().cloned().collect::>(); @@ -163,12 +156,10 @@ impl LanguageServer for Backend { self.client.publish_diagnostics(uri, vec![], None).await; } - let mut shared_state_lock = self.shared_state.lock().await; - - shared_state_lock.compilation_state = updated_state; - shared_state_lock.compilation_options = options; + let mut compilation_state_lock = self.compilation_state.lock().await; + *compilation_state_lock = updated_state; - self.publish_diagnostics_for_all_files(&mut shared_state_lock) + self.publish_diagnostics_for_all_files(&mut compilation_state_lock) .await; } @@ -187,7 +178,7 @@ impl LanguageServer for Backend { .unwrap(); let position = params.text_document_position_params.position; - let compilation_state = &self.shared_state.lock().await.compilation_state; + let compilation_state = &self.compilation_state.lock().await; let location = match get_definition_span(compilation_state, param_uri, position) { Some(location) => location, @@ -224,7 +215,7 @@ impl LanguageServer for Backend { ) .unwrap(); let position = params.text_document_position_params.position; - let compilation_state = &self.shared_state.lock().await.compilation_state; + let compilation_state = &self.compilation_state.lock().await; Ok( get_hover_info(compilation_state, uri, position).map(|info| Hover { contents: HoverContents::Scalar(MarkedString::String(info)), @@ -247,49 +238,31 @@ impl LanguageServer for Backend { impl Backend { async fn handle_file_change(&self) { - let (updated_state, options) = self.compile_slice_files().await; + let updated_state = self.compile_slice_files().await; - let mut shared_state_lock = self.shared_state.lock().await; + let mut compilation_state_lock = self.compilation_state.lock().await; + *compilation_state_lock = updated_state; - shared_state_lock.compilation_state = updated_state; - shared_state_lock.compilation_options = options; - - self.publish_diagnostics_for_all_files(&mut shared_state_lock) + self.publish_diagnostics_for_all_files(&mut compilation_state_lock) .await; } - async fn compile_slice_files(&self) -> (CompilationState, SliceOptions) { + async fn compile_slice_files(&self) -> CompilationState { self.client .log_message(MessageType::INFO, "compiling slice") .await; - let references = self.slice_config.lock().await.resolve_reference_paths(); - - // If debug is enabled, log the resolved references - #[cfg(debug_assertions)] - self.client - .log_message(MessageType::LOG, format!("references: {:?}", references)) - .await; - - // Compile the Slice files - let options = SliceOptions { - references, - ..Default::default() - }; - ( - slicec::compile_from_options(&options, |_| {}, |_| {}), - options, - ) + let config = self.slice_config.lock().await; + slicec::compile_from_options(config.as_slice_options(), |_| {}, |_| {}) } - async fn publish_diagnostics_for_all_files(&self, shared_state: &mut SharedState) { - let compilation_options = &shared_state.compilation_options; - let compilation_state = &mut shared_state.compilation_state; + async fn publish_diagnostics_for_all_files(&self, compilation_state: &mut CompilationState) { + let config = self.slice_config.lock().await; let diagnostics = std::mem::take(&mut compilation_state.diagnostics).into_updated( &compilation_state.ast, &compilation_state.files, - compilation_options, + config.as_slice_options(), ); // Group the diagnostics by file since diagnostics are published per file and diagnostic.span contains the file URL diff --git a/server/src/shared_state.rs b/server/src/shared_state.rs deleted file mode 100644 index b967d9a..0000000 --- a/server/src/shared_state.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) ZeroC, Inc. - -use slicec::{compilation_state::CompilationState, slice_options::SliceOptions}; - -#[derive(Debug)] -pub struct SharedState { - pub compilation_state: CompilationState, - pub compilation_options: SliceOptions, -} - -impl SharedState { - pub fn new() -> Self { - Self { - compilation_state: CompilationState::create(), - compilation_options: SliceOptions::default(), - } - } -}