From 8a959e4bf667144f5d37e3f3aaa0318c29adf747 Mon Sep 17 00:00:00 2001 From: Riley-Kilgore Date: Thu, 12 Sep 2024 21:40:58 -0700 Subject: [PATCH] Compile between messages if edits have been made --- crates/aiken-lsp/src/lib.rs | 19 ++++--- crates/aiken-lsp/src/server.rs | 49 ++++++++++++++++- crates/aiken-lsp/src/server/lsp_project.rs | 10 +++- crates/aiken-project/src/lib.rs | 64 ++++++++++++++++++++++ crates/aiken-project/src/options.rs | 8 ++- 5 files changed, 138 insertions(+), 12 deletions(-) diff --git a/crates/aiken-lsp/src/lib.rs b/crates/aiken-lsp/src/lib.rs index 15beac070..6f154d1fb 100644 --- a/crates/aiken-lsp/src/lib.rs +++ b/crates/aiken-lsp/src/lib.rs @@ -53,14 +53,17 @@ pub fn start() -> Result<(), Error> { fn capabilities() -> lsp_types::ServerCapabilities { lsp_types::ServerCapabilities { // THIS IS STILL WEIRD, ONLY ENABLE IF DEVELOPING - // completion_provider: Some(lsp_types::CompletionOptions { - // resolve_provider: None, - // trigger_characters: Some(vec![".".into(), " ".into()]), - // all_commit_characters: None, - // work_done_progress_options: lsp_types::WorkDoneProgressOptions { - // work_done_progress: None, - // }, - // }), + completion_provider: Some(lsp_types::CompletionOptions { + resolve_provider: None, + trigger_characters: Some(vec![".".into(), " ".into()]), + completion_item: Some(lsp_types::CompletionOptionsCompletionItem { + label_details_support: Some(true), + }), + all_commit_characters: None, + work_done_progress_options: lsp_types::WorkDoneProgressOptions { + work_done_progress: None, + }, + }), code_action_provider: Some(lsp_types::CodeActionProviderCapability::Simple(true)), document_formatting_provider: Some(lsp_types::OneOf::Left(true)), definition_provider: Some(lsp_types::OneOf::Left(true)), diff --git a/crates/aiken-lsp/src/server.rs b/crates/aiken-lsp/src/server.rs index eafaf185e..b39858003 100644 --- a/crates/aiken-lsp/src/server.rs +++ b/crates/aiken-lsp/src/server.rs @@ -40,6 +40,7 @@ use std::{ collections::{HashMap, HashSet}, fs, path::{Path, PathBuf}, + time::{Duration, Instant}, }; pub mod lsp_project; @@ -71,6 +72,10 @@ pub struct Server { /// An instance of a LspProject compiler: Option, + + last_change: Instant, + compile_scheduled: bool, + debounce_duration: Duration, } impl Server { @@ -98,10 +103,21 @@ impl Server { /// Compile the project if we are in one. Otherwise do nothing. fn compile(&mut self, connection: &Connection) -> Result<(), ServerError> { + self.compile_with_edits(connection, false) + } + + /// Helper function used by compile and by the LSP to generate code completions. + fn compile_with_edits( + &mut self, + connection: &Connection, + use_edits: bool, + ) -> Result<(), ServerError> { self.notify_client_of_compilation_start(connection)?; + let edited = if use_edits { Some(&self.edited) } else { None }; + if let Some(compiler) = self.compiler.as_mut() { - let result = compiler.compile(); + let result = compiler.compile_with_edits(edited); for warning in compiler.project.warnings() { self.process_diagnostic(warning)?; @@ -219,6 +235,10 @@ impl Server { self.edited.insert(path, changes.text); } + self.last_change = Instant::now(); + self.compile_scheduled = true; + dbg!("Modified Document"); + Ok(()) } @@ -588,11 +608,35 @@ impl Server { self.handle_notification(&connection, notification)? } } + // Check if it's time to compile after processing each message + self.check_compile(&connection)?; } Ok(()) } + fn check_compile(&mut self, connection: &Connection) -> Result<(), ServerError> { + if self.compile_scheduled && self.last_change.elapsed() >= self.debounce_duration { + dbg!("Entered check_compile (after initial check)"); + self.compile_scheduled = false; + if let Ok(config) = Config::load(&self.root) { + dbg!("Config Okay"); + self.config = Some(config); + self.create_new_compiler(); + self.compile_with_edits(connection, true)?; + dbg!("after compile_with_edits"); + } else { + dbg!("Failed to reload aiken.toml"); + self.stored_messages.push(lsp_types::ShowMessageParams { + typ: lsp_types::MessageType::ERROR, + message: "Failed to reload aiken.toml".to_string(), + }); + } + self.publish_stored_diagnostics(connection)?; + } + Ok(()) + } + pub fn new( initialize_params: InitializeParams, config: Option, @@ -607,6 +651,9 @@ impl Server { stored_diagnostics: HashMap::new(), stored_messages: Vec::new(), compiler: None, + last_change: Instant::now(), + compile_scheduled: false, + debounce_duration: Duration::from_millis(300), }; server.create_new_compiler(); diff --git a/crates/aiken-lsp/src/server/lsp_project.rs b/crates/aiken-lsp/src/server/lsp_project.rs index 9ab0367fe..df3ef3303 100644 --- a/crates/aiken-lsp/src/server/lsp_project.rs +++ b/crates/aiken-lsp/src/server/lsp_project.rs @@ -27,9 +27,16 @@ impl LspProject { } pub fn compile(&mut self) -> Result<(), Vec> { + self.compile_with_edits(None) + } + + pub fn compile_with_edits( + &mut self, + edited: Option<&HashMap>, + ) -> Result<(), Vec> { let checkpoint = self.project.checkpoint(); - let result = self.project.check( + let result = self.project.check_with_edits( true, None, false, @@ -38,6 +45,7 @@ impl LspProject { PropertyTest::DEFAULT_MAX_SUCCESS, Tracing::silent(), None, + edited, ); self.project.restore(checkpoint); diff --git a/crates/aiken-project/src/lib.rs b/crates/aiken-project/src/lib.rs index f2fa4f87a..7c52810eb 100644 --- a/crates/aiken-project/src/lib.rs +++ b/crates/aiken-project/src/lib.rs @@ -202,6 +202,7 @@ where code_gen_mode: CodeGenMode::Build(uplc), tracing, env, + edited: None, }; self.compile(options) @@ -267,6 +268,32 @@ where property_max_success: usize, tracing: Tracing, env: Option, + ) -> Result<(), Vec> { + self.check_with_edits( + skip_tests, + match_tests, + verbose, + exact_match, + seed, + property_max_success, + tracing, + env, + None, + ) + } + + #[allow(clippy::too_many_arguments)] + pub fn check_with_edits( + &mut self, + skip_tests: bool, + match_tests: Option>, + verbose: bool, + exact_match: bool, + seed: u32, + property_max_success: usize, + tracing: Tracing, + env: Option, + edited: Option<&HashMap>, ) -> Result<(), Vec> { let options = Options { tracing, @@ -282,6 +309,7 @@ where property_max_success, } }, + edited: edited, }; self.compile(options) @@ -353,6 +381,42 @@ where let mut modules = self.parse_sources(self.config.name.clone())?; + if let Some(edited) = options.edited { + for (module_name, edited_content) in edited { + if let Some(module) = modules.get_mut(module_name) { + // Parse the edited content + match aiken_lang::parser::module(&edited_content, module.kind) { + Ok((mut ast, extra)) => { + ast.name.clone_from(&module.name); + *module = ParsedModule { + kind: module.kind, + ast, + code: edited_content.to_string(), + name: module.name.clone(), + path: module.path.clone(), + extra, + package: module.package.clone(), + }; + } + Err(errs) => { + return Err(errs + .into_iter() + .map(|e| Error::Parse { + path: module.path.clone(), + src: edited_content.to_string(), + named: Box::new(NamedSource::new( + module.path.display().to_string(), + edited_content.to_string(), + )), + error: Box::new(e), + }) + .collect()); + } + } + } + } + } + self.type_check(&mut modules, options.tracing, env, true)?; match options.code_gen_mode { diff --git a/crates/aiken-project/src/options.rs b/crates/aiken-project/src/options.rs index c15517063..ed134c556 100644 --- a/crates/aiken-project/src/options.rs +++ b/crates/aiken-project/src/options.rs @@ -1,17 +1,21 @@ +use std::collections::HashMap; + use aiken_lang::ast::Tracing; -pub struct Options { +pub struct Options<'a> { pub code_gen_mode: CodeGenMode, pub tracing: Tracing, pub env: Option, + pub edited: Option<&'a HashMap>, } -impl Default for Options { +impl<'a> Default for Options<'a> { fn default() -> Self { Self { code_gen_mode: CodeGenMode::NoOp, tracing: Tracing::silent(), env: None, + edited: None, } } }