diff --git a/crates/els/code_action.rs b/crates/els/code_action.rs index 8743de5f..4448f265 100644 --- a/crates/els/code_action.rs +++ b/crates/els/code_action.rs @@ -28,11 +28,12 @@ impl Server { send_log("visitor not found")?; return Ok(None); }; - let Some(artifact) = self.artifacts.get(&uri) else { + let Some(result) = self.artifacts.get(&uri) else { send_log("artifact not found")?; return Ok(None); }; - let warns = artifact + let warns = result + .artifact .warns .iter() .filter(|warn| warn.core.main_message.ends_with("is not used")) diff --git a/crates/els/code_lens.rs b/crates/els/code_lens.rs index a99ba140..c4225bc0 100644 --- a/crates/els/code_lens.rs +++ b/crates/els/code_lens.rs @@ -24,8 +24,8 @@ impl Server { fn send_trait_impls_lens(&mut self, uri: &NormalizedUrl) -> ELSResult> { let mut result = vec![]; - if let Some(artifact) = self.artifacts.get(uri) { - if let Some(hir) = &artifact.object { + if let Some(analysis) = self.artifacts.get(uri) { + if let Some(hir) = &analysis.artifact.object { for chunk in hir.module.iter() { match chunk { Expr::Def(def) if def.def_kind().is_trait() => { diff --git a/crates/els/diagnostics.rs b/crates/els/diagnostics.rs index d8fc295a..b3174f89 100644 --- a/crates/els/diagnostics.rs +++ b/crates/els/diagnostics.rs @@ -4,12 +4,12 @@ use erg_common::style::*; use erg_common::traits::Stream; use erg_compiler::artifact::BuildRunnable; -use erg_compiler::erg_parser::Parser; use erg_compiler::error::CompileErrors; use lsp_types::{Diagnostic, DiagnosticSeverity, Position, PublishDiagnosticsParams, Range, Url}; -use crate::server::{send, send_log, DefaultFeatures, ELSResult, Server}; +use crate::diff::{ASTDiff, HIRDiff}; +use crate::server::{send, send_log, AnalysisResult, DefaultFeatures, ELSResult, Server}; use crate::util::{self, NormalizedUrl}; impl Server { @@ -36,7 +36,10 @@ impl Server { send_log(format!("{uri}, warns: {}", diags.len()))?; self.send_diagnostics(uri, diags)?; } - self.artifacts.insert(uri.clone(), artifact.into()); + if let Some(module) = self.file_cache.get_ast(&uri) { + self.artifacts + .insert(uri.clone(), AnalysisResult::new(module, artifact.into())); + } } Err(artifact) => { send_log(format!("found errors: {}", artifact.errors.len()))?; @@ -55,7 +58,10 @@ impl Server { send_log(format!("{uri}, errs & warns: {}", diags.len()))?; self.send_diagnostics(uri, diags)?; } - self.artifacts.insert(uri.clone(), artifact); + if let Some(module) = self.file_cache.get_ast(&uri) { + self.artifacts + .insert(uri.clone(), AnalysisResult::new(module, artifact)); + } } } if let Some(module) = checker.pop_context() { @@ -72,38 +78,26 @@ impl Server { } pub(crate) fn quick_check_file(&mut self, uri: NormalizedUrl) -> ELSResult<()> { - // send_log(format!("checking {uri}"))?; - let Some(ts) = self.file_cache.get_token_stream(&uri) else { + let Some(old) = self.artifacts.get(&uri).map(|r| &r.ast) else { + crate::_log!("not found"); return Ok(()); }; - let mut parser = Parser::new(ts); - let Ok(module) = parser.parse() else { + let Some(new) = self.file_cache.get_ast(&uri) else { + crate::_log!("not found"); return Ok(()); }; - let path = util::uri_to_path(&uri); - let code = self.file_cache.get_entire_code(&uri)?; - let mode = if path.to_string_lossy().ends_with(".d.er") { - "declare" - } else { - "exec" - }; - if let Some(mut lowerer) = self.get_lowerer(&path) {} - let mut checker = self.get_checker(path); - match checker.build(code, mode) { - Ok(artifact) => { - self.artifacts.insert(uri.clone(), artifact.into()); + let ast_diff = ASTDiff::diff(old, &new); + crate::_log!("diff: {ast_diff:?}"); + if let Some(mut lowerer) = self.get_lowerer(&uri) { + let hir = self + .artifacts + .get_mut(&uri) + .and_then(|r| r.artifact.object.as_mut()); + if let Some((hir_diff, hir)) = HIRDiff::new(ast_diff, &mut lowerer).zip(hir) { + crate::_log!("hir_diff: {hir_diff:?}"); + hir_diff.update(hir); } - Err(artifact) => { - self.artifacts.insert(uri.clone(), artifact); - } - } - if let Some(module) = checker.pop_context() { - self.modules.insert(uri.clone(), module); - } - let dependents = self.dependents_of(&uri); - for dep in dependents { - // _log!("dep: {dep}"); - self.quick_check_file(dep)?; + self.restore_mod_ctx(&uri, lowerer.pop_mod_ctx().unwrap()); } Ok(()) } diff --git a/crates/els/diff.rs b/crates/els/diff.rs index 0c6448f2..ce24b5de 100644 --- a/crates/els/diff.rs +++ b/crates/els/diff.rs @@ -2,7 +2,7 @@ use std::cmp::Ordering::*; use erg_common::traits::Stream; use erg_compiler::erg_parser::ast; -use erg_compiler::erg_parser::ast::AST; +use erg_compiler::erg_parser::ast::Module; use erg_compiler::hir; use erg_compiler::hir::HIR; use erg_compiler::lower::ASTLowerer; @@ -23,30 +23,27 @@ pub enum ASTDiff { /// diff(old: {x, y, z}, new: {x, a, z}) => ASTDiff::Modification(1) /// diff(old: {x, y, z}, new: {x, y, z}) => ASTDiff::Nop impl ASTDiff { - pub fn diff(old: AST, new: AST) -> ASTDiff { - match old.module.len().cmp(&new.module.len()) { + pub fn diff(old: &Module, new: &Module) -> ASTDiff { + match old.len().cmp(&new.len()) { Less => { let idx = new - .module .iter() - .zip(old.module.iter()) + .zip(old.iter()) .position(|(new, old)| new != old) - .unwrap(); - Self::Addition(idx, new.module.get(idx).unwrap().clone()) + .unwrap_or(new.len() - 1); + Self::Addition(idx, new.get(idx).unwrap().clone()) } Greater => Self::Deletion( - old.module - .iter() - .zip(new.module.iter()) + old.iter() + .zip(new.iter()) .position(|(old, new)| old != new) - .unwrap(), + .unwrap_or(old.len() - 1), ), Equal => old - .module .iter() - .zip(new.module.iter()) + .zip(new.iter()) .position(|(old, new)| old != new) - .map(|idx| Self::Modification(idx, new.module.get(idx).unwrap().clone())) + .map(|idx| Self::Modification(idx, new.get(idx).unwrap().clone())) .unwrap_or(Self::Nop), } } diff --git a/crates/els/file_cache.rs b/crates/els/file_cache.rs index 4bb16bb9..fa26f670 100644 --- a/crates/els/file_cache.rs +++ b/crates/els/file_cache.rs @@ -1,6 +1,8 @@ use std::fs::File; use std::io::Read; +use erg_compiler::erg_parser::ast::Module; +use erg_compiler::erg_parser::Parser; use lsp_types::{ DidChangeTextDocumentParams, FileOperationFilter, FileOperationPattern, FileOperationPatternKind, FileOperationRegistrationOptions, OneOf, Position, Range, @@ -126,6 +128,12 @@ impl FileCache { self.files.borrow_mut().get(uri)?.token_stream.clone() } + pub fn get_ast(&self, uri: &NormalizedUrl) -> Option { + let ts = self.get_token_stream(uri)?; + let mut parser = Parser::new(ts); + parser.parse().ok() + } + pub fn get_token(&self, uri: &NormalizedUrl, pos: Position) -> Option { let _ = self.load_once(uri); let ent = self.files.borrow_mut(); diff --git a/crates/els/inlay_hint.rs b/crates/els/inlay_hint.rs index 1580abb1..aa7ce4cc 100644 --- a/crates/els/inlay_hint.rs +++ b/crates/els/inlay_hint.rs @@ -95,7 +95,7 @@ impl Server { let mut result = vec![]; if let Some(IncompleteArtifact { object: Some(hir), .. - }) = self.artifacts.get(&uri) + }) = self.artifacts.get(&uri).map(|r| &r.artifact) { for chunk in hir.module.iter() { result.extend(self.get_expr_hint(chunk)); diff --git a/crates/els/rename.rs b/crates/els/rename.rs index 52421017..2f4a6436 100644 --- a/crates/els/rename.rs +++ b/crates/els/rename.rs @@ -209,7 +209,7 @@ impl Server { let mut imports = vec![]; if let Some(IncompleteArtifact { object: Some(hir), .. - }) = self.artifacts.get(target) + }) = self.artifacts.get(target).map(|r| &r.artifact) { for chunk in hir.module.iter() { imports.extend(Self::extract_import_symbols(chunk, needle_module_name)); diff --git a/crates/els/server.rs b/crates/els/server.rs index 32a3ab38..97b91c4c 100644 --- a/crates/els/server.rs +++ b/crates/els/server.rs @@ -3,11 +3,10 @@ use std::io; use std::io::{stdin, stdout, BufRead, Read, StdinLock, StdoutLock, Write}; use std::ops::Not; use std::path::{Path, PathBuf}; -use std::rc::Rc; use std::str::FromStr; use erg_common::consts::PYTHON_MODE; -use erg_compiler::error::{CompileErrors, CompileWarnings}; +use erg_compiler::erg_parser::ast::Module; use erg_compiler::lower::ASTLowerer; use serde::{Deserialize, Serialize}; use serde_json::json; @@ -188,6 +187,18 @@ pub(crate) fn send_invalid_req_error() -> ELSResult<()> { send_error(None, -32601, "received an invalid request") } +#[derive(Debug)] +pub struct AnalysisResult { + pub ast: Module, + pub artifact: IncompleteArtifact, +} + +impl AnalysisResult { + pub fn new(ast: Module, artifact: IncompleteArtifact) -> Self { + Self { ast, artifact } + } +} + /// A Language Server, which can be used any object implementing `BuildRunnable` internally by passing it as a generic parameter. #[derive(Debug)] pub struct Server { @@ -200,7 +211,7 @@ pub struct Server { pub(crate) file_cache: FileCache, pub(crate) comp_cache: CompletionCache, pub(crate) modules: Dict, - pub(crate) artifacts: Dict, + pub(crate) artifacts: Dict, pub(crate) current_sig: Option, pub(crate) _checker: std::marker::PhantomData, } @@ -541,24 +552,19 @@ impl Server { } } - pub(crate) fn get_lowerer(&self, path: &Path) -> Option { - let module = Rc::get_mut(&mut self.get_shared().unwrap().mod_cache.get_mut(path)?.module)?; - let module = std::mem::take(module); + pub(crate) fn get_lowerer(&mut self, uri: &NormalizedUrl) -> Option { + let module = std::mem::take(self.modules.get_mut(uri)?); Some(ASTLowerer::new_with_ctx(module)) } - pub(crate) fn restore_mod_ctx(&self, path: &Path, module: ModuleContext) { - self.get_shared() - .unwrap() - .mod_cache - .get_mut(path) - .unwrap() - .module = Rc::new(module); + pub(crate) fn restore_mod_ctx(&mut self, uri: &NormalizedUrl, module: ModuleContext) { + *self.modules.get_mut(uri).unwrap() = module; } pub(crate) fn get_visitor(&self, uri: &NormalizedUrl) -> Option { self.artifacts .get(uri)? + .artifact .object .as_ref() .map(|hir| HIRVisitor::new(hir, &self.file_cache, uri.clone())) @@ -611,7 +617,7 @@ impl Server { ctxs.extend(singular_ctxs); } } else { - send_log("expr not found: {token}")?; + _log!("expr not found: {token}"); } } Ok(ctxs)