Skip to main content

normalize_languages/
matlab.rs

1//! MATLAB language support.
2
3use crate::{ContainerBody, Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6/// MATLAB language support.
7pub struct Matlab;
8
9impl Language for Matlab {
10    fn name(&self) -> &'static str {
11        "MATLAB"
12    }
13    fn extensions(&self) -> &'static [&'static str] {
14        &["m"]
15    }
16    fn grammar_name(&self) -> &'static str {
17        "matlab"
18    }
19
20    fn as_symbols(&self) -> Option<&dyn LanguageSymbols> {
21        Some(self)
22    }
23
24    fn extract_imports(&self, node: &Node, content: &str) -> Vec<Import> {
25        if node.kind() != "command" {
26            return Vec::new();
27        }
28
29        let text = &content[node.byte_range()];
30        if !text.starts_with("import ") {
31            return Vec::new();
32        }
33
34        vec![Import {
35            module: text[7..].trim().to_string(),
36            names: Vec::new(),
37            alias: None,
38            is_wildcard: text.contains('*'),
39            is_relative: false,
40            line: node.start_position().row + 1,
41        }]
42    }
43
44    fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
45        // MATLAB: import package.* or import package.function
46        if import.is_wildcard {
47            format!("import {}.*", import.module)
48        } else {
49            format!("import {}", import.module)
50        }
51    }
52
53    fn is_test_symbol(&self, symbol: &crate::Symbol) -> bool {
54        let name = symbol.name.as_str();
55        match symbol.kind {
56            crate::SymbolKind::Function | crate::SymbolKind::Method => name.starts_with("test_"),
57            crate::SymbolKind::Module => name == "tests" || name == "test",
58            _ => false,
59        }
60    }
61
62    fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
63        // MATLAB class_definition has no dedicated body field; use node itself
64        Some(*node)
65    }
66
67    fn analyze_container_body(
68        &self,
69        body_node: &Node,
70        content: &str,
71        inner_indent: &str,
72    ) -> Option<ContainerBody> {
73        // classdef Foo\n  methods...\nend — skip first line, strip `end`
74        crate::body::analyze_keyword_end_body(body_node, content, inner_indent)
75    }
76
77    fn node_name<'a>(&self, node: &Node, content: &'a str) -> Option<&'a str> {
78        if let Some(name_node) = node.child_by_field_name("name") {
79            return Some(&content[name_node.byte_range()]);
80        }
81        let mut cursor = node.walk();
82        for child in node.children(&mut cursor) {
83            if child.kind() == "identifier" {
84                return Some(&content[child.byte_range()]);
85            }
86        }
87        None
88    }
89}
90
91impl LanguageSymbols for Matlab {}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96    use crate::validate_unused_kinds_audit;
97
98    #[test]
99    fn unused_node_kinds_audit() {
100        #[rustfmt::skip]
101        let documented_unused: &[&str] = &[
102            // Operators
103            "binary_operator", "boolean_operator", "comparison_operator", "global_operator",
104            "handle_operator", "metaclass_operator", "not_operator", "persistent_operator",
105            "postfix_operator", "spread_operator", "unary_operator",
106            // Statements
107            "arguments_statement", "break_statement", "continue_statement", "return_statement",
108            "spmd_statement",
109            // Control flow clauses
110            "case_clause", "else_clause", "elseif_clause", "otherwise_clause",
111            // Class-related
112            "class_property", "enum", "enumeration", "superclass", "superclasses",
113            // Function-related
114            "block", "field_expression", "formatting_sequence", "function_arguments",
115            "function_call", "function_output", "function_signature", "identifier", "lambda",
116            "parfor_options", "validation_functions",
117            // control flow — not extracted as symbols
118            "if_statement",
119            "catch_clause",
120            "switch_statement",
121            "while_statement",
122            "for_statement",
123            "try_statement",
124            "methods",
125        ];
126        validate_unused_kinds_audit(&Matlab, documented_unused)
127            .expect("MATLAB unused node kinds audit failed");
128    }
129}