Massive refactor of highlighting logic

This commit is contained in:
Timothy Warren 2021-03-16 11:39:13 -04:00
parent aa849373ad
commit d5b880dd9e
2 changed files with 135 additions and 105 deletions

View File

@ -190,51 +190,37 @@ impl Row {
None None
} }
pub fn highlight(&mut self, opts: HighlightingOptions, word: Option<&str>) { fn highlight_match(&mut self, word: Option<&str>) {
let mut highlighting = Vec::new();
let chars: Vec<char> = self.string.chars().collect();
let mut matches = Vec::new();
let mut search_index = 0;
if let Some(word) = word { if let Some(word) = word {
while let Some(search_match) = self.find(word, search_index, SearchDirection::Forward) { if word.is_empty() {
matches.push(search_match); return;
}
let mut index = 0;
while let Some(search_match) = self.find(word, index, SearchDirection::Forward) {
if let Some(next_index) = search_match.checked_add(word[..].graphemes(true).count()) if let Some(next_index) = search_match.checked_add(word[..].graphemes(true).count())
{ {
search_index = next_index #[allow(clippy::indexing_slicing)]
for i in index.saturating_add(search_match)..next_index {
self.highlighting[i] = highlighting::Type::Match;
}
index = next_index;
} else { } else {
break; break;
} }
} }
} }
let mut prev_is_separator = true;
let mut in_string = false;
let mut index = 0;
while let Some(c) = chars.get(index) {
if let Some(word) = word {
if matches.contains(&index) {
for _ in word[..].graphemes(true) {
index += 1;
highlighting.push(highlighting::Type::Match);
} }
continue; fn highlight_char(
} &mut self,
} index: &mut usize,
opts: HighlightingOptions,
let previous_highlight = if index > 0 { c: char,
highlighting chars: &[char],
.get(index - 1) ) -> bool {
.unwrap_or(&highlighting::Type::None) if opts.characters() && c == '\'' {
} else {
&highlighting::Type::None
};
if opts.characters() && !in_string && *c == '\'' {
prev_is_separator = true;
if let Some(next_char) = chars.get(index.saturating_add(1)) { if let Some(next_char) = chars.get(index.saturating_add(1)) {
let closing_index = if *next_char == '\\' { let closing_index = if *next_char == '\\' {
index.saturating_add(3) index.saturating_add(3)
@ -244,80 +230,124 @@ impl Row {
if let Some(closing_char) = chars.get(closing_index) { if let Some(closing_char) = chars.get(closing_index) {
if *closing_char == '\'' { if *closing_char == '\'' {
for _ in 0..=closing_index.saturating_sub(index) { for _ in 0..=closing_index.saturating_sub(*index) {
highlighting.push(highlighting::Type::Character); self.highlighting.push(highlighting::Type::Character);
index += 1; *index += 1;
} }
continue; return true;
}
} }
} }
} }
highlighting.push(highlighting::Type::None); false
index += 1;
continue;
} }
if opts.strings() { fn highlight_comment(
if in_string { &mut self,
highlighting.push(highlighting::Type::String); index: &mut usize,
opts: HighlightingOptions,
// Don't let an escaped character stop string highlighting c: char,
if *c == '\\' && index < self.len().saturating_sub(1) { chars: &[char],
highlighting.push(highlighting::Type::String); ) -> bool {
index += 2; if opts.comments() && c == '/' && *index < chars.len() {
continue;
}
if *c == '"' {
in_string = false;
prev_is_separator = true;
} else {
prev_is_separator = false;
}
index += 1;
continue;
} else if prev_is_separator && *c == '"' {
highlighting.push(highlighting::Type::String);
in_string = true;
prev_is_separator = true;
index += 1;
continue;
}
}
if opts.comments() && *c == '/' {
if let Some(next_char) = chars.get(index.saturating_add(1)) { if let Some(next_char) = chars.get(index.saturating_add(1)) {
if *next_char == '/' { if *next_char == '/' {
for _ in index..chars.len() { for _ in *index..chars.len() {
highlighting.push(highlighting::Type::Comment); self.highlighting.push(highlighting::Type::Comment);
*index += 1;
} }
break; return true;
} }
}; };
} }
if opts.numbers() { false
if (c.is_ascii_digit()
&& (prev_is_separator || *previous_highlight == highlighting::Type::Number))
|| (*c == '.' && *previous_highlight == highlighting::Type::Number)
{
highlighting.push(highlighting::Type::Number)
} else {
highlighting.push(highlighting::Type::None)
}
} else {
highlighting.push(highlighting::Type::None)
} }
prev_is_separator = c.is_ascii_punctuation() || c.is_ascii_whitespace(); fn highlight_string(
&mut self,
index: &mut usize,
opts: HighlightingOptions,
c: char,
chars: &[char],
) -> bool {
if opts.strings() && c == '"' {
loop {
self.highlighting.push(highlighting::Type::String);
*index += 1;
if let Some(next_char) = chars.get(*index) {
if *next_char == '"' {
break;
}
} else {
break;
}
}
self.highlighting.push(highlighting::Type::String);
*index += 1;
return true;
}
false
}
fn highlight_number(
&mut self,
index: &mut usize,
opts: HighlightingOptions,
c: char,
chars: &[char],
) -> bool {
if opts.numbers() && c.is_ascii_digit() {
if *index > 0 {
#[allow(clippy::indexing_slicing, clippy::integer_arithmetic)]
let prev_char = chars[*index - 1];
if !prev_char.is_ascii_punctuation() && !prev_char.is_ascii_whitespace() {
return false;
}
}
loop {
self.highlighting.push(highlighting::Type::Number);
*index += 1;
if let Some(next_char) = chars.get(*index) {
if *next_char != '.' && !next_char.is_ascii_digit() {
break;
}
} else {
break;
}
}
return true;
}
false
}
pub fn highlight(&mut self, opts: HighlightingOptions, word: Option<&str>) {
self.highlighting = Vec::new();
let chars: Vec<char> = self.string.chars().collect();
let mut index = 0;
while let Some(c) = chars.get(index) {
if self.highlight_char(&mut index, opts, *c, &chars)
|| self.highlight_comment(&mut index, opts, *c, &chars)
|| self.highlight_string(&mut index, opts, *c, &chars)
|| self.highlight_number(&mut index, opts, *c, &chars)
{
continue;
}
self.highlighting.push(highlighting::Type::None);
index += 1; index += 1;
} }
self.highlighting = highlighting; self.highlight_match(word);
} }
} }