1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
use std::fmt::Debug;
use std::path::{Component, Path};
use clap::ValueEnum;
use const_format::formatcp;
use super::{LanguageScoper, QuerySource, TSLanguage, TSQuery, TSQueryError, TreeSitterRegex};
use crate::find::Find;
use crate::scoping::langs::IGNORE;
/// A compiled query for the Go language.
#[derive(Debug)]
pub struct CompiledQuery(super::CompiledQuery);
impl TryFrom<QuerySource> for CompiledQuery {
type Error = TSQueryError;
/// Create a new compiled query for the Go language.
///
/// # Errors
///
/// See the concrete type of the [`TSQueryError`](tree_sitter::QueryError)variant for when this method errors.
fn try_from(query: QuerySource) -> Result<Self, Self::Error> {
let q = super::CompiledQuery::from_source(&tree_sitter_go::LANGUAGE.into(), &query)?;
Ok(Self(q))
}
}
impl From<PreparedQuery> for CompiledQuery {
fn from(query: PreparedQuery) -> Self {
Self(super::CompiledQuery::from_prepared_query(
&tree_sitter_go::LANGUAGE.into(),
&query.as_string(),
))
}
}
/// Prepared tree-sitter queries for Go.
#[derive(Debug, Clone, ValueEnum)]
pub enum PreparedQuery {
/// Comments (single- and multi-line).
Comments,
/// Strings (interpreted and raw; excluding struct tags).
Strings,
/// Imports.
Imports,
/// Expressions (all of them!).
Expression,
/// Type definitions.
TypeDef,
/// Type alias assignments.
TypeAlias,
/// `struct` type definitions.
Struct,
/// `struct` type definitions, where the struct name matches the provided pattern.
#[value(skip)] // Non-unit enum variants need: https://github.com/clap-rs/clap/issues/2621
StructNamed(TreeSitterRegex),
/// `interface` type definitions.
Interface,
/// `interface` type definitions, where the interface name matches the provided pattern.
#[value(skip)] // Non-unit enum variants need: https://github.com/clap-rs/clap/issues/2621
InterfaceNamed(TreeSitterRegex),
/// `const` specifications.
Const,
/// `var` specifications.
Var,
/// `func` definitions.
Func,
/// `func` definitions, where the function name matches the provided pattern.
#[value(skip)] // Non-unit enum variants need: https://github.com/clap-rs/clap/issues/2621
FuncNamed(TreeSitterRegex),
/// Method `func` definitions (`func (recv Recv) SomeFunc()`).
Method,
/// Free `func` definitions (`func SomeFunc()`).
FreeFunc,
/// `func init()` definitions.
InitFunc,
/// Type parameters (generics).
TypeParams,
/// `defer` blocks.
Defer,
/// `select` blocks.
Select,
/// `go` blocks.
Go,
/// `switch` blocks.
Switch,
/// Labeled statements.
Labeled,
/// `goto` statements.
Goto,
/// Struct tags.
StructTags,
}
impl PreparedQuery {
fn as_string(&self) -> String {
match self {
Self::Comments => "(comment) @comment".into(),
Self::Strings => {
formatcp!(
r"
[
(raw_string_literal)
(interpreted_string_literal)
(import_spec (interpreted_string_literal)) @{0}
(field_declaration tag: (raw_string_literal)) @{0}
]
@string",
IGNORE
)
}
.into(),
Self::Imports => r"(import_spec path: (interpreted_string_literal) @path)".into(),
Self::Expression => r"(_expression) @expr".into(),
Self::TypeDef => r"(type_declaration) @type_decl".into(),
Self::TypeAlias => r"(type_alias) @type_alias".into(),
Self::Struct => r"(type_declaration (type_spec type: (struct_type))) @struct".into(),
Self::StructNamed(pattern) => {
format!(
r#"(
type_declaration(
type_spec
name: _ @name
type: (struct_type)
)
(#match? @name "{pattern}")
) @struct"#
)
}
Self::Interface => {
r"(type_declaration (type_spec type: (interface_type))) @interface".into()
}
Self::InterfaceNamed(pattern) => {
format!(
r#"(
type_declaration(
type_spec
name: _ @name
type: (interface_type)
)
(#match? @name "{pattern}")
) @interface"#
)
}
Self::Const => "(const_spec) @const".into(),
Self::Var => "(var_spec) @var".into(),
Self::Func => {
r"
[
(method_declaration)
(function_declaration)
(func_literal)
] @func"
}
.into(),
Self::FuncNamed(pattern) => {
format!(
r#"(
[
(method_declaration
name: _ @name
)
(function_declaration
name: _ @name
)
]
(#match? @name "{pattern}")
) @func"#
)
}
Self::Method => "(method_declaration) @method".into(),
Self::FreeFunc => "(function_declaration) @free_func".into(),
Self::InitFunc => {
r#"(function_declaration
name: (identifier) @id (#eq? @id "init")
) @init_func"#
}
.into(),
Self::TypeParams => "(type_parameter_declaration) @type_params".into(),
Self::Defer => "(defer_statement) @defer".into(),
Self::Select => "(select_statement) @select".into(),
Self::Go => "(go_statement) @go".into(),
Self::Switch => "(expression_switch_statement) @switch".into(),
Self::Labeled => "(labeled_statement) @labeled".into(),
Self::Goto => "(goto_statement) @goto".into(),
Self::StructTags => "(field_declaration tag: (raw_string_literal) @tag)".into(),
}
}
}
impl LanguageScoper for CompiledQuery {
fn lang() -> TSLanguage {
tree_sitter_go::LANGUAGE.into()
}
fn pos_query(&self) -> &TSQuery {
&self.0.positive_query
}
fn neg_query(&self) -> Option<&TSQuery> {
self.0.negative_query.as_ref()
}
}
impl Find for CompiledQuery {
fn extensions(&self) -> &'static [&'static str] {
&["go"]
}
fn is_path_invalid(&self, path: &Path) -> bool {
for component in path.components() {
if let Component::Normal(item) = component {
// https://go.dev/ref/mod#vendoring
if item.as_encoded_bytes() == b"vendor" {
return true;
}
}
}
false
}
}