Skip to content

Commit e621791

Browse files
committed
[function-grep] made filter mutable so now we can use tree sitter query
effeciently as a generic filter but this means everything else related to filters needs to be mutable, so we loose paralelization for filtering
1 parent df1e359 commit e621791

File tree

6 files changed

+128
-16
lines changed

6 files changed

+128
-16
lines changed

function-grep/src/filter.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use general_filters::{
22
FunctionInImpl, FunctionInLines, FunctionWithParameterPython, FunctionWithParameterRust,
3+
TreeSitterQueryFilter,
34
};
45
use std::{
56
collections::{hash_map, HashMap},
@@ -126,7 +127,7 @@ pub trait HasFilterInformation {
126127
}
127128
}
128129
}
129-
type FilterFunction = Box<dyn Fn(&Node<'_>, &str) -> bool + Send + Sync>;
130+
type FilterFunction = Box<dyn FnMut(&Node<'_>, &str) -> bool + Send + Sync>;
130131

131132
// TODO: make our own FromStr that also requires the proggramer to sepcify that attributes each
132133
// filter has and their type so that we can make macro that creates parser, and also so that we can
@@ -163,7 +164,7 @@ impl<Supports: std::fmt::Debug> std::fmt::Debug for InstantiatedFilter<Supports>
163164

164165
impl<Supports> InstantiatedFilter<Supports> {
165166
#[must_use]
166-
pub fn filter(&self, node: &Node<'_>, code: &str) -> bool {
167+
pub fn filter(&mut self, node: &Node<'_>, code: &str) -> bool {
167168
(self.filter_function)(node, code)
168169
}
169170

@@ -319,6 +320,15 @@ impl Filters<'static> {
319320
FunctionInLines.filter_info().filter_name().to_string(),
320321
SingleOrMany::All(&FunctionInLines as &'static dyn Filter<Supports = All>),
321322
),
323+
(
324+
TreeSitterQueryFilter
325+
.filter_info()
326+
.filter_name()
327+
.to_string(),
328+
SingleOrMany::All(
329+
&TreeSitterQueryFilter as &'static dyn Filter<Supports = All>,
330+
),
331+
),
322332
(
323333
FunctionInImpl.filter_info().filter_name().to_string(),
324334
SingleOrMany::Many(Many {

function-grep/src/filter/general_filters.rs

Lines changed: 104 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::collections::HashMap;
1+
use std::collections::{HashMap, HashSet};
22

33
use tree_sitter::{Node, Query, QueryCursor};
44

@@ -113,6 +113,62 @@ impl HasFilterInformation for FunctionInImpl {
113113
}
114114
}
115115

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+
}
116172
pub struct FunctionWithParameterRust;
117173
pub struct FunctionWithParameterPython;
118174

@@ -223,3 +279,50 @@ fn parse_with_param(s: &str) -> Result<String, String> {
223279
}
224280
}
225281
}
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+
}

function-grep/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,11 @@ impl ParsedFile {
140140
///
141141
/// # Errors
142142
/// If the filter [`f`] filters out all the results of this file
143-
pub fn filter(&self, f: &InstantiatedFilterType) -> Result<Self, Error> {
143+
pub fn filter(&self, f: &mut InstantiatedFilterType) -> Result<Self, Error> {
144144
match f {
145145
InstantiatedFilterType::Many(supported_language) => {
146146
let lang = self.language();
147-
let filter = supported_language.filters.get(lang);
147+
let filter = supported_language.filters.get_mut(lang);
148148
let filter = filter.ok_or(Error::FilterLangaugeMismatch);
149149
filter.and_then(|f| self.filter_inner(f))
150150
}
@@ -153,7 +153,7 @@ impl ParsedFile {
153153
}
154154
}
155155

156-
fn filter_inner<T>(&self, f: &InstantiatedFilter<T>) -> Result<Self, Error> {
156+
fn filter_inner<T>(&self, f: &mut InstantiatedFilter<T>) -> Result<Self, Error> {
157157
let root = self.tree.root_node();
158158
let ranges: Box<[Range]> = self
159159
.ranges()

function_history_backend_thread/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,12 +125,12 @@ pub fn command_thread(
125125
),
126126
}
127127
}
128-
FullCommand::Filter(filter) => {
128+
FullCommand::Filter(mut filter) => {
129129
if let CommandResult::History(hist) = filter.thing {
130130
if log {
131131
log::info!("Filtering history with filter {:?}", filter.filter);
132132
}
133-
match hist.filter_by(&filter.filter) {
133+
match hist.filter_by(&mut filter.filter) {
134134
Ok(hist) => {
135135
if log {
136136
log::info!("Filtered history");

git-function-history-lib/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,8 +314,7 @@ pub fn get_function_history(
314314
if commits.is_empty() {
315315
Err("no history found")?;
316316
}
317-
let fh = FunctionHistory::new(name.to_string(), commits);
318-
Ok(fh)
317+
Ok(FunctionHistory::new(name.to_string(), commits))
319318
}
320319

321320
fn sender(

git-function-history-lib/src/types.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ impl Commit {
119119
/// # Errors
120120
///
121121
/// Will result in an `Err` if a non-valid filter is given, or if no results are found for the given filter
122-
pub fn filter_by(&self, filter: &Filter) -> Result<Self, ErrorReason> {
122+
pub fn filter_by(&self, filter: &mut Filter) -> Result<Self, ErrorReason> {
123123
match filter {
124124
Filter::FileAbsolute(_)
125125
| Filter::FileRelative(_)
@@ -142,14 +142,14 @@ impl Commit {
142142
None
143143
}
144144
}
145-
Filter::FileRelative(file) => {
145+
Filter::FileRelative(ref file) => {
146146
if f.file_name()?.ends_with(file) {
147147
Some(f.clone())
148148
} else {
149149
None
150150
}
151151
}
152-
Filter::Directory(dir) => {
152+
Filter::Directory(ref dir) => {
153153
if f.file_name()?.contains(dir) {
154154
Some(f.clone())
155155
} else {
@@ -324,10 +324,10 @@ impl FunctionHistory {
324324
/// # Errors
325325
///
326326
/// returns `Err` if no files or commits are match the filter specified
327-
pub fn filter_by(&self, filter: &Filter) -> Result<Self, ErrorReason> {
327+
pub fn filter_by(&self, filter: &mut Filter) -> Result<Self, ErrorReason> {
328328
#[cfg(feature = "parallel")]
329329
let t = self.commit_history.par_iter();
330-
#[cfg(not(feature = "parallel"))]
330+
// #[cfg(not(feature = "parallel"))]
331331
let t = self.commit_history.iter();
332332
let vec: Vec<Commit> = t
333333
.filter_map(|f| match filter {
@@ -378,7 +378,7 @@ impl FunctionHistory {
378378
None
379379
}
380380
}
381-
Filter::Message(message) => {
381+
Filter::Message(ref message) => {
382382
if f.message.contains(message) {
383383
Some(f.clone())
384384
} else {

0 commit comments

Comments
 (0)