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