kind_report/report/
code.rs1use fxhash::{FxHashMap, FxHashSet};
2use kind_span::{Pos, SyntaxCtxIndex};
3use std::{collections::hash_map::Iter, fmt::Display};
4use unicode_width::UnicodeWidthStr;
5
6use crate::data::Marker;
7
8pub struct LineGuide(Vec<usize>);
11
12pub struct FileMarkers(pub Vec<Marker>);
13
14pub struct SortedMarkers(FxHashMap<SyntaxCtxIndex, FileMarkers>);
17
18impl SortedMarkers {
19 pub fn is_empty(&self) -> bool {
20 self.0.is_empty()
21 }
22
23 pub fn iter(&self) -> Iter<SyntaxCtxIndex, FileMarkers> {
24 self.0.iter()
25 }
26}
27
28#[derive(Clone, Copy)]
29pub struct Point {
30 pub line: usize,
31 pub column: usize,
32}
33
34impl Display for Point {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 write!(f, "{}:{}", self.line + 1, self.column + 1)
37 }
38}
39
40pub struct Spaces {
41 pub width: usize,
42 pub tabs: usize,
43}
44
45impl LineGuide {
46 pub fn get(code: &str) -> LineGuide {
47 let mut guide = Vec::new();
48 let mut size = 0;
49 for chr in code.chars() {
50 size += chr.len_utf8();
51 if chr == '\n' {
52 guide.push(size);
53 }
54 }
55 guide.push(code.len());
56 LineGuide(guide)
57 }
58
59 pub fn find(&self, pos: Pos) -> Point {
60 for i in 0..self.0.len() {
61 if self.0[i] > pos.index as usize {
62 return Point {
63 line: i,
64 column: pos.index as usize - (if i == 0 { 0 } else { self.0[i - 1] }),
65 };
66 }
67 }
68 let line = self.0.len() - 1;
69 Point {
70 line,
71 column: pos.index as usize - (if line == 0 { 0 } else { self.0[line - 1] }),
72 }
73 }
74
75 pub fn len(&self) -> usize {
76 self.0.len()
77 }
78}
79
80pub fn count_width(str: &str) -> Spaces {
81 Spaces {
82 width: UnicodeWidthStr::width(str),
83 tabs: str.chars().filter(|x| *x == '\t').count(),
84 }
85}
86
87pub fn group_markers(markers: &[Marker]) -> SortedMarkers {
88 let mut file_group = FxHashMap::default();
89
90 for marker in markers {
91 let group = file_group
92 .entry(marker.position.ctx)
93 .or_insert_with(Vec::new);
94 group.push(marker.clone())
95 }
96
97 for group in file_group.values_mut() {
98 group.sort_by(|x, y| x.position.start.cmp(&y.position.end));
99 }
100
101 SortedMarkers(
102 file_group
103 .into_iter()
104 .map(|(x, y)| (x, FileMarkers(y)))
105 .collect(),
106 )
107}
108
109pub fn group_marker_lines<'a>(
110 guide: &'a LineGuide,
111 markers: &'a FileMarkers,
112) -> (
113 FxHashSet<usize>,
114 FxHashMap<usize, Vec<(Point, Point, &'a Marker)>>,
115 Vec<(Point, Point, &'a Marker)>,
116) {
117 let mut lines_set = FxHashSet::default();
118 let mut markers_by_line: FxHashMap<usize, Vec<(Point, Point, &Marker)>> = FxHashMap::default();
119 let mut multi_line_markers: Vec<(Point, Point, &Marker)> = Vec::new();
120
121 for marker in &markers.0 {
122 let start = guide.find(marker.position.start);
123 let end = guide.find(marker.position.end);
124
125 if let Some(row) = markers_by_line.get_mut(&start.line) {
126 row.push((start, end, marker))
127 } else {
128 markers_by_line.insert(start.line, vec![(start, end, marker)]);
129 }
130
131 if end.line != start.line {
132 multi_line_markers.push((start, end, marker));
133 } else if marker.main {
134 let start = start.line.saturating_sub(1);
136 let end = if start + 2 >= guide.len() {
137 guide.len() - 1
138 } else {
139 start + 2
140 };
141 for i in start..=end {
142 lines_set.insert(i);
143 }
144 }
145
146 if end.line - start.line <= 3 {
147 for i in start.line..=end.line {
148 lines_set.insert(i);
149 }
150 } else {
151 lines_set.insert(start.line);
152 lines_set.insert(end.line);
153 }
154 }
155
156 (lines_set, markers_by_line, multi_line_markers)
157}