Skip to content

Commit

Permalink
add tree sitter match limit to avoid slowdowns for larger files
Browse files Browse the repository at this point in the history
Affects all tree sitter queries and should speedup both
syntax highlighting and text object queries.
This has been shown to fix significant slowdowns with textobjects
for rust files as small as 3k loc.
  • Loading branch information
pascalkuthe committed Nov 10, 2022
1 parent 758bace commit dac3df4
Showing 1 changed file with 21 additions and 0 deletions.
21 changes: 21 additions & 0 deletions helix-core/src/syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,23 @@ impl<'a> CapturedNode<'a> {
}
}

/// The number of matches a TS cursor can at once to avoid performance problems for medium to large files.
/// Set with `set_match_limit`.
/// Using such a limit means that we loose valid captures in so there is fundementally a tradeoff here.
///
/// Old tree sitter versions used a limit of 32 by default until this limit was removed in version `0.19.5` (must now best set manually).
/// However this causes performance issues for medium to large files.
/// In helix this problem caused textobjt motions to take multiple seconds to complete in medium sized rust files (3k loc).
/// Neovim also encountered this problem and reintroduced this limit after it was removed upstream
/// (see https://github.com/neovim/neovim/issues/14897 and https://github.com/neovim/neovim/pull/14915).
/// The number used here is fundementally a tradeoff between breaking some obscure edgecases and performance.
///
/// 64 was choosen because neovim uses that value.
/// It has been choosen somewhat arbitrarly (https://github.com/neovim/neovim/pull/18397) mostly based
/// upon how many issues occur in practice.
/// So this could be changed if we ran into problems.
const TREE_SITTER_MATCH_LIMIT: u32 = 64;

impl TextObjectQuery {
/// Run the query on the given node and return sub nodes which match given
/// capture ("function.inside", "class.around", etc).
Expand Down Expand Up @@ -394,6 +411,8 @@ impl TextObjectQuery {
.iter()
.find_map(|cap| self.query.capture_index_for_name(cap))?;

cursor.set_match_limit(TREE_SITTER_MATCH_LIMIT);

let nodes = cursor
.captures(&self.query, node, RopeProvider(slice))
.filter_map(move |(mat, _)| {
Expand Down Expand Up @@ -843,6 +862,7 @@ impl Syntax {
let mut cursor = ts_parser.cursors.pop().unwrap_or_else(QueryCursor::new);
// TODO: might need to set cursor range
cursor.set_byte_range(0..usize::MAX);
cursor.set_match_limit(TREE_SITTER_MATCH_LIMIT);

let source_slice = source.slice(..);

Expand Down Expand Up @@ -1032,6 +1052,7 @@ impl Syntax {

// if reusing cursors & no range this resets to whole range
cursor_ref.set_byte_range(range.clone().unwrap_or(0..usize::MAX));
cursor_ref.set_match_limit(TREE_SITTER_MATCH_LIMIT);

let mut captures = cursor_ref
.captures(
Expand Down

0 comments on commit dac3df4

Please sign in to comment.