fix: clarify conflict pane roles and status indicators
Use Resolution and Other version borders, omit branch/path metadata, and show unsaved output state only on the writable pane. Track native explorer conflict C from authoritative jj state across draft/check/error transitions and clear it only after confirmed resolution. Focused resolver and save tests pass; live terminal labels and explorer verified without saving the mock conflicts. Full library run: 795 pass, one unrelated async plugin fixture_barrier startup failure.
This commit is contained in:
+102
-11
@@ -1482,6 +1482,8 @@ pub struct ResolverFile {
|
||||
pub path: PathBuf,
|
||||
pub draft_buffer: usize,
|
||||
pub status: ResolverStatus,
|
||||
/// Retained through draft edits and failed/pending jj confirmation.
|
||||
jj_conflicted: bool,
|
||||
pub sources: Vec<ResolverSource>,
|
||||
pub text: bool,
|
||||
pub whole_file_choice: Option<usize>,
|
||||
@@ -1492,6 +1494,22 @@ pub struct ResolverFile {
|
||||
comparison_key: Option<(u64, usize)>,
|
||||
}
|
||||
|
||||
impl ResolverFile {
|
||||
fn apply_jj_status(&mut self, status: crate::resolve::Status) {
|
||||
self.status = match status {
|
||||
crate::resolve::Status::Unresolved => {
|
||||
self.jj_conflicted = true;
|
||||
ResolverStatus::Unresolved
|
||||
}
|
||||
crate::resolve::Status::Resolved => {
|
||||
self.jj_conflicted = false;
|
||||
ResolverStatus::Resolved
|
||||
}
|
||||
crate::resolve::Status::Unconfirmed => ResolverStatus::Unconfirmed,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResolverWorkspace {
|
||||
pub files: Vec<ResolverFile>,
|
||||
pub current_file: usize,
|
||||
@@ -2285,6 +2303,16 @@ impl Editor {
|
||||
};
|
||||
}
|
||||
}
|
||||
if let Some(mut picker) = resolver
|
||||
.files
|
||||
.get_mut(resolver.current_file)
|
||||
.and_then(|file| file.diff.files.take())
|
||||
{
|
||||
for file in &resolver.files {
|
||||
picker.set_conflicted(&file.path, file.jj_conflicted);
|
||||
}
|
||||
resolver.files[resolver.current_file].diff.files = Some(picker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2394,6 +2422,7 @@ impl Editor {
|
||||
files.push(ResolverFile {
|
||||
path: file.path.clone(), draft_buffer,
|
||||
status: ResolverStatus::Unresolved, text, whole_file_choice: None,
|
||||
jj_conflicted: file.status != crate::resolve::Status::Resolved,
|
||||
error: file.notice.clone(),
|
||||
seed_source,
|
||||
projection,
|
||||
@@ -2478,12 +2507,8 @@ impl Editor {
|
||||
.map(split_lines);
|
||||
let was_text = resolver.files[file].text;
|
||||
resolver.files[file].text = saved_text.is_some();
|
||||
let status = match model.files[file].status {
|
||||
crate::resolve::Status::Resolved => ResolverStatus::Resolved,
|
||||
crate::resolve::Status::Unresolved => ResolverStatus::Unresolved,
|
||||
crate::resolve::Status::Unconfirmed => ResolverStatus::Unconfirmed,
|
||||
};
|
||||
resolver.files[file].status = status;
|
||||
resolver.files[file].apply_jj_status(model.files[file].status);
|
||||
let status = resolver.files[file].status;
|
||||
resolver.files[file].error = outcome.confirmation.clone();
|
||||
resolver.files[file].whole_file_choice = None;
|
||||
if let Some(buffer) = if self.active.id == buffer {
|
||||
@@ -2639,6 +2664,7 @@ impl Editor {
|
||||
resolver.files[file].comparison_key = None;
|
||||
self.blur_file_picker();
|
||||
self.refresh_resolver_comparison();
|
||||
self.refresh_resolver_status();
|
||||
if self.resolver().unwrap().view == ResolverView::History {
|
||||
self.set_resolver_view(ResolverView::History);
|
||||
}
|
||||
@@ -12552,6 +12578,7 @@ mod tests {
|
||||
path: "file.txt".into(),
|
||||
draft_buffer: result,
|
||||
status: ResolverStatus::Unsaved,
|
||||
jj_conflicted: true,
|
||||
text: true,
|
||||
whole_file_choice: None,
|
||||
error: None,
|
||||
@@ -12688,11 +12715,6 @@ mod tests {
|
||||
ordinary.diff().unwrap().display_rows
|
||||
);
|
||||
assert!(resolver.diff_editing());
|
||||
assert_eq!(resolver.diff().unwrap().side_label(Focus::Before), "Draft");
|
||||
assert_eq!(
|
||||
resolver.diff().unwrap().side_label(Focus::After),
|
||||
"Alternative"
|
||||
);
|
||||
assert!(resolver.diff().unwrap().side_editable(Focus::Before));
|
||||
assert!(!resolver.diff().unwrap().side_editable(Focus::After));
|
||||
let draft_buffer = resolver.buffer_id();
|
||||
@@ -12791,6 +12813,7 @@ mod tests {
|
||||
path: "conflict.txt".into(),
|
||||
draft_buffer,
|
||||
status: ResolverStatus::Unsaved,
|
||||
jj_conflicted: true,
|
||||
text: true,
|
||||
whole_file_choice: None,
|
||||
error: None,
|
||||
@@ -12812,6 +12835,73 @@ mod tests {
|
||||
editor
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolver_explorer_retains_last_jj_conflict_state_until_confirmation() {
|
||||
let mut editor = resolver_editor("draft", &[("draft", false), ("other", false)]);
|
||||
let path = Path::new("conflict.txt");
|
||||
let marker = |editor: &Editor| {
|
||||
editor
|
||||
.resolver_workspace_diff()
|
||||
.unwrap()
|
||||
.files
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.is_conflicted(path)
|
||||
};
|
||||
assert_eq!(
|
||||
editor.resolver().unwrap().files[0].status,
|
||||
ResolverStatus::Unsaved
|
||||
);
|
||||
assert!(marker(&editor));
|
||||
for confirmed in [
|
||||
crate::resolve::Status::Unresolved,
|
||||
crate::resolve::Status::Resolved,
|
||||
crate::resolve::Status::Unresolved,
|
||||
] {
|
||||
editor.resolvers.get_mut(&editor.tab_id).unwrap().files[0].apply_jj_status(confirmed);
|
||||
editor.refresh_resolver_status();
|
||||
let conflicted = confirmed == crate::resolve::Status::Unresolved;
|
||||
assert_eq!(marker(&editor), conflicted);
|
||||
assert_eq!(
|
||||
editor.resolver().unwrap().files[0].status,
|
||||
ResolverStatus::Unsaved
|
||||
);
|
||||
|
||||
editor.resolvers.get_mut(&editor.tab_id).unwrap().files[0].status =
|
||||
ResolverStatus::Checking;
|
||||
editor.refresh_resolver_status();
|
||||
assert_eq!(
|
||||
editor.resolver().unwrap().files[0].status,
|
||||
ResolverStatus::Checking
|
||||
);
|
||||
assert_eq!(marker(&editor), conflicted);
|
||||
|
||||
editor.resolvers.get_mut(&editor.tab_id).unwrap().files[0]
|
||||
.apply_jj_status(crate::resolve::Status::Unconfirmed);
|
||||
assert_eq!(
|
||||
editor.resolver().unwrap().files[0].status,
|
||||
ResolverStatus::Unconfirmed
|
||||
);
|
||||
editor.active.saved_lines.clone_from(&editor.active.lines);
|
||||
editor.refresh_resolver_status();
|
||||
assert_eq!(
|
||||
editor.resolver().unwrap().files[0].status,
|
||||
ResolverStatus::Unconfirmed
|
||||
);
|
||||
assert_eq!(marker(&editor), conflicted);
|
||||
editor.apply(vec![Effect::Insert(" edit".into())]).unwrap();
|
||||
editor.refresh_resolver_status();
|
||||
assert_eq!(
|
||||
editor.resolver().unwrap().files[0].status,
|
||||
ResolverStatus::Unsaved
|
||||
);
|
||||
assert_eq!(marker(&editor), conflicted);
|
||||
|
||||
editor.select_resolver_file(0);
|
||||
assert_eq!(marker(&editor), conflicted);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolver_takes_only_current_diff_hunk_and_undo_preserves_native_newlines() {
|
||||
for (draft, alternative, chosen) in [
|
||||
@@ -12944,6 +13034,7 @@ mod tests {
|
||||
path: "second.txt".into(),
|
||||
draft_buffer: buffer,
|
||||
status: ResolverStatus::Unresolved,
|
||||
jj_conflicted: true,
|
||||
text: true,
|
||||
whole_file_choice: None,
|
||||
error: None,
|
||||
|
||||
Reference in New Issue
Block a user