1use crate::config::CompletionType;
4use std::borrow::Cow::{self, Borrowed, Owned};
5use std::cell::Cell;
6
7pub trait Highlighter {
14 fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
20 let _ = pos;
21 Borrowed(line)
22 }
23 fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
26 &'s self,
27 prompt: &'p str,
28 default: bool,
29 ) -> Cow<'b, str> {
30 let _ = default;
31 Borrowed(prompt)
32 }
33 fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
36 Borrowed(hint)
37 }
38 fn highlight_candidate<'c>(
43 &self,
44 candidate: &'c str, completion: CompletionType,
46 ) -> Cow<'c, str> {
47 let _ = completion;
48 Borrowed(candidate)
49 }
50 fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool {
58 let _ = (line, pos, forced);
59 false
60 }
61}
62
63impl Highlighter for () {}
64
65impl<'r, H: ?Sized + Highlighter> Highlighter for &'r H {
66 fn highlight<'l>(&self, line: &'l str, pos: usize) -> Cow<'l, str> {
67 (**self).highlight(line, pos)
68 }
69
70 fn highlight_prompt<'b, 's: 'b, 'p: 'b>(
71 &'s self,
72 prompt: &'p str,
73 default: bool,
74 ) -> Cow<'b, str> {
75 (**self).highlight_prompt(prompt, default)
76 }
77
78 fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
79 (**self).highlight_hint(hint)
80 }
81
82 fn highlight_candidate<'c>(
83 &self,
84 candidate: &'c str,
85 completion: CompletionType,
86 ) -> Cow<'c, str> {
87 (**self).highlight_candidate(candidate, completion)
88 }
89
90 fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool {
91 (**self).highlight_char(line, pos, forced)
92 }
93}
94
95#[derive(Default)]
99pub struct MatchingBracketHighlighter {
100 bracket: Cell<Option<(u8, usize)>>, }
102
103impl MatchingBracketHighlighter {
104 #[must_use]
106 pub fn new() -> Self {
107 Self {
108 bracket: Cell::new(None),
109 }
110 }
111}
112
113impl Highlighter for MatchingBracketHighlighter {
114 fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
115 if line.len() <= 1 {
116 return Borrowed(line);
117 }
118 if let Some((bracket, pos)) = self.bracket.get() {
120 if let Some((matching, idx)) = find_matching_bracket(line, pos, bracket) {
121 let mut copy = line.to_owned();
122 copy.replace_range(idx..=idx, &format!("\x1b[1;34m{}\x1b[0m", matching as char));
123 return Owned(copy);
124 }
125 }
126 Borrowed(line)
127 }
128
129 fn highlight_char(&self, line: &str, pos: usize, forced: bool) -> bool {
130 if forced {
131 self.bracket.set(None);
132 return false;
133 }
134 self.bracket.set(check_bracket(line, pos));
136 self.bracket.get().is_some()
137 }
138}
139
140fn find_matching_bracket(line: &str, pos: usize, bracket: u8) -> Option<(u8, usize)> {
141 let matching = matching_bracket(bracket);
142 let mut idx;
143 let mut unmatched = 1;
144 if is_open_bracket(bracket) {
145 idx = pos + 1;
147 let bytes = &line.as_bytes()[idx..];
148 for b in bytes {
149 if *b == matching {
150 unmatched -= 1;
151 if unmatched == 0 {
152 debug_assert_eq!(matching, line.as_bytes()[idx]);
153 return Some((matching, idx));
154 }
155 } else if *b == bracket {
156 unmatched += 1;
157 }
158 idx += 1;
159 }
160 debug_assert_eq!(idx, line.len());
161 } else {
162 idx = pos;
164 let bytes = &line.as_bytes()[..idx];
165 for b in bytes.iter().rev() {
166 if *b == matching {
167 unmatched -= 1;
168 if unmatched == 0 {
169 debug_assert_eq!(matching, line.as_bytes()[idx - 1]);
170 return Some((matching, idx - 1));
171 }
172 } else if *b == bracket {
173 unmatched += 1;
174 }
175 idx -= 1;
176 }
177 debug_assert_eq!(idx, 0);
178 }
179 None
180}
181
182fn check_bracket(line: &str, pos: usize) -> Option<(u8, usize)> {
184 if line.is_empty() {
185 return None;
186 }
187 let mut pos = pos;
188 if pos >= line.len() {
189 pos = line.len() - 1; let b = line.as_bytes()[pos]; if is_close_bracket(b) {
192 Some((b, pos))
193 } else {
194 None
195 }
196 } else {
197 let mut under_cursor = true;
198 loop {
199 let b = line.as_bytes()[pos];
200 if is_close_bracket(b) {
201 return if pos == 0 { None } else { Some((b, pos)) };
202 } else if is_open_bracket(b) {
203 return if pos + 1 == line.len() {
204 None
205 } else {
206 Some((b, pos))
207 };
208 } else if under_cursor && pos > 0 {
209 under_cursor = false;
210 pos -= 1; } else {
212 return None;
213 }
214 }
215 }
216}
217
218const fn matching_bracket(bracket: u8) -> u8 {
219 match bracket {
220 b'{' => b'}',
221 b'}' => b'{',
222 b'[' => b']',
223 b']' => b'[',
224 b'(' => b')',
225 b')' => b'(',
226 b => b,
227 }
228}
229const fn is_open_bracket(bracket: u8) -> bool {
230 matches!(bracket, b'{' | b'[' | b'(')
231}
232const fn is_close_bracket(bracket: u8) -> bool {
233 matches!(bracket, b'}' | b']' | b')')
234}
235
236#[cfg(test)]
237mod tests {
238 #[test]
239 pub fn find_matching_bracket() {
240 use super::find_matching_bracket;
241 assert_eq!(find_matching_bracket("(...", 0, b'('), None);
242 assert_eq!(find_matching_bracket("...)", 3, b')'), None);
243
244 assert_eq!(find_matching_bracket("()..", 0, b'('), Some((b')', 1)));
245 assert_eq!(find_matching_bracket("(..)", 0, b'('), Some((b')', 3)));
246
247 assert_eq!(find_matching_bracket("..()", 3, b')'), Some((b'(', 2)));
248 assert_eq!(find_matching_bracket("(..)", 3, b')'), Some((b'(', 0)));
249
250 assert_eq!(find_matching_bracket("(())", 0, b'('), Some((b')', 3)));
251 assert_eq!(find_matching_bracket("(())", 3, b')'), Some((b'(', 0)));
252 }
253 #[test]
254 pub fn check_bracket() {
255 use super::check_bracket;
256 assert_eq!(check_bracket(")...", 0), None);
257 assert_eq!(check_bracket("(...", 2), None);
258 assert_eq!(check_bracket("...(", 3), None);
259 assert_eq!(check_bracket("...(", 4), None);
260 assert_eq!(check_bracket("..).", 4), None);
261
262 assert_eq!(check_bracket("(...", 0), Some((b'(', 0)));
263 assert_eq!(check_bracket("(...", 1), Some((b'(', 0)));
264 assert_eq!(check_bracket("...)", 3), Some((b')', 3)));
265 assert_eq!(check_bracket("...)", 4), Some((b')', 3)));
266 }
267 #[test]
268 pub fn matching_bracket() {
269 use super::matching_bracket;
270 assert_eq!(matching_bracket(b'('), b')');
271 assert_eq!(matching_bracket(b')'), b'(');
272 }
273
274 #[test]
275 pub fn is_open_bracket() {
276 use super::is_close_bracket;
277 use super::is_open_bracket;
278 assert!(is_open_bracket(b'('));
279 assert!(is_close_bracket(b')'));
280 }
281}