feat: refresh diffs when selected jj bookmarks move

This commit is contained in:
Valdemar Lorenzen
2026-10-15 15:18:13 +02:00
parent 1a02502488
commit c32d4a7590
8 changed files with 991 additions and 62 deletions
+48 -16
View File
@@ -63,6 +63,10 @@ impl ComparisonMode {
pub enum RevisionTarget {
Working,
Commit(String),
Bookmark {
name: String,
remote: Option<String>,
},
Remote {
remote: String,
reference: String,
@@ -178,6 +182,7 @@ pub enum Outcome {
},
Comparison(Comparison),
CheckoutRefresh(Option<Comparison>),
BookmarkRefresh(Comparison),
File {
comparison_id: u64,
file_index: usize,
@@ -299,6 +304,7 @@ pub struct DiffState {
max_before: usize,
max_after: usize,
after_widths: Vec<usize>,
pub(crate) refresh_view: Option<(usize, usize, usize)>,
}
impl DiffState {
@@ -355,6 +361,7 @@ impl DiffState {
max_before: 0,
max_after: 0,
after_widths: Vec::new(),
refresh_view: None,
}
}
pub(crate) fn set_resolver_paths(&mut self, root: PathBuf, paths: Vec<PathBuf>, file: usize) {
@@ -516,10 +523,15 @@ impl DiffState {
|| self.request_kind.is_some()
|| self.pending.is_some()
|| self.comparison.is_none()
|| self
.after
.as_ref()
.is_none_or(|after| after.target == RevisionTarget::Working && !follow_working)
|| self.after.as_ref().is_none_or(|after| {
after.target == RevisionTarget::Working
&& !follow_working
&& !self
.before
.iter()
.chain(self.after.iter())
.any(|revision| matches!(revision.target, RevisionTarget::Bookmark { .. }))
})
{
return;
}
@@ -635,18 +647,22 @@ impl DiffState {
}
self.request_kind = None;
let deferred = self.pending.take();
let bookmark_refresh = matches!(&result, Ok(Outcome::BookmarkRefresh(_)));
match result {
Ok(Outcome::CheckoutRefresh(None)) => {
let recovered = self.error.take().is_some();
if let Some(operation) = deferred {
self.queue(operation);
}
return false;
return recovered;
}
Ok(Outcome::CheckoutRefresh(Some(mut comparison))) => {
let pinned = self
.after
.as_ref()
.is_some_and(|after| after.target != RevisionTarget::Working);
Ok(Outcome::CheckoutRefresh(Some(mut comparison)))
| Ok(Outcome::BookmarkRefresh(mut comparison)) => {
let pinned = !bookmark_refresh
&& self
.after
.as_ref()
.is_some_and(|after| after.target != RevisionTarget::Working);
let view = (self.top, self.left_before, self.left_after);
if !pinned {
self.reviewed_files.clear();
@@ -690,12 +706,17 @@ impl DiffState {
self.live_files.clear();
self.live_buffer = None;
self.live_revision = None;
self.refresh_view = None;
self.follow_caret = false;
self.prepare_selected_file();
if pinned {
self.top = view.0.min(self.display_rows.len().saturating_sub(1));
self.left_before = view.1.min(self.max_before);
self.left_after = view.2.min(self.max_after);
if pinned || bookmark_refresh {
if self.selected_file_loaded() {
self.top = view.0.min(self.display_rows.len().saturating_sub(1));
self.left_before = view.1.min(self.max_before);
self.left_after = view.2.min(self.max_after);
} else {
self.refresh_view = Some(view);
}
}
self.normalize_focus();
}
@@ -713,7 +734,10 @@ impl DiffState {
}
return true;
}
if matches!(result, Ok(Outcome::CheckoutRefresh(_))) {
if matches!(
result,
Ok(Outcome::CheckoutRefresh(_) | Outcome::BookmarkRefresh(_))
) {
return false;
}
if let Ok(Outcome::File {
@@ -758,6 +782,7 @@ impl DiffState {
Ok(Outcome::Comparison(comparison)) if expected == Some(RequestKind::Comparison) => {
self.live_files.clear();
self.frozen_after.clear();
self.refresh_view = None;
self.reviewed_files.clear();
self.live_buffer = None;
self.live_revision = None;
@@ -811,6 +836,11 @@ impl DiffState {
*selected = file;
if self.file == file_index {
self.align_file();
if let Some(view) = self.refresh_view.take() {
self.top = view.0.min(self.display_rows.len().saturating_sub(1));
self.left_before = view.1.min(self.max_before);
self.left_after = view.2.min(self.max_after);
}
self.normalize_focus();
}
self.error = None;
@@ -1488,6 +1518,7 @@ impl DiffState {
return true;
}
self.file = index;
self.refresh_view = None;
self.normalize_focus();
self.live_buffer = None;
self.live_revision = None;
@@ -4267,7 +4298,8 @@ mod tests {
..
})
));
assert!(!state.apply_result(8, Ok(Outcome::CheckoutRefresh(None))));
assert!(state.apply_result(8, Ok(Outcome::CheckoutRefresh(None))));
assert!(state.error.is_none());
assert_eq!(state.comparison_id, Some(7));
assert_eq!(state.reviewed_files, BTreeSet::from([0]));