Merge pull request #2085 from tree-sitter/faster-query-in-range
Improve the performance of running a query in a small range of a large file
This commit is contained in:
commit
c51896d32d
13 changed files with 1326 additions and 899 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -584,6 +584,7 @@ dependencies = [
|
|||
"tree-sitter-highlight",
|
||||
"tree-sitter-loader",
|
||||
"tree-sitter-tags",
|
||||
"unindent",
|
||||
"walkdir",
|
||||
"webbrowser",
|
||||
"which",
|
||||
|
|
@ -647,6 +648,12 @@ version = "0.1.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
|
||||
[[package]]
|
||||
name = "unindent"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa30f5ea51ff7edfc797c6d3f9ec8cbd8cfedef5371766b7181d33977f4814f"
|
||||
|
||||
[[package]]
|
||||
name = "utf8-width"
|
||||
version = "0.1.6"
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ name = "tree-sitter-cli"
|
|||
description = "CLI tool for developing, testing, and using Tree-sitter parsers"
|
||||
version = "0.20.7"
|
||||
authors = ["Max Brunsfeld <maxbrunsfeld@gmail.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
keywords = ["incremental", "parsing"]
|
||||
|
|
@ -73,6 +73,7 @@ rand = "0.8"
|
|||
tempfile = "3"
|
||||
pretty_assertions = "0.7.2"
|
||||
ctor = "0.1"
|
||||
unindent = "0.2"
|
||||
|
||||
[build-dependencies]
|
||||
toml = "0.5"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ use clap::{App, AppSettings, Arg, SubCommand};
|
|||
use glob::glob;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{env, fs, u64};
|
||||
use tree_sitter::Point;
|
||||
use tree_sitter_cli::parse::ParseOutput;
|
||||
use tree_sitter_cli::{
|
||||
generate, highlight, logger, parse, playground, query, tags, test, test_highlight, test_tags,
|
||||
|
|
@ -173,6 +174,8 @@ fn run() -> Result<()> {
|
|||
.index(1)
|
||||
.required(true),
|
||||
)
|
||||
.arg(&time_arg)
|
||||
.arg(&quiet_arg)
|
||||
.arg(&paths_file_arg)
|
||||
.arg(&paths_arg.clone().index(2))
|
||||
.arg(
|
||||
|
|
@ -181,6 +184,12 @@ fn run() -> Result<()> {
|
|||
.long("byte-range")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("row-range")
|
||||
.help("The range of rows in which the query will be executed")
|
||||
.long("row-range")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(&scope_arg)
|
||||
.arg(Arg::with_name("captures").long("captures").short("c"))
|
||||
.arg(Arg::with_name("test").long("test")),
|
||||
|
|
@ -456,6 +465,8 @@ fn run() -> Result<()> {
|
|||
|
||||
("query", Some(matches)) => {
|
||||
let ordered_captures = matches.values_of("captures").is_some();
|
||||
let quiet = matches.values_of("quiet").is_some();
|
||||
let time = matches.values_of("time").is_some();
|
||||
let paths = collect_paths(matches.value_of("paths-file"), matches.values_of("paths"))?;
|
||||
let loader_config = config.get()?;
|
||||
loader.find_all_languages(&loader_config)?;
|
||||
|
|
@ -465,9 +476,17 @@ fn run() -> Result<()> {
|
|||
matches.value_of("scope"),
|
||||
)?;
|
||||
let query_path = Path::new(matches.value_of("query-path").unwrap());
|
||||
let range = matches.value_of("byte-range").map(|br| {
|
||||
let r: Vec<&str> = br.split(":").collect();
|
||||
r[0].parse().unwrap()..r[1].parse().unwrap()
|
||||
let byte_range = matches.value_of("byte-range").and_then(|arg| {
|
||||
let mut parts = arg.split(":");
|
||||
let start = parts.next()?.parse().ok()?;
|
||||
let end = parts.next().unwrap().parse().ok()?;
|
||||
Some(start..end)
|
||||
});
|
||||
let point_range = matches.value_of("row-range").and_then(|arg| {
|
||||
let mut parts = arg.split(":");
|
||||
let start = parts.next()?.parse().ok()?;
|
||||
let end = parts.next().unwrap().parse().ok()?;
|
||||
Some(Point::new(start, 0)..Point::new(end, 0))
|
||||
});
|
||||
let should_test = matches.is_present("test");
|
||||
query::query_files_at_paths(
|
||||
|
|
@ -475,8 +494,11 @@ fn run() -> Result<()> {
|
|||
paths,
|
||||
query_path,
|
||||
ordered_captures,
|
||||
range,
|
||||
byte_range,
|
||||
point_range,
|
||||
should_test,
|
||||
quiet,
|
||||
time,
|
||||
)?;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,16 +5,20 @@ use std::{
|
|||
io::{self, Write},
|
||||
ops::Range,
|
||||
path::Path,
|
||||
time::Instant,
|
||||
};
|
||||
use tree_sitter::{Language, Parser, Query, QueryCursor};
|
||||
use tree_sitter::{Language, Parser, Point, Query, QueryCursor};
|
||||
|
||||
pub fn query_files_at_paths(
|
||||
language: Language,
|
||||
paths: Vec<String>,
|
||||
query_path: &Path,
|
||||
ordered_captures: bool,
|
||||
range: Option<Range<usize>>,
|
||||
byte_range: Option<Range<usize>>,
|
||||
point_range: Option<Range<Point>>,
|
||||
should_test: bool,
|
||||
quiet: bool,
|
||||
print_time: bool,
|
||||
) -> Result<()> {
|
||||
let stdout = io::stdout();
|
||||
let mut stdout = stdout.lock();
|
||||
|
|
@ -24,9 +28,12 @@ pub fn query_files_at_paths(
|
|||
let query = Query::new(language, &query_source).with_context(|| "Query compilation failed")?;
|
||||
|
||||
let mut query_cursor = QueryCursor::new();
|
||||
if let Some(range) = range {
|
||||
if let Some(range) = byte_range {
|
||||
query_cursor.set_byte_range(range);
|
||||
}
|
||||
if let Some(range) = point_range {
|
||||
query_cursor.set_point_range(range);
|
||||
}
|
||||
|
||||
let mut parser = Parser::new();
|
||||
parser.set_language(language)?;
|
||||
|
|
@ -40,22 +47,25 @@ pub fn query_files_at_paths(
|
|||
fs::read(&path).with_context(|| format!("Error reading source file {:?}", path))?;
|
||||
let tree = parser.parse(&source_code, None).unwrap();
|
||||
|
||||
let start = Instant::now();
|
||||
if ordered_captures {
|
||||
for (mat, capture_index) in
|
||||
query_cursor.captures(&query, tree.root_node(), source_code.as_slice())
|
||||
{
|
||||
let capture = mat.captures[capture_index];
|
||||
let capture_name = &query.capture_names()[capture.index as usize];
|
||||
writeln!(
|
||||
&mut stdout,
|
||||
" pattern: {:>2}, capture: {} - {}, start: {}, end: {}, text: `{}`",
|
||||
mat.pattern_index,
|
||||
capture.index,
|
||||
capture_name,
|
||||
capture.node.start_position(),
|
||||
capture.node.end_position(),
|
||||
capture.node.utf8_text(&source_code).unwrap_or("")
|
||||
)?;
|
||||
if !quiet {
|
||||
writeln!(
|
||||
&mut stdout,
|
||||
" pattern: {:>2}, capture: {} - {}, start: {}, end: {}, text: `{}`",
|
||||
mat.pattern_index,
|
||||
capture.index,
|
||||
capture_name,
|
||||
capture.node.start_position(),
|
||||
capture.node.end_position(),
|
||||
capture.node.utf8_text(&source_code).unwrap_or("")
|
||||
)?;
|
||||
}
|
||||
results.push(query_testing::CaptureInfo {
|
||||
name: capture_name.to_string(),
|
||||
start: capture.node.start_position(),
|
||||
|
|
@ -64,27 +74,31 @@ pub fn query_files_at_paths(
|
|||
}
|
||||
} else {
|
||||
for m in query_cursor.matches(&query, tree.root_node(), source_code.as_slice()) {
|
||||
writeln!(&mut stdout, " pattern: {}", m.pattern_index)?;
|
||||
if !quiet {
|
||||
writeln!(&mut stdout, " pattern: {}", m.pattern_index)?;
|
||||
}
|
||||
for capture in m.captures {
|
||||
let start = capture.node.start_position();
|
||||
let end = capture.node.end_position();
|
||||
let capture_name = &query.capture_names()[capture.index as usize];
|
||||
if end.row == start.row {
|
||||
writeln!(
|
||||
&mut stdout,
|
||||
" capture: {} - {}, start: {}, end: {}, text: `{}`",
|
||||
capture.index,
|
||||
capture_name,
|
||||
start,
|
||||
end,
|
||||
capture.node.utf8_text(&source_code).unwrap_or("")
|
||||
)?;
|
||||
} else {
|
||||
writeln!(
|
||||
&mut stdout,
|
||||
" capture: {}, start: {}, end: {}",
|
||||
capture_name, start, end,
|
||||
)?;
|
||||
if !quiet {
|
||||
if end.row == start.row {
|
||||
writeln!(
|
||||
&mut stdout,
|
||||
" capture: {} - {}, start: {}, end: {}, text: `{}`",
|
||||
capture.index,
|
||||
capture_name,
|
||||
start,
|
||||
end,
|
||||
capture.node.utf8_text(&source_code).unwrap_or("")
|
||||
)?;
|
||||
} else {
|
||||
writeln!(
|
||||
&mut stdout,
|
||||
" capture: {}, start: {}, end: {}",
|
||||
capture_name, start, end,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
results.push(query_testing::CaptureInfo {
|
||||
name: capture_name.to_string(),
|
||||
|
|
@ -103,6 +117,9 @@ pub fn query_files_at_paths(
|
|||
if should_test {
|
||||
query_testing::assert_expected_captures(results, path, &mut parser, language)?
|
||||
}
|
||||
if print_time {
|
||||
writeln!(&mut stdout, "{:?}", start.elapsed())?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ use super::helpers::{
|
|||
allocations,
|
||||
fixtures::get_language,
|
||||
query_helpers::{Match, Pattern},
|
||||
ITERATION_COUNT,
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use rand::{prelude::StdRng, SeedableRng};
|
||||
|
|
@ -10,6 +11,7 @@ use tree_sitter::{
|
|||
CaptureQuantifier, Language, Node, Parser, Point, Query, QueryCapture, QueryCursor, QueryError,
|
||||
QueryErrorKind, QueryMatch, QueryPredicate, QueryPredicateArg, QueryProperty,
|
||||
};
|
||||
use unindent::Unindent;
|
||||
|
||||
lazy_static! {
|
||||
static ref EXAMPLE_FILTER: Option<String> = env::var("TREE_SITTER_TEST_EXAMPLE_FILTER").ok();
|
||||
|
|
@ -1874,7 +1876,6 @@ fn test_query_matches_within_byte_range() {
|
|||
cursor
|
||||
.set_byte_range(0..8)
|
||||
.matches(&query, tree.root_node(), source.as_bytes());
|
||||
|
||||
assert_eq!(
|
||||
collect_matches(matches, &query, source),
|
||||
&[
|
||||
|
|
@ -1888,7 +1889,6 @@ fn test_query_matches_within_byte_range() {
|
|||
cursor
|
||||
.set_byte_range(5..15)
|
||||
.matches(&query, tree.root_node(), source.as_bytes());
|
||||
|
||||
assert_eq!(
|
||||
collect_matches(matches, &query, source),
|
||||
&[
|
||||
|
|
@ -1902,7 +1902,6 @@ fn test_query_matches_within_byte_range() {
|
|||
cursor
|
||||
.set_byte_range(12..0)
|
||||
.matches(&query, tree.root_node(), source.as_bytes());
|
||||
|
||||
assert_eq!(
|
||||
collect_matches(matches, &query, source),
|
||||
&[
|
||||
|
|
@ -1920,20 +1919,28 @@ fn test_query_matches_within_point_range() {
|
|||
let language = get_language("javascript");
|
||||
let query = Query::new(language, "(identifier) @element").unwrap();
|
||||
|
||||
let source = "[a, b,\n c, d,\n e, f,\n g]";
|
||||
let source = "
|
||||
[
|
||||
a, b,
|
||||
c, d,
|
||||
e, f,
|
||||
g, h,
|
||||
i, j,
|
||||
k, l,
|
||||
]
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let mut parser = Parser::new();
|
||||
parser.set_language(language).unwrap();
|
||||
let tree = parser.parse(&source, None).unwrap();
|
||||
|
||||
let mut cursor = QueryCursor::new();
|
||||
|
||||
let matches = cursor
|
||||
.set_point_range(Point::new(0, 0)..Point::new(1, 3))
|
||||
.set_point_range(Point::new(1, 0)..Point::new(2, 3))
|
||||
.matches(&query, tree.root_node(), source.as_bytes());
|
||||
|
||||
assert_eq!(
|
||||
collect_matches(matches, &query, source),
|
||||
collect_matches(matches, &query, &source),
|
||||
&[
|
||||
(0, vec![("element", "a")]),
|
||||
(0, vec![("element", "b")]),
|
||||
|
|
@ -1942,11 +1949,10 @@ fn test_query_matches_within_point_range() {
|
|||
);
|
||||
|
||||
let matches = cursor
|
||||
.set_point_range(Point::new(1, 0)..Point::new(2, 3))
|
||||
.set_point_range(Point::new(2, 0)..Point::new(3, 3))
|
||||
.matches(&query, tree.root_node(), source.as_bytes());
|
||||
|
||||
assert_eq!(
|
||||
collect_matches(matches, &query, source),
|
||||
collect_matches(matches, &query, &source),
|
||||
&[
|
||||
(0, vec![("element", "c")]),
|
||||
(0, vec![("element", "d")]),
|
||||
|
|
@ -1954,16 +1960,19 @@ fn test_query_matches_within_point_range() {
|
|||
]
|
||||
);
|
||||
|
||||
// Zero end point is treated like no end point.
|
||||
let matches = cursor
|
||||
.set_point_range(Point::new(2, 1)..Point::new(0, 0))
|
||||
.set_point_range(Point::new(4, 1)..Point::new(0, 0))
|
||||
.matches(&query, tree.root_node(), source.as_bytes());
|
||||
|
||||
assert_eq!(
|
||||
collect_matches(matches, &query, source),
|
||||
collect_matches(matches, &query, &source),
|
||||
&[
|
||||
(0, vec![("element", "e")]),
|
||||
(0, vec![("element", "f")]),
|
||||
(0, vec![("element", "g")]),
|
||||
(0, vec![("element", "h")]),
|
||||
(0, vec![("element", "i")]),
|
||||
(0, vec![("element", "j")]),
|
||||
(0, vec![("element", "k")]),
|
||||
(0, vec![("element", "l")]),
|
||||
]
|
||||
);
|
||||
});
|
||||
|
|
@ -2210,6 +2219,57 @@ fn test_query_captures_within_byte_range_assigned_after_iterating() {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_matches_within_range_of_long_repetition() {
|
||||
allocations::record(|| {
|
||||
let language = get_language("rust");
|
||||
let query = Query::new(
|
||||
language,
|
||||
"
|
||||
(function_item name: (identifier) @fn-name)
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let source = "
|
||||
fn zero() {}
|
||||
fn one() {}
|
||||
fn two() {}
|
||||
fn three() {}
|
||||
fn four() {}
|
||||
fn five() {}
|
||||
fn six() {}
|
||||
fn seven() {}
|
||||
fn eight() {}
|
||||
fn nine() {}
|
||||
fn ten() {}
|
||||
fn eleven() {}
|
||||
fn twelve() {}
|
||||
"
|
||||
.unindent();
|
||||
|
||||
let mut parser = Parser::new();
|
||||
let mut cursor = QueryCursor::new();
|
||||
|
||||
parser.set_language(language).unwrap();
|
||||
let tree = parser.parse(&source, None).unwrap();
|
||||
|
||||
let matches = cursor
|
||||
.set_point_range(Point::new(8, 0)..Point::new(20, 0))
|
||||
.matches(&query, tree.root_node(), source.as_bytes());
|
||||
assert_eq!(
|
||||
collect_matches(matches, &query, &source),
|
||||
&[
|
||||
(0, vec![("fn-name", "eight")]),
|
||||
(0, vec![("fn-name", "nine")]),
|
||||
(0, vec![("fn-name", "ten")]),
|
||||
(0, vec![("fn-name", "eleven")]),
|
||||
(0, vec![("fn-name", "twelve")]),
|
||||
]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_matches_different_queries_same_cursor() {
|
||||
allocations::record(|| {
|
||||
|
|
@ -3634,17 +3694,22 @@ fn test_query_random() {
|
|||
.parse(include_str!("helpers/query_helpers.rs"), None)
|
||||
.unwrap();
|
||||
|
||||
// let start_seed = *SEED;
|
||||
let start_seed = 0;
|
||||
let end_seed = start_seed + *ITERATION_COUNT;
|
||||
|
||||
for i in 0..100 {
|
||||
let seed = (start_seed + i) as u64;
|
||||
for seed in start_seed..(start_seed + end_seed) {
|
||||
let seed = seed as u64;
|
||||
let mut rand = StdRng::seed_from_u64(seed);
|
||||
let (pattern_ast, _) = Pattern::random_pattern_in_tree(&pattern_tree, &mut rand);
|
||||
let pattern = pattern_ast.to_string();
|
||||
let expected_matches = pattern_ast.matches_in_tree(&test_tree);
|
||||
|
||||
let query = Query::new(language, &pattern).unwrap();
|
||||
let query = match Query::new(language, &pattern) {
|
||||
Ok(query) => query,
|
||||
Err(e) => {
|
||||
panic!("failed to build query for pattern {pattern} - {e}. seed: {seed}");
|
||||
}
|
||||
};
|
||||
let mut actual_matches = cursor
|
||||
.matches(
|
||||
&query,
|
||||
|
|
@ -4070,6 +4135,103 @@ fn test_query_is_pattern_rooted() {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_is_pattern_non_local() {
|
||||
struct Row {
|
||||
description: &'static str,
|
||||
pattern: &'static str,
|
||||
language: Language,
|
||||
is_non_local: bool,
|
||||
}
|
||||
|
||||
let rows = [
|
||||
Row {
|
||||
description: "simple token",
|
||||
pattern: r#"(identifier)"#,
|
||||
language: get_language("python"),
|
||||
is_non_local: false,
|
||||
},
|
||||
Row {
|
||||
description: "siblings that can occur in an argument list",
|
||||
pattern: r#"((identifier) (identifier))"#,
|
||||
language: get_language("python"),
|
||||
is_non_local: true,
|
||||
},
|
||||
Row {
|
||||
description: "siblings that can occur in a statement block",
|
||||
pattern: r#"((return_statement) (return_statement))"#,
|
||||
language: get_language("python"),
|
||||
is_non_local: true,
|
||||
},
|
||||
Row {
|
||||
description: "siblings that can occur in a source file",
|
||||
pattern: r#"((function_definition) (class_definition))"#,
|
||||
language: get_language("python"),
|
||||
is_non_local: true,
|
||||
},
|
||||
Row {
|
||||
description: "siblings that can't occur in any repetition",
|
||||
pattern: r#"("{" "}")"#,
|
||||
language: get_language("python"),
|
||||
is_non_local: false,
|
||||
},
|
||||
Row {
|
||||
description: "siblings that can't occur in any repetition, wildcard root",
|
||||
pattern: r#"(_ "{" "}") @foo"#,
|
||||
language: get_language("javascript"),
|
||||
is_non_local: false,
|
||||
},
|
||||
Row {
|
||||
description: "siblings that can occur in a class body, wildcard root",
|
||||
pattern: r#"(_ (method_definition) (method_definition)) @foo"#,
|
||||
language: get_language("javascript"),
|
||||
is_non_local: true,
|
||||
},
|
||||
Row {
|
||||
description: "top-level repetitions that can occur in a class body",
|
||||
pattern: r#"(method_definition)+ @foo"#,
|
||||
language: get_language("javascript"),
|
||||
is_non_local: true,
|
||||
},
|
||||
Row {
|
||||
description: "top-level repetitions that can occur in a statement block",
|
||||
pattern: r#"(return_statement)+ @foo"#,
|
||||
language: get_language("javascript"),
|
||||
is_non_local: true,
|
||||
},
|
||||
Row {
|
||||
description: "rooted pattern that can occur in a statement block",
|
||||
pattern: r#"(return_statement) @foo"#,
|
||||
language: get_language("javascript"),
|
||||
is_non_local: false,
|
||||
},
|
||||
];
|
||||
|
||||
allocations::record(|| {
|
||||
eprintln!("");
|
||||
|
||||
for row in &rows {
|
||||
if let Some(filter) = EXAMPLE_FILTER.as_ref() {
|
||||
if !row.description.contains(filter.as_str()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
eprintln!(" query example: {:?}", row.description);
|
||||
let query = Query::new(row.language, row.pattern).unwrap();
|
||||
assert_eq!(
|
||||
query.is_pattern_non_local(0),
|
||||
row.is_non_local,
|
||||
"Description: {}, Pattern: {:?}",
|
||||
row.description,
|
||||
row.pattern
|
||||
.split_ascii_whitespace()
|
||||
.collect::<Vec<_>>()
|
||||
.join(" "),
|
||||
)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_capture_quantifiers() {
|
||||
struct Row {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ name = "tree-sitter"
|
|||
description = "Rust bindings to the Tree-sitter parsing library"
|
||||
version = "0.20.9"
|
||||
authors = ["Max Brunsfeld <maxbrunsfeld@gmail.com>"]
|
||||
edition = "2018"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
readme = "binding_rust/README.md"
|
||||
keywords = ["incremental", "parsing"]
|
||||
|
|
|
|||
|
|
@ -677,6 +677,9 @@ extern "C" {
|
|||
length: *mut u32,
|
||||
) -> *const TSQueryPredicateStep;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn ts_query_is_pattern_non_local(self_: *const TSQuery, pattern_index: u32) -> bool;
|
||||
}
|
||||
extern "C" {
|
||||
pub fn ts_query_is_pattern_rooted(self_: *const TSQuery, pattern_index: u32) -> bool;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1736,11 +1736,17 @@ impl Query {
|
|||
}
|
||||
|
||||
/// Check if a given pattern within a query has a single root node.
|
||||
#[doc(alias = "ts_query_is_pattern_guaranteed_at_step")]
|
||||
#[doc(alias = "ts_query_is_pattern_rooted")]
|
||||
pub fn is_pattern_rooted(&self, index: usize) -> bool {
|
||||
unsafe { ffi::ts_query_is_pattern_rooted(self.ptr.as_ptr(), index as u32) }
|
||||
}
|
||||
|
||||
/// Check if a given pattern within a query has a single root node.
|
||||
#[doc(alias = "ts_query_is_pattern_non_local")]
|
||||
pub fn is_pattern_non_local(&self, index: usize) -> bool {
|
||||
unsafe { ffi::ts_query_is_pattern_non_local(self.ptr.as_ptr(), index as u32) }
|
||||
}
|
||||
|
||||
/// Check if a given step in a query is 'definite'.
|
||||
///
|
||||
/// A query step is 'definite' if its parent pattern will be guaranteed to match
|
||||
|
|
|
|||
|
|
@ -750,15 +750,26 @@ const TSQueryPredicateStep *ts_query_predicates_for_pattern(
|
|||
uint32_t *length
|
||||
);
|
||||
|
||||
bool ts_query_is_pattern_rooted(
|
||||
const TSQuery *self,
|
||||
uint32_t pattern_index
|
||||
);
|
||||
/*
|
||||
* Check if the given pattern in the query has a single root node.
|
||||
*/
|
||||
bool ts_query_is_pattern_rooted(const TSQuery *self, uint32_t pattern_index);
|
||||
|
||||
bool ts_query_is_pattern_guaranteed_at_step(
|
||||
const TSQuery *self,
|
||||
uint32_t byte_offset
|
||||
);
|
||||
/*
|
||||
* Check if the given pattern in the query is 'non local'.
|
||||
*
|
||||
* A non-local pattern has multiple root nodes and can match within a
|
||||
* repeating sequence of nodes, as specified by the grammar. Non-local
|
||||
* patterns disable certain optimizations that would otherwise be possible
|
||||
* when executing a query on a specific range of a syntax tree.
|
||||
*/
|
||||
bool ts_query_is_pattern_non_local(const TSQuery *self, uint32_t pattern_index);
|
||||
|
||||
/*
|
||||
* Check if a given pattern is guaranteed to match once a given step is reached.
|
||||
* The step is specified by its byte offset in the query's source code.
|
||||
*/
|
||||
bool ts_query_is_pattern_guaranteed_at_step(const TSQuery *self, uint32_t byte_offset);
|
||||
|
||||
/**
|
||||
* Get the name and length of one of the query's captures, or one of the
|
||||
|
|
|
|||
1719
lib/src/query.c
1719
lib/src/query.c
File diff suppressed because it is too large
Load diff
|
|
@ -291,6 +291,12 @@ static inline uint32_t ts_subtree_repeat_depth(Subtree self) {
|
|||
return self.data.is_inline ? 0 : self.ptr->repeat_depth;
|
||||
}
|
||||
|
||||
static inline uint32_t ts_subtree_is_repetition(Subtree self) {
|
||||
return self.data.is_inline
|
||||
? 0
|
||||
: !self.ptr->named && !self.ptr->visible && self.ptr->child_count != 0;
|
||||
}
|
||||
|
||||
static inline uint32_t ts_subtree_node_count(Subtree self) {
|
||||
return (self.data.is_inline || self.ptr->child_count == 0) ? 1 : self.ptr->node_count;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,34 +98,43 @@ void ts_tree_cursor_delete(TSTreeCursor *_self) {
|
|||
|
||||
// TSTreeCursor - walking the tree
|
||||
|
||||
bool ts_tree_cursor_goto_first_child(TSTreeCursor *_self) {
|
||||
TreeCursorStep ts_tree_cursor_goto_first_child_internal(TSTreeCursor *_self) {
|
||||
TreeCursor *self = (TreeCursor *)_self;
|
||||
|
||||
bool did_descend;
|
||||
do {
|
||||
did_descend = false;
|
||||
|
||||
bool visible;
|
||||
TreeCursorEntry entry;
|
||||
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
|
||||
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
|
||||
if (visible) {
|
||||
array_push(&self->stack, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ts_subtree_visible_child_count(*entry.subtree) > 0) {
|
||||
array_push(&self->stack, entry);
|
||||
did_descend = true;
|
||||
break;
|
||||
}
|
||||
bool visible;
|
||||
TreeCursorEntry entry;
|
||||
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
|
||||
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
|
||||
if (visible) {
|
||||
array_push(&self->stack, entry);
|
||||
return TreeCursorStepVisible;
|
||||
}
|
||||
} while (did_descend);
|
||||
if (ts_subtree_visible_child_count(*entry.subtree) > 0) {
|
||||
array_push(&self->stack, entry);
|
||||
return TreeCursorStepHidden;
|
||||
}
|
||||
}
|
||||
return TreeCursorStepNone;
|
||||
}
|
||||
|
||||
bool ts_tree_cursor_goto_first_child(TSTreeCursor *self) {
|
||||
for (;;) {
|
||||
switch (ts_tree_cursor_goto_first_child_internal(self)) {
|
||||
case TreeCursorStepHidden:
|
||||
continue;
|
||||
case TreeCursorStepVisible:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *_self, uint32_t goal_byte) {
|
||||
static inline int64_t ts_tree_cursor_goto_first_child_for_byte_and_point(
|
||||
TSTreeCursor *_self,
|
||||
uint32_t goal_byte,
|
||||
TSPoint goal_point
|
||||
) {
|
||||
TreeCursor *self = (TreeCursor *)_self;
|
||||
uint32_t initial_size = self->stack.size;
|
||||
uint32_t visible_child_index = 0;
|
||||
|
|
@ -138,48 +147,8 @@ int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *_self, uint32_t g
|
|||
TreeCursorEntry entry;
|
||||
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
|
||||
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
|
||||
uint32_t end_byte = entry.position.bytes + ts_subtree_size(*entry.subtree).bytes;
|
||||
bool at_goal = end_byte >= goal_byte;
|
||||
uint32_t visible_child_count = ts_subtree_visible_child_count(*entry.subtree);
|
||||
|
||||
if (at_goal) {
|
||||
if (visible) {
|
||||
array_push(&self->stack, entry);
|
||||
return visible_child_index;
|
||||
}
|
||||
|
||||
if (visible_child_count > 0) {
|
||||
array_push(&self->stack, entry);
|
||||
did_descend = true;
|
||||
break;
|
||||
}
|
||||
} else if (visible) {
|
||||
visible_child_index++;
|
||||
} else {
|
||||
visible_child_index += visible_child_count;
|
||||
}
|
||||
}
|
||||
} while (did_descend);
|
||||
|
||||
self->stack.size = initial_size;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int64_t ts_tree_cursor_goto_first_child_for_point(TSTreeCursor *_self, TSPoint goal_point) {
|
||||
TreeCursor *self = (TreeCursor *)_self;
|
||||
uint32_t initial_size = self->stack.size;
|
||||
uint32_t visible_child_index = 0;
|
||||
|
||||
bool did_descend;
|
||||
do {
|
||||
did_descend = false;
|
||||
|
||||
bool visible;
|
||||
TreeCursorEntry entry;
|
||||
CursorChildIterator iterator = ts_tree_cursor_iterate_children(self);
|
||||
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
|
||||
TSPoint end_point = point_add(entry.position.extent, ts_subtree_size(*entry.subtree).extent);
|
||||
bool at_goal = point_gte(end_point, goal_point);
|
||||
Length entry_end = length_add(entry.position, ts_subtree_size(*entry.subtree));
|
||||
bool at_goal = entry_end.bytes >= goal_byte && point_gte(entry_end.extent, goal_point);
|
||||
uint32_t visible_child_count = ts_subtree_visible_child_count(*entry.subtree);
|
||||
if (at_goal) {
|
||||
if (visible) {
|
||||
|
|
@ -203,7 +172,15 @@ int64_t ts_tree_cursor_goto_first_child_for_point(TSTreeCursor *_self, TSPoint g
|
|||
return -1;
|
||||
}
|
||||
|
||||
bool ts_tree_cursor_goto_next_sibling(TSTreeCursor *_self) {
|
||||
int64_t ts_tree_cursor_goto_first_child_for_byte(TSTreeCursor *self, uint32_t goal_byte) {
|
||||
return ts_tree_cursor_goto_first_child_for_byte_and_point(self, goal_byte, POINT_ZERO);
|
||||
}
|
||||
|
||||
int64_t ts_tree_cursor_goto_first_child_for_point(TSTreeCursor *self, TSPoint goal_point) {
|
||||
return ts_tree_cursor_goto_first_child_for_byte_and_point(self, 0, goal_point);
|
||||
}
|
||||
|
||||
TreeCursorStep ts_tree_cursor_goto_next_sibling_internal(TSTreeCursor *_self) {
|
||||
TreeCursor *self = (TreeCursor *)_self;
|
||||
uint32_t initial_size = self->stack.size;
|
||||
|
||||
|
|
@ -221,19 +198,30 @@ bool ts_tree_cursor_goto_next_sibling(TSTreeCursor *_self) {
|
|||
while (ts_tree_cursor_child_iterator_next(&iterator, &entry, &visible)) {
|
||||
if (visible) {
|
||||
array_push(&self->stack, entry);
|
||||
return true;
|
||||
return TreeCursorStepVisible;
|
||||
}
|
||||
|
||||
if (ts_subtree_visible_child_count(*entry.subtree)) {
|
||||
array_push(&self->stack, entry);
|
||||
ts_tree_cursor_goto_first_child(_self);
|
||||
return true;
|
||||
return TreeCursorStepHidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self->stack.size = initial_size;
|
||||
return false;
|
||||
return TreeCursorStepNone;
|
||||
}
|
||||
|
||||
bool ts_tree_cursor_goto_next_sibling(TSTreeCursor *self) {
|
||||
switch (ts_tree_cursor_goto_next_sibling_internal(self)) {
|
||||
case TreeCursorStepHidden:
|
||||
ts_tree_cursor_goto_first_child(self);
|
||||
return true;
|
||||
case TreeCursorStepVisible:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool ts_tree_cursor_goto_parent(TSTreeCursor *_self) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,12 @@ typedef struct {
|
|||
Array(TreeCursorEntry) stack;
|
||||
} TreeCursor;
|
||||
|
||||
typedef enum {
|
||||
TreeCursorStepNone,
|
||||
TreeCursorStepHidden,
|
||||
TreeCursorStepVisible,
|
||||
} TreeCursorStep;
|
||||
|
||||
void ts_tree_cursor_init(TreeCursor *, TSNode);
|
||||
void ts_tree_cursor_current_status(
|
||||
const TSTreeCursor *,
|
||||
|
|
@ -26,6 +32,15 @@ void ts_tree_cursor_current_status(
|
|||
unsigned *
|
||||
);
|
||||
|
||||
TreeCursorStep ts_tree_cursor_goto_first_child_internal(TSTreeCursor *);
|
||||
TreeCursorStep ts_tree_cursor_goto_next_sibling_internal(TSTreeCursor *);
|
||||
|
||||
static inline Subtree ts_tree_cursor_current_subtree(const TSTreeCursor *_self) {
|
||||
const TreeCursor *self = (const TreeCursor *)_self;
|
||||
TreeCursorEntry *last_entry = array_back(&self->stack);
|
||||
return *last_entry->subtree;
|
||||
}
|
||||
|
||||
TSNode ts_tree_cursor_parent_node(const TSTreeCursor *);
|
||||
|
||||
#endif // TREE_SITTER_TREE_CURSOR_H_
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue