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