tree_sitter 0.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/Makefile +116 -0
- data/README.md +482 -0
- data/ext/tree_sitter/Cargo.toml +18 -0
- data/ext/tree_sitter/extconf.rb +6 -0
- data/ext/tree_sitter/src/language.rs +152 -0
- data/ext/tree_sitter/src/lib.rs +140 -0
- data/ext/tree_sitter/src/node.rs +248 -0
- data/ext/tree_sitter/src/parser.rs +126 -0
- data/ext/tree_sitter/src/point.rs +45 -0
- data/ext/tree_sitter/src/query.rs +161 -0
- data/ext/tree_sitter/src/range.rs +50 -0
- data/ext/tree_sitter/src/tree.rs +38 -0
- data/lib/tree_sitter/formatting.rb +236 -0
- data/lib/tree_sitter/inserter.rb +306 -0
- data/lib/tree_sitter/query_rewriter.rb +314 -0
- data/lib/tree_sitter/refactor.rb +214 -0
- data/lib/tree_sitter/rewriter.rb +155 -0
- data/lib/tree_sitter/transformer.rb +324 -0
- data/lib/tree_sitter/version.rb +5 -0
- data/lib/tree_sitter.rb +25 -0
- metadata +113 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
use libloading::{Library, Symbol};
|
|
2
|
+
use magnus::{Error, Ruby};
|
|
3
|
+
use once_cell::sync::Lazy;
|
|
4
|
+
use std::collections::HashMap;
|
|
5
|
+
use std::sync::RwLock;
|
|
6
|
+
use tree_sitter_language::LanguageFn;
|
|
7
|
+
|
|
8
|
+
// Global registry of loaded languages
|
|
9
|
+
static LANGUAGES: Lazy<RwLock<HashMap<String, LoadedLanguage>>> =
|
|
10
|
+
Lazy::new(|| RwLock::new(HashMap::new()));
|
|
11
|
+
|
|
12
|
+
struct LoadedLanguage {
|
|
13
|
+
language: tree_sitter::Language,
|
|
14
|
+
#[allow(dead_code)]
|
|
15
|
+
library: Library, // Keep library alive!
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Safety: tree_sitter::Language is thread-safe
|
|
19
|
+
unsafe impl Send for LoadedLanguage {}
|
|
20
|
+
unsafe impl Sync for LoadedLanguage {}
|
|
21
|
+
|
|
22
|
+
/// Register a language from a shared library path
|
|
23
|
+
pub fn register_language(name: String, library_path: String) -> Result<(), Error> {
|
|
24
|
+
let ruby = Ruby::get().unwrap();
|
|
25
|
+
|
|
26
|
+
// Load the shared library
|
|
27
|
+
let library = unsafe { Library::new(&library_path) }.map_err(|e| {
|
|
28
|
+
Error::new(
|
|
29
|
+
ruby.exception_runtime_error(),
|
|
30
|
+
format!("Failed to load library '{}': {}", library_path, e),
|
|
31
|
+
)
|
|
32
|
+
})?;
|
|
33
|
+
|
|
34
|
+
// The symbol name follows tree-sitter convention: tree_sitter_{language}
|
|
35
|
+
let symbol_name = format!("tree_sitter_{}", name);
|
|
36
|
+
let language_fn: Symbol<LanguageFn> =
|
|
37
|
+
unsafe { library.get(symbol_name.as_bytes()) }.map_err(|e| {
|
|
38
|
+
Error::new(
|
|
39
|
+
ruby.exception_runtime_error(),
|
|
40
|
+
format!(
|
|
41
|
+
"Failed to find symbol '{}' in '{}': {}",
|
|
42
|
+
symbol_name, library_path, e
|
|
43
|
+
),
|
|
44
|
+
)
|
|
45
|
+
})?;
|
|
46
|
+
|
|
47
|
+
let language: tree_sitter::Language = (*language_fn).into();
|
|
48
|
+
|
|
49
|
+
// Store language function in registry
|
|
50
|
+
let mut registry = LANGUAGES.write().map_err(|_| {
|
|
51
|
+
Error::new(
|
|
52
|
+
ruby.exception_runtime_error(),
|
|
53
|
+
"Failed to acquire language registry lock",
|
|
54
|
+
)
|
|
55
|
+
})?;
|
|
56
|
+
|
|
57
|
+
registry.insert(name, LoadedLanguage { language, library });
|
|
58
|
+
|
|
59
|
+
Ok(())
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/// Get a registered language by name
|
|
63
|
+
pub fn get_language(name: String) -> Result<Language, Error> {
|
|
64
|
+
let ruby = Ruby::get().unwrap();
|
|
65
|
+
|
|
66
|
+
let registry = LANGUAGES.read().map_err(|_| {
|
|
67
|
+
Error::new(
|
|
68
|
+
ruby.exception_runtime_error(),
|
|
69
|
+
"Failed to acquire language registry lock",
|
|
70
|
+
)
|
|
71
|
+
})?;
|
|
72
|
+
|
|
73
|
+
let loaded = registry.get(&name).ok_or_else(|| {
|
|
74
|
+
Error::new(
|
|
75
|
+
ruby.exception_arg_error(),
|
|
76
|
+
format!(
|
|
77
|
+
"Language '{}' not registered. Call TreeSitter.register_language first.",
|
|
78
|
+
name
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
})?;
|
|
82
|
+
|
|
83
|
+
Ok(Language {
|
|
84
|
+
name: name.clone(),
|
|
85
|
+
inner: loaded.language.clone(),
|
|
86
|
+
})
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/// List all registered language names
|
|
90
|
+
pub fn list_languages() -> Result<Vec<String>, Error> {
|
|
91
|
+
let ruby = Ruby::get().unwrap();
|
|
92
|
+
|
|
93
|
+
let registry = LANGUAGES.read().map_err(|_| {
|
|
94
|
+
Error::new(
|
|
95
|
+
ruby.exception_runtime_error(),
|
|
96
|
+
"Failed to acquire language registry lock",
|
|
97
|
+
)
|
|
98
|
+
})?;
|
|
99
|
+
|
|
100
|
+
Ok(registry.keys().cloned().collect())
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/// Get a language from the registry (internal use).
|
|
104
|
+
/// Returns the raw `tree_sitter::Language` instead of the wrapped `Language` struct,
|
|
105
|
+
/// avoiding redundant wrapping when callers just need the inner type (e.g., for
|
|
106
|
+
/// `parser.set_language()`). Also takes `&str` to avoid allocation.
|
|
107
|
+
pub fn get_language_internal(name: &str) -> Result<tree_sitter::Language, Error> {
|
|
108
|
+
let ruby = Ruby::get().unwrap();
|
|
109
|
+
|
|
110
|
+
let registry = LANGUAGES.read().map_err(|_| {
|
|
111
|
+
Error::new(
|
|
112
|
+
ruby.exception_runtime_error(),
|
|
113
|
+
"Failed to acquire language registry lock",
|
|
114
|
+
)
|
|
115
|
+
})?;
|
|
116
|
+
|
|
117
|
+
let loaded = registry.get(name).ok_or_else(|| {
|
|
118
|
+
Error::new(
|
|
119
|
+
ruby.exception_arg_error(),
|
|
120
|
+
format!(
|
|
121
|
+
"Language '{}' not registered. Call TreeSitter.register_language first.",
|
|
122
|
+
name
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
})?;
|
|
126
|
+
|
|
127
|
+
Ok(loaded.language.clone())
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
#[magnus::wrap(class = "TreeSitter::Language")]
|
|
131
|
+
#[derive(Clone)]
|
|
132
|
+
pub struct Language {
|
|
133
|
+
pub name: String,
|
|
134
|
+
pub inner: tree_sitter::Language,
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
impl Language {
|
|
138
|
+
pub fn name(&self) -> &str {
|
|
139
|
+
&self.name
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
pub fn version(&self) -> usize {
|
|
143
|
+
self.inner.abi_version()
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/// Returns the number of distinct node types (kinds) defined in this language's grammar.
|
|
147
|
+
/// Each node in a syntax tree has a kind like "function_definition" or "identifier".
|
|
148
|
+
/// Can be useful for allocation or iteration over all kinds.
|
|
149
|
+
pub fn node_kind_count(&self) -> usize {
|
|
150
|
+
self.inner.node_kind_count()
|
|
151
|
+
}
|
|
152
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
mod language;
|
|
2
|
+
mod node;
|
|
3
|
+
mod parser;
|
|
4
|
+
mod point;
|
|
5
|
+
mod query;
|
|
6
|
+
mod range;
|
|
7
|
+
mod tree;
|
|
8
|
+
|
|
9
|
+
use magnus::{function, method, prelude::*, Error, Ruby};
|
|
10
|
+
|
|
11
|
+
#[magnus::init]
|
|
12
|
+
fn init(ruby: &Ruby) -> Result<(), Error> {
|
|
13
|
+
let module = ruby.define_module("TreeSitter")?;
|
|
14
|
+
|
|
15
|
+
module.define_singleton_method(
|
|
16
|
+
"register_language",
|
|
17
|
+
function!(language::register_language, 2),
|
|
18
|
+
)?;
|
|
19
|
+
module.define_singleton_method("language", function!(language::get_language, 1))?;
|
|
20
|
+
module.define_singleton_method("languages", function!(language::list_languages, 0))?;
|
|
21
|
+
|
|
22
|
+
let language_class = module.define_class("Language", ruby.class_object())?;
|
|
23
|
+
language_class.define_method("name", method!(language::Language::name, 0))?;
|
|
24
|
+
language_class.define_method("version", method!(language::Language::version, 0))?;
|
|
25
|
+
language_class.define_method(
|
|
26
|
+
"node_kind_count",
|
|
27
|
+
method!(language::Language::node_kind_count, 0),
|
|
28
|
+
)?;
|
|
29
|
+
|
|
30
|
+
let parser_class = module.define_class("Parser", ruby.class_object())?;
|
|
31
|
+
parser_class.define_singleton_method("new", function!(parser::Parser::new, 0))?;
|
|
32
|
+
parser_class.define_method("language=", method!(parser::Parser::set_language, 1))?;
|
|
33
|
+
parser_class.define_method("language", method!(parser::Parser::language, 0))?;
|
|
34
|
+
parser_class.define_method("parse", method!(parser::Parser::parse, -1))?;
|
|
35
|
+
parser_class.define_method("timeout_micros", method!(parser::Parser::timeout_micros, 0))?;
|
|
36
|
+
parser_class.define_method(
|
|
37
|
+
"timeout_micros=",
|
|
38
|
+
method!(parser::Parser::set_timeout_micros, 1),
|
|
39
|
+
)?;
|
|
40
|
+
parser_class.define_method("reset", method!(parser::Parser::reset, 0))?;
|
|
41
|
+
|
|
42
|
+
let tree_class = module.define_class("Tree", ruby.class_object())?;
|
|
43
|
+
tree_class.define_method("root_node", method!(tree::Tree::root_node, 0))?;
|
|
44
|
+
tree_class.define_method("source", method!(tree::Tree::source, 0))?;
|
|
45
|
+
tree_class.define_method("language", method!(tree::Tree::language, 0))?;
|
|
46
|
+
|
|
47
|
+
let node_class = module.define_class("Node", ruby.class_object())?;
|
|
48
|
+
|
|
49
|
+
// Navigation
|
|
50
|
+
node_class.define_method("parent", method!(node::Node::parent, 0))?;
|
|
51
|
+
node_class.define_method("child", method!(node::Node::child, 1))?;
|
|
52
|
+
node_class.define_method("child_count", method!(node::Node::child_count, 0))?;
|
|
53
|
+
node_class.define_method("named_child", method!(node::Node::named_child, 1))?;
|
|
54
|
+
node_class.define_method(
|
|
55
|
+
"named_child_count",
|
|
56
|
+
method!(node::Node::named_child_count, 0),
|
|
57
|
+
)?;
|
|
58
|
+
node_class.define_method(
|
|
59
|
+
"child_by_field_name",
|
|
60
|
+
method!(node::Node::child_by_field_name, 1),
|
|
61
|
+
)?;
|
|
62
|
+
node_class.define_method("children", method!(node::Node::children, 0))?;
|
|
63
|
+
node_class.define_method("named_children", method!(node::Node::named_children, 0))?;
|
|
64
|
+
node_class.define_method("next_sibling", method!(node::Node::next_sibling, 0))?;
|
|
65
|
+
node_class.define_method("prev_sibling", method!(node::Node::prev_sibling, 0))?;
|
|
66
|
+
node_class.define_method(
|
|
67
|
+
"next_named_sibling",
|
|
68
|
+
method!(node::Node::next_named_sibling, 0),
|
|
69
|
+
)?;
|
|
70
|
+
node_class.define_method(
|
|
71
|
+
"prev_named_sibling",
|
|
72
|
+
method!(node::Node::prev_named_sibling, 0),
|
|
73
|
+
)?;
|
|
74
|
+
|
|
75
|
+
// Properties
|
|
76
|
+
node_class.define_method("kind", method!(node::Node::kind, 0))?;
|
|
77
|
+
node_class.define_method("type", method!(node::Node::kind, 0))?; // Alias
|
|
78
|
+
node_class.define_method("kind_id", method!(node::Node::kind_id, 0))?;
|
|
79
|
+
node_class.define_method("named?", method!(node::Node::is_named, 0))?;
|
|
80
|
+
node_class.define_method("missing?", method!(node::Node::is_missing, 0))?;
|
|
81
|
+
node_class.define_method("extra?", method!(node::Node::is_extra, 0))?;
|
|
82
|
+
node_class.define_method("error?", method!(node::Node::is_error, 0))?;
|
|
83
|
+
node_class.define_method("has_error?", method!(node::Node::has_error, 0))?;
|
|
84
|
+
node_class.define_method("has_changes?", method!(node::Node::has_changes, 0))?;
|
|
85
|
+
|
|
86
|
+
// Position
|
|
87
|
+
node_class.define_method("start_byte", method!(node::Node::start_byte, 0))?;
|
|
88
|
+
node_class.define_method("end_byte", method!(node::Node::end_byte, 0))?;
|
|
89
|
+
node_class.define_method("start_point", method!(node::Node::start_point, 0))?;
|
|
90
|
+
node_class.define_method("end_point", method!(node::Node::end_point, 0))?;
|
|
91
|
+
node_class.define_method("range", method!(node::Node::range, 0))?;
|
|
92
|
+
|
|
93
|
+
// Text
|
|
94
|
+
node_class.define_method("text", method!(node::Node::text, 0))?;
|
|
95
|
+
node_class.define_method("to_sexp", method!(node::Node::to_sexp, 0))?;
|
|
96
|
+
node_class.define_method("to_s", method!(node::Node::to_sexp, 0))?;
|
|
97
|
+
node_class.define_method("inspect", method!(node::Node::inspect, 0))?;
|
|
98
|
+
node_class.define_method("==", method!(node::Node::eq, 1))?;
|
|
99
|
+
node_class.define_method("eql?", method!(node::Node::eq, 1))?;
|
|
100
|
+
|
|
101
|
+
let point_class = module.define_class("Point", ruby.class_object())?;
|
|
102
|
+
point_class.define_singleton_method("new", function!(point::Point::new, 2))?;
|
|
103
|
+
point_class.define_method("row", method!(point::Point::row, 0))?;
|
|
104
|
+
point_class.define_method("column", method!(point::Point::column, 0))?;
|
|
105
|
+
point_class.define_method("to_a", method!(point::Point::to_a, 0))?;
|
|
106
|
+
point_class.define_method("inspect", method!(point::Point::inspect, 0))?;
|
|
107
|
+
point_class.define_method("==", method!(point::Point::eq, 1))?;
|
|
108
|
+
|
|
109
|
+
// Range class
|
|
110
|
+
let range_class = module.define_class("Range", ruby.class_object())?;
|
|
111
|
+
range_class.define_method("start_byte", method!(range::Range::start_byte, 0))?;
|
|
112
|
+
range_class.define_method("end_byte", method!(range::Range::end_byte, 0))?;
|
|
113
|
+
range_class.define_method("start_point", method!(range::Range::start_point, 0))?;
|
|
114
|
+
range_class.define_method("end_point", method!(range::Range::end_point, 0))?;
|
|
115
|
+
range_class.define_method("size", method!(range::Range::size, 0))?;
|
|
116
|
+
range_class.define_method("inspect", method!(range::Range::inspect, 0))?;
|
|
117
|
+
|
|
118
|
+
let query_class = module.define_class("Query", ruby.class_object())?;
|
|
119
|
+
query_class.define_singleton_method("new", function!(query::Query::new, 2))?;
|
|
120
|
+
query_class.define_method("capture_names", method!(query::Query::capture_names, 0))?;
|
|
121
|
+
query_class.define_method("pattern_count", method!(query::Query::pattern_count, 0))?;
|
|
122
|
+
|
|
123
|
+
let cursor_class = module.define_class("QueryCursor", ruby.class_object())?;
|
|
124
|
+
cursor_class.define_singleton_method("new", function!(query::QueryCursor::new, 0))?;
|
|
125
|
+
cursor_class.define_method("matches", method!(query::QueryCursor::matches, 3))?;
|
|
126
|
+
cursor_class.define_method("captures", method!(query::QueryCursor::captures, 3))?;
|
|
127
|
+
|
|
128
|
+
let match_class = module.define_class("QueryMatch", ruby.class_object())?;
|
|
129
|
+
match_class.define_method(
|
|
130
|
+
"pattern_index",
|
|
131
|
+
method!(query::QueryMatch::pattern_index, 0),
|
|
132
|
+
)?;
|
|
133
|
+
match_class.define_method("captures", method!(query::QueryMatch::captures, 0))?;
|
|
134
|
+
|
|
135
|
+
let capture_class = module.define_class("QueryCapture", ruby.class_object())?;
|
|
136
|
+
capture_class.define_method("name", method!(query::QueryCapture::name, 0))?;
|
|
137
|
+
capture_class.define_method("node", method!(query::QueryCapture::node, 0))?;
|
|
138
|
+
|
|
139
|
+
Ok(())
|
|
140
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
use crate::point::Point;
|
|
2
|
+
use crate::range::Range;
|
|
3
|
+
use magnus::{RArray, Ruby};
|
|
4
|
+
use std::sync::Arc;
|
|
5
|
+
|
|
6
|
+
/// Node wrapper that stores both the node data and a reference to the tree
|
|
7
|
+
/// This allows child navigation while keeping the tree alive
|
|
8
|
+
#[magnus::wrap(class = "TreeSitter::Node")]
|
|
9
|
+
#[derive(Clone)]
|
|
10
|
+
pub struct Node {
|
|
11
|
+
// Store the tree to keep nodes valid (public for query.rs)
|
|
12
|
+
pub tree: Arc<tree_sitter::Tree>,
|
|
13
|
+
|
|
14
|
+
// Source text for text extraction (public for query.rs)
|
|
15
|
+
pub source: Arc<String>,
|
|
16
|
+
|
|
17
|
+
// Node identification via byte range (used to relocate node in tree)
|
|
18
|
+
start_byte: usize,
|
|
19
|
+
end_byte: usize,
|
|
20
|
+
|
|
21
|
+
// Cached properties
|
|
22
|
+
kind: String,
|
|
23
|
+
kind_id: u16,
|
|
24
|
+
is_named: bool,
|
|
25
|
+
is_missing: bool,
|
|
26
|
+
is_extra: bool,
|
|
27
|
+
is_error: bool,
|
|
28
|
+
has_error: bool,
|
|
29
|
+
has_changes: bool,
|
|
30
|
+
start_point: Point,
|
|
31
|
+
end_point: Point,
|
|
32
|
+
child_count: usize,
|
|
33
|
+
named_child_count: usize,
|
|
34
|
+
sexp: String,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
impl Node {
|
|
38
|
+
pub fn new(ts_node: tree_sitter::Node, source: Arc<String>, tree: Arc<tree_sitter::Tree>) -> Self {
|
|
39
|
+
Self {
|
|
40
|
+
tree,
|
|
41
|
+
source,
|
|
42
|
+
start_byte: ts_node.start_byte(),
|
|
43
|
+
end_byte: ts_node.end_byte(),
|
|
44
|
+
kind: ts_node.kind().to_string(),
|
|
45
|
+
kind_id: ts_node.kind_id(),
|
|
46
|
+
is_named: ts_node.is_named(),
|
|
47
|
+
is_missing: ts_node.is_missing(),
|
|
48
|
+
is_extra: ts_node.is_extra(),
|
|
49
|
+
is_error: ts_node.is_error(),
|
|
50
|
+
has_error: ts_node.has_error(),
|
|
51
|
+
has_changes: ts_node.has_changes(),
|
|
52
|
+
start_point: Point::from_ts(ts_node.start_position()),
|
|
53
|
+
end_point: Point::from_ts(ts_node.end_position()),
|
|
54
|
+
child_count: ts_node.child_count(),
|
|
55
|
+
named_child_count: ts_node.named_child_count(),
|
|
56
|
+
sexp: ts_node.to_sexp(),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Relocate the tree-sitter node from the stored tree
|
|
61
|
+
fn get_ts_node(&self) -> Option<tree_sitter::Node<'_>> {
|
|
62
|
+
let root = self.tree.root_node();
|
|
63
|
+
root.descendant_for_byte_range(self.start_byte, self.end_byte)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Public method for query.rs to access the tree-sitter node
|
|
67
|
+
pub fn get_ts_node_pub(&self) -> Option<tree_sitter::Node<'_>> {
|
|
68
|
+
let root = self.tree.root_node();
|
|
69
|
+
root.descendant_for_byte_range(self.start_byte, self.end_byte)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Navigation methods
|
|
73
|
+
|
|
74
|
+
pub fn parent(&self) -> Option<Node> {
|
|
75
|
+
let ts_node = self.get_ts_node()?;
|
|
76
|
+
ts_node
|
|
77
|
+
.parent()
|
|
78
|
+
.map(|n| Node::new(n, self.source.clone(), self.tree.clone()))
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
pub fn child(&self, index: usize) -> Option<Node> {
|
|
82
|
+
let ts_node = self.get_ts_node()?;
|
|
83
|
+
ts_node
|
|
84
|
+
.child(index)
|
|
85
|
+
.map(|n| Node::new(n, self.source.clone(), self.tree.clone()))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
pub fn child_count(&self) -> usize {
|
|
89
|
+
self.child_count
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
pub fn named_child(&self, index: usize) -> Option<Node> {
|
|
93
|
+
let ts_node = self.get_ts_node()?;
|
|
94
|
+
ts_node
|
|
95
|
+
.named_child(index)
|
|
96
|
+
.map(|n| Node::new(n, self.source.clone(), self.tree.clone()))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
pub fn named_child_count(&self) -> usize {
|
|
100
|
+
self.named_child_count
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
pub fn child_by_field_name(&self, name: String) -> Option<Node> {
|
|
104
|
+
let ts_node = self.get_ts_node()?;
|
|
105
|
+
ts_node
|
|
106
|
+
.child_by_field_name(&name)
|
|
107
|
+
.map(|n| Node::new(n, self.source.clone(), self.tree.clone()))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
pub fn children(&self) -> RArray {
|
|
111
|
+
let ruby = Ruby::get().unwrap();
|
|
112
|
+
let array = ruby.ary_new();
|
|
113
|
+
let Some(ts_node) = self.get_ts_node() else {
|
|
114
|
+
return array;
|
|
115
|
+
};
|
|
116
|
+
let mut cursor = ts_node.walk();
|
|
117
|
+
for n in ts_node.children(&mut cursor) {
|
|
118
|
+
let _ = array.push(Node::new(n, self.source.clone(), self.tree.clone()));
|
|
119
|
+
}
|
|
120
|
+
array
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pub fn named_children(&self) -> RArray {
|
|
124
|
+
let ruby = Ruby::get().unwrap();
|
|
125
|
+
let array = ruby.ary_new();
|
|
126
|
+
let Some(ts_node) = self.get_ts_node() else {
|
|
127
|
+
return array;
|
|
128
|
+
};
|
|
129
|
+
let mut cursor = ts_node.walk();
|
|
130
|
+
for n in ts_node.named_children(&mut cursor) {
|
|
131
|
+
let _ = array.push(Node::new(n, self.source.clone(), self.tree.clone()));
|
|
132
|
+
}
|
|
133
|
+
array
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
pub fn next_sibling(&self) -> Option<Node> {
|
|
137
|
+
let ts_node = self.get_ts_node()?;
|
|
138
|
+
ts_node
|
|
139
|
+
.next_sibling()
|
|
140
|
+
.map(|n| Node::new(n, self.source.clone(), self.tree.clone()))
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
pub fn prev_sibling(&self) -> Option<Node> {
|
|
144
|
+
let ts_node = self.get_ts_node()?;
|
|
145
|
+
ts_node
|
|
146
|
+
.prev_sibling()
|
|
147
|
+
.map(|n| Node::new(n, self.source.clone(), self.tree.clone()))
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
pub fn next_named_sibling(&self) -> Option<Node> {
|
|
151
|
+
let ts_node = self.get_ts_node()?;
|
|
152
|
+
ts_node
|
|
153
|
+
.next_named_sibling()
|
|
154
|
+
.map(|n| Node::new(n, self.source.clone(), self.tree.clone()))
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
pub fn prev_named_sibling(&self) -> Option<Node> {
|
|
158
|
+
let ts_node = self.get_ts_node()?;
|
|
159
|
+
ts_node
|
|
160
|
+
.prev_named_sibling()
|
|
161
|
+
.map(|n| Node::new(n, self.source.clone(), self.tree.clone()))
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Properties
|
|
165
|
+
pub fn kind(&self) -> &str {
|
|
166
|
+
&self.kind
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
pub fn kind_id(&self) -> u16 {
|
|
170
|
+
self.kind_id
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
pub fn is_named(&self) -> bool {
|
|
174
|
+
self.is_named
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
pub fn is_missing(&self) -> bool {
|
|
178
|
+
self.is_missing
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
pub fn is_extra(&self) -> bool {
|
|
182
|
+
self.is_extra
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
pub fn is_error(&self) -> bool {
|
|
186
|
+
self.is_error
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
pub fn has_error(&self) -> bool {
|
|
190
|
+
self.has_error
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
pub fn has_changes(&self) -> bool {
|
|
194
|
+
self.has_changes
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Position
|
|
198
|
+
pub fn start_byte(&self) -> usize {
|
|
199
|
+
self.start_byte
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
pub fn end_byte(&self) -> usize {
|
|
203
|
+
self.end_byte
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
pub fn start_point(&self) -> Point {
|
|
207
|
+
self.start_point.clone()
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
pub fn end_point(&self) -> Point {
|
|
211
|
+
self.end_point.clone()
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
pub fn range(&self) -> Range {
|
|
215
|
+
Range::new(
|
|
216
|
+
self.start_byte,
|
|
217
|
+
self.end_byte,
|
|
218
|
+
self.start_point.clone(),
|
|
219
|
+
self.end_point.clone(),
|
|
220
|
+
)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Text
|
|
224
|
+
pub fn text(&self) -> &str {
|
|
225
|
+
if self.end_byte <= self.source.len() {
|
|
226
|
+
&self.source[self.start_byte..self.end_byte]
|
|
227
|
+
} else {
|
|
228
|
+
""
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
pub fn to_sexp(&self) -> &str {
|
|
233
|
+
&self.sexp
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
pub fn inspect(&self) -> String {
|
|
237
|
+
format!(
|
|
238
|
+
"#<TreeSitter::Node kind={:?} start_byte={} end_byte={}>",
|
|
239
|
+
self.kind, self.start_byte, self.end_byte
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
pub fn eq(&self, other: &Node) -> bool {
|
|
244
|
+
self.start_byte == other.start_byte
|
|
245
|
+
&& self.end_byte == other.end_byte
|
|
246
|
+
&& self.kind == other.kind
|
|
247
|
+
}
|
|
248
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
use crate::language::{get_language_internal, Language};
|
|
2
|
+
use crate::tree::Tree;
|
|
3
|
+
use magnus::{prelude::*, Error, RString, Ruby, TryConvert, Value};
|
|
4
|
+
use std::cell::RefCell;
|
|
5
|
+
use std::time::Instant;
|
|
6
|
+
|
|
7
|
+
#[magnus::wrap(class = "TreeSitter::Parser")]
|
|
8
|
+
pub struct Parser {
|
|
9
|
+
inner: RefCell<tree_sitter::Parser>,
|
|
10
|
+
language_name: RefCell<Option<String>>,
|
|
11
|
+
timeout_micros: RefCell<u64>,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
impl Parser {
|
|
15
|
+
pub fn new() -> Result<Self, Error> {
|
|
16
|
+
let parser = tree_sitter::Parser::new();
|
|
17
|
+
Ok(Self {
|
|
18
|
+
inner: RefCell::new(parser),
|
|
19
|
+
language_name: RefCell::new(None),
|
|
20
|
+
timeout_micros: RefCell::new(0),
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pub fn set_language(&self, lang: Value) -> Result<(), Error> {
|
|
25
|
+
let name: String = if RString::from_value(lang).is_some() {
|
|
26
|
+
<String as TryConvert>::try_convert(lang)?
|
|
27
|
+
} else {
|
|
28
|
+
// Assume it's a Language object?
|
|
29
|
+
let language: &Language = <&Language as TryConvert>::try_convert(lang)?;
|
|
30
|
+
language.name.clone()
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
let ts_language = get_language_internal(&name)?;
|
|
34
|
+
|
|
35
|
+
let ruby = Ruby::get().unwrap();
|
|
36
|
+
let mut parser = self.inner.borrow_mut();
|
|
37
|
+
parser.set_language(&ts_language).map_err(|e| {
|
|
38
|
+
Error::new(
|
|
39
|
+
ruby.exception_runtime_error(),
|
|
40
|
+
format!("Failed to set language: {}", e),
|
|
41
|
+
)
|
|
42
|
+
})?;
|
|
43
|
+
|
|
44
|
+
*self.language_name.borrow_mut() = Some(name);
|
|
45
|
+
Ok(())
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
pub fn language(&self) -> Result<Option<Language>, Error> {
|
|
49
|
+
let name = self.language_name.borrow();
|
|
50
|
+
match &*name {
|
|
51
|
+
Some(n) => {
|
|
52
|
+
let ts_lang = get_language_internal(n)?;
|
|
53
|
+
Ok(Some(Language {
|
|
54
|
+
name: n.clone(),
|
|
55
|
+
inner: ts_lang,
|
|
56
|
+
}))
|
|
57
|
+
}
|
|
58
|
+
None => Ok(None),
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub fn parse(&self, args: &[Value]) -> Result<Option<Tree>, Error> {
|
|
63
|
+
let ruby = Ruby::get().unwrap();
|
|
64
|
+
|
|
65
|
+
if args.is_empty() {
|
|
66
|
+
return Err(Error::new(
|
|
67
|
+
ruby.exception_arg_error(),
|
|
68
|
+
"wrong number of arguments",
|
|
69
|
+
));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let source: String = <String as TryConvert>::try_convert(args[0])?;
|
|
73
|
+
let old_tree: Option<&Tree> = if args.len() > 1 && !args[1].is_nil() {
|
|
74
|
+
Some(<&Tree as TryConvert>::try_convert(args[1])?)
|
|
75
|
+
} else {
|
|
76
|
+
None
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
let language_name = self.language_name.borrow().clone().ok_or_else(|| {
|
|
80
|
+
Error::new(
|
|
81
|
+
ruby.exception_runtime_error(),
|
|
82
|
+
"No language set. Call `parser.language = 'name'` first.",
|
|
83
|
+
)
|
|
84
|
+
})?;
|
|
85
|
+
|
|
86
|
+
let mut parser = self.inner.borrow_mut();
|
|
87
|
+
let old_ts_tree = old_tree.map(|t| (*t.inner).clone());
|
|
88
|
+
|
|
89
|
+
let timeout = *self.timeout_micros.borrow();
|
|
90
|
+
let result = if timeout > 0 {
|
|
91
|
+
let start = Instant::now();
|
|
92
|
+
let source_bytes = source.as_bytes();
|
|
93
|
+
let mut progress_callback =
|
|
94
|
+
|_: &tree_sitter::ParseState| start.elapsed().as_micros() < timeout as u128;
|
|
95
|
+
let options =
|
|
96
|
+
tree_sitter::ParseOptions::new().progress_callback(&mut progress_callback);
|
|
97
|
+
let mut source_callback = |offset: usize, _: tree_sitter::Point| {
|
|
98
|
+
if offset < source_bytes.len() {
|
|
99
|
+
&source_bytes[offset..]
|
|
100
|
+
} else {
|
|
101
|
+
&[]
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
parser.parse_with_options(&mut source_callback, old_ts_tree.as_ref(), Some(options))
|
|
105
|
+
} else {
|
|
106
|
+
parser.parse(&source, old_ts_tree.as_ref())
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
match result {
|
|
110
|
+
Some(tree) => Ok(Some(Tree::new(tree, source, language_name))),
|
|
111
|
+
None => Ok(None),
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
pub fn timeout_micros(&self) -> u64 {
|
|
116
|
+
*self.timeout_micros.borrow()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
pub fn set_timeout_micros(&self, timeout: u64) {
|
|
120
|
+
*self.timeout_micros.borrow_mut() = timeout;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
pub fn reset(&self) {
|
|
124
|
+
self.inner.borrow_mut().reset();
|
|
125
|
+
}
|
|
126
|
+
}
|