From d5b880dd9e85af0b469e5c6c4cb46d4f42972975 Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Tue, 16 Mar 2021 11:39:13 -0400 Subject: [PATCH] Massive refactor of highlighting logic --- src/filetype.rs | 2 +- src/row.rs | 238 +++++++++++++++++++++++++++--------------------- 2 files changed, 135 insertions(+), 105 deletions(-) diff --git a/src/filetype.rs b/src/filetype.rs index 9e0a21d..fb9ce0e 100644 --- a/src/filetype.rs +++ b/src/filetype.rs @@ -62,4 +62,4 @@ impl HighlightingOptions { pub fn comments(self) -> bool { self.comments } -} \ No newline at end of file +} diff --git a/src/row.rs b/src/row.rs index aa7e2a8..78fd646 100644 --- a/src/row.rs +++ b/src/row.rs @@ -190,134 +190,164 @@ impl Row { None } - pub fn highlight(&mut self, opts: HighlightingOptions, word: Option<&str>) { - let mut highlighting = Vec::new(); - let chars: Vec = self.string.chars().collect(); - let mut matches = Vec::new(); - let mut search_index = 0; - + fn highlight_match(&mut self, word: Option<&str>) { if let Some(word) = word { - while let Some(search_match) = self.find(word, search_index, SearchDirection::Forward) { - matches.push(search_match); + if word.is_empty() { + 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()) { - 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 { 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); + fn highlight_char( + &mut self, + index: &mut usize, + opts: HighlightingOptions, + c: char, + chars: &[char], + ) -> bool { + if opts.characters() && c == '\'' { + if let Some(next_char) = chars.get(index.saturating_add(1)) { + let closing_index = if *next_char == '\\' { + index.saturating_add(3) + } else { + index.saturating_add(2) + }; + + if let Some(closing_char) = chars.get(closing_index) { + if *closing_char == '\'' { + for _ in 0..=closing_index.saturating_sub(*index) { + self.highlighting.push(highlighting::Type::Character); + *index += 1; + } + + return true; + } + } + } + } + + false + } + + fn highlight_comment( + &mut self, + index: &mut usize, + opts: HighlightingOptions, + c: char, + chars: &[char], + ) -> bool { + if opts.comments() && c == '/' && *index < chars.len() { + if let Some(next_char) = chars.get(index.saturating_add(1)) { + if *next_char == '/' { + for _ in *index..chars.len() { + self.highlighting.push(highlighting::Type::Comment); + *index += 1; } - continue; + return true; + } + }; + } + + false + } + + 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; } } - let previous_highlight = if index > 0 { - highlighting - .get(index - 1) - .unwrap_or(&highlighting::Type::None) - } else { - &highlighting::Type::None - }; + self.highlighting.push(highlighting::Type::String); + *index += 1; + return true; + } - if opts.characters() && !in_string && *c == '\'' { - prev_is_separator = true; + false + } - if let Some(next_char) = chars.get(index.saturating_add(1)) { - let closing_index = if *next_char == '\\' { - index.saturating_add(3) - } else { - index.saturating_add(2) - }; - - if let Some(closing_char) = chars.get(closing_index) { - if *closing_char == '\'' { - for _ in 0..=closing_index.saturating_sub(index) { - highlighting.push(highlighting::Type::Character); - index += 1; - } - - continue; - } - } + 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; } + } - highlighting.push(highlighting::Type::None); - index += 1; + 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 = 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; } - if opts.strings() { - if in_string { - highlighting.push(highlighting::Type::String); - - // Don't let an escaped character stop string highlighting - if *c == '\\' && index < self.len().saturating_sub(1) { - highlighting.push(highlighting::Type::String); - index += 2; - 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 *next_char == '/' { - for _ in index..chars.len() { - highlighting.push(highlighting::Type::Comment); - } - - break; - } - }; - } - - if opts.numbers() { - 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(); + self.highlighting.push(highlighting::Type::None); index += 1; } - self.highlighting = highlighting; + self.highlight_match(word); } }