feat: default diffs to branch changes

This commit is contained in:
Valdemar Lorenzen
2026-10-13 10:41:37 +02:00
parent fafec2a6b0
commit 6c254f9b0d
10 changed files with 562 additions and 33 deletions
+99 -5
View File
@@ -39,6 +39,20 @@ impl RepoKind {
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ComparisonMode {
#[default]
BranchChanges,
Snapshots,
}
impl ComparisonMode {
pub fn label(self) -> &'static str {
match self {
Self::BranchChanges => "Branch changes",
Self::Snapshots => "Snapshots",
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RevisionTarget {
Working,
@@ -105,6 +119,7 @@ pub enum Operation {
Compare {
before: Revision,
after: Revision,
mode: ComparisonMode,
buffers: Box<[(PathBuf, String)]>,
},
LoadFile {
@@ -187,6 +202,7 @@ pub struct DiffState {
pub phase: Phase,
pub word_wrap: bool,
pub view_mode: ViewMode,
pub comparison_mode: ComparisonMode,
pub split_axis: crate::panes::SplitAxis,
pub before: Option<Revision>,
pub after: Option<Revision>,
@@ -231,6 +247,7 @@ impl DiffState {
phase: Phase::Before,
word_wrap: false,
view_mode: ViewMode::Split,
comparison_mode: ComparisonMode::default(),
split_axis: crate::panes::SplitAxis::Vertical,
before: None,
after: None,
@@ -548,6 +565,9 @@ impl DiffState {
}
return;
};
if revision.target == RevisionTarget::Working {
self.comparison_mode = ComparisonMode::Snapshots;
}
if let RevisionTarget::FetchRemote(remote) = &revision.target {
self.queue(Operation::FetchRemote(remote.clone()));
} else if let RevisionTarget::ListRemote(remote) = &revision.target {
@@ -559,13 +579,43 @@ impl DiffState {
self.query.clear();
self.refilter();
self.error = None;
} else if let Some(before) = &self.before {
self.after = Some(revision.clone());
self.queue(Operation::Compare {
} else {
self.after = Some(revision);
self.queue_comparison();
}
}
pub(crate) fn working_selected(&self) -> bool {
[&self.before, &self.after]
.into_iter()
.flatten()
.any(|revision| revision.target == RevisionTarget::Working)
}
pub(crate) fn toggle_comparison_mode(&mut self) -> Result<(), &'static str> {
if self.working_selected() {
return Err("Working checkout requires Snapshots");
}
self.comparison_mode = match self.comparison_mode {
ComparisonMode::BranchChanges => ComparisonMode::Snapshots,
ComparisonMode::Snapshots => ComparisonMode::BranchChanges,
};
self.queue_comparison();
Ok(())
}
fn queue_comparison(&mut self) {
if let (Some(before), Some(after)) = (&self.before, &self.after) {
let operation = Operation::Compare {
before: before.clone(),
after: revision,
after: after.clone(),
mode: self.comparison_mode,
buffers: Box::new([]),
});
};
self.phase = Phase::After;
self.comparison = None;
self.comparison_id = None;
self.queue(operation);
}
}
@@ -3299,4 +3349,48 @@ mod tests {
})
));
}
#[test]
fn mode_action_keeps_filter_input_and_rejects_superseded_comparisons() {
let mut editor = editor();
open(&mut editor);
editor
.bind("normal", "m", Some("core.diff.mode.toggle".into()))
.unwrap();
text(&mut editor, "m");
assert_eq!(editor.diff().unwrap().query, "m");
assert_eq!(
editor.diff().unwrap().comparison_mode,
ComparisonMode::BranchChanges
);
editor.handle(Input::Ctrl('u'));
editor.handle(Input::Down);
editor.handle(Input::Enter);
editor.handle(Input::Down);
editor.handle(Input::Enter);
let (old, _) = editor.take_diff_request().unwrap();
editor.execute("diff.mode.toggle");
assert!(!editor.apply_diff_result(old, Err("superseded".into())));
let (current, operation) = editor.take_diff_request().unwrap();
assert!(matches!(
operation,
Operation::Compare {
mode: ComparisonMode::Snapshots,
..
}
));
assert!(editor.apply_diff_result(current, Err("retryable".into())));
assert_eq!(editor.diff().unwrap().phase, Phase::After);
assert_eq!(editor.diff().unwrap().error.as_deref(), Some("retryable"));
editor.execute("diff.mode.toggle");
let (_, operation) = editor.take_diff_request().unwrap();
assert!(matches!(
operation,
Operation::Compare {
mode: ComparisonMode::BranchChanges,
..
}
));
assert!(editor.diff().unwrap().error.is_none());
}
}