|
1 |
| -use std::collections::HashMap; |
| 1 | +use std::collections::{HashMap, HashSet}; |
2 | 2 |
|
3 | 3 | use tree_sitter::{Node, Query, QueryCursor};
|
4 | 4 |
|
@@ -113,6 +113,62 @@ impl HasFilterInformation for FunctionInImpl {
|
113 | 113 | }
|
114 | 114 | }
|
115 | 115 |
|
| 116 | +pub struct TreeSitterQueryFilter; |
| 117 | +impl HasFilterInformation for TreeSitterQueryFilter { |
| 118 | + fn filter_name(&self) -> String { |
| 119 | + "tree_sitter_query".to_string() |
| 120 | + } |
| 121 | + |
| 122 | + fn description(&self) -> String { |
| 123 | + "filter using an arbritrary tree sitter query".to_string() |
| 124 | + } |
| 125 | + |
| 126 | + fn supports(&self) -> Self::Supports { |
| 127 | + All |
| 128 | + } |
| 129 | + |
| 130 | + fn attributes(&self) -> Attributes { |
| 131 | + HashMap::from([(Attribute("query".to_string()), AttributeType::String)]) |
| 132 | + } |
| 133 | + |
| 134 | + type Supports = All; |
| 135 | +} |
| 136 | +impl Filter for TreeSitterQueryFilter { |
| 137 | + fn parse_filter(&self, s: &str) -> Result<FilterFunction, String> { |
| 138 | + let mut languages: HashMap<tree_sitter::Language, Result<Query, ()>> = HashMap::new(); |
| 139 | + let query: String = { |
| 140 | + let mut substring = s.split(' ').filter(|s| *s != " ").peekable(); |
| 141 | + let fst = substring.peek().ok_or( |
| 142 | + "invalid options for tree_sitter_query filter\nexpected [string] or name: [string]", |
| 143 | + )?; |
| 144 | + match *fst { |
| 145 | + "query:" => { |
| 146 | + substring.next(); |
| 147 | + substring.collect::<Vec<_>>().join(" ") |
| 148 | + } |
| 149 | + |
| 150 | + _ => substring.collect::<Vec<_>>().join(" "), |
| 151 | + } |
| 152 | + }; |
| 153 | + Ok(Box::new(move |node: &Node<'_>, code| { |
| 154 | + let language = node.language(); |
| 155 | + let language: &tree_sitter::Language = &language; |
| 156 | + let mut cursor = QueryCursor::new(); |
| 157 | + cursor.set_max_start_depth(Some(0)); |
| 158 | + let text_provider = code.as_bytes(); |
| 159 | + match languages |
| 160 | + .entry(language.clone()) |
| 161 | + .or_insert(Query::new(language, &query).map_err(|_| ())) |
| 162 | + { |
| 163 | + Ok(query) => cursor |
| 164 | + .matches(&query, *node, text_provider) |
| 165 | + .next() |
| 166 | + .is_some(), |
| 167 | + Err(_) => false, |
| 168 | + } |
| 169 | + })) |
| 170 | + } |
| 171 | +} |
116 | 172 | pub struct FunctionWithParameterRust;
|
117 | 173 | pub struct FunctionWithParameterPython;
|
118 | 174 |
|
@@ -223,3 +279,50 @@ fn parse_with_param(s: &str) -> Result<String, String> {
|
223 | 279 | }
|
224 | 280 | }
|
225 | 281 | }
|
| 282 | + |
| 283 | +#[cfg(test)] |
| 284 | +mod tests { |
| 285 | + use crate::{ |
| 286 | + filter::Filter, |
| 287 | + supported_languages::{Rust, SupportedLanguage}, |
| 288 | + ParsedFile, |
| 289 | + }; |
| 290 | + |
| 291 | + use super::TreeSitterQueryFilter; |
| 292 | + |
| 293 | + #[test] |
| 294 | + fn tree_sitter_query() { |
| 295 | + let file = ParsedFile::search_file( |
| 296 | + include_str!("../../../git-function-history-lib/src/test_functions.rs"), |
| 297 | + &Rust.to_language("empty_test").unwrap(), |
| 298 | + ) |
| 299 | + .unwrap(); |
| 300 | + println!("original:\n{file}"); |
| 301 | + let mut filter = TreeSitterQueryFilter |
| 302 | + .to_filter(" (function_item type_parameters: (type_parameters))") |
| 303 | + .unwrap(); |
| 304 | + |
| 305 | + let filtered = file.filter_inner(&mut filter); |
| 306 | + assert!(filtered.is_ok()); |
| 307 | + let filtered = filtered.unwrap(); |
| 308 | + println!("filtered:\n{filtered}"); |
| 309 | + } |
| 310 | + #[test] |
| 311 | + fn tree_sitter_query_with_predicate() { |
| 312 | + let file = ParsedFile::search_file( |
| 313 | + include_str!("../../../git-function-history-lib/src/test_functions.rs"), |
| 314 | + &Rust.to_language("empty_test").unwrap(), |
| 315 | + ) |
| 316 | + .unwrap(); |
| 317 | + println!("original:\n{file}"); |
| 318 | + let mut filter = TreeSitterQueryFilter |
| 319 | + .to_filter(" (function_item type_parameters: (type_parameters (type_identifier) @type) (#eq? @type \"T\") |
| 320 | +)") |
| 321 | + .unwrap(); |
| 322 | + |
| 323 | + let filtered = file.filter_inner(&mut filter); |
| 324 | + assert!(filtered.is_ok()); |
| 325 | + let filtered = filtered.unwrap(); |
| 326 | + println!("filtered:\n{filtered}"); |
| 327 | + } |
| 328 | +} |
0 commit comments