1#![doc=include_str!("../readme.md")]
2use lazy_static::lazy_static as lstatic;
3type P = u32;
4
5const CLEAR_LINE: [u32;4] = [0x1b, 0x5b, 0x30, 0x4b];
6const NEWLINE: [u32;1] = [0x0a];
7
8pub fn ansi_split(input: &str) -> Vec<String> {
10 lstatic! {
11 static ref IS_ANSI: regex::Regex = regex::Regex::new(
12 r#"(?x)
13 [\u001b\u009b][\[\]()\#;]*(?:(?:(?:[A-Za-z\d]*(?:;[A-Za-z\d]*)*)?\u0007)
14 | (?:(?:\d{1,4}(?:;\d{0,4})*)?[\dA-PRZcf-ntqry=><~]))
15 "#,
16 ).unwrap();
17 };
18 let mut ptr = 0;
19 let mut result = vec![];
20 let ibytes = input.bytes().collect::<Vec<u8>>();
21 for c in IS_ANSI.captures_iter(input) {
22 let m = c.get(0).unwrap();
23 let part = m.as_str().to_string();
24 let offset = m.start();
25 if ptr != offset && ptr < offset {
26 result.push(String::from_utf8(ibytes[ptr..offset].to_vec()).unwrap());
27 }
28 if ptr == offset && !result.is_empty() {
29 result.last_mut().unwrap().push_str(&part);
30 } else {
31 if offset == 0 { result.push(String::default()) }
32 result.push(part);
33 }
34 ptr = m.end();
35 }
36 if ptr < ibytes.len() {
37 result.push(String::from_utf8(ibytes[ptr..].to_vec()).unwrap());
38 }
39 if result.is_empty() { return vec![input.to_string()] }
40 result
41}
42
43#[derive(Debug,Clone)]
44pub struct Diff {
45 x: P,
46 y: P,
47 width: P,
48 height: P,
49 buffer: String,
50 out: Vec<String>,
51 lines: Vec<Line>,
52}
53
54impl Diff {
55 pub fn new(size: (P,P)) -> Self {
57 Diff {
58 x: 0,
59 y: 0,
60 width: size.0,
61 height: size.1,
62 buffer: String::default(),
63 out: vec![],
64 lines: vec![],
65 }
66 }
67 pub fn resize(&mut self, size: (P,P)) {
69 self.width = size.0;
70 self.height = size.1;
71 let buf = self.buffer.clone();
72 self.update(&buf);
73 match self.lines.last() {
74 Some(last) => {
75 self.x = last.remainder;
76 self.y = last.y + last.height;
77 },
78 None => {
79 self.x = 0;
80 self.y = 0;
81 }
82 }
83 }
84 pub fn update(&mut self, buffer: &str) -> String {
86 self.buffer = buffer.to_string();
87 let next_lines = Line::split(buffer, self.width);
88 let min = next_lines.len().min(self.lines.len());
89 self.out = vec![];
90 let mut scrub = false;
91 for i in 0..min {
92 let a = next_lines.get(i).unwrap();
93 let (b_y, b_length, b_height) = {
94 let b = self.lines.get(i).unwrap();
95 if a == b { continue }
96 if !scrub && self.x != self.width && Self::inline_diff(&a,&b) {
97 let left = a.diff_left(b) as usize;
98 let right = a.diff_right(b) as usize;
99 let slice = &a.raw[left .. (a.raw.len() - right).max(left)];
100 if left + right > 4 && left + slice.len() < self.width as usize - 1 {
101 self.move_to(left as P, a.y);
102 self.push(&String::from_iter(slice));
103 self.x += slice.len() as P;
104 continue
105 }
106 }
107 (b.y, b.length, b.height)
108 };
109 self.move_to(0, a.y);
110 self.write(a);
111 if a.y != b_y || a.height != b_height { scrub = true }
112 if b_length > a.length || scrub { self.push(&to_str(CLEAR_LINE)) }
113 if a.newline { self.newline() }
114 }
115
116 for line in &next_lines[min..] {
117 self.move_to(0, line.y);
118 self.write(line);
119 if scrub { self.push(&to_str(CLEAR_LINE)) }
120 if line.newline { self.newline() }
121 }
122
123 let prev_last = self.lines.last();
124 let next_last = next_lines.last();
125 let clear = match (prev_last, next_last) {
126 (Some(plast),None) => Some(plast.y + plast.height),
127 (Some(plast),Some(nlast)) => {
128 if nlast.y + nlast.height < plast.y + plast.height {
129 Some(plast.y + plast.height)
130 } else {
131 None
132 }
133 },
134 (None,_) => None,
135 };
136 if let Some(n) = clear {
137 self.clear_down(n);
138 }
139
140 if let Some(last) = next_last {
142 self.move_to(last.remainder, last.y + last.height);
143 }
144
145 self.lines = next_lines;
146 self.out.join("")
147 }
148 fn inline_diff(a: &Line, b: &Line) -> bool {
149 a.length == b.length
150 && a.parts.len() == 1 && b.parts.len() == 1
151 && a.y == b.y
152 && a.newline && b.newline
153 && a.width == b.width
154 }
155 fn move_to(&mut self, x: P, y: P) {
156 if x > self.x { self.push(&Self::move_right(x - self.x)) }
157 else if x < self.x { self.push(&Self::move_left(self.x - x)) }
158 if y > self.y { self.push(&Self::move_down(y - self.y)) }
159 else if y < self.y { self.push(&Self::move_up(self.y - y)) }
160 self.x = x;
161 self.y = y;
162 }
163 fn push(&mut self, buf: &str) {
164 self.out.push(buf.to_string());
165 }
166 fn newline(&mut self) {
167 self.push(&to_str(NEWLINE));
168 self.x = 0;
169 self.y += 1;
170 }
171 fn clear_down(&mut self, y: P) {
172 let mut x = self.x;
173 let mut i = self.y;
174 while i <= y {
175 self.move_to(x, i);
176 self.push(&to_str(CLEAR_LINE));
177 x = 0;
178 i += 1;
179 }
180 }
181 fn move_up(n: P) -> String { code1(&[0x1b, 0x5b], n, &[0x41]) }
182 fn move_down(n: P) -> String { code1(&[0x1b, 0x5b], n, &[0x42]) }
183 fn move_right(n: P) -> String { code1(&[0x1b, 0x5b], n, &[0x43]) }
184 fn move_left(n: P) -> String { code1(&[0x1b, 0x5b], n, &[0x44]) }
185 fn write(&mut self, line: &Line) {
186 self.out.push(line.to_string());
187 self.x = line.remainder;
188 self.y += line.height;
189 }
190}
191
192impl ToString for Diff {
193 fn to_string(&self) -> String {
194 self.buffer.clone()
195 }
196}
197
198#[derive(Debug,Clone)]
199struct Line {
200 y: P,
201 width: P,
202 height: P,
203 parts: Vec<String>,
204 length: usize,
205 raw: Vec<char>,
206 newline: bool,
207 remainder: P,
208}
209
210impl Line {
211 pub fn new(s: &str, y: P, nl: bool, term_width: P) -> Self {
212 let parts = ansi_split(s);
213 let length = Self::parts_len(&parts);
214 let mut height = length as P / term_width;
215 let mut remainder = length as P - (height * term_width);
216 if height > 0 && remainder == 0 {
217 height -= 1;
218 remainder = term_width;
219 }
220 Self {
221 y,
222 width: term_width,
223 parts,
224 length,
225 raw: s.chars().collect(),
226 newline: nl,
227 height,
228 remainder,
229 }
230 }
231 pub fn diff_left(&self, other: &Self) -> P {
232 let mut left = 0_usize;
233 while left < self.length {
234 if self.raw.get(left) != other.raw.get(left) { break }
235 left += 1;
236 }
237 left as P
238 }
239 pub fn diff_right(&self, other: &Self) -> P {
240 let mut right = 0;
241 while right < self.length {
242 let r = self.length - right - 1;
243 if self.raw.get(r) != other.raw.get(r) { break }
244 right += 1;
245 }
246 right as P
247 }
248 pub fn split(input: &str, term_width: P) -> Vec<Self> {
249 let mut y = 0;
250 let lines = input.split('\n').collect::<Vec<&str>>();
251 let len = lines.len();
252 lines.iter().enumerate().map(move |(i,line)| {
253 let line = Line::new(line, y, i < len - 1, term_width);
254 y += line.height + (if line.newline { 1 } else { 0 });
255 line
256 }).collect()
257 }
258 fn parts_len(parts: &[String]) -> usize {
259 let mut sum = 0;
260 let mut i = 0;
261 while i < parts.len() {
262 sum += parts.get(i).unwrap().len();
263 i += 2;
264 }
265 sum
266 }
267}
268
269impl ToString for Line {
270 fn to_string(&self) -> String {
271 String::from_iter(self.raw.iter())
272 }
273}
274
275impl PartialEq for Line {
276 fn eq(&self, other: &Self) -> bool {
277 self.y == other.y && self.width == other.width
278 && self.raw == other.raw && self.newline == other.newline
279 }
280}
281
282fn to_str<const N: usize>(xs: [u32;N]) -> String {
283 let chars = xs.iter().map(|c| char::from_u32(*c).unwrap()).collect::<Vec<char>>();
284 String::from_iter(&chars)
285}
286
287fn code1(pre: &[u32], n: P, post: &[u32]) -> String {
288 let sn = format!["{}", n];
289 let spre = String::from_iter(pre.iter().map(|c| char::from_u32(*c).unwrap()));
290 let spost = String::from_iter(post.iter().map(|c| char::from_u32(*c).unwrap()));
291 spre + &sn + &spost
292}