probe_code/language/
csharp.rs

1use super::language_trait::LanguageImpl;
2use tree_sitter::{Language as TSLanguage, Node};
3
4/// Implementation of LanguageImpl for C#
5pub struct CSharpLanguage;
6
7impl Default for CSharpLanguage {
8    fn default() -> Self {
9        Self::new()
10    }
11}
12
13impl CSharpLanguage {
14    pub fn new() -> Self {
15        CSharpLanguage
16    }
17}
18
19impl LanguageImpl for CSharpLanguage {
20    fn get_tree_sitter_language(&self) -> TSLanguage {
21        // Create a new instance of the Language type from the current tree-sitter version
22        unsafe { std::mem::transmute(tree_sitter_c_sharp::LANGUAGE) }
23    }
24
25    fn get_extension(&self) -> &'static str {
26        "cs"
27    }
28
29    fn is_acceptable_parent(&self, node: &Node) -> bool {
30        matches!(
31            node.kind(),
32            "method_declaration"
33                | "class_declaration"
34                | "struct_declaration"
35                | "interface_declaration"
36                | "enum_declaration"
37                | "namespace_declaration"
38                | "property_declaration"
39                | "constructor_declaration"
40                | "delegate_declaration"
41                | "event_declaration"
42        )
43    }
44
45    fn is_test_node(&self, node: &Node, source: &[u8]) -> bool {
46        let debug_mode = std::env::var("DEBUG").unwrap_or_default() == "1";
47        let node_type = node.kind();
48
49        // C#: Check for test methods with attributes
50        if node_type == "method_declaration" {
51            let mut cursor = node.walk();
52
53            // Check for test attributes (NUnit, MSTest, xUnit)
54            let mut has_test_attribute = false;
55
56            for child in node.children(&mut cursor) {
57                if child.kind() == "attribute_list" {
58                    let attr_text = child.utf8_text(source).unwrap_or("");
59                    if attr_text.contains("[Test")
60                        || attr_text.contains("[TestMethod")
61                        || attr_text.contains("[Fact")
62                        || attr_text.contains("[Theory")
63                    {
64                        has_test_attribute = true;
65                        break;
66                    }
67                }
68            }
69
70            if has_test_attribute {
71                if debug_mode {
72                    println!("DEBUG: Test node detected (C#): test attribute");
73                }
74                return true;
75            }
76
77            // Also check for method name starting with "Test"
78            for child in node.children(&mut cursor) {
79                if child.kind() == "identifier" {
80                    let name = child.utf8_text(source).unwrap_or("");
81                    if name.starts_with("Test") {
82                        if debug_mode {
83                            println!("DEBUG: Test node detected (C#): Test method");
84                        }
85                        return true;
86                    }
87                }
88            }
89        }
90
91        // Check for test classes
92        if node_type == "class_declaration" {
93            let mut cursor = node.walk();
94
95            // Check for test class attributes
96            for child in node.children(&mut cursor) {
97                if child.kind() == "attribute_list" {
98                    let attr_text = child.utf8_text(source).unwrap_or("");
99                    if attr_text.contains("[TestClass") || attr_text.contains("[TestFixture") {
100                        if debug_mode {
101                            println!("DEBUG: Test node detected (C#): test class attribute");
102                        }
103                        return true;
104                    }
105                }
106            }
107
108            // Check for class name ending with "Tests" or "Test"
109            for child in node.children(&mut cursor) {
110                if child.kind() == "identifier" {
111                    let name = child.utf8_text(source).unwrap_or("");
112                    if name.ends_with("Tests") || name.ends_with("Test") {
113                        if debug_mode {
114                            println!(
115                                "DEBUG: Test node detected (C#): Test class naming convention"
116                            );
117                        }
118                        return true;
119                    }
120                }
121            }
122        }
123
124        false
125    }
126
127    fn find_parent_function<'a>(&self, node: Node<'a>) -> Option<Node<'a>> {
128        let mut current = node;
129
130        while let Some(parent) = current.parent() {
131            if parent.kind() == "method_declaration" {
132                return Some(parent);
133            }
134            current = parent;
135        }
136
137        None
138    }
139}