1use crate::renderer::{char_width, num_overlap, LineAnnotation, LineAnnotationType};
2use crate::{Annotation, AnnotationKind, Patch};
3use std::borrow::Cow;
4use std::cmp::{max, min};
5use std::ops::Range;
6
7#[derive(Debug)]
8pub(crate) struct SourceMap<'a> {
9 lines: Vec<LineInfo<'a>>,
10 pub(crate) source: &'a str,
11}
12
13impl<'a> SourceMap<'a> {
14 pub(crate) fn new(source: &'a str, line_start: usize) -> Self {
15 if source.is_empty() {
18 return Self {
19 lines: vec![LineInfo {
20 line: "",
21 line_index: line_start,
22 start_byte: 0,
23 end_byte: 0,
24 end_line_size: 0,
25 }],
26 source,
27 };
28 }
29
30 let mut current_index = 0;
31
32 let mut mapping = vec![];
33 for (idx, (line, end_line)) in CursorLines::new(source).enumerate() {
34 let line_length = line.len();
35 let line_range = current_index..current_index + line_length;
36 let end_line_size = end_line.len();
37
38 mapping.push(LineInfo {
39 line,
40 line_index: line_start + idx,
41 start_byte: line_range.start,
42 end_byte: line_range.end + end_line_size,
43 end_line_size,
44 });
45
46 current_index += line_length + end_line_size;
47 }
48 Self {
49 lines: mapping,
50 source,
51 }
52 }
53
54 pub(crate) fn get_line(&self, idx: usize) -> Option<&'a str> {
55 self.lines
56 .iter()
57 .find(|l| l.line_index == idx)
58 .map(|info| info.line)
59 }
60
61 pub(crate) fn span_to_locations(&self, span: Range<usize>) -> (Loc, Loc) {
62 let start_info = self
63 .lines
64 .iter()
65 .find(|info| span.start >= info.start_byte && span.start < info.end_byte)
66 .unwrap_or(self.lines.last().unwrap());
67 let (mut start_char_pos, start_display_pos) = start_info.line
68 [0..(span.start - start_info.start_byte).min(start_info.line.len())]
69 .chars()
70 .fold((0, 0), |(char_pos, byte_pos), c| {
71 let display = char_width(c);
72 (char_pos + 1, byte_pos + display)
73 });
74 if (span.start - start_info.start_byte).saturating_sub(start_info.line.len()) > 0 {
76 start_char_pos += 1;
77 }
78 let start = Loc {
79 line: start_info.line_index,
80 char: start_char_pos,
81 display: start_display_pos,
82 byte: span.start,
83 };
84
85 if span.start == span.end {
86 return (start, start);
87 }
88
89 let end_info = self
90 .lines
91 .iter()
92 .find(|info| span.end >= info.start_byte && span.end < info.end_byte)
93 .unwrap_or(self.lines.last().unwrap());
94 let (end_char_pos, end_display_pos) = end_info.line
95 [0..(span.end - end_info.start_byte).min(end_info.line.len())]
96 .chars()
97 .fold((0, 0), |(char_pos, byte_pos), c| {
98 let display = char_width(c);
99 (char_pos + 1, byte_pos + display)
100 });
101
102 let mut end = Loc {
103 line: end_info.line_index,
104 char: end_char_pos,
105 display: end_display_pos,
106 byte: span.end,
107 };
108 if start.line != end.line && end.byte > end_info.end_byte - end_info.end_line_size {
109 end.char += 1;
110 end.display += 1;
111 }
112
113 (start, end)
114 }
115
116 pub(crate) fn span_to_snippet(&self, span: Range<usize>) -> Option<&str> {
117 self.source.get(span)
118 }
119
120 pub(crate) fn span_to_lines(&self, span: Range<usize>) -> Vec<&LineInfo<'a>> {
121 let mut lines = vec![];
122 let start = span.start;
123 let end = span.end;
124 for line_info in &self.lines {
125 if start >= line_info.end_byte {
126 continue;
127 }
128 if end < line_info.start_byte {
129 break;
130 }
131 lines.push(line_info);
132 }
133 lines
134 }
135
136 pub(crate) fn annotated_lines(
137 &self,
138 annotations: Vec<Annotation<'a>>,
139 fold: bool,
140 ) -> (usize, Vec<AnnotatedLineInfo<'a>>) {
141 let source_len = self.source.len();
142 if let Some(bigger) = annotations.iter().find_map(|x| {
143 if source_len + 1 < x.span.end {
145 Some(&x.span)
146 } else {
147 None
148 }
149 }) {
150 panic!("Annotation range `{bigger:?}` is beyond the end of buffer `{source_len}`")
151 }
152
153 let mut annotated_line_infos = self
154 .lines
155 .iter()
156 .map(|info| AnnotatedLineInfo {
157 line: info.line,
158 line_index: info.line_index,
159 annotations: vec![],
160 keep: false,
161 })
162 .collect::<Vec<_>>();
163 let mut multiline_annotations = vec![];
164
165 for Annotation {
166 span,
167 label,
168 kind,
169 highlight_source,
170 } in annotations
171 {
172 let (lo, mut hi) = self.span_to_locations(span.clone());
173 if kind == AnnotationKind::Visible {
174 for line_idx in lo.line..=hi.line {
175 self.keep_line(&mut annotated_line_infos, line_idx);
176 }
177 continue;
178 }
179 if lo.display == hi.display && lo.line == hi.line {
186 hi.display += 1;
187 }
188
189 if lo.line == hi.line {
190 let line_ann = LineAnnotation {
191 start: lo,
192 end: hi,
193 kind,
194 label,
195 annotation_type: LineAnnotationType::Singleline,
196 highlight_source,
197 };
198 self.add_annotation_to_file(&mut annotated_line_infos, lo.line, line_ann);
199 } else {
200 multiline_annotations.push(MultilineAnnotation {
201 depth: 1,
202 start: lo,
203 end: hi,
204 kind,
205 label,
206 overlaps_exactly: false,
207 highlight_source,
208 });
209 }
210 }
211
212 let mut primary_spans = vec![];
213
214 multiline_annotations.sort_by_key(|ml| (ml.start.line, usize::MAX - ml.end.line));
216 for ann in multiline_annotations.clone() {
217 if ann.kind.is_primary() {
218 primary_spans.push((ann.start, ann.end));
219 }
220 for a in &mut multiline_annotations {
221 if !ann.same_span(a)
224 && num_overlap(ann.start.line, ann.end.line, a.start.line, a.end.line, true)
225 {
226 a.increase_depth();
227 } else if ann.same_span(a) && &ann != a {
228 a.overlaps_exactly = true;
229 } else {
230 if primary_spans
231 .iter()
232 .any(|(s, e)| a.start == *s && a.end == *e)
233 {
234 a.kind = AnnotationKind::Primary;
235 }
236 break;
237 }
238 }
239 }
240
241 let mut max_depth = 0; for ann in &multiline_annotations {
243 max_depth = max(max_depth, ann.depth);
244 }
245 for a in &mut multiline_annotations {
247 a.depth = max_depth - a.depth + 1;
248 }
249 for ann in multiline_annotations {
250 let mut end_ann = ann.as_end();
251 if ann.overlaps_exactly {
252 end_ann.annotation_type = LineAnnotationType::Singleline;
253 } else {
254 self.add_annotation_to_file(
277 &mut annotated_line_infos,
278 ann.start.line,
279 ann.as_start(),
280 );
281 let middle = min(ann.start.line + 4, ann.end.line);
286 let filter = |s: &str| {
290 let s = s.trim();
291 !(s.starts_with("//") && !(s.starts_with("///") || s.starts_with("//!")))
293 && !["", "{", "}", "(", ")", "[", "]"].contains(&s)
295 };
296 let until = (ann.start.line..middle)
297 .rev()
298 .filter_map(|line| self.get_line(line).map(|s| (line + 1, s)))
299 .find(|(_, s)| filter(s))
300 .map_or(ann.start.line, |(line, _)| line);
301 for line in ann.start.line + 1..until {
302 self.add_annotation_to_file(&mut annotated_line_infos, line, ann.as_line());
304 }
305 let line_end = ann.end.line - 1;
306 let end_is_empty = self.get_line(line_end).map_or(false, |s| !filter(s));
307 if middle < line_end && !end_is_empty {
308 self.add_annotation_to_file(&mut annotated_line_infos, line_end, ann.as_line());
309 }
310 }
311 self.add_annotation_to_file(&mut annotated_line_infos, end_ann.end.line, end_ann);
312 }
313
314 if fold {
315 annotated_line_infos.retain(|l| !l.annotations.is_empty() || l.keep);
316 }
317
318 (max_depth, annotated_line_infos)
319 }
320
321 fn add_annotation_to_file(
322 &self,
323 annotated_line_infos: &mut Vec<AnnotatedLineInfo<'a>>,
324 line_index: usize,
325 line_ann: LineAnnotation<'a>,
326 ) {
327 if let Some(line_info) = annotated_line_infos
328 .iter_mut()
329 .find(|line_info| line_info.line_index == line_index)
330 {
331 line_info.annotations.push(line_ann);
332 } else {
333 let info = self
334 .lines
335 .iter()
336 .find(|l| l.line_index == line_index)
337 .unwrap();
338 annotated_line_infos.push(AnnotatedLineInfo {
339 line: info.line,
340 line_index,
341 annotations: vec![line_ann],
342 keep: false,
343 });
344 annotated_line_infos.sort_by_key(|l| l.line_index);
345 }
346 }
347
348 fn keep_line(&self, annotated_line_infos: &mut Vec<AnnotatedLineInfo<'a>>, line_index: usize) {
349 if let Some(line_info) = annotated_line_infos
350 .iter_mut()
351 .find(|line_info| line_info.line_index == line_index)
352 {
353 line_info.keep = true;
354 } else {
355 let info = self
356 .lines
357 .iter()
358 .find(|l| l.line_index == line_index)
359 .unwrap();
360 annotated_line_infos.push(AnnotatedLineInfo {
361 line: info.line,
362 line_index,
363 annotations: vec![],
364 keep: true,
365 });
366 annotated_line_infos.sort_by_key(|l| l.line_index);
367 }
368 }
369
370 pub(crate) fn splice_lines<'b>(
371 &'b self,
372 mut patches: Vec<Patch<'b>>,
373 ) -> Vec<(String, Vec<Patch<'b>>, Vec<Vec<SubstitutionHighlight>>)> {
374 fn push_trailing(
375 buf: &mut String,
376 line_opt: Option<&str>,
377 lo: &Loc,
378 hi_opt: Option<&Loc>,
379 ) -> usize {
380 let mut line_count = 0;
381 let (lo, hi_opt) = (lo.char, hi_opt.map(|hi| hi.char));
384 if let Some(line) = line_opt {
385 if let Some(lo) = line.char_indices().map(|(i, _)| i).nth(lo) {
386 let hi_opt = hi_opt.and_then(|hi| line.char_indices().map(|(i, _)| i).nth(hi));
388 match hi_opt {
389 Some(hi) if hi > lo => {
391 line_count = line[lo..hi].matches('\n').count();
393 buf.push_str(&line[lo..hi]);
394 }
395 Some(_) => (),
396 None => {
398 line_count = line[lo..].matches('\n').count();
400 buf.push_str(&line[lo..]);
401 }
402 }
403 }
404 if hi_opt.is_none() {
406 buf.push('\n');
407 }
408 }
409 line_count
410 }
411
412 let source_len = self.source.len();
413 if let Some(bigger) = patches.iter().find_map(|x| {
414 if source_len + 1 < x.span.end {
416 Some(&x.span)
417 } else {
418 None
419 }
420 }) {
421 panic!("Patch span `{bigger:?}` is beyond the end of buffer `{source_len}`")
422 }
423
424 patches.sort_by_key(|p| p.span.start);
427
428 let Some(lo) = patches.iter().map(|p| p.span.start).min() else {
430 return Vec::new();
431 };
432 let Some(hi) = patches.iter().map(|p| p.span.end).max() else {
433 return Vec::new();
434 };
435
436 let lines = self.span_to_lines(lo..hi);
437
438 let mut highlights = vec![];
439 let (mut prev_hi, _) = self.span_to_locations(lo..hi);
449 prev_hi.char = 0;
450 let mut prev_line = lines.first().map(|line| line.line);
451 let mut buf = String::new();
452
453 let mut line_highlight = vec![];
454 let mut acc = 0;
457 for part in &mut patches {
458 part.trim_trivial_replacements(self);
462 let (cur_lo, cur_hi) = self.span_to_locations(part.span.clone());
463 if prev_hi.line == cur_lo.line {
464 let mut count = push_trailing(&mut buf, prev_line, &prev_hi, Some(&cur_lo));
465 while count > 0 {
466 highlights.push(std::mem::take(&mut line_highlight));
467 acc = 0;
468 count -= 1;
469 }
470 } else {
471 acc = 0;
472 highlights.push(std::mem::take(&mut line_highlight));
473 let mut count = push_trailing(&mut buf, prev_line, &prev_hi, None);
474 while count > 0 {
475 highlights.push(std::mem::take(&mut line_highlight));
476 count -= 1;
477 }
478 for idx in prev_hi.line + 1..(cur_lo.line) {
480 if let Some(line) = self.get_line(idx) {
481 buf.push_str(line.as_ref());
482 buf.push('\n');
483 highlights.push(std::mem::take(&mut line_highlight));
484 }
485 }
486 if let Some(cur_line) = self.get_line(cur_lo.line) {
487 let end = match cur_line.char_indices().nth(cur_lo.char) {
488 Some((i, _)) => i,
489 None => cur_line.len(),
490 };
491 buf.push_str(&cur_line[..end]);
492 }
493 }
494 let len: isize = part
496 .replacement
497 .split('\n')
498 .next()
499 .unwrap_or(&part.replacement)
500 .chars()
501 .map(|c| match c {
502 '\t' => 4,
503 _ => 1,
504 })
505 .sum();
506 line_highlight.push(SubstitutionHighlight {
507 start: (cur_lo.char as isize + acc) as usize,
508 end: (cur_lo.char as isize + acc + len) as usize,
509 });
510 buf.push_str(&part.replacement);
511 acc += len - (cur_hi.char as isize - cur_lo.char as isize);
516 prev_hi = cur_hi;
517 prev_line = self.get_line(prev_hi.line);
518 for line in part.replacement.split('\n').skip(1) {
519 acc = 0;
520 highlights.push(std::mem::take(&mut line_highlight));
521 let end: usize = line
522 .chars()
523 .map(|c| match c {
524 '\t' => 4,
525 _ => 1,
526 })
527 .sum();
528 line_highlight.push(SubstitutionHighlight { start: 0, end });
529 }
530 }
531 highlights.push(std::mem::take(&mut line_highlight));
532 if !buf.ends_with('\n') {
534 push_trailing(&mut buf, prev_line, &prev_hi, None);
535 }
536 while buf.ends_with('\n') {
538 buf.pop();
539 }
540 if highlights.iter().all(|parts| parts.is_empty()) {
541 Vec::new()
542 } else {
543 vec![(buf, patches, highlights)]
544 }
545 }
546}
547
548#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
549pub(crate) struct MultilineAnnotation<'a> {
550 pub depth: usize,
551 pub start: Loc,
552 pub end: Loc,
553 pub kind: AnnotationKind,
554 pub label: Option<Cow<'a, str>>,
555 pub overlaps_exactly: bool,
556 pub highlight_source: bool,
557}
558
559impl<'a> MultilineAnnotation<'a> {
560 pub(crate) fn increase_depth(&mut self) {
561 self.depth += 1;
562 }
563
564 pub(crate) fn same_span(&self, other: &MultilineAnnotation<'_>) -> bool {
566 self.start == other.start && self.end == other.end
567 }
568
569 pub(crate) fn as_start(&self) -> LineAnnotation<'a> {
570 LineAnnotation {
571 start: self.start,
572 end: Loc {
573 line: self.start.line,
574 char: self.start.char + 1,
575 display: self.start.display + 1,
576 byte: self.start.byte + 1,
577 },
578 kind: self.kind,
579 label: None,
580 annotation_type: LineAnnotationType::MultilineStart(self.depth),
581 highlight_source: self.highlight_source,
582 }
583 }
584
585 pub(crate) fn as_end(&self) -> LineAnnotation<'a> {
586 LineAnnotation {
587 start: Loc {
588 line: self.end.line,
589 char: self.end.char.saturating_sub(1),
590 display: self.end.display.saturating_sub(1),
591 byte: self.end.byte.saturating_sub(1),
592 },
593 end: self.end,
594 kind: self.kind,
595 label: self.label.clone(),
596 annotation_type: LineAnnotationType::MultilineEnd(self.depth),
597 highlight_source: self.highlight_source,
598 }
599 }
600
601 pub(crate) fn as_line(&self) -> LineAnnotation<'a> {
602 LineAnnotation {
603 start: Loc::default(),
604 end: Loc::default(),
605 kind: self.kind,
606 label: None,
607 annotation_type: LineAnnotationType::MultilineLine(self.depth),
608 highlight_source: self.highlight_source,
609 }
610 }
611}
612
613#[derive(Debug)]
614pub(crate) struct LineInfo<'a> {
615 pub(crate) line: &'a str,
616 pub(crate) line_index: usize,
617 pub(crate) start_byte: usize,
618 pub(crate) end_byte: usize,
619 end_line_size: usize,
620}
621
622#[derive(Debug)]
623pub(crate) struct AnnotatedLineInfo<'a> {
624 pub(crate) line: &'a str,
625 pub(crate) line_index: usize,
626 pub(crate) annotations: Vec<LineAnnotation<'a>>,
627 pub(crate) keep: bool,
628}
629
630#[derive(Clone, Copy, Debug, Default, PartialOrd, Ord, PartialEq, Eq)]
632pub(crate) struct Loc {
633 pub(crate) line: usize,
635 pub(crate) char: usize,
637 pub(crate) display: usize,
639 pub(crate) byte: usize,
641}
642
643struct CursorLines<'a>(&'a str);
644
645impl CursorLines<'_> {
646 fn new(src: &str) -> CursorLines<'_> {
647 CursorLines(src)
648 }
649}
650
651#[derive(Copy, Clone, Debug, PartialEq)]
652enum EndLine {
653 Eof,
654 Lf,
655 Crlf,
656}
657
658impl EndLine {
659 pub(crate) fn len(self) -> usize {
661 match self {
662 EndLine::Eof => 0,
663 EndLine::Lf => 1,
664 EndLine::Crlf => 2,
665 }
666 }
667}
668
669impl<'a> Iterator for CursorLines<'a> {
670 type Item = (&'a str, EndLine);
671
672 fn next(&mut self) -> Option<Self::Item> {
673 if self.0.is_empty() {
674 None
675 } else {
676 self.0
677 .find('\n')
678 .map(|x| {
679 let ret = if 0 < x {
680 if self.0.as_bytes()[x - 1] == b'\r' {
681 (&self.0[..x - 1], EndLine::Crlf)
682 } else {
683 (&self.0[..x], EndLine::Lf)
684 }
685 } else {
686 ("", EndLine::Lf)
687 };
688 self.0 = &self.0[x + 1..];
689 ret
690 })
691 .or_else(|| {
692 let ret = Some((self.0, EndLine::Eof));
693 self.0 = "";
694 ret
695 })
696 }
697 }
698}
699
700#[derive(Debug, Clone, Copy)]
703pub(crate) struct SubstitutionHighlight {
704 pub(crate) start: usize,
705 pub(crate) end: usize,
706}