1use quarb::{AstAdapter, NodeId, Value};
24
25#[derive(Debug, thiserror::Error)]
27pub enum CodeError {
28 #[error("code: {0}")]
29 Io(#[from] std::io::Error),
30 #[error("code: no grammar for extension {0:?}")]
31 Language(String),
32 #[error("code: parse produced no tree")]
33 Parse,
34}
35
36struct Node {
37 kind: &'static str,
38 field: Option<&'static str>,
39 parent: Option<NodeId>,
40 children: Vec<NodeId>,
41 start: usize,
43 end: usize,
44 start_line: usize,
45 end_line: usize,
46 fields: Vec<(&'static str, usize)>,
48}
49
50pub struct CodeAdapter {
52 source: String,
53 nodes: Vec<Node>,
54}
55
56fn language(ext: &str) -> Option<tree_sitter::Language> {
58 match ext {
59 "rs" => Some(tree_sitter_rust::LANGUAGE.into()),
60 "py" => Some(tree_sitter_python::LANGUAGE.into()),
61 "js" | "mjs" | "cjs" | "jsx" => Some(tree_sitter_javascript::LANGUAGE.into()),
62 _ => None,
63 }
64}
65
66pub fn supported(ext: &str) -> bool {
68 language(ext).is_some()
69}
70
71impl CodeAdapter {
72 pub fn parse(text: &str, ext: &str) -> Result<Self, CodeError> {
74 let lang = language(ext).ok_or_else(|| CodeError::Language(ext.to_string()))?;
75 let mut parser = tree_sitter::Parser::new();
76 parser
77 .set_language(&lang)
78 .map_err(|_| CodeError::Language(ext.to_string()))?;
79 let tree = parser.parse(text, None).ok_or(CodeError::Parse)?;
80 let mut nodes = Vec::new();
81 build(&mut nodes, tree.root_node());
82 Ok(CodeAdapter {
83 source: text.to_string(),
84 nodes,
85 })
86 }
87
88 pub fn open(path: &std::path::Path) -> Result<Self, CodeError> {
90 let ext = path
91 .extension()
92 .and_then(|e| e.to_str())
93 .unwrap_or("")
94 .to_ascii_lowercase();
95 let text = std::fs::read_to_string(path)?;
96 Self::parse(&text, &ext)
97 }
98
99 pub fn locator(&self, node: NodeId) -> String {
101 let mut parts = Vec::new();
102 let mut cur = Some(node);
103 while let Some(n) = cur {
104 let nd = &self.nodes[n.0 as usize];
105 if nd.parent.is_some() {
106 parts.push(format!("{}:{}", nd.kind, nd.start_line));
107 }
108 cur = nd.parent;
109 }
110 parts.reverse();
111 format!("/{}", parts.join("/"))
112 }
113
114 fn text_of(&self, n: &Node) -> &str {
115 &self.source[n.start.min(self.source.len())..n.end.min(self.source.len())]
116 }
117}
118
119fn build(nodes: &mut Vec<Node>, root: tree_sitter::Node<'_>) {
124 let mut stack: Vec<(tree_sitter::Node<'_>, Option<NodeId>, Option<&'static str>)> =
126 vec![(root, None, None)];
127 while let Some((ts, parent, field)) = stack.pop() {
128 let id = NodeId(nodes.len() as u64);
129 nodes.push(Node {
130 kind: ts.kind(),
131 field,
132 parent,
133 children: Vec::new(),
134 start: ts.start_byte(),
135 end: ts.end_byte(),
136 start_line: ts.start_position().row + 1,
137 end_line: ts.end_position().row + 1,
138 fields: Vec::new(),
139 });
140 if let Some(p) = parent {
143 let pnode = &mut nodes[p.0 as usize];
144 if let Some(f) = field {
145 pnode.fields.push((f, pnode.children.len()));
146 }
147 pnode.children.push(id);
148 }
149 let mut cursor = ts.walk();
153 let mut kids = Vec::new();
154 if cursor.goto_first_child() {
155 loop {
156 let child = cursor.node();
157 if child.is_named() {
158 kids.push((child, cursor.field_name()));
159 }
160 if !cursor.goto_next_sibling() {
161 break;
162 }
163 }
164 }
165 for (child, f) in kids.into_iter().rev() {
166 stack.push((child, Some(id), f));
167 }
168 }
169}
170
171impl AstAdapter for CodeAdapter {
172 fn root(&self) -> NodeId {
173 NodeId(0)
174 }
175
176 fn children(&self, node: NodeId) -> Vec<NodeId> {
177 self.nodes[node.0 as usize].children.clone()
178 }
179
180 fn name(&self, node: NodeId) -> Option<String> {
184 let n = &self.nodes[node.0 as usize];
185 n.parent.map(|_| n.kind.to_string())
186 }
187
188 fn parent(&self, node: NodeId) -> Option<NodeId> {
189 self.nodes[node.0 as usize].parent
190 }
191
192 fn traits(&self, node: NodeId) -> Vec<String> {
195 vec![self.nodes[node.0 as usize].kind.to_string()]
196 }
197
198 fn property(&self, node: NodeId, name: &str) -> Option<Value> {
201 let n = &self.nodes[node.0 as usize];
202 let (_, idx) = n.fields.iter().find(|(f, _)| *f == name)?;
203 let child = &self.nodes[n.children[*idx].0 as usize];
204 Some(Value::Str(self.text_of(child).to_string()))
205 }
206
207 fn default_value(&self, node: NodeId) -> Option<Value> {
209 Some(Value::Str(
210 self.text_of(&self.nodes[node.0 as usize]).to_string(),
211 ))
212 }
213
214 fn metadata(&self, node: NodeId, key: &str) -> Option<Value> {
215 let n = &self.nodes[node.0 as usize];
216 match key {
217 "kind" => Some(Value::Str(n.kind.to_string())),
218 "field" => n.field.map(|f| Value::Str(f.to_string())),
219 "start-line" => Some(Value::Int(n.start_line as i64)),
220 "end-line" => Some(Value::Int(n.end_line as i64)),
221 "n-children" => Some(Value::Int(n.children.len() as i64)),
222 _ => None,
223 }
224 }
225}