Git · marked(): regions as Git grouped them in marked text, base from diff3

This commit is contained in:
ZacharyZhang-NY committed 2026-10-07 03:06:03 -04:00
1 parent 63d4a459c7
commit ef4ac5b1b8
2 files changed
+58 -1

No files matched your search

+55
View File
@@ -161,6 +161,42 @@ pub fn conflicts(text: &str) -> Vec<Conflict> {
found
}
/// Regions as Git grouped them in marked `text`; base from diff3 sections.
pub fn marked(text: &str) -> Vec<Region> {
let lines: Vec<String> = text
.tokenize_lines()
.into_iter()
.map(str::to_string)
.collect();
let same = |range: std::ops::Range<usize>| Region {
kind: RegionKind::Unchanged,
base: lines[range.clone()].to_vec(),
ours: lines[range.clone()].to_vec(),
theirs: lines[range].to_vec(),
};
let mut out = Vec::new();
let mut at = 0;
for conflict in conflicts(text) {
if conflict.start > at {
out.push(same(at..conflict.start));
}
let ours_end = conflict.base.unwrap_or(conflict.middle);
out.push(Region {
kind: RegionKind::Conflict,
ours: lines[conflict.start + 1..ours_end].to_vec(),
base: conflict
.base
.map_or_else(Vec::new, |base| lines[base + 1..conflict.middle].to_vec()),
theirs: lines[conflict.middle + 1..conflict.end].to_vec(),
});
at = conflict.end + 1;
}
if at < lines.len() {
out.push(same(at..lines.len()));
}
out
}
/// `text` with conflict `ix` settled; none if absent.
pub fn resolve(text: &str, ix: usize, take: Take) -> Option<String> {
let Some(conflict) = conflicts(text).get(ix).copied() else {
@@ -191,6 +227,25 @@ pub fn resolve(text: &str, ix: usize, take: Take) -> Option<String> {
mod tests {
use super::*;
#[test]
fn marked_text_keeps_gits_grouping_and_its_base() {
let text = "first\n<<<<<<< ours\nOURS a\nunchanged\nOURS b\n||||||| base\na\nunchanged\nb\n=======\nTHEIRS a\nunchanged\nTHEIRS b\n>>>>>>> theirs\nlast\n";
let found = marked(text);
let kinds: Vec<RegionKind> = found.iter().map(|region| region.kind).collect();
assert_eq!(
kinds,
[
RegionKind::Unchanged,
RegionKind::Conflict,
RegionKind::Unchanged
],
"one block, as Git wrote it"
);
assert_eq!(found[1].base, ["a\n", "unchanged\n", "b\n"]);
assert_eq!(found[1].ours.len(), 3);
assert_eq!(found[2].ours, ["last\n"]);
}
#[test]
fn a_diff3_conflict_leaves_its_base_out() {
let text = "a\n<<<<<<< HEAD\nours\n||||||| base\nold\n=======\ntheirs\n>>>>>>> side\nz\n";
+3 -1
View File
@@ -25,7 +25,9 @@ pub use diff::{DiffLine, LineKind, Stretch, diff, mark_words, pairs, stat};
pub use graph::{GraphRow, Half, Stroke, lanes};
pub use history::FileHistory;
pub use merge::{ConflictResolver, ThreeWayMerge};
pub use merging::{Conflict, Region, RegionKind, Take, conflicts, regions, resolve, result};
pub use merging::{
Conflict, Region, RegionKind, Take, conflicts, marked, regions, resolve, result,
};
pub use refs::{Branch, BranchList, BranchSelector};
pub use review::{PullRequest, PullRequestCard, PullState, ReviewComment, ReviewNote};
pub use rows::Place;