From deff5f22f6f4122b211211ae2f1ca2a7ad0c7e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Thu, 26 Jan 2023 17:19:35 +0200 Subject: [PATCH] Tweak change collapsing --- crates/rust-analyzer/src/global_state.rs | 100 +++++++++++------------ 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index de11abdcf82..aca6c923570 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -3,7 +3,7 @@ //! //! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. -use std::{mem, sync::Arc, time::Instant}; +use std::{sync::Arc, time::Instant}; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; @@ -179,10 +179,9 @@ impl GlobalState { pub(crate) fn process_changes(&mut self) -> bool { let _p = profile::span("GlobalState::process_changes"); - // A file was added or deleted - let mut has_structure_changes = false; let mut workspace_structure_change = None; + let mut file_changes = FxHashMap::default(); let (change, changed_files) = { let mut change = Change::new(); let (vfs, line_endings_map) = &mut *self.vfs.write(); @@ -191,57 +190,56 @@ impl GlobalState { return false; } - // important: this needs to be a stable sort, the order between changes is relevant - // for the same file ids - changed_files.sort_by_key(|file| file.file_id); - // We need to fix up the changed events a bit, if we have a create or modify for a file - // id that is followed by a delete we actually no longer observe the file text from the - // create or modify which may cause problems later on - let mut collapsed_create_delete = false; - changed_files.dedup_by(|a, b| { + // We need to fix up the changed events a bit. If we have a create or modify for a file + // id that is followed by a delete we actually skip observing the file text from the + // earlier event, to avoid problems later on. + for changed_file in &changed_files { use vfs::ChangeKind::*; - let has_collapsed_create_delete = mem::replace(&mut collapsed_create_delete, false); - - if a.file_id != b.file_id { - return false; - } - - // true => delete the second element (a), we swap them here as they are inverted by dedup_by - match (b.change_kind, a.change_kind) { - // duplicate can be merged - (Create, Create) | (Modify, Modify) | (Delete, Delete) => true, - // just leave the create, modify is irrelevant - (Create, Modify) => true, - // modify becomes irrelevant if the file is deleted - (Modify, Delete) => { - mem::swap(a, b); - true - } - // Remove the create message, and in the following loop, also remove the delete - (Create, Delete) => { - collapsed_create_delete = true; - b.change_kind = Delete; - true - } - // trailing delete from earlier - (Delete, Create | Modify) if has_collapsed_create_delete => { - b.change_kind = Create; - true - } - // this is equivalent to a modify - (Delete, Create) => { - b.change_kind = Modify; - true - } - // can't really occur - (Modify, Create) => false, - (Delete, Modify) => false, - } - }); - if collapsed_create_delete { - changed_files.pop(); + file_changes + .entry(changed_file.file_id) + .and_modify(|(change, just_created)| { + // None -> Delete => keep + // Create -> Delete => collapse + // + match (change, just_created, changed_file.change_kind) { + // latter `Delete` wins + (change, _, Delete) => *change = Delete, + // merge `Create` with `Create` or `Modify` + (Create, _, Create | Modify) => {} + // collapse identical `Modify`es + (Modify, _, Modify) => {} + // equivalent to `Modify` + (change @ Delete, just_created, Create) => { + *change = Modify; + *just_created = true; + } + // shouldn't occur, but collapse into `Create` + (change @ Delete, just_created, Modify) => { + *change = Create; + *just_created = true; + } + // shouldn't occur, but collapse into `Modify` + (Modify, _, Create) => {} + } + }) + .or_insert(( + changed_file.change_kind, + matches!(changed_file.change_kind, Create), + )); } + + changed_files.extend( + file_changes + .into_iter() + .filter(|(_, (change_kind, just_created))| { + !matches!((change_kind, just_created), (vfs::ChangeKind::Delete, true)) + }) + .map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind }), + ); + + // A file was added or deleted + let mut has_structure_changes = false; for file in &changed_files { if let Some(path) = vfs.file_path(file.file_id).as_path() { let path = path.to_path_buf();