Auto merge of #14034 - lnicola:tweak-change-collapsing, r=Veykril

internal: Tweak change collapsing

CC https://github.com/rust-lang/rust-analyzer/pull/14025#discussion_r1087946555.
This commit is contained in:
bors 2023-01-31 10:37:50 +00:00
commit be76f357af

View File

@ -3,7 +3,7 @@
//! //!
//! Each tick provides an immutable snapshot of the state as `WorldSnapshot`. //! 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 crossbeam_channel::{unbounded, Receiver, Sender};
use flycheck::FlycheckHandle; use flycheck::FlycheckHandle;
@ -179,10 +179,9 @@ impl GlobalState {
pub(crate) fn process_changes(&mut self) -> bool { pub(crate) fn process_changes(&mut self) -> bool {
let _p = profile::span("GlobalState::process_changes"); 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 workspace_structure_change = None;
let mut file_changes = FxHashMap::default();
let (change, changed_files) = { let (change, changed_files) = {
let mut change = Change::new(); let mut change = Change::new();
let (vfs, line_endings_map) = &mut *self.vfs.write(); let (vfs, line_endings_map) = &mut *self.vfs.write();
@ -191,57 +190,56 @@ impl GlobalState {
return false; return false;
} }
// important: this needs to be a stable sort, the order between changes is relevant // We need to fix up the changed events a bit. If we have a create or modify for a file
// for the same file ids // id that is followed by a delete we actually skip observing the file text from the
changed_files.sort_by_key(|file| file.file_id); // earlier event, to avoid problems later on.
// We need to fix up the changed events a bit, if we have a create or modify for a file for changed_file in &changed_files {
// 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| {
use vfs::ChangeKind::*; use vfs::ChangeKind::*;
let has_collapsed_create_delete = mem::replace(&mut collapsed_create_delete, false); file_changes
.entry(changed_file.file_id)
if a.file_id != b.file_id { .and_modify(|(change, just_created)| {
return false; // None -> Delete => keep
} // Create -> Delete => collapse
//
// true => delete the second element (a), we swap them here as they are inverted by dedup_by match (change, just_created, changed_file.change_kind) {
match (b.change_kind, a.change_kind) { // latter `Delete` wins
// duplicate can be merged (change, _, Delete) => *change = Delete,
(Create, Create) | (Modify, Modify) | (Delete, Delete) => true, // merge `Create` with `Create` or `Modify`
// just leave the create, modify is irrelevant (Create, _, Create | Modify) => {}
(Create, Modify) => true, // collapse identical `Modify`es
// modify becomes irrelevant if the file is deleted (Modify, _, Modify) => {}
(Modify, Delete) => { // equivalent to `Modify`
mem::swap(a, b); (change @ Delete, just_created, Create) => {
true *change = Modify;
} *just_created = true;
// Remove the create message, and in the following loop, also remove the delete }
(Create, Delete) => { // shouldn't occur, but collapse into `Create`
collapsed_create_delete = true; (change @ Delete, just_created, Modify) => {
b.change_kind = Delete; *change = Create;
true *just_created = true;
} }
// trailing delete from earlier // shouldn't occur, but collapse into `Modify`
(Delete, Create | Modify) if has_collapsed_create_delete => { (Modify, _, Create) => {}
b.change_kind = Create; }
true })
} .or_insert((
// this is equivalent to a modify changed_file.change_kind,
(Delete, Create) => { matches!(changed_file.change_kind, Create),
b.change_kind = Modify; ));
true
}
// can't really occur
(Modify, Create) => false,
(Delete, Modify) => false,
}
});
if collapsed_create_delete {
changed_files.pop();
} }
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 { for file in &changed_files {
if let Some(path) = vfs.file_path(file.file_id).as_path() { if let Some(path) = vfs.file_path(file.file_id).as_path() {
let path = path.to_path_buf(); let path = path.to_path_buf();