1use alloc::rc::Rc;
21use alloc::string::String;
22use alloc::vec::Vec;
23use core::cell::RefCell;
24
25use crate::code_doc::*;
26use crate::highlight::*;
27
28pub(crate) const COL_LONG: usize = 4096;
30const COL_STEP: usize = 1024;
32const TOK_STEP: usize = 1024;
33const TOK_REACH: usize = 4096;
35const LONG_KEEP: usize = 256;
37const LONG_BYTES: usize = 256 << 20;
38
39fn col_marks(t: &str, tw: usize) -> Vec<(usize, usize)> {
42 let mut out = alloc::vec![(0, 0)];
43 col_walk(t, 0, 0, tw, &mut out, &mut |_, _| None);
44 out
45}
46
47fn col_walk(t: &str, i: usize, col: usize, tw: usize, out: &mut Vec<(usize, usize)>, meet: &mut dyn FnMut(usize, usize) -> Option<usize>) -> Option<(usize, usize)> {
52 let tw = tw.clamp(1, 16);
53 let b = t.as_bytes();
54 let (mut i, mut col) = (i, col);
55 let mut next = out.last().map_or(0, |m| m.0) + COL_STEP;
56 while i < t.len() {
57 let k = ascii_run(b, i, t.len());
58 let (n, w) = if k > 0 { (i + k, k) } else { cell(t, i, col, tw) };
59 if let Some(d) = meet(i, if k > 0 { n } else { i + 1 }) {
60 return Some((i + d, col + d));
61 }
62 while k > 0 && next < n {
64 out.push((next, col + (next - i)));
65 next += COL_STEP;
66 }
67 (i, col) = (n, col + w);
68 if i >= next && i < t.len() {
69 out.push((i, col));
70 next = i + COL_STEP;
71 }
72 }
73 None
74}
75
76fn col_marks_edit(old: &str, marks: &[(usize, usize)], new: &str, p: usize, s: usize, tw: usize) -> Vec<(usize, usize)> {
81 let tw = tw.clamp(1, 16);
82 let delta = new.len() as isize - old.len() as isize;
83 let k = marks.partition_point(|m| m.0 + 16 < p).max(1);
85 let mut out = marks[..k].to_vec();
86 let (b0, c0) = out[k - 1];
87 let from = new.len() - s + 16;
89 let at = |j: usize| (marks[j].0 as isize + delta) as usize;
90 let mut j = marks.partition_point(|m| (m.0 as isize + delta) < from as isize);
91 let met = col_walk(new, b0, c0, tw, &mut out, &mut |a, b| {
92 while j < marks.len() && at(j) < a.max(from) {
93 j += 1;
94 }
95 (j < marks.len() && at(j) < b).then(|| at(j) - a)
96 });
97 let Some((y, cy)) = met else { return out };
98 out.retain(|m| m.0 < y);
99 let d = cy as isize - marks[j].1 as isize;
100 let tab = new[y..].find('\t').map(|t| y + t);
102 let d1 = tab.map_or(d, |t| {
103 let a = col_from(old, marks[j].0, marks[j].1, (t as isize - delta) as usize, tw) as isize;
104 let (wn, wo) = (tw as isize - (a + d).rem_euclid(tw as isize), tw as isize - a.rem_euclid(tw as isize));
105 d + wn - wo
106 });
107 for m in &marks[j..] {
108 let x = (m.0 as isize + delta) as usize;
109 let sh = if tab.is_some_and(|t| x > t) { d1 } else { d };
110 out.push((x, (m.1 as isize + sh) as usize));
111 }
112 out
113}
114
115struct Toks {
119 h: Highlighter,
120 st: u32,
121 end: u32,
122 toks: Rc<Vec<Token>>,
123 marks: Vec<(usize, usize, usize)>,
124}
125
126impl Toks {
127 fn scan(h: Highlighter, t: &str, st: u32) -> Toks {
129 let mut toks = Vec::new();
130 let mut marks = alloc::vec![(0, 0, 0)];
131 let (_, end) = h.line_part(t, st, 0, &mut toks, &mut |i, out| {
132 if i >= marks.last().map_or(0, |m| m.0) + TOK_STEP {
133 marks.push((i, out.len(), out.last().map_or(0, |x| x.end)));
134 }
135 false
136 });
137 Toks { h, st, end: end.unwrap_or(0), toks: Rc::new(toks), marks }
138 }
139
140 fn edit(&mut self, old: &str, new: &str, p: usize, s: usize) {
143 let delta = new.len() as isize - old.len() as isize;
144 let k = self.marks.partition_point(|m| m.0 + TOK_REACH <= p).max(1) - 1;
145 let (r, rk, rend) = self.marks[k];
146 let toks = Rc::make_mut(&mut self.toks);
147 let head = rk.saturating_sub(1);
149 let mut mid: Vec<Token> = toks[head..rk].iter().map(|x| Token { end: rend, ..*x }).collect();
150 let base = head;
151 let bol = |t: &str| t.len() - t.trim_start_matches([' ', '\t']).len();
154 let from = (new.len() - s + 4).max(bol(new) + 1).max((bol(old) as isize + delta + 1).max(0) as usize);
155 let om = &self.marks;
156 let mut j = om.partition_point(|m| (m.0 as isize + delta) < from as isize);
157 let mut marks: Vec<(usize, usize, usize)> = self.marks[..=k].to_vec();
158 let mut met = None;
159 let (_, end) = self.h.line_part(new, self.st, r, &mut mid, &mut |i, out| {
160 if i >= from {
161 while j < om.len() && (om[j].0 as isize + delta) < i as isize {
162 j += 1;
163 }
164 if let Some(&(_, ok, oend)) = om.get(j).filter(|m| (m.0 as isize + delta) as usize == i) {
165 let same = match (out.last(), ok.checked_sub(1).map(|q| toks[q])) {
167 (Some(a), Some(b)) => a.kind == b.kind && a.end as isize == oend as isize + delta,
168 (None, None) => true,
169 _ => false,
170 };
171 if same {
172 met = Some(j);
173 return true;
174 }
175 }
176 }
177 if i > r && i >= marks.last().map_or(0, |m| m.0) + TOK_STEP {
178 marks.push((i, base + out.len(), out.last().map_or(0, |x| x.end)));
179 }
180 false
181 });
182 match met {
183 Some(j) => {
184 let (_, ok, _) = self.marks[j];
185 if let (Some(a), Some(q)) = (mid.last_mut(), ok.checked_sub(1)) {
187 a.end = (toks[q].end as isize + delta) as usize;
188 }
189 let nk = base + mid.len();
190 toks.splice(head..ok, mid);
191 for x in toks[nk..].iter_mut() {
192 x.start = (x.start as isize + delta) as usize;
193 x.end = (x.end as isize + delta) as usize;
194 }
195 let sh = nk as isize - ok as isize;
196 for m in &self.marks[j..] {
197 let e = if m.1 > 0 { (m.2 as isize + delta) as usize } else { 0 };
199 marks.push(((m.0 as isize + delta) as usize, (m.1 as isize + sh) as usize, e));
200 }
201 }
202 None => {
203 toks.truncate(head);
204 toks.extend(mid);
205 self.end = end.unwrap_or(0);
206 }
207 }
208 self.marks = marks;
209 }
210}
211
212struct LongLine {
215 line: usize,
216 text: String,
217 checked: bool,
218 marks: Option<Vec<(usize, usize)>>,
219 toks: Option<Toks>,
220}
221
222impl LongLine {
223 fn size(&self) -> usize {
225 let t = self.toks.as_ref().map_or(0, |k| k.toks.len() * core::mem::size_of::<Token>() + k.marks.len() * 24);
226 self.text.len() + self.marks.as_ref().map_or(0, |m| m.len() * 16) + t
227 }
228}
229
230#[derive(Default)]
231struct Long {
232 uid: u32,
233 rev: u64,
234 tw: usize,
235 lines: Vec<LongLine>,
236}
237
238#[derive(Default)]
240pub(crate) struct Longs(RefCell<Long>);
241
242fn alike(a: &str, b: &str) -> (usize, usize) {
244 let (x, y) = (a.as_bytes(), b.as_bytes());
245 let m = x.len().min(y.len());
246 let mut p = 0;
248 while p + 64 <= m && x[p..p + 64] == y[p..p + 64] {
249 p += 64;
250 }
251 while p < m && x[p] == y[p] {
252 p += 1;
253 }
254 let mut s = 0;
255 while s + 64 <= m - p && x[x.len() - s - 64..x.len() - s] == y[y.len() - s - 64..y.len() - s] {
256 s += 64;
257 }
258 while s < m - p && x[x.len() - s - 1] == y[y.len() - s - 1] {
259 s += 1;
260 }
261 (p, s)
262}
263
264pub fn code_long_check(text: &str, h: &Highlighter, tw: u8, edits: &[(usize, usize, &str)]) -> Vec<String> {
269 let tw = tw.clamp(1, 16) as usize;
270 let mut t = String::from(text);
271 let mut marks = col_marks(&t, tw);
272 let mut tk = Toks::scan(*h, &t, 0);
273 let mut out = Vec::new();
274 let floor = |t: &str, mut i: usize| {
276 i = i.min(t.len());
277 while !t.is_char_boundary(i) {
278 i -= 1;
279 }
280 i
281 };
282 for (k, &(a, b, s)) in edits.iter().enumerate() {
283 let a = floor(&t, a);
284 let b = floor(&t, b).max(a);
285 let n = alloc::format!("{}{}{}", &t[..a], s, &t[b..]);
286 let (p, q) = alike(&t, &n);
287 marks = col_marks_edit(&t, &marks, &n, p, q, tw);
288 tk.edit(&t, &n, p, q);
289 t = n;
290 let bounds = text::segment::graphemes(&t);
292 if let Some(m) = marks.iter().find(|m| bounds.binary_search(&m.0).is_err() || col_in(&t, m.0, tw) != m.1) {
293 out.push(alloc::format!("edit {k}: checkpoint {m:?}: column {}", col_in(&t, m.0, tw)));
294 }
295 if marks.windows(2).any(|w| w[0].0 >= w[1].0) {
296 out.push(alloc::format!("edit {k}: checkpoints out of order"));
297 }
298 let mut seen = Vec::new();
300 let mut fresh = Vec::new();
301 let (_, end) = h.line_part(&t, 0, 0, &mut fresh, &mut |i, o| {
302 seen.push((i, o.len(), o.last().map_or(0, |x| x.end)));
303 false
304 });
305 if *tk.toks != fresh || tk.end != end.unwrap_or(0) {
306 let at = tk.toks.iter().zip(&fresh).position(|(x, y)| x != y).unwrap_or(fresh.len().min(tk.toks.len()));
307 out.push(alloc::format!("edit {k}: tokens differ from {at}: {:?} / {:?}", tk.toks.get(at), fresh.get(at)));
308 }
309 if let Some(m) = tk.marks.iter().skip(1).find(|m| seen.binary_search(m).is_err()) {
310 out.push(alloc::format!("edit {k}: token place {m:?} not one of a whole scan"));
311 }
312 }
313 out
314}
315
316impl Longs {
317 fn with<R>(&self, doc: &CodeDoc, line: usize, tw: usize, f: impl FnOnce(&mut LongLine, &str) -> R) -> R {
319 let mut s = self.0.borrow_mut();
320 let same = s.uid == doc.uid() && s.tw == tw;
321 if !same || s.rev != doc.revision() {
322 match doc.changes_since(s.rev).filter(|_| same) {
323 Some(cs) => {
324 for c in cs {
325 s.lines.retain_mut(|e| {
327 if e.line >= c.first + c.old {
328 e.line = e.line - c.old + c.new;
329 true
330 } else if e.line >= c.first && c.old == c.new {
331 e.checked = false;
332 true
333 } else {
334 e.line < c.first
335 }
336 });
337 }
338 }
339 None => s.lines.clear(),
340 }
341 (s.uid, s.rev, s.tw) = (doc.uid(), doc.revision(), tw);
342 }
343 let t = doc.line(line);
344 let k = match s.lines.iter().position(|e| e.line == line) {
345 Some(k) => k,
346 None => {
347 let mut bytes: usize = s.lines.iter().map(|e| e.size()).sum::<usize>() + t.len();
349 let mut k = 0;
350 while k < s.lines.len() && (s.lines.len() - k >= LONG_KEEP || bytes > LONG_BYTES) {
351 bytes -= s.lines[k].size();
352 k += 1;
353 }
354 s.lines.drain(..k);
355 s.lines.push(LongLine { line, text: String::from(t), checked: true, marks: None, toks: None });
356 s.lines.len() - 1
357 }
358 };
359 let e = &mut s.lines[k];
360 if !e.checked {
361 e.checked = true;
362 if e.text != t {
363 let (p, q) = alike(&e.text, t);
364 if let Some(m) = e.marks.take() {
365 e.marks = Some(col_marks_edit(&e.text, &m, t, p, q, tw));
366 }
367 if let Some(tk) = e.toks.as_mut() {
368 tk.edit(&e.text, t, p, q);
369 }
370 e.text.clear();
371 e.text.push_str(t);
372 }
373 }
374 f(e, t)
375 }
376
377 fn mark(&self, doc: &CodeDoc, line: usize, tw: usize, x: usize, by_col: bool) -> (usize, usize) {
379 if doc.line(line).len() < COL_LONG {
380 return (0, 0);
381 }
382 self.with(doc, line, tw, |e, t| {
383 let m = e.marks.get_or_insert_with(|| col_marks(t, tw));
384 m[m.partition_point(|p| if by_col { p.1 <= x } else { p.0 <= x }).saturating_sub(1)]
385 })
386 }
387
388 pub(crate) fn col(&self, doc: &CodeDoc, line: usize, byte: usize, tw: usize) -> usize {
390 let (b, c) = self.mark(doc, line, tw, byte, false);
391 col_from(doc.line(line), b, c, byte, tw)
392 }
393
394 pub(crate) fn byte(&self, doc: &CodeDoc, line: usize, col: usize, tw: usize) -> usize {
396 let (b, c) = self.mark(doc, line, tw, col, true);
397 byte_from(doc.line(line), b, c, col, tw)
398 }
399
400 pub(crate) fn floor(&self, doc: &CodeDoc, line: usize, col: usize, tw: usize) -> (usize, usize) {
402 let (b, c) = self.mark(doc, line, tw, col, true);
403 cell_floor(doc.line(line), b, c, col, tw)
404 }
405
406 pub(crate) fn tokens(&self, doc: &CodeDoc, line: usize, tw: usize, h: &Highlighter, st: u32) -> (u32, Rc<Vec<Token>>) {
408 self.with(doc, line, tw, |e, t| {
409 if !e.toks.as_ref().is_some_and(|k| k.st == st && core::ptr::eq(k.h.grammar(), h.grammar())) {
410 e.toks = Some(Toks::scan(*h, t, st));
411 }
412 match &e.toks {
413 Some(k) => (k.end, k.toks.clone()),
414 None => (0, Rc::default()),
415 }
416 })
417 }
418}