imara_diff/
unified_diff.rs1use std::fmt::{Display, Write};
2use std::ops::Range;
3
4use crate::intern::{InternedInput, Interner, Token};
5use crate::Sink;
6
7pub struct UnifiedDiffBuilder<'a, W, T>
10where
11 W: Write,
12 T: Display,
13{
14 before: &'a [Token],
15 after: &'a [Token],
16 interner: &'a Interner<T>,
17
18 pos: u32,
19 before_hunk_start: u32,
20 after_hunk_start: u32,
21 before_hunk_len: u32,
22 after_hunk_len: u32,
23
24 buffer: String,
25 dst: W,
26}
27
28impl<'a, T> UnifiedDiffBuilder<'a, String, T>
29where
30 T: Display,
31{
32 pub fn new(input: &'a InternedInput<T>) -> Self {
35 Self {
36 before_hunk_start: 0,
37 after_hunk_start: 0,
38 before_hunk_len: 0,
39 after_hunk_len: 0,
40 buffer: String::with_capacity(8),
41 dst: String::new(),
42 interner: &input.interner,
43 before: &input.before,
44 after: &input.after,
45 pos: 0,
46 }
47 }
48}
49
50impl<'a, W, T> UnifiedDiffBuilder<'a, W, T>
51where
52 W: Write,
53 T: Display,
54{
55 pub fn with_writer(input: &'a InternedInput<T>, writer: W) -> Self {
58 Self {
59 before_hunk_start: 0,
60 after_hunk_start: 0,
61 before_hunk_len: 0,
62 after_hunk_len: 0,
63 buffer: String::with_capacity(8),
64 dst: writer,
65 interner: &input.interner,
66 before: &input.before,
67 after: &input.after,
68 pos: 0,
69 }
70 }
71
72 fn print_tokens(&mut self, tokens: &[Token], prefix: char) {
73 for &token in tokens {
74 writeln!(&mut self.buffer, "{prefix}{}", self.interner[token]).unwrap();
75 }
76 }
77
78 fn flush(&mut self) {
79 if self.before_hunk_len == 0 && self.after_hunk_len == 0 {
80 return;
81 }
82
83 let end = (self.pos + 3).min(self.before.len() as u32);
84 self.update_pos(end, end);
85
86 writeln!(
87 &mut self.dst,
88 "@@ -{},{} +{},{} @@",
89 self.before_hunk_start + 1,
90 self.before_hunk_len,
91 self.after_hunk_start + 1,
92 self.after_hunk_len,
93 )
94 .unwrap();
95 write!(&mut self.dst, "{}", &self.buffer).unwrap();
96 self.buffer.clear();
97 self.before_hunk_len = 0;
98 self.after_hunk_len = 0
99 }
100
101 fn update_pos(&mut self, print_to: u32, move_to: u32) {
102 self.print_tokens(&self.before[self.pos as usize..print_to as usize], ' ');
103 let len = print_to - self.pos;
104 self.pos = move_to;
105 self.before_hunk_len += len;
106 self.after_hunk_len += len;
107 }
108}
109
110impl<W, T> Sink for UnifiedDiffBuilder<'_, W, T>
111where
112 W: Write,
113 T: Display,
114{
115 type Out = W;
116
117 fn process_change(&mut self, before: Range<u32>, after: Range<u32>) {
118 if before.start - self.pos > 6 {
119 self.flush();
120 self.pos = before.start - 3;
121 self.before_hunk_start = self.pos;
122 self.after_hunk_start = after.start - 3;
123 }
124 self.update_pos(before.start, before.end);
125 self.before_hunk_len += before.end - before.start;
126 self.after_hunk_len += after.end - after.start;
127 self.print_tokens(
128 &self.before[before.start as usize..before.end as usize],
129 '-',
130 );
131 self.print_tokens(&self.after[after.start as usize..after.end as usize], '+');
132 }
133
134 fn finish(mut self) -> Self::Out {
135 self.flush();
136 self.dst
137 }
138}