feat: persist diff file review checkmarks
This commit is contained in:
+118
-16
@@ -587,6 +587,8 @@ pub struct DiffState {
|
||||
pub live_revision: Option<u64>,
|
||||
pub follow_caret: bool,
|
||||
live_files: BTreeSet<usize>,
|
||||
/// Files edited since their frozen comparison was loaded; undo does not restore review marks.
|
||||
modified_after: BTreeSet<usize>,
|
||||
frozen_after: BTreeMap<usize, (Vec<String>, bool)>,
|
||||
context_lines: usize,
|
||||
expand_lines: usize,
|
||||
@@ -648,6 +650,7 @@ impl DiffState {
|
||||
live_revision: None,
|
||||
follow_caret: false,
|
||||
live_files: BTreeSet::new(),
|
||||
modified_after: BTreeSet::new(),
|
||||
frozen_after: BTreeMap::new(),
|
||||
context_lines: 10,
|
||||
expand_lines: 15,
|
||||
@@ -1005,6 +1008,7 @@ impl DiffState {
|
||||
self.snapshot_roots = [None, None];
|
||||
self.frozen_after.clear();
|
||||
self.live_files.clear();
|
||||
self.modified_after.clear();
|
||||
self.live_buffer = None;
|
||||
self.live_revision = None;
|
||||
self.refresh_view = None;
|
||||
@@ -1082,6 +1086,7 @@ impl DiffState {
|
||||
}
|
||||
Ok(Outcome::Comparison(comparison)) if expected == Some(RequestKind::Comparison) => {
|
||||
self.live_files.clear();
|
||||
self.modified_after.clear();
|
||||
self.frozen_after.clear();
|
||||
self.refresh_view = None;
|
||||
self.reviewed_files.clear();
|
||||
@@ -1777,14 +1782,82 @@ impl DiffState {
|
||||
} else {
|
||||
self.file
|
||||
};
|
||||
if comparison.files.get(index).is_none() {
|
||||
return Err("No changed file is selected");
|
||||
}
|
||||
if self.reviewed_files.remove(&index) {
|
||||
Ok(false)
|
||||
} else {
|
||||
let file = comparison
|
||||
.files
|
||||
.get(index)
|
||||
.ok_or("No changed file is selected")?;
|
||||
let reviewed = !self.reviewed_files.remove(&index);
|
||||
if reviewed {
|
||||
self.reviewed_files.insert(index);
|
||||
Ok(true)
|
||||
}
|
||||
// Resolver comparisons have no immutable revision identity or local review.
|
||||
if comparison
|
||||
.revisions
|
||||
.iter()
|
||||
.all(|revision| !revision.is_empty())
|
||||
{
|
||||
if self.local_review.document.root.as_os_str().is_empty() {
|
||||
self.local_review.document = crate::review::Review::new(comparison.root.clone());
|
||||
}
|
||||
let records = &mut self.local_review.document.reviewed_files;
|
||||
records.retain(|record| {
|
||||
record.revisions != comparison.revisions || record.path != file.path
|
||||
});
|
||||
if reviewed {
|
||||
records.push(crate::review::ReviewedFile {
|
||||
revisions: comparison.revisions.clone(),
|
||||
path: file.path.clone(),
|
||||
old_path: file.old_path.clone(),
|
||||
after: self
|
||||
.live_files
|
||||
.contains(&index)
|
||||
.then(|| file.after.join("\n")),
|
||||
});
|
||||
}
|
||||
self.local_review.dirty = true;
|
||||
}
|
||||
Ok(reviewed)
|
||||
}
|
||||
|
||||
pub(crate) fn restore_reviewed_files(&mut self) {
|
||||
self.reviewed_files.clear();
|
||||
let Some(comparison) = self.comparison.as_ref() else {
|
||||
return;
|
||||
};
|
||||
if self.local_review.document.root != comparison.root {
|
||||
return;
|
||||
}
|
||||
let records: BTreeMap<_, _> = self
|
||||
.local_review
|
||||
.document
|
||||
.reviewed_files
|
||||
.iter()
|
||||
.filter(|record| record.revisions == comparison.revisions)
|
||||
.map(|record| (&record.path, record))
|
||||
.collect();
|
||||
for (index, file) in comparison.files.iter().enumerate() {
|
||||
let Some(record) = records
|
||||
.get(&file.path)
|
||||
.filter(|record| record.old_path == file.old_path)
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
let matches = match &record.after {
|
||||
None => !self.modified_after.contains(&index),
|
||||
Some(text) => {
|
||||
file.loaded
|
||||
&& (text
|
||||
.split('\n')
|
||||
.eq(file.after.iter().map(String::as_str).chain(
|
||||
((!self.live_files.contains(&index) && file.after_eof)
|
||||
|| file.after.is_empty())
|
||||
.then_some(""),
|
||||
)))
|
||||
}
|
||||
};
|
||||
if matches {
|
||||
self.reviewed_files.insert(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2173,15 +2246,44 @@ impl DiffState {
|
||||
}
|
||||
let was_live = self.live_files.contains(&self.file);
|
||||
let changed_lines = changed_line_range(&file.after, lines);
|
||||
if self.reviewed_files.contains(&self.file) {
|
||||
let unchanged = if was_live {
|
||||
changed_lines.0 == changed_lines.1 && changed_lines.1 == changed_lines.2
|
||||
} else {
|
||||
let eof = lines.len() > 1 && lines.last().is_some_and(String::is_empty);
|
||||
file.after_eof == eof && file.after == live_content(lines, eof)
|
||||
};
|
||||
if !unchanged {
|
||||
self.reviewed_files.remove(&self.file);
|
||||
let unchanged = if was_live {
|
||||
changed_lines.0 == changed_lines.1 && changed_lines.1 == changed_lines.2
|
||||
} else {
|
||||
let eof = lines.len() > 1 && lines.last().is_some_and(String::is_empty);
|
||||
file.after_eof == eof && file.after == live_content(lines, eof)
|
||||
};
|
||||
if !unchanged {
|
||||
self.modified_after.insert(self.file);
|
||||
}
|
||||
let comparison = self.comparison.as_ref().unwrap();
|
||||
let record = (self.local_review.document.root == comparison.root)
|
||||
.then(|| {
|
||||
self.local_review
|
||||
.document
|
||||
.reviewed_files
|
||||
.iter()
|
||||
.position(|record| {
|
||||
record.revisions == comparison.revisions
|
||||
&& record.path == file.path
|
||||
&& record.old_path == file.old_path
|
||||
})
|
||||
})
|
||||
.flatten();
|
||||
let matches = record
|
||||
.and_then(|position| {
|
||||
self.local_review.document.reviewed_files[position]
|
||||
.after
|
||||
.as_ref()
|
||||
})
|
||||
.map(|text| text.split('\n').eq(lines.iter().map(String::as_str)));
|
||||
if matches == Some(true) {
|
||||
self.reviewed_files.insert(self.file);
|
||||
}
|
||||
if self.reviewed_files.contains(&self.file) && !matches.unwrap_or(unchanged) {
|
||||
self.reviewed_files.remove(&self.file);
|
||||
if let Some(position) = record {
|
||||
self.local_review.document.reviewed_files.remove(position);
|
||||
self.local_review.dirty = true;
|
||||
}
|
||||
}
|
||||
if was_live && changed_lines.0 == changed_lines.1 && changed_lines.1 == changed_lines.2 {
|
||||
|
||||
Reference in New Issue
Block a user