MCPcopy Create free account
hub / github.com/atomicdotdev/atomic / hunks_from_changed_lines

Method hunks_from_changed_lines

atomic-cli/src/commands/diff/helpers.rs:128–281  ·  view source on GitHub ↗

Group numbered changed lines into diff hunks with true file offsets. `changed` must be in file order. Changes separated by more than `2 * context` unchanged lines become separate hunks; closer changes merge with the gap emitted as context. Context line content is taken from `before_lines` (the file's recorded before-content); when it is `None`, hunks are emitted with zero context but still carry

(
        changed: &[NumberedLine],
        before_lines: Option<&[Vec<u8>]>,
        context: usize,
    )

Source from the content-addressed store, hash-verified

126 /// at boundary `p` is `@@ -p,0 +n,k @@`, a pure deletion is
127 /// `@@ -l,k +p,0 @@`, and context lines count toward both sides.
128 pub(super) fn hunks_from_changed_lines(
129 changed: &[NumberedLine],
130 before_lines: Option<&[Vec<u8>]>,
131 context: usize,
132 ) -> Vec<DiffHunk> {
133 let mut hunks = Vec::new();
134 if changed.is_empty() {
135 return hunks;
136 }
137
138 let old_len = before_lines.map(|l| l.len()).unwrap_or(0);
139 let ctx = if before_lines.is_some() { context } else { 0 };
140 let merge_gap = 2 * ctx as isize;
141
142 // 1. Group into runs of nearby changes, measured in old-file
143 // coordinates.
144 let mut runs: Vec<(usize, usize)> = Vec::new();
145 let mut run_start = 0usize;
146 let mut run_hi = changed[0].old_pos();
147 for (i, nl) in changed.iter().enumerate().skip(1) {
148 let lo = nl.old_pos();
149 // Unchanged lines between the run and this change. An addition
150 // sits at a boundary, so the gap is measured from the boundary
151 // itself; a removal occupies a line, hence the extra -1.
152 let gap = match nl.line.status {
153 LineStatus::Removed => lo as isize - run_hi as isize - 1,
154 _ => lo as isize - run_hi as isize,
155 };
156 if gap > merge_gap {
157 runs.push((run_start, i));
158 run_start = i;
159 }
160 run_hi = run_hi.max(nl.old_pos());
161 }
162 runs.push((run_start, changed.len()));
163
164 // 2. Emit one hunk per run.
165 for (start, end) in runs {
166 let lines = &changed[start..end];
167
168 let removed_count = lines.iter().filter(|l| l.line.is_removed()).count();
169 let added_count = lines.len() - removed_count;
170
171 let old_lo = lines.iter().map(|l| l.old_pos()).min().unwrap_or(0);
172 let old_hi = lines.iter().map(|l| l.old_pos()).max().unwrap_or(0);
173 let new_lo = lines.iter().map(|l| l.new_pos()).min().unwrap_or(0);
174
175 // Old-file range covered by the hunk, including context.
176 let (old_start, old_end, old_count) = if removed_count > 0 {
177 let s = old_lo.saturating_sub(ctx).max(1);
178 let e = if ctx > 0 {
179 old_hi.saturating_add(ctx).min(old_len)
180 } else {
181 old_hi
182 };
183 let e = e.max(s);
184 (s, e, e - s + 1)
185 } else {

Callers

nothing calls this directly

Calls 12

old_posMethod · 0.80
skipMethod · 0.80
is_removedMethod · 0.80
new_posMethod · 0.80
is_contextMethod · 0.80
getMethod · 0.65
is_emptyMethod · 0.45
lenMethod · 0.45
iterMethod · 0.45
pushMethod · 0.45
countMethod · 0.45
cloneMethod · 0.45

Tested by

no test coverage detected