function_grep/
supported_languages.rs

1use tree_sitter::Language;
2
3pub trait SupportedLanguage: Send + Sync {
4    /// The name of this language
5    fn name(&self) -> &'static str;
6    /// The list of file extensions used for this language.
7    fn file_exts(&self) -> &'static [&'static str];
8    /// The [`tree_sitter::Language`] for this language
9    fn language(&self) -> Language;
10    // TODO: type saftey for query
11    /// Given an identifier(name)
12    /// this should produce a string that is the sexp of a query
13    /// that finds all matches of function-like things with given name
14    /// # Example:
15    /// ```rust
16    /// fn query(name: &str) -> String {
17    ///     format!("((function_item
18    ///   name: (identifier) @method-name)
19    ///   @method-definition
20    /// (#eq? @method-name {name}))
21    /// ((let_declaration
22    ///   pattern: (identifier) @method-name
23    ///   value: (closure_expression)) @method-definition
24    /// (#eq? @method-name {name}))
25    /// ((const_item
26    ///   name: (identifier) @method-name
27    ///   value: (closure_expression)) @method-definition
28    /// (#eq? @method-name {name}))
29    /// ((static_item
30    ///   name: (identifier) @method-name
31    ///   value: (closure_expression)) @method-definition
32    /// (#eq? @method-name {name}))")
33    /// }
34    /// ```
35    fn query(&self, name: &str) -> String;
36}
37
38#[macro_export]
39/// Use to more easily make new [`SupportedLanguage`]s.
40/// First provide the name (which is used as the type of the language), followed by the tree sitter
41/// languge in parenthesis, next you put the file extensions in brackets with a leading .
42/// to specify the query we use ?= variable -> string literal query.
43/// In the query you when you want use the variable just do {variable}.
44///
45/// Example:
46/// ```rust
47/// use function_grep::construct_language;
48/// use function_grep::supported_languages::SupportedLanguage;
49/// use tree_sitter::Language;
50/// construct_language!(C(tree_sitter_c::language()).[c h]?=
51///    name ->  "((function_definition
52///  declarator:
53///  (function_declarator declarator: (identifier) @method-name))
54///  @method-definition
55///  (#eq? @method-name {name}))
56/// ((declaration declarator:
57///  (function_declarator declarator: (identifier) @method-name))
58///  @method-definition
59///  (#eq? @method-name {name}))"
60/// );
61/// ```
62macro_rules! construct_language {
63    ($name:ident($tslang:expr).[$($ext:ident)+]?=$query_name:ident->$query:literal ) => {
64        #[derive(Debug, Clone, Copy)]
65        pub struct $name;
66        impl SupportedLanguage for $name {
67            fn query(&self, $query_name: &str) -> String {
68                format!($query)
69            }
70
71            fn name(&self) -> &'static str {
72                stringify!($name)
73            }
74
75            fn file_exts(&self) -> &'static [&'static str] {
76                &[$(stringify!($ext)),+]
77            }
78
79            fn language(&self) -> Language {
80                $tslang
81            }
82        }
83    };
84}
85
86#[cfg(feature = "c")]
87construct_language!(C(tree_sitter_c::language()).[c h]?=
88   name ->  "((function_definition
89 declarator:
90 (function_declarator declarator: (identifier) @method-name))
91 @method-definition
92 (#eq? @method-name {name}))
93((declaration declarator:
94 (function_declarator declarator: (identifier) @method-name))
95 @method-definition
96 (#eq? @method-name {name}))"
97);
98
99#[cfg(feature = "rust")]
100construct_language!(Rust(tree_sitter_rust::language()).[rs]?=name->
101
102            "((function_item
103  name: (identifier) @method-name)
104  @method-definition
105(#eq? @method-name {name}))
106((let_declaration
107  pattern: (identifier) @method-name
108  value: (closure_expression)) @method-definition
109(#eq? @method-name {name}))
110((const_item
111  name: (identifier) @method-name
112  value: (closure_expression)) @method-definition
113(#eq? @method-name {name}))
114((static_item
115  name: (identifier) @method-name
116  value: (closure_expression)) @method-definition
117(#eq? @method-name {name}))"
118);
119
120#[cfg(feature = "python")]
121construct_language!(Python(tree_sitter_python::language()).[py]?=name->
122
123            "((function_definition
124 name: (identifier) @method-name)
125 @method-definition 
126(#eq? @method-name {name}))
127((assignment 
128 left: ((identifier) @method-name) 
129 right: (lambda)) @method-definition 
130(#eq? @method-name {name}))
131"
132);
133
134#[cfg(feature = "java")]
135construct_language!(Java(tree_sitter_java::language()).[java]?=name->
136"((method_declaration
137 name: (identifier) @method-name)
138 @method-definition
139(#eq? @method-name {name}))
140((local_variable_declaration
141 declarator: (variable_declarator
142 name: (identifier) @method-name
143 value: (lambda_expression)))
144 @method-definition
145(#eq? @method-name {name}))
146((field_declaration
147 declarator: (variable_declarator
148 name: (identifier) @method-name
149 value: (lambda_expression)))
150 @method-definition
151(#eq? @method-name {name}))"
152);
153
154#[cfg(feature = "ocaml")]
155construct_language!(OCaml(tree_sitter_ocaml::language_ocaml()).[ml]?=name->
156"((value_definition
157 (let_binding pattern: (value_name) @method-name (parameter)))
158 @method-defintion
159(#eq? @method-name {name}))
160((value_definition
161 (let_binding pattern: (parenthesized_operator) @method-name (parameter)))
162 @method-defintion
163(#eq? @method-name {name}))
164((value_definition
165 (let_binding pattern: (value_name) @method-name body: (function_expression)))
166 @method-defintion
167(#eq? @method-name {name}))
168((value_definition
169 (let_binding pattern: (value_name) @method-name body: (fun_expression)))
170 @method-defintion
171(#eq? @method-name {name}))");
172
173#[must_use]
174/// Use this to obtain some defualt languages (what languages are presend depend of the features
175/// you allow).
176pub fn predefined_languages() -> &'static [&'static dyn SupportedLanguage] {
177    &[
178        #[cfg(feature = "rust")]
179        &Rust,
180        #[cfg(feature = "c")]
181        &C,
182        #[cfg(feature = "python")]
183        &Python,
184        #[cfg(feature = "java")]
185        &Java,
186        #[cfg(feature = "ocaml")]
187        &OCaml,
188    ]
189}