fix: dependents check

This commit is contained in:
Shunsuke Shibayama 2023-09-15 15:31:54 +09:00
parent 965d0c2c2d
commit 27ad6123da
6 changed files with 90 additions and 25 deletions

View file

@ -37,10 +37,42 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
Parser::parse(code).ok().map(|artifact| artifact.ast)
}
pub(crate) fn check_file<S: Into<String>>(
pub(crate) fn any_changes(&self, uri: &NormalizedUrl) -> bool {
let deps = self.dependencies_of(uri);
if deps.is_empty() {
return true;
}
for dep in deps {
let Some(old) = self.get_ast(&dep) else {
return true;
};
if let Some(new) = self.build_ast(&dep) {
if !ASTDiff::diff(old, &new).is_nop() {
return true;
}
}
}
_log!(self, "no changes: {uri}");
false
}
pub(crate) fn recheck_file(
&mut self,
uri: NormalizedUrl,
code: S,
code: impl Into<String>,
) -> ELSResult<()> {
if !self.any_changes(&uri) {
_log!(self, "no changes: {uri}");
return Ok(());
}
// self.clear_cache(&uri);
self.check_file(uri, code)
}
pub(crate) fn check_file(
&mut self,
uri: NormalizedUrl,
code: impl Into<String>,
) -> ELSResult<()> {
_log!(self, "checking {uri}");
if self.file_cache.editing.borrow().contains(&uri) {
@ -53,13 +85,6 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
} else {
"exec"
};
let old = self.get_ast(&uri);
if let Some((old, new)) = old.zip(self.build_ast(&uri)) {
if ASTDiff::diff(old, &new).is_nop() {
_log!(self, "no changes: {uri}");
return Ok(());
}
}
let mut checker = self.get_checker(path.clone());
let artifact = match checker.build(code.into(), mode) {
Ok(artifact) => {

View file

@ -239,8 +239,8 @@ impl FileCache {
}
pub(crate) fn update(&self, uri: &NormalizedUrl, code: String, ver: Option<i32>) {
let ent = self.files.borrow_mut();
let entry = ent.get(uri);
let lock = self.files.borrow_mut();
let entry = lock.get(uri);
if let Some(entry) = entry {
if ver.map_or(false, |ver| ver <= entry.ver) {
// crate::_log!(self, "171: double update detected: {ver:?}, {}, code:\n{}", entry.ver, entry.code);
@ -255,7 +255,7 @@ impl FileCache {
1
}
});
drop(ent);
drop(lock);
self.files.borrow_mut().insert(
uri.clone(),
FileCacheEntry {

View file

@ -150,12 +150,19 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
})
}
/// self is __included__
/// self is __included__.
/// if self is not in the graph, return empty vec
pub fn dependencies_of(&self, uri: &NormalizedUrl) -> Vec<NormalizedUrl> {
let graph = &self.shared.graph;
let path = NormalizedPathBuf::from(util::uri_to_path(uri));
graph.sort().unwrap();
let self_node = graph.get_node(&path).unwrap();
if let Err(err) = graph.sort() {
// maybe key not found == self is not in the graph
crate::_log!(self, "err: {err}");
return vec![];
};
let Some(self_node) = graph.get_node(&path) else {
return vec![];
};
graph
.ref_inner()
.iter()

View file

@ -698,8 +698,7 @@ impl<Checker: BuildRunnable, Parser: Parsable> Server<Checker, Parser> {
NormalizedUrl::parse(msg["params"]["textDocument"]["uri"].as_str().unwrap())?;
self.send_log(format!("{method}: {uri}"))?;
let code = self.file_cache.get_entire_code(&uri)?;
self.clear_cache(&uri);
self.check_file(uri, code)
self.recheck_file(uri, code)
}
"textDocument/didChange" => {
let params = DidChangeTextDocumentParams::deserialize(msg["params"].clone())?;

View file

@ -2,22 +2,51 @@
use crate::dict::Dict;
use crate::set::Set;
use std::fmt::Debug;
use std::hash::Hash;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TopoSortError {
pub enum TopoSortErrorKind {
CyclicReference,
KeyNotFound,
}
impl std::fmt::Display for TopoSortError {
impl std::fmt::Display for TopoSortErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:?}")
}
}
impl std::error::Error for TopoSortErrorKind {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TopoSortError {
pub kind: TopoSortErrorKind,
pub msg: String,
}
impl std::fmt::Display for TopoSortError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", self.kind, self.msg)
}
}
impl std::error::Error for TopoSortError {}
impl TopoSortError {
pub fn new(kind: TopoSortErrorKind, msg: String) -> Self {
Self { kind, msg }
}
pub fn key_not_found(msg: String) -> Self {
Self::new(TopoSortErrorKind::KeyNotFound, msg)
}
pub fn cycle_detected(msg: String) -> Self {
Self::new(TopoSortErrorKind::CyclicReference, msg)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Node<T: Eq + Hash, U> {
pub id: T,
@ -65,7 +94,7 @@ fn reorder_by_key<T: Eq + Hash, U>(mut g: Graph<T, U>, idx: Vec<T>) -> Graph<T,
g
}
fn dfs<T: Eq + Hash + Clone, U>(
fn dfs<T: Eq + Hash + Clone + Debug, U: Debug>(
g: &Graph<T, U>,
v: T,
used: &mut Set<T>,
@ -73,12 +102,14 @@ fn dfs<T: Eq + Hash + Clone, U>(
) -> Result<(), TopoSortError> {
used.insert(v.clone());
let Some(vertex) = g.iter().find(|n| n.id == v) else {
return Err(TopoSortError::KeyNotFound);
return Err(TopoSortError::key_not_found(format!("{g:?}: {v:?}")));
};
for node_id in vertex.depends_on.iter() {
// detecting cycles
if used.contains(node_id) && !idx.contains(node_id) {
return Err(TopoSortError::CyclicReference);
return Err(TopoSortError::cycle_detected(format!(
"{v:?} -> {node_id:?}"
)));
}
if !used.contains(node_id) {
dfs(g, node_id.clone(), used, idx)?;
@ -90,7 +121,9 @@ fn dfs<T: Eq + Hash + Clone, U>(
/// perform topological sort on a graph
#[allow(clippy::result_unit_err)]
pub fn tsort<T: Eq + Hash + Clone, U>(g: Graph<T, U>) -> Result<Graph<T, U>, TopoSortError> {
pub fn tsort<T: Eq + Hash + Clone + Debug, U: Debug>(
g: Graph<T, U>,
) -> Result<Graph<T, U>, TopoSortError> {
let n = g.len();
let mut idx = Vec::with_capacity(n);
let mut used = Set::new();

View file

@ -66,7 +66,8 @@ impl SharedCompilerResource {
self.warns.clear();
}
/// Clear all information about the module. All child modules are also cleared.
/// Clear all information about the module.
/// Graph information is not cleared (due to ELS).
pub fn clear(&self, path: &Path) {
for child in self.graph.children(path) {
self.clear(&child);
@ -74,7 +75,7 @@ impl SharedCompilerResource {
self.mod_cache.remove(path);
self.py_mod_cache.remove(path);
self.index.remove_path(path);
self.graph.remove(path);
// self.graph.remove(path);
self.promises.remove(path);
self.errors.remove(path);
self.warns.remove(path);