tree_stump 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,311 @@
1
+ use magnus::{function, method, prelude::*, typed_data, value::Lazy, Error, RClass, Ruby};
2
+
3
+ use libloading::Library;
4
+ use tree_sitter::ffi::TSLanguage;
5
+
6
+ use std::collections::HashMap;
7
+ use std::sync::Mutex;
8
+ use std::sync::OnceLock;
9
+
10
+ mod data;
11
+ mod language;
12
+ mod parser;
13
+ mod query;
14
+ mod tree;
15
+ mod util;
16
+
17
+ use crate::language::{LanguageRef, LookaheadIterator};
18
+ use crate::parser::Parser;
19
+ use crate::query::{Query, QueryCursor, QueryMatch};
20
+ use crate::tree::{Node, Tree, TreeCursor};
21
+
22
+ pub static LANG_LIBRARIES: OnceLock<Mutex<HashMap<String, Library>>> = OnceLock::new();
23
+ pub static LANG_LANGUAGES: OnceLock<Mutex<HashMap<String, tree_sitter::Language>>> =
24
+ OnceLock::new();
25
+
26
+ pub static QUERY_CAPTURE_CLASS: Lazy<RClass> =
27
+ Lazy::new(|ruby| ruby.define_struct(None, ("node", "index")).unwrap());
28
+
29
+ fn register_lang(lang: String, path: String) -> () {
30
+ let func_name = String::from("tree_sitter_") + &lang;
31
+ let language;
32
+
33
+ let libraries = LANG_LIBRARIES.get_or_init(|| Mutex::new(HashMap::new()));
34
+ let languages = LANG_LANGUAGES.get_or_init(|| Mutex::new(HashMap::new()));
35
+
36
+ unsafe {
37
+ let mut libraries = libraries.lock().unwrap();
38
+ let lib = libraries.entry(lang.clone()).or_insert_with(|| {
39
+ let loaded = Library::new(path).expect("Failed to load library");
40
+ loaded
41
+ });
42
+
43
+ let func: libloading::Symbol<unsafe extern "C" fn() -> *const TSLanguage> =
44
+ lib.get(func_name.as_bytes()).unwrap();
45
+
46
+ language = tree_sitter::Language::from_raw(func());
47
+
48
+ let mut languages = languages.lock().unwrap();
49
+ languages.insert(lang.to_string(), language);
50
+ };
51
+ }
52
+
53
+ fn available_langs() -> Vec<String> {
54
+ let languages = LANG_LANGUAGES.get_or_init(|| Mutex::new(HashMap::new()));
55
+ let languages = languages.lock().unwrap();
56
+ languages.keys().cloned().collect()
57
+ }
58
+
59
+ #[magnus::init]
60
+ fn init(ruby: &Ruby) -> Result<(), Error> {
61
+ let namespace = ruby.define_module("TreeStump")?;
62
+ namespace.define_singleton_method("register_lang", function!(register_lang, 2))?;
63
+ namespace.define_singleton_method("available_langs", function!(available_langs, 0))?;
64
+
65
+ let parser_class = namespace.define_class("Parser", ruby.class_object())?;
66
+ parser_class.define_singleton_method("new", function!(Parser::new, 0))?;
67
+ parser_class.define_method("set_language", method!(Parser::set_language, 1))?;
68
+ parser_class.define_method("parse", method!(Parser::parse, 1))?;
69
+ parser_class.define_method("reset", method!(Parser::reset, 0))?;
70
+ parser_class.define_method("timeout_micros", method!(Parser::timeout_micros, 0))?;
71
+ parser_class.define_method("set_timeout_micros", method!(Parser::set_timeout_micros, 1))?;
72
+ parser_class.define_method("build_query", method!(Parser::build_query, 1))?;
73
+
74
+ let tree_class = namespace.define_class("Tree", ruby.class_object())?;
75
+ tree_class.define_method("root_node", method!(Tree::root_node, 0))?;
76
+ tree_class.define_method("language", method!(Tree::language, 0))?;
77
+ tree_class.define_method("walk", method!(Tree::walk, 0))?;
78
+ tree_class.define_method("print_dot_graph", method!(Tree::print_dot_graph, 1))?;
79
+ tree_class.define_method("inspect", method!(Tree::inspect, 0))?;
80
+
81
+ let tree_cursor_class = namespace.define_class("TreeCursor", ruby.class_object())?;
82
+ tree_cursor_class.define_method("node", method!(TreeCursor::node, 0))?;
83
+ tree_cursor_class.define_method("field_id", method!(TreeCursor::field_id, 0))?;
84
+ tree_cursor_class
85
+ .define_method("goto_first_child", method!(TreeCursor::goto_first_child, 0))?;
86
+ tree_cursor_class.define_method("goto_last_child", method!(TreeCursor::goto_last_child, 0))?;
87
+ tree_cursor_class.define_method("goto_parent", method!(TreeCursor::goto_parent, 0))?;
88
+ tree_cursor_class.define_method(
89
+ "goto_next_sibling",
90
+ method!(TreeCursor::goto_next_sibling, 0),
91
+ )?;
92
+ tree_cursor_class.define_method("goto_descendant", method!(TreeCursor::goto_descendant, 1))?;
93
+ tree_cursor_class.define_method(
94
+ "goto_previous_sibling",
95
+ method!(TreeCursor::goto_previous_sibling, 0),
96
+ )?;
97
+ tree_cursor_class.define_method(
98
+ "goto_first_child_for_byte",
99
+ method!(TreeCursor::goto_first_child_for_byte, 1),
100
+ )?;
101
+ tree_cursor_class.define_method("reset", method!(TreeCursor::reset, 1))?;
102
+ tree_cursor_class.define_method("reset_to", method!(TreeCursor::reset_to, 1))?;
103
+
104
+ let node_class = namespace.define_class("Node", ruby.class_object())?;
105
+ node_class.define_method("hash", method!(<Node as typed_data::Hash>::hash, 0))?;
106
+ node_class.define_method("==", method!(<Node as typed_data::IsEql>::is_eql, 1))?;
107
+ node_class.define_method("eql?", method!(<Node as typed_data::IsEql>::is_eql, 1))?;
108
+ node_class.define_method("id", method!(Node::id, 0))?;
109
+ node_class.define_method("kind", method!(Node::kind, 0))?;
110
+ node_class.define_method("kind_id", method!(Node::kind_id, 0))?;
111
+ node_class.define_method("grammar_id", method!(Node::grammar_id, 0))?;
112
+ node_class.define_method("grammar_name", method!(Node::grammar_name, 0))?;
113
+ node_class.define_method("language", method!(Node::language, 0))?;
114
+ node_class.define_method("is_named?", method!(Node::is_named, 0))?;
115
+ node_class.define_method("is_extra?", method!(Node::is_extra, 0))?;
116
+ node_class.define_method("has_changes?", method!(Node::has_changes, 0))?;
117
+ node_class.define_method("has_error?", method!(Node::has_error, 0))?;
118
+ node_class.define_method("is_error?", method!(Node::is_error, 0))?;
119
+ node_class.define_method("parse_state", method!(Node::parse_state, 0))?;
120
+ node_class.define_method("next_parse_state", method!(Node::next_parse_state, 0))?;
121
+ node_class.define_method("start_byte", method!(Node::start_byte, 0))?;
122
+ node_class.define_method("end_byte", method!(Node::end_byte, 0))?;
123
+ node_class.define_method("byte_range", method!(Node::byte_range, 0))?;
124
+ node_class.define_method("range", method!(Node::range, 0))?;
125
+ node_class.define_method("start_position", method!(Node::start_position, 0))?;
126
+ node_class.define_method("end_position", method!(Node::end_position, 0))?;
127
+ node_class.define_method("child", method!(Node::child, 1))?;
128
+ node_class.define_method("child_count", method!(Node::child_count, 0))?;
129
+ node_class.define_method("named_child", method!(Node::named_child, 1))?;
130
+ node_class.define_method("named_child_count", method!(Node::named_child_count, 0))?;
131
+ node_class.define_method("child_by_field_name", method!(Node::child_by_field_name, 1))?;
132
+ node_class.define_method("child_by_field_id", method!(Node::child_by_field_id, 1))?;
133
+ node_class.define_method(
134
+ "field_name_for_child",
135
+ method!(Node::field_name_for_child, 1),
136
+ )?;
137
+ node_class.define_method("parent", method!(Node::parent, 0))?;
138
+ node_class.define_method("children", method!(Node::children, 0))?;
139
+ node_class.define_method(
140
+ "children_with_cursor",
141
+ method!(Node::children_with_cursor, 1),
142
+ )?;
143
+ node_class.define_method(
144
+ "named_children_with_cursor",
145
+ method!(Node::named_children_with_cursor, 1),
146
+ )?;
147
+ node_class.define_method(
148
+ "children_by_field_name_with_cursor",
149
+ method!(Node::children_by_field_name_with_cursor, 2),
150
+ )?;
151
+ node_class.define_method(
152
+ "children_by_field_id_with_cursor",
153
+ method!(Node::children_by_field_id_with_cursor, 2),
154
+ )?;
155
+ node_class.define_method(
156
+ "child_containing_descendant",
157
+ method!(Node::child_containing_descendant, 1),
158
+ )?;
159
+ node_class.define_method("next_sibling", method!(Node::next_sibling, 0))?;
160
+ node_class.define_method("prev_sibling", method!(Node::prev_sibling, 0))?;
161
+ node_class.define_method("next_named_sibling", method!(Node::next_named_sibling, 0))?;
162
+ node_class.define_method("prev_named_sibling", method!(Node::prev_named_sibling, 0))?;
163
+ node_class.define_method("descendant_count", method!(Node::descendant_count, 0))?;
164
+ node_class.define_method(
165
+ "descendant_for_byte_range",
166
+ method!(Node::descendant_for_byte_range, 2),
167
+ )?;
168
+ node_class.define_method(
169
+ "named_descendant_for_byte_range",
170
+ method!(Node::named_descendant_for_byte_range, 2),
171
+ )?;
172
+ node_class.define_method(
173
+ "descendant_for_point_range",
174
+ method!(Node::descendant_for_point_range, 2),
175
+ )?;
176
+ node_class.define_method(
177
+ "named_descendant_for_point_range",
178
+ method!(Node::named_descendant_for_point_range, 2),
179
+ )?;
180
+
181
+ node_class.define_method("to_sexp", method!(Node::to_sexp, 0))?;
182
+ node_class.define_method("utf8_text", method!(Node::utf8_text, 1))?;
183
+ node_class.define_method("walk", method!(Node::walk, 0))?;
184
+
185
+ node_class.define_method("inspect", method!(Node::inspect, 0))?;
186
+ node_class.define_method("to_s", method!(Node::to_s, 0))?;
187
+
188
+ let point_class = namespace.define_class("Point", ruby.class_object())?;
189
+ point_class.define_singleton_method("new", function!(data::Point::new, 2))?;
190
+ point_class.define_method("hash", method!(<data::Point as typed_data::Hash>::hash, 0))?;
191
+ point_class.define_method("==", method!(<data::Point as typed_data::IsEql>::is_eql, 1))?;
192
+ point_class.define_method(
193
+ "eql?",
194
+ method!(<data::Point as typed_data::IsEql>::is_eql, 1),
195
+ )?;
196
+ point_class.define_method("row", method!(data::Point::get_row, 0))?;
197
+ point_class.define_method("column", method!(data::Point::get_column, 0))?;
198
+ point_class.define_method("inspect", method!(data::Point::inspect, 0))?;
199
+ point_class.define_method("to_s", method!(data::Point::to_s, 0))?;
200
+
201
+ let range_class = namespace.define_class("Range", ruby.class_object())?;
202
+ range_class.define_singleton_method("new", function!(data::Range::new, 4))?;
203
+ range_class.define_method("hash", method!(<data::Range as typed_data::Hash>::hash, 0))?;
204
+ range_class.define_method("==", method!(<data::Range as typed_data::IsEql>::is_eql, 1))?;
205
+ range_class.define_method(
206
+ "eql?",
207
+ method!(<data::Range as typed_data::IsEql>::is_eql, 1),
208
+ )?;
209
+ range_class.define_method("start_byte", method!(data::Range::get_start_byte, 0))?;
210
+ range_class.define_method("end_byte", method!(data::Range::get_end_byte, 0))?;
211
+ range_class.define_method("start_point", method!(data::Range::get_start_point, 0))?;
212
+ range_class.define_method("end_point", method!(data::Range::get_end_point, 0))?;
213
+ range_class.define_method("inspect", method!(data::Range::inspect, 0))?;
214
+ range_class.define_method("to_s", method!(data::Range::to_s, 0))?;
215
+
216
+ let language_class = namespace.define_class("LanguageRef", ruby.class_object())?;
217
+ language_class.define_method("version", method!(LanguageRef::version, 0))?;
218
+ language_class.define_method("node_kind_count", method!(LanguageRef::node_kind_count, 0))?;
219
+ language_class.define_method(
220
+ "parse_state_count",
221
+ method!(LanguageRef::parse_state_count, 0),
222
+ )?;
223
+ language_class.define_method(
224
+ "node_kind_for_id",
225
+ method!(LanguageRef::node_kind_for_id, 1),
226
+ )?;
227
+ language_class.define_method(
228
+ "id_for_node_kind",
229
+ method!(LanguageRef::id_for_node_kind, 2),
230
+ )?;
231
+ language_class.define_method(
232
+ "node_kind_is_named",
233
+ method!(LanguageRef::node_kind_is_named, 1),
234
+ )?;
235
+ language_class.define_method(
236
+ "node_kind_is_visible",
237
+ method!(LanguageRef::node_kind_is_visible, 1),
238
+ )?;
239
+ language_class.define_method(
240
+ "field_name_for_id",
241
+ method!(LanguageRef::field_name_for_id, 1),
242
+ )?;
243
+ language_class.define_method(
244
+ "field_id_for_name",
245
+ method!(LanguageRef::field_id_for_name, 1),
246
+ )?;
247
+ language_class.define_method("next_state", method!(LanguageRef::next_state, 2))?;
248
+ language_class.define_method(
249
+ "lookahead_iterator",
250
+ method!(LanguageRef::lookahead_iterator, 1),
251
+ )?;
252
+
253
+ let lookahead_iterator_class =
254
+ namespace.define_class("LookaheadIterator", ruby.class_object())?;
255
+ lookahead_iterator_class.define_method("next", method!(LookaheadIterator::next, 0))?;
256
+ lookahead_iterator_class.define_method(
257
+ "current_symbol_name",
258
+ method!(LookaheadIterator::current_symbol_name, 0),
259
+ )?;
260
+
261
+ let query_class = namespace.define_class("Query", ruby.class_object())?;
262
+ query_class.define_method(
263
+ "start_byte_for_pattern",
264
+ method!(Query::start_byte_for_pattern, 1),
265
+ )?;
266
+ query_class.define_method("pattern_count", method!(Query::pattern_count, 0))?;
267
+ query_class.define_method("capture_names", method!(Query::capture_names, 0))?;
268
+ query_class.define_method(
269
+ "capture_quantifiers",
270
+ method!(Query::capture_quantifiers, 1),
271
+ )?;
272
+ query_class.define_method(
273
+ "capture_index_for_name",
274
+ method!(Query::capture_index_for_name, 1),
275
+ )?;
276
+ query_class.define_method("disable_capture", method!(Query::disable_capture, 1))?;
277
+ query_class.define_method("disable_pattern", method!(Query::disable_pattern, 1))?;
278
+ query_class.define_method("is_pattern_rooted", method!(Query::is_pattern_rooted, 1))?;
279
+ query_class.define_method(
280
+ "is_pattern_guaranteed_at_step",
281
+ method!(Query::is_pattern_guaranteed_at_step, 1),
282
+ )?;
283
+
284
+ Lazy::force(&QUERY_CAPTURE_CLASS, ruby);
285
+ let struct_class = Lazy::try_get_inner(&QUERY_CAPTURE_CLASS).unwrap();
286
+ namespace.const_set("QueryCapture", struct_class)?;
287
+
288
+ let query_match_class = namespace.define_class("QueryMatch", ruby.class_object())?;
289
+ query_match_class.define_method("pattern_index", method!(QueryMatch::pattern_index, 0))?;
290
+ query_match_class.define_method("captures", method!(QueryMatch::captures, 0))?;
291
+
292
+ let query_cursor_class = namespace.define_class("QueryCursor", ruby.class_object())?;
293
+ query_cursor_class.define_singleton_method("new", function!(QueryCursor::new, 0))?;
294
+ query_cursor_class.define_method("match_limit", method!(QueryCursor::match_limit, 0))?;
295
+ query_cursor_class
296
+ .define_method("set_match_limit", method!(QueryCursor::set_match_limit, 1))?;
297
+ query_cursor_class.define_method(
298
+ "did_exceed_match_limit",
299
+ method!(QueryCursor::did_exceed_match_limit, 0),
300
+ )?;
301
+ query_cursor_class.define_method("matches", method!(QueryCursor::matches, 3))?;
302
+ query_cursor_class.define_method("set_byte_range", method!(QueryCursor::set_byte_range, 1))?;
303
+ query_cursor_class
304
+ .define_method("set_point_range", method!(QueryCursor::set_point_range, 1))?;
305
+ query_cursor_class.define_method(
306
+ "set_max_start_depth",
307
+ method!(QueryCursor::set_max_start_depth, 1),
308
+ )?;
309
+
310
+ Ok(())
311
+ }
@@ -0,0 +1,67 @@
1
+ use crate::query::Query;
2
+ use crate::tree::Tree;
3
+ use crate::util::build_error;
4
+ use crate::LANG_LANGUAGES;
5
+
6
+ use std::cell::RefCell;
7
+ use std::collections::HashMap;
8
+ use std::sync::{Arc, Mutex};
9
+
10
+ #[magnus::wrap(class = "TreeStump::Parser")]
11
+ pub struct Parser {
12
+ raw_parser: RefCell<tree_sitter::Parser>,
13
+ }
14
+
15
+ impl Parser {
16
+ pub fn new() -> Self {
17
+ Self {
18
+ raw_parser: RefCell::new(tree_sitter::Parser::new()),
19
+ }
20
+ }
21
+
22
+ pub fn set_language(&self, lang: String) -> Result<bool, magnus::Error> {
23
+ let languages = LANG_LANGUAGES.get_or_init(|| Mutex::new(HashMap::new()));
24
+ let languages = languages.lock().unwrap();
25
+ let language = languages.get(&lang);
26
+ match language {
27
+ Some(language) => {
28
+ let result = self.raw_parser.borrow_mut().set_language(language);
29
+ result.map_or_else(|e| Err(build_error(e.to_string())), |_| Ok(true))
30
+ }
31
+ None => Err(build_error(format!("Language {} is not registered", lang))),
32
+ }
33
+ }
34
+
35
+ pub fn parse(&self, source: String) -> Result<Tree, magnus::Error> {
36
+ let tree = self.raw_parser.borrow_mut().parse(source, None);
37
+
38
+ match tree {
39
+ Some(tree) => Ok(Tree::from(Arc::new(tree))),
40
+ None => Err(build_error("Failed to parse")),
41
+ }
42
+ }
43
+
44
+ pub fn reset(&self) {
45
+ self.raw_parser.borrow_mut().reset();
46
+ }
47
+
48
+ pub fn timeout_micros(&self) -> u64 {
49
+ self.raw_parser.borrow().timeout_micros()
50
+ }
51
+
52
+ pub fn set_timeout_micros(&self, timeout: u64) {
53
+ self.raw_parser.borrow_mut().set_timeout_micros(timeout);
54
+ }
55
+
56
+ fn language(&self) -> Option<tree_sitter::Language> {
57
+ self.raw_parser.borrow().language()
58
+ }
59
+
60
+ pub fn build_query(&self, source: String) -> Result<Query, magnus::Error> {
61
+ let lang = self.language();
62
+ lang.map_or_else(
63
+ || Err(build_error("Failed to get language from parser")),
64
+ |lang| Query::new(&lang, source),
65
+ )
66
+ }
67
+ }
@@ -0,0 +1,209 @@
1
+ use std::{cell::RefCell, sync::Arc};
2
+
3
+ use magnus::{
4
+ block::Yield,
5
+ symbol::IntoSymbol,
6
+ typed_data,
7
+ value::{InnerRef, Opaque, ReprValue},
8
+ Class, Error, IntoValue, RArray, RStruct, RTypedData, Ruby, Value,
9
+ };
10
+
11
+ use crate::{data::Point, tree::Node, util::build_error, QUERY_CAPTURE_CLASS};
12
+
13
+ #[magnus::wrap(class = "TreeStump::Query", free_immediately)]
14
+ #[derive(Debug)]
15
+ pub struct Query {
16
+ pub raw_query: RefCell<tree_sitter::Query>,
17
+ }
18
+
19
+ impl Query {
20
+ pub fn new(language: &tree_sitter::Language, source: String) -> Result<Self, magnus::Error> {
21
+ let raw_query = tree_sitter::Query::new(language, source.as_str());
22
+ let raw_query = raw_query.map_err(|e| build_error(e.to_string()));
23
+ raw_query.map(|q| Self {
24
+ raw_query: RefCell::new(q),
25
+ })
26
+ }
27
+
28
+ pub fn start_byte_for_pattern(&self, pattern_index: usize) -> usize {
29
+ self.raw_query
30
+ .borrow()
31
+ .start_byte_for_pattern(pattern_index)
32
+ }
33
+
34
+ pub fn pattern_count(&self) -> usize {
35
+ self.raw_query.borrow().pattern_count()
36
+ }
37
+
38
+ pub fn capture_names(&self) -> Vec<String> {
39
+ self.raw_query
40
+ .borrow()
41
+ .capture_names()
42
+ .iter()
43
+ .map(|s| s.to_string())
44
+ .collect()
45
+ }
46
+
47
+ pub fn capture_quantifiers(&self, index: usize) -> Result<RArray, Error> {
48
+ let raw_query = self.raw_query.borrow();
49
+ let quantifiers = raw_query
50
+ .capture_quantifiers(index)
51
+ .iter()
52
+ .map(|q| format!("{:?}", q).into_symbol());
53
+ let ruby = Ruby::get().expect("Ruby is not initialized");
54
+ let array = ruby.ary_from_iter(quantifiers);
55
+ Ok(array)
56
+ }
57
+
58
+ pub fn capture_index_for_name(&self, name: String) -> Option<u32> {
59
+ self.raw_query
60
+ .borrow()
61
+ .capture_index_for_name(name.as_str())
62
+ }
63
+
64
+ pub fn disable_capture(&self, name: String) {
65
+ self.raw_query.borrow_mut().disable_capture(&name);
66
+ }
67
+
68
+ pub fn disable_pattern(&self, index: usize) {
69
+ self.raw_query.borrow_mut().disable_pattern(index);
70
+ }
71
+
72
+ pub fn is_pattern_rooted(&self, index: usize) -> bool {
73
+ self.raw_query.borrow().is_pattern_rooted(index)
74
+ }
75
+
76
+ pub fn is_pattern_guaranteed_at_step(&self, index: usize) -> bool {
77
+ self.raw_query.borrow().is_pattern_guaranteed_at_step(index)
78
+ }
79
+ }
80
+
81
+ #[magnus::wrap(class = "TreeStump::QueryMatch", free_immediately)]
82
+ pub struct QueryMatch {
83
+ pattern_index: usize,
84
+ captures: Opaque<RArray>,
85
+ }
86
+
87
+ impl QueryMatch {
88
+ pub fn pattern_index(&self) -> usize {
89
+ self.pattern_index
90
+ }
91
+
92
+ pub fn captures(ruby: &Ruby, rb_self: typed_data::Obj<Self>) -> Result<RArray, Error> {
93
+ Ok(ruby.get_inner(rb_self.captures))
94
+ }
95
+ }
96
+
97
+ #[magnus::wrap(class = "TreeStump::QueryCursor", free_immediately)]
98
+ pub struct QueryCursor {
99
+ raw_cursor: RefCell<tree_sitter::QueryCursor>,
100
+ }
101
+
102
+ impl QueryCursor {
103
+ pub fn new() -> Self {
104
+ Self {
105
+ raw_cursor: RefCell::new(tree_sitter::QueryCursor::new()),
106
+ }
107
+ }
108
+
109
+ pub fn match_limit(&self) -> u32 {
110
+ self.raw_cursor.borrow().match_limit()
111
+ }
112
+
113
+ pub fn set_match_limit(&self, limit: u32) {
114
+ self.raw_cursor.borrow_mut().set_match_limit(limit);
115
+ }
116
+
117
+ pub fn did_exceed_match_limit(&self) -> bool {
118
+ self.raw_cursor.borrow().did_exceed_match_limit()
119
+ }
120
+
121
+ pub fn matches<'tree>(
122
+ ruby: &Ruby,
123
+ rb_self: typed_data::Obj<Self>,
124
+ query: typed_data::Obj<Query>,
125
+ node: typed_data::Obj<Node<'tree>>,
126
+ source: String,
127
+ ) -> Result<Yield<impl Iterator<Item = Value>>, Error> {
128
+ let mut cursor = rb_self.raw_cursor.borrow_mut();
129
+ let raw_query = query.raw_query.borrow();
130
+
131
+ let matches = cursor.matches(&raw_query, node.get_raw_node(), source.as_bytes());
132
+ let struct_class = QUERY_CAPTURE_CLASS.get_inner_ref_with(ruby);
133
+ let array = ruby.ary_new();
134
+
135
+ for m in matches {
136
+ let captures = ruby.ary_new();
137
+ for c in m.captures {
138
+ let r_struct = RStruct::from_value(
139
+ struct_class
140
+ .new_instance((Node::new(Arc::clone(&node.raw_tree), c.node), c.index))?,
141
+ );
142
+ captures.push(r_struct)?
143
+ }
144
+ let query_match = QueryMatch {
145
+ pattern_index: m.pattern_index,
146
+ captures: Opaque::from(captures),
147
+ };
148
+ array.push(query_match)?
149
+ }
150
+
151
+ if ruby.block_given() {
152
+ Ok(Yield::Iter(array.into_iter()))
153
+ } else {
154
+ Ok(Yield::Enumerator(rb_self.enumeratorize(
155
+ "matches",
156
+ (query, node, source.into_value()),
157
+ )))
158
+ }
159
+ }
160
+
161
+ pub fn set_byte_range(
162
+ _ruby: &Ruby,
163
+ rb_self: typed_data::Obj<Self>,
164
+ range: magnus::Range,
165
+ ) -> Result<typed_data::Obj<Self>, Error> {
166
+ let mut cursor = rb_self.raw_cursor.borrow_mut();
167
+ let len = range.funcall("size", ())?;
168
+ let std_range = range.to_range_with_len(len)?;
169
+ cursor.set_byte_range(std_range);
170
+ Ok(rb_self)
171
+ }
172
+
173
+ pub fn set_point_range(
174
+ _ruby: &Ruby,
175
+ rb_self: typed_data::Obj<Self>,
176
+ range: magnus::Range,
177
+ ) -> Result<typed_data::Obj<Self>, Error> {
178
+ let excl = range.excl();
179
+
180
+ if excl {
181
+ return Err(build_error("Point range must be inclusive"));
182
+ }
183
+
184
+ let start: Value = range.beg()?;
185
+ let end: Value = range.end()?;
186
+
187
+ let start_typed_data = RTypedData::from_value(start).expect("Expected typed data");
188
+ let start = start_typed_data.get::<Point>()?;
189
+
190
+ let end_typed_data = RTypedData::from_value(end).expect("Expected typed data");
191
+ let end = end_typed_data.get::<Point>()?;
192
+
193
+ let point_range = start.into_raw()..end.into_raw();
194
+
195
+ let mut cursor = rb_self.raw_cursor.borrow_mut();
196
+ cursor.set_point_range(point_range);
197
+ Ok(rb_self)
198
+ }
199
+
200
+ pub fn set_max_start_depth(
201
+ _ruby: &Ruby,
202
+ rb_self: typed_data::Obj<Self>,
203
+ depth: Option<u32>,
204
+ ) -> Result<typed_data::Obj<Self>, Error> {
205
+ let mut cursor = rb_self.raw_cursor.borrow_mut();
206
+ cursor.set_max_start_depth(depth);
207
+ Ok(rb_self)
208
+ }
209
+ }