oak_rust/lsp/highlighter/
mod.rs1#![doc = include_str!("readme.md")]
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum HighlightKind {
6 Keyword,
8 String,
10 Number,
12 Comment,
14 Macro,
16 Identifier,
18}
19
20pub trait Highlighter {
22 fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)>;
24}
25
26pub struct RustHighlighter {
30 pub use_parser: bool,
32}
33
34impl Default for RustHighlighter {
35 fn default() -> Self {
36 Self { use_parser: false }
37 }
38}
39
40impl RustHighlighter {
41 pub fn new() -> Self {
43 Self::default()
44 }
45
46 pub fn with_parser() -> Self {
48 Self { use_parser: true }
49 }
50
51 fn highlight_keywords(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
53 let mut highlights = Vec::new();
54 let keywords = [
55 "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn", "for", "if", "impl", "in", "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super", "trait",
56 "true", "type", "unsafe", "use", "where", "while", "async", "await", "dyn", "abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", "unsized", "virtual", "yield", "try", "union", "raw",
57 ];
58
59 for keyword in &keywords {
60 let mut start = 0;
61 while let Some(pos) = text[start..].find(keyword) {
62 let absolute_pos = start + pos;
63 let end_pos = absolute_pos + keyword.len();
64
65 let is_word_boundary_before = absolute_pos == 0 || !text.chars().nth(absolute_pos - 1).unwrap_or(' ').is_alphanumeric();
67 let is_word_boundary_after = end_pos >= text.len() || !text.chars().nth(end_pos).unwrap_or(' ').is_alphanumeric();
68
69 if is_word_boundary_before && is_word_boundary_after {
70 highlights.push((absolute_pos, end_pos, HighlightKind::Keyword))
71 }
72
73 start = absolute_pos + 1
74 }
75 }
76
77 highlights
78 }
79
80 fn highlight_strings(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
82 let mut highlights = Vec::new();
83 let mut chars = text.char_indices().peekable();
84
85 while let Some((i, ch)) = chars.next() {
86 match ch {
87 '"' => {
88 let start = i;
89 let mut end = i + 1;
90 let mut escaped = false;
91
92 while let Some((j, next_ch)) = chars.next() {
93 end = j + next_ch.len_utf8();
94 if escaped {
95 escaped = false;
96 }
97 else if next_ch == '\\' {
98 escaped = true;
99 }
100 else if next_ch == '"' {
101 break;
102 }
103 }
104
105 highlights.push((start, end, HighlightKind::String))
106 }
107 '\'' => {
108 let start = i;
109 let mut end = i + 1;
110 let mut escaped = false;
111
112 while let Some((j, next_ch)) = chars.next() {
113 end = j + next_ch.len_utf8();
114 if escaped {
115 escaped = false;
116 }
117 else if next_ch == '\\' {
118 escaped = true;
119 }
120 else if next_ch == '\'' {
121 break;
122 }
123 }
124
125 highlights.push((start, end, HighlightKind::String))
126 }
127 _ => {}
128 }
129 }
130
131 highlights
132 }
133
134 fn highlight_numbers(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
136 let mut highlights = Vec::new();
137 let mut chars = text.char_indices().peekable();
138
139 while let Some((i, ch)) = chars.next() {
140 if ch.is_ascii_digit() {
141 let start = i;
142 let mut end = i + 1;
143
144 while let Some(&(j, next_ch)) = chars.peek() {
146 if next_ch.is_ascii_digit() || next_ch == '.' || next_ch == '_' {
147 end = j + next_ch.len_utf8();
148 chars.next();
149 }
150 else {
151 break;
152 }
153 }
154
155 highlights.push((start, end, HighlightKind::Number))
156 }
157 }
158
159 highlights
160 }
161
162 fn highlight_comments(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
164 let mut highlights = Vec::new();
165 let lines: Vec<&str> = text.lines().collect();
166 let mut pos = 0;
167
168 for line in lines {
169 if let Some(comment_start) = line.find("//") {
170 let start = pos + comment_start;
171 let end = pos + line.len();
172 highlights.push((start, end, HighlightKind::Comment))
173 }
174 pos += line.len() + 1; }
176
177 highlights
178 }
179}
180
181impl Highlighter for RustHighlighter {
182 fn highlight(&self, text: &str) -> Vec<(usize, usize, HighlightKind)> {
183 let mut highlights = Vec::new();
184
185 highlights.extend(self.highlight_keywords(text));
186 highlights.extend(self.highlight_strings(text));
187 highlights.extend(self.highlight_numbers(text));
188 highlights.extend(self.highlight_comments(text));
189
190 highlights.sort_by_key(|&(start, _, _)| start);
192 highlights
193 }
194}