diff --git a/Cargo.lock b/Cargo.lock index 48d26635..ab9b191e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1340,7 +1340,6 @@ dependencies = [ "topiary-core", "topiary-queries", "tree-sitter-bash", - "tree-sitter-facade", "tree-sitter-json", "tree-sitter-nickel", "tree-sitter-ocaml", @@ -1369,8 +1368,8 @@ dependencies = [ "tokio", "tokio-test", "toml", + "tree-sitter", "tree-sitter-bash", - "tree-sitter-facade", "tree-sitter-json", "tree-sitter-nickel", "tree-sitter-ocaml", @@ -1389,7 +1388,6 @@ dependencies = [ "cfg-if", "itertools 0.11.0", "topiary-core", - "tree-sitter-facade", "wasm-bindgen", "wasm-bindgen-futures", ] @@ -1417,18 +1415,6 @@ dependencies = [ "tree-sitter", ] -[[package]] -name = "tree-sitter-facade" -version = "0.9.3" -source = "git+https://github.com/tweag/tree-sitter-facade#1b290e795e700a57d8bd303f98a9715ab1c4f598" -dependencies = [ - "js-sys", - "tree-sitter", - "wasm-bindgen", - "web-sys", - "web-tree-sitter-sys", -] - [[package]] name = "tree-sitter-json" version = "0.20.0" diff --git a/Cargo.toml b/Cargo.toml index d3fcca7e..e6083cff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,8 +59,8 @@ test-log = "0.2.11" tokio = "1.32" tokio-test = "0.4" toml = "0.7" +tree-sitter = "0.20" tree-sitter-bash = { git = "https://github.com/tree-sitter/tree-sitter-bash" } -tree-sitter-facade = { git = "https://github.com/tweag/tree-sitter-facade" } tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json.git" } tree-sitter-nickel = { git = "https://github.com/nickel-lang/tree-sitter-nickel", rev = "b1a4718601ebd29a62bf3a7fd1069a99ccf48093" } tree-sitter-ocaml = { git = "https://github.com/tree-sitter/tree-sitter-ocaml.git" } diff --git a/topiary-cli/Cargo.toml b/topiary-cli/Cargo.toml index 10b1b6eb..02ca0f87 100644 --- a/topiary-cli/Cargo.toml +++ b/topiary-cli/Cargo.toml @@ -41,7 +41,6 @@ tokio = { workspace = true, features = ["fs", "rt-multi-thread", "sync", "macros toml = { workspace = true } topiary-core = { path = "../topiary-core" } topiary-queries = { path = "../topiary-queries" } -tree-sitter-facade = { workspace = true } tree-sitter-json = { workspace = true } tree-sitter-rust = { workspace = true } diff --git a/topiary-core/Cargo.toml b/topiary-core/Cargo.toml index bcf2dc5c..72a5ad4f 100644 --- a/topiary-core/Cargo.toml +++ b/topiary-core/Cargo.toml @@ -24,7 +24,7 @@ regex = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } toml = { workspace = true } -tree-sitter-facade = { workspace = true } +tree-sitter = { workspace = true } unescape = { workspace = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/topiary-core/benches/benchmark.rs b/topiary-core/benches/benchmark.rs index 44da79a8..91851f57 100644 --- a/topiary-core/benches/benchmark.rs +++ b/topiary-core/benches/benchmark.rs @@ -11,7 +11,7 @@ async fn format() { let configuration = Configuration::parse_default_configuration().unwrap(); let language = configuration.get_language("ocaml").unwrap(); let grammar = language.grammar().await.unwrap(); - let query = TopiaryQuery::new(&grammar, &query_content).unwrap(); + let query = TopiaryQuery::new(grammar, &query_content).unwrap(); let mut input = input.as_bytes(); let mut output = io::BufWriter::new(Vec::new()); @@ -21,7 +21,7 @@ async fn format() { &mut output, &query, language, - &grammar, + grammar, Operation::Format { skip_idempotence: true, tolerate_parsing_errors: false, diff --git a/topiary-core/src/atom_collection.rs b/topiary-core/src/atom_collection.rs index db96a26e..853d7f00 100644 --- a/topiary-core/src/atom_collection.rs +++ b/topiary-core/src/atom_collection.rs @@ -5,7 +5,7 @@ use std::{ ops::Deref, }; -use tree_sitter_facade::Node; +use tree_sitter::Node; use crate::{Atom, FormatterError, FormatterResult, ScopeCondition, ScopeInformation}; @@ -177,13 +177,13 @@ impl AtomCollection { // instead of creating it for both branches, create them once here. let scope_information_prepend = || -> FormatterResult { Ok(ScopeInformation { - line_number: node.start_position().row(), + line_number: node.start_position().row, scope_id: requires_scope_id()?.to_owned(), }) }; let scope_information_append = || -> FormatterResult { Ok(ScopeInformation { - line_number: node.end_position().row(), + line_number: node.end_position().row, scope_id: requires_scope_id()?.to_owned(), }) }; @@ -592,7 +592,7 @@ impl AtomCollection { /// The second pass applies the modifications to the atoms. fn post_process_scopes(&mut self) { type ScopeId = String; - type LineIndex = u32; + type LineIndex = usize; type ScopedNodeId = usize; // `opened_scopes` maintains stacks of opened scopes, // the line at which they started, @@ -1026,8 +1026,8 @@ fn detect_multi_line_nodes(dfs_nodes: &[Node]) -> HashSet { dfs_nodes .iter() .filter_map(|node| { - let start_line = node.start_position().row(); - let end_line = node.end_position().row(); + let start_line = node.start_position().row; + let end_line = node.end_position().row; if end_line > start_line { log::debug!("Multi-line node {}: {:?}", node.id(), node,); @@ -1055,7 +1055,7 @@ fn detect_multi_line_nodes(dfs_nodes: &[Node]) -> HashSet { /// /// A `NodesWithLinebreaks` struct that contains two sets of node IDs: one for the nodes that have a line break /// before them, and one for the nodes that have a line break after them. -fn detect_line_breaks(dfs_nodes: &[Node], minimum_line_breaks: u32) -> NodesWithLinebreaks { +fn detect_line_breaks(dfs_nodes: &[Node], minimum_line_breaks: usize) -> NodesWithLinebreaks { // Zip the flattened vector with its own tail => Iterator of pairs of adjacent nodes // Filter this by the threshold distance between pair components // Unzip into "nodes with spaces before" and "after" sets, respectively @@ -1063,8 +1063,8 @@ fn detect_line_breaks(dfs_nodes: &[Node], minimum_line_breaks: u32) -> NodesWith .iter() .zip(dfs_nodes[1..].iter()) .filter_map(|(left, right)| { - let last = left.end_position().row(); - let next = right.start_position().row(); + let last = left.end_position().row; + let next = right.start_position().row; if next >= last + minimum_line_breaks { log::debug!( diff --git a/topiary-core/src/error.rs b/topiary-core/src/error.rs index f3d98347..4408d607 100644 --- a/topiary-core/src/error.rs +++ b/topiary-core/src/error.rs @@ -21,10 +21,10 @@ pub enum FormatterError { /// Tree-sitter could not parse the input without errors. Parsing { - start_line: u32, - start_column: u32, - end_line: u32, - end_column: u32, + start_line: usize, + start_column: usize, + end_line: usize, + end_column: usize, }, /// The query contains a pattern that had no match in the input file. @@ -33,7 +33,7 @@ pub enum FormatterError { /// There was an error in the query file. If this happened using our /// provided query files, it is a bug. Please log an issue. - Query(String, Option), + Query(String, Option), /// Could not detect the input language from the (filename, /// Option) @@ -204,8 +204,8 @@ impl From for FormatterError { } } -impl From for FormatterError { - fn from(e: tree_sitter_facade::LanguageError) -> Self { +impl From for FormatterError { + fn from(e: tree_sitter::LanguageError) -> Self { Self::Internal( "Error while loading language grammar".into(), Some(Box::new(e)), @@ -213,12 +213,6 @@ impl From for FormatterError { } } -impl From for FormatterError { - fn from(e: tree_sitter_facade::ParserError) -> Self { - Self::Internal("Error while parsing".into(), Some(Box::new(e))) - } -} - impl From for FormatterError { fn from(e: toml::de::Error) -> Self { Self::Internal( diff --git a/topiary-core/src/language.rs b/topiary-core/src/language.rs index 5f8adcaf..74607002 100644 --- a/topiary-core/src/language.rs +++ b/topiary-core/src/language.rs @@ -62,7 +62,7 @@ impl Language { /// /// If the language is not supported, a `FormatterError` will be returned. #[cfg(not(target_arch = "wasm32"))] - pub async fn grammar(&self) -> FormatterResult { + pub async fn grammar(&self) -> FormatterResult { Ok(match self.name.as_str() { "bash" => tree_sitter_bash::language(), "json" => tree_sitter_json::language(), diff --git a/topiary-core/src/lib.rs b/topiary-core/src/lib.rs index 0d42af8c..84b58694 100644 --- a/topiary-core/src/lib.rs +++ b/topiary-core/src/lib.rs @@ -12,9 +12,9 @@ use std::io; +use crate::tree_sitter::Position; use itertools::Itertools; use pretty_assertions::StrComparison; -use tree_sitter::Position; pub use crate::{ configuration::{default_configuration_toml, Configuration}, @@ -36,7 +36,7 @@ pub mod test_utils; #[derive(Clone, Debug, Eq, PartialEq)] pub struct ScopeInformation { - line_number: u32, + line_number: usize, scope_id: String, } @@ -196,7 +196,7 @@ pub fn formatter( output: &mut impl io::Write, query: &TopiaryQuery, language: &Language, - grammar: &tree_sitter_facade::Language, + grammar: ::tree_sitter::Language, operation: Operation, ) -> FormatterResult<()> { let content = read_input(input).map_err(|e| { @@ -277,7 +277,7 @@ fn idempotence_check( content: &str, query: &TopiaryQuery, language: &Language, - grammar: &tree_sitter_facade::Language, + grammar: ::tree_sitter::Language, tolerate_parsing_errors: bool, ) -> FormatterResult<()> { log::info!("Checking for idempotence ..."); @@ -334,14 +334,14 @@ mod tests { let configuration = Configuration::parse_default_configuration().unwrap(); let language = configuration.get_language("json").unwrap(); let grammar = language.grammar().await.unwrap(); - let query = TopiaryQuery::new(&grammar, query_content).unwrap(); + let query = TopiaryQuery::new(grammar, query_content).unwrap(); match formatter( &mut input, &mut output, &query, language, - &grammar, + grammar, Operation::Format { skip_idempotence: true, tolerate_parsing_errors: false, @@ -369,14 +369,14 @@ mod tests { let configuration = Configuration::parse_default_configuration().unwrap(); let language = configuration.get_language("json").unwrap(); let grammar = language.grammar().await.unwrap(); - let query = TopiaryQuery::new(&grammar, &query_content).unwrap(); + let query = TopiaryQuery::new(grammar, &query_content).unwrap(); formatter( &mut input, &mut output, &query, language, - &grammar, + grammar, Operation::Format { skip_idempotence: true, tolerate_parsing_errors: true, diff --git a/topiary-core/src/tree_sitter.rs b/topiary-core/src/tree_sitter.rs index d4aa600b..b15eef78 100644 --- a/topiary-core/src/tree_sitter.rs +++ b/topiary-core/src/tree_sitter.rs @@ -1,9 +1,7 @@ use std::collections::HashSet; +use ::tree_sitter::{Node, Parser, Point, Query, QueryCapture, QueryCursor, QueryPredicate, Tree}; use serde::Serialize; -use tree_sitter_facade::{ - Node, Parser, Point, Query, QueryCapture, QueryCursor, QueryPredicate, Tree, -}; use crate::{ atom_collection::{AtomCollection, QueryPredicates}, @@ -23,8 +21,8 @@ pub enum Visualisation { /// is how editors usually refer to a position. Derived from tree_sitter::Point. #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] pub struct Position { - pub row: u32, - pub column: u32, + pub row: usize, + pub column: usize, } /// Topiary often needs both the tree-sitter `Query` and the original content @@ -44,7 +42,7 @@ impl TopiaryQuery { /// This function will return an error if tree-sitter failed to parse the /// query file. pub fn new( - grammar: &tree_sitter_facade::Language, + grammar: ::tree_sitter::Language, query_content: &str, ) -> FormatterResult { let query = Query::new(grammar, query_content) @@ -60,8 +58,8 @@ impl TopiaryQuery { impl From for Position { fn from(point: Point) -> Self { Self { - row: point.row() + 1, - column: point.column() + 1, + row: point.row + 1, + column: point.column + 1, } } } @@ -108,7 +106,7 @@ impl From> for SyntaxNode { // A struct to statically store the public fields of query match results, // to avoid running queries twice. struct LocalQueryMatch<'a> { - pattern_index: u32, + pattern_index: usize, captures: Vec>, } @@ -125,7 +123,7 @@ struct LocalQueryMatch<'a> { pub fn apply_query( input_content: &str, query: &TopiaryQuery, - grammar: &tree_sitter_facade::Language, + grammar: ::tree_sitter::Language, tolerate_parsing_errors: bool, should_check_input_exhaustivity: bool, ) -> FormatterResult { @@ -138,18 +136,18 @@ pub fn apply_query( let mut matches: Vec = Vec::new(); let capture_names = query.query.capture_names(); - for query_match in query.query.matches(&root, source, &mut cursor) { - let local_captures: Vec = query_match.captures().collect(); + for query_match in cursor.matches(&query.query, root, source) { + let local_captures: Vec = query_match.captures.to_owned(); matches.push(LocalQueryMatch { - pattern_index: query_match.pattern_index(), + pattern_index: query_match.pattern_index, captures: local_captures, }); } if should_check_input_exhaustivity { let ref_match_count = matches.len(); - check_input_exhaustivity(ref_match_count, query, grammar, &root, source)?; + check_input_exhaustivity(ref_match_count, query, grammar, root, source)?; } // Find the ids of all tree-sitter nodes that were identified as a leaf @@ -176,22 +174,22 @@ pub fn apply_query( let mut predicates = QueryPredicates::default(); for p in query.query.general_predicates(m.pattern_index) { - predicates = handle_predicate(&p, &predicates)?; + predicates = handle_predicate(p, &predicates)?; } check_predicates(&predicates)?; // If any capture is a do_nothing, then do nothing. if m.captures .iter() - .map(|c| c.name(&capture_names)) + .map(|c| capture_names[c.index as usize].as_str()) .any(|name| name == "do_nothing") { continue; } for c in m.captures { - let name = c.name(&capture_names); - atoms.resolve_capture(&name, &c.node(), &predicates)?; + let name = capture_names[c.index as usize].as_str(); + atoms.resolve_capture(&name, &c.node, &predicates)?; } } @@ -209,16 +207,16 @@ pub fn apply_query( // or the last error if all grammars fail. pub fn parse<'a>( content: &str, - grammar: &'a tree_sitter_facade::Language, + grammar: ::tree_sitter::Language, tolerate_parsing_errors: bool, -) -> FormatterResult<(Tree, &'a tree_sitter_facade::Language)> { - let mut parser = Parser::new()?; +) -> FormatterResult<(Tree, ::tree_sitter::Language)> { + let mut parser = Parser::new(); parser.set_language(grammar).map_err(|_| { FormatterError::Internal("Could not apply Tree-sitter grammar".into(), None) })?; let tree = parser - .parse(content, None)? + .parse(content, None) .ok_or_else(|| FormatterError::Internal("Could not parse input".into(), None))?; // Fail parsing if we don't get a complete syntax tree. @@ -236,10 +234,10 @@ fn check_for_error_nodes(node: &Node) -> FormatterResult<()> { // Report 1-based lines and columns. return Err(FormatterError::Parsing { - start_line: start.row() + 1, - start_column: start.column() + 1, - end_line: end.row() + 1, - end_column: end.column() + 1, + start_line: start.row + 1, + start_column: start.column + 1, + end_line: end.row + 1, + end_column: end.column + 1, }); } @@ -259,8 +257,8 @@ fn collect_leaf_ids(matches: &[LocalQueryMatch], capture_names: &[String]) -> Ha for m in matches { for c in &m.captures { - if c.name(capture_names) == "leaf" { - ids.insert(c.node().id()); + if capture_names[c.index as usize] == "leaf" { + ids.insert(c.node.id()); } } } @@ -288,25 +286,33 @@ fn handle_predicate( predicate: &QueryPredicate, predicates: &QueryPredicates, ) -> FormatterResult { - let operator = &*predicate.operator(); + let operator = predicate.operator.to_string(); + let arg1 = predicate + .args + .get(0) + .ok_or_else(|| FormatterError::Query(format!("{operator} needs an argument"), None)); if "delimiter!" == operator { - let arg = - predicate.args().into_iter().next().ok_or_else(|| { - FormatterError::Query(format!("{operator} needs an argument"), None) - })?; - Ok(QueryPredicates { - delimiter: Some(arg), - ..predicates.clone() - }) + match arg1? { + tree_sitter::QueryPredicateArg::Capture(_) => Err(FormatterError::Query( + format!("{operator} capture arguments are as of yet unsupported"), + None, + )), + tree_sitter::QueryPredicateArg::String(str) => Ok(QueryPredicates { + delimiter: Some(str.as_ref().to_owned()), + ..predicates.clone() + }), + } } else if "scope_id!" == operator { - let arg = - predicate.args().into_iter().next().ok_or_else(|| { - FormatterError::Query(format!("{operator} needs an argument"), None) - })?; - Ok(QueryPredicates { - scope_id: Some(arg), - ..predicates.clone() - }) + match arg1? { + tree_sitter::QueryPredicateArg::Capture(_) => Err(FormatterError::Query( + format!("{operator} capture arguments are as of yet unsupported"), + None, + )), + tree_sitter::QueryPredicateArg::String(str) => Ok(QueryPredicates { + scope_id: Some(str.as_ref().to_owned()), + ..predicates.clone() + }), + } } else if "single_line_only!" == operator { Ok(QueryPredicates { single_line_only: true, @@ -318,23 +324,27 @@ fn handle_predicate( ..predicates.clone() }) } else if "single_line_scope_only!" == operator { - let arg = - predicate.args().into_iter().next().ok_or_else(|| { - FormatterError::Query(format!("{operator} needs an argument"), None) - })?; - Ok(QueryPredicates { - single_line_scope_only: Some(arg), - ..predicates.clone() - }) + match arg1? { + tree_sitter::QueryPredicateArg::Capture(_) => Err(FormatterError::Query( + format!("{operator} capture arguments are as of yet unsupported"), + None, + )), + tree_sitter::QueryPredicateArg::String(str) => Ok(QueryPredicates { + single_line_scope_only: Some(str.as_ref().to_owned()), + ..predicates.clone() + }), + } } else if "multi_line_scope_only!" == operator { - let arg = - predicate.args().into_iter().next().ok_or_else(|| { - FormatterError::Query(format!("{operator} needs an argument"), None) - })?; - Ok(QueryPredicates { - multi_line_scope_only: Some(arg), - ..predicates.clone() - }) + match arg1? { + tree_sitter::QueryPredicateArg::Capture(_) => Err(FormatterError::Query( + format!("{operator} capture arguments are as of yet unsupported"), + None, + )), + tree_sitter::QueryPredicateArg::String(str) => Ok(QueryPredicates { + multi_line_scope_only: Some(str.as_ref().to_owned()), + ..predicates.clone() + }), + } } else { Ok(predicates.clone()) } @@ -386,8 +396,8 @@ fn check_predicates(predicates: &QueryPredicates) -> FormatterResult<()> { fn check_input_exhaustivity( ref_match_count: usize, original_query: &TopiaryQuery, - grammar: &tree_sitter_facade::Language, - root: &Node, + grammar: ::tree_sitter::Language, + root: Node, source: &[u8], ) -> FormatterResult<()> { let pattern_count = original_query.query.pattern_count(); @@ -409,7 +419,8 @@ fn check_input_exhaustivity( .map_err(|e| FormatterError::Query("Error parsing query file".into(), Some(e)))?; query.disable_pattern(i); let mut cursor = QueryCursor::new(); - let match_count = query.matches(root, source, &mut cursor).count(); + let match_count = cursor.matches(&query, root, source).count(); + if match_count == ref_match_count { let index_start = query.start_byte_for_pattern(i); let index_end = if i == pattern_count - 1 { diff --git a/topiary-playground/Cargo.toml b/topiary-playground/Cargo.toml index ec05dd64..0c9f3b7b 100644 --- a/topiary-playground/Cargo.toml +++ b/topiary-playground/Cargo.toml @@ -25,7 +25,6 @@ crate-type = ["cdylib"] [dependencies] cfg-if = { workspace = true } topiary-core = { path = "../topiary-core" } -tree-sitter-facade = { workspace = true } wasm-bindgen = { workspace = true } wasm-bindgen-futures = { workspace = true }