diff --git a/tags/src/lib.rs b/tags/src/lib.rs index 41b4557a..ca5699ca 100644 --- a/tags/src/lib.rs +++ b/tags/src/lib.rs @@ -91,6 +91,7 @@ where matches: I, _tree: Tree, source: &'a [u8], + prev_line_info: Option, config: &'a TagsConfiguration, cancellation_flag: Option<&'a AtomicUsize>, iter_count: usize, @@ -98,6 +99,13 @@ where scopes: Vec>, } +struct LineInfo { + utf8_position: Point, + utf8_byte: usize, + utf16_column: usize, + line_range: Range, +} + impl TagsConfiguration { pub fn new(language: Language, tags_query: &str, locals_query: &str) -> Result { let query = Query::new(language, &format!("{}{}", locals_query, tags_query))?; @@ -260,6 +268,7 @@ impl TagsContext { source, config, cancellation_flag, + prev_line_info: None, tag_queue: Vec::new(), iter_count: 0, scopes: vec![LocalScope { @@ -425,10 +434,46 @@ where let range = tag_node.byte_range(); let span = name_node.start_position()..name_node.end_position(); - let utf16_column_range = - get_utf16_column_range(self.source, &name_range, &span); - let line_range = - line_range(self.source, name_range.start, span.start, MAX_LINE_LEN); + + // Compute tag properties that depend on the text of the containing line. If the + // previous tag occurred on the same line, then reuse results from the previous tag. + let line_range; + let mut prev_utf16_column = 0; + let mut prev_utf8_byte = name_range.start - span.start.column; + let line_info = self.prev_line_info.as_ref().and_then(|info| { + if info.utf8_position.row == span.start.row { + Some(info) + } else { + None + } + }); + if let Some(line_info) = line_info { + line_range = line_info.line_range.clone(); + if line_info.utf8_position.column <= span.start.column { + prev_utf8_byte = line_info.utf8_byte; + prev_utf16_column = line_info.utf16_column; + } + } else { + line_range = self::line_range( + self.source, + name_range.start, + span.start, + MAX_LINE_LEN, + ); + } + + let utf16_start_column = prev_utf16_column + + utf16_len(&self.source[prev_utf8_byte..name_range.start]); + let utf16_end_column = + utf16_start_column + utf16_len(&self.source[name_range.clone()]); + let utf16_column_range = utf16_start_column..utf16_end_column; + + self.prev_line_info = Some(LineInfo { + utf8_position: span.end, + utf8_byte: name_range.end, + utf16_column: utf16_end_column, + line_range: line_range.clone(), + }); let tag = Tag { line_range, span, @@ -570,17 +615,6 @@ fn line_range( line_start_byte..line_end_byte } -fn get_utf16_column_range( - text: &[u8], - byte_range: &Range, - point_range: &Range, -) -> Range { - let line_start_byte = byte_range.start - point_range.start.column; - let preceding_text_on_line = &text[line_start_byte..byte_range.start]; - let start_col = utf16_len(preceding_text_on_line); - start_col..(start_col + utf16_len(&text[byte_range.clone()])) -} - fn utf16_len(bytes: &[u8]) -> usize { LossyUtf8::new(bytes) .flat_map(|chunk| chunk.chars().map(char::len_utf16))