ratatui_toolkit/widgets/code_diff/widget/methods/helpers/build_aligned_lines.rs
1use crate::widgets::code_diff::diff_hunk::DiffHunk;
2use crate::widgets::code_diff::diff_line::DiffLine;
3use crate::widgets::code_diff::enums::DiffLineKind;
4
5/// A pair of lines for side-by-side display.
6///
7/// In side-by-side mode, we need to align lines so that:
8/// - Context lines appear on both sides at the same row
9/// - Removed lines appear on the left, with empty space on the right
10/// - Added lines appear on the right, with empty space on the left
11#[derive(Debug, Clone)]
12pub struct AlignedLinePair {
13 /// The line to show on the left (old version).
14 pub left: Option<DiffLine>,
15
16 /// The line to show on the right (new version).
17 pub right: Option<DiffLine>,
18}
19
20/// Builds aligned line pairs from a hunk for side-by-side display.
21///
22/// This function takes the lines from a hunk and pairs them up so that:
23/// - Context lines have the same content on both sides
24/// - Removed lines are on the left with None on the right
25/// - Added lines are on the right with None on the left
26/// - Consecutive removes/adds are paired up when possible
27///
28/// # Arguments
29///
30/// * `hunk` - The diff hunk to process
31///
32/// # Returns
33///
34/// A vector of aligned line pairs for rendering
35pub fn build_aligned_lines(hunk: &DiffHunk) -> Vec<AlignedLinePair> {
36 let mut pairs: Vec<AlignedLinePair> = Vec::new();
37 let mut pending_removes: Vec<DiffLine> = Vec::new();
38 let mut pending_adds: Vec<DiffLine> = Vec::new();
39
40 for line in &hunk.lines {
41 match line.kind {
42 DiffLineKind::Context | DiffLineKind::HunkHeader => {
43 // Flush pending removes/adds before context
44 flush_pending(&mut pairs, &mut pending_removes, &mut pending_adds);
45
46 // Context lines appear on both sides
47 pairs.push(AlignedLinePair {
48 left: Some(line.clone()),
49 right: Some(line.clone()),
50 });
51 }
52 DiffLineKind::Removed => {
53 // If we have pending adds, we should flush first
54 // (removes should come before adds in the grouping)
55 if !pending_adds.is_empty() {
56 flush_pending(&mut pairs, &mut pending_removes, &mut pending_adds);
57 }
58 pending_removes.push(line.clone());
59 }
60 DiffLineKind::Added => {
61 pending_adds.push(line.clone());
62 }
63 }
64 }
65
66 // Flush any remaining pending lines
67 flush_pending(&mut pairs, &mut pending_removes, &mut pending_adds);
68
69 pairs
70}
71
72/// Flushes pending removed and added lines into aligned pairs.
73///
74/// This pairs up removes with adds where possible, showing them
75/// side by side. Any extras are shown with None on the opposite side.
76fn flush_pending(
77 pairs: &mut Vec<AlignedLinePair>,
78 pending_removes: &mut Vec<DiffLine>,
79 pending_adds: &mut Vec<DiffLine>,
80) {
81 let remove_count = pending_removes.len();
82 let add_count = pending_adds.len();
83 let max_count = remove_count.max(add_count);
84
85 for i in 0..max_count {
86 let left = pending_removes.get(i).cloned();
87 let right = pending_adds.get(i).cloned();
88 pairs.push(AlignedLinePair { left, right });
89 }
90
91 pending_removes.clear();
92 pending_adds.clear();
93}