similarity_elixir/
elixir_parser.rs1use similarity_core::language_parser::{
2 GenericFunctionDef, GenericTypeDef, Language, LanguageParser,
3};
4use similarity_core::tree::TreeNode;
5use std::error::Error;
6use std::rc::Rc;
7use tree_sitter::{Node, Parser};
8
9pub struct ElixirParser {
10 parser: Parser,
11}
12
13impl ElixirParser {
14 pub fn new() -> Result<Self, Box<dyn Error + Send + Sync>> {
15 let mut parser = Parser::new();
16 parser
17 .set_language(&tree_sitter_elixir::LANGUAGE.into())
18 .map_err(|e| format!("Failed to set Elixir language: {e:?}"))?;
19 Ok(Self { parser })
20 }
21
22 fn extract_functions_from_node(
23 &self,
24 node: Node,
25 source: &str,
26 functions: &mut Vec<GenericFunctionDef>,
27 module_name: Option<&str>,
28 ) {
29 let node_kind = node.kind();
30
31 if node_kind == "call" {
33 if let Some(target_node) = node.child_by_field_name("target") {
34 if let Ok(target_text) = target_node.utf8_text(source.as_bytes()) {
35 match target_text {
36 "def" | "defp" | "defmacro" | "defmacrop" => {
38 if let Some(func_def) =
39 self.extract_function_definition(node, source, module_name)
40 {
41 functions.push(func_def);
42 }
43 return; }
45 "defmodule" | "defprotocol" | "defimpl" => {
47 let new_module_name = node
49 .child_by_field_name("arguments")
50 .and_then(|args| args.child(0))
51 .and_then(|n| n.utf8_text(source.as_bytes()).ok())
52 .unwrap_or("");
53
54 let do_block = node.child(2).filter(|n| n.kind() == "do_block");
56 if let Some(do_block) = do_block {
57 for child in do_block.children(&mut do_block.walk()) {
58 self.extract_functions_from_node(
59 child,
60 source,
61 functions,
62 Some(new_module_name),
63 );
64 }
65 }
66 return; }
68 _ => {} }
70 }
71 }
72 }
73
74 for child in node.children(&mut node.walk()) {
76 self.extract_functions_from_node(child, source, functions, module_name);
77 }
78 }
79
80 fn extract_function_definition(
81 &self,
82 node: Node,
83 source: &str,
84 module_name: Option<&str>,
85 ) -> Option<GenericFunctionDef> {
86 let name_string = node
88 .child(1)
89 .filter(|n| n.kind() == "arguments")
90 .and_then(|args| args.child(0))
91 .and_then(|call_node| {
92 if call_node.kind() == "call" {
93 call_node.child_by_field_name("target")
94 } else {
95 None
96 }
97 })
98 .and_then(|n| n.utf8_text(source.as_bytes()).ok())
99 .map(String::from)?;
100
101 let params_node = node
103 .child(1)
104 .filter(|n| n.kind() == "arguments")
105 .and_then(|args| args.child(0))
106 .and_then(|call_node| {
107 if call_node.kind() == "call" {
108 call_node.child(1).filter(|n| n.kind() == "arguments")
109 } else {
110 None
111 }
112 });
113
114 let body_node = node.child(2).filter(|n| n.kind() == "do_block");
116
117 let params = self.extract_parameters(params_node, source);
118
119 Some(GenericFunctionDef {
120 name: name_string,
121 start_line: node.start_position().row as u32 + 1,
122 end_line: node.end_position().row as u32 + 1,
123 body_start_line: body_node.map(|n| n.start_position().row as u32 + 1).unwrap_or(0),
124 body_end_line: body_node.map(|n| n.end_position().row as u32 + 1).unwrap_or(0),
125 parameters: params,
126 is_method: module_name.is_some(),
127 class_name: module_name.map(String::from),
128 is_async: false,
129 is_generator: false,
130 decorators: Vec::new(),
131 })
132 }
133
134 fn extract_parameters(&self, params_node: Option<Node>, source: &str) -> Vec<String> {
135 let Some(node) = params_node else {
136 return Vec::new();
137 };
138
139 let mut params = Vec::new();
140 for child in node.children(&mut node.walk()) {
141 if child.kind() == "identifier" {
142 if let Ok(param_text) = child.utf8_text(source.as_bytes()) {
143 params.push(param_text.to_string());
144 }
145 }
146 }
147 params
148 }
149
150 fn build_tree_from_node(node: Node, source: &str, id: &mut usize) -> TreeNode {
151 let label = node.kind().to_string();
152 let value = if node.child_count() == 0 {
153 node.utf8_text(source.as_bytes()).ok().unwrap_or_default().to_string()
154 } else {
155 String::new()
156 };
157
158 let current_id = *id;
159 *id += 1;
160
161 let mut tree_node = TreeNode::new(label, value, current_id);
162
163 for child in node.children(&mut node.walk()) {
164 let child_node = Self::build_tree_from_node(child, source, id);
165 tree_node.add_child(Rc::new(child_node));
166 }
167
168 tree_node
169 }
170}
171
172impl LanguageParser for ElixirParser {
173 fn language(&self) -> Language {
174 Language::Unknown }
176
177 fn parse(
178 &mut self,
179 source: &str,
180 _path: &str,
181 ) -> Result<Rc<TreeNode>, Box<dyn Error + Send + Sync>> {
182 let tree = self.parser.parse(source, None).ok_or("Failed to parse Elixir code")?;
183 let mut id = 0;
184 Ok(Rc::new(Self::build_tree_from_node(tree.root_node(), source, &mut id)))
185 }
186
187 fn extract_functions(
188 &mut self,
189 source: &str,
190 _path: &str,
191 ) -> Result<Vec<GenericFunctionDef>, Box<dyn Error + Send + Sync>> {
192 let tree = self.parser.parse(source, None).ok_or("Failed to parse Elixir code")?;
193
194 let mut functions = Vec::new();
195 self.extract_functions_from_node(tree.root_node(), source, &mut functions, None);
196 Ok(functions)
197 }
198
199 fn extract_types(
200 &mut self,
201 source: &str,
202 _path: &str,
203 ) -> Result<Vec<GenericTypeDef>, Box<dyn Error + Send + Sync>> {
204 let tree = self.parser.parse(source, None).ok_or("Failed to parse Elixir code")?;
205
206 let mut types = Vec::new();
207 Self::extract_types_from_node(tree.root_node(), source, &mut types);
208 Ok(types)
209 }
210}
211
212impl ElixirParser {
213 fn extract_types_from_node(node: Node, source: &str, types: &mut Vec<GenericTypeDef>) {
214 if node.kind() == "call" {
215 if let Some(target_node) = node.child_by_field_name("target") {
216 if let Ok(target_text) = target_node.utf8_text(source.as_bytes()) {
217 if matches!(target_text, "defmodule" | "defprotocol" | "defimpl") {
218 let name = node
220 .child_by_field_name("arguments")
221 .and_then(|args| args.child(0))
222 .and_then(|n| n.utf8_text(source.as_bytes()).ok())
223 .unwrap_or("");
224
225 types.push(GenericTypeDef {
226 name: name.to_string(),
227 start_line: node.start_position().row as u32 + 1,
228 end_line: node.end_position().row as u32 + 1,
229 kind: match target_text {
230 "defmodule" => "module",
231 "defprotocol" => "protocol",
232 "defimpl" => "implementation",
233 _ => "unknown",
234 }
235 .to_string(),
236 fields: Vec::new(),
237 });
238 }
239 }
240 }
241 }
242
243 for child in node.children(&mut node.walk()) {
245 Self::extract_types_from_node(child, source, types);
246 }
247 }
248}