Skip to main content

normalize_languages/
idris.rs

1//! Idris language support.
2
3use crate::{ContainerBody, Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6/// Idris language support.
7pub struct Idris;
8
9impl Language for Idris {
10    fn name(&self) -> &'static str {
11        "Idris"
12    }
13    fn extensions(&self) -> &'static [&'static str] {
14        &["idr", "lidr"]
15    }
16    fn grammar_name(&self) -> &'static str {
17        "idris"
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() != "import" {
26            return Vec::new();
27        }
28
29        let text = &content[node.byte_range()];
30        vec![Import {
31            module: text.trim().to_string(),
32            names: Vec::new(),
33            alias: None,
34            is_wildcard: false,
35            is_relative: false,
36            line: node.start_position().row + 1,
37        }]
38    }
39
40    fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
41        // Idris: import Module
42        format!("import {}", import.module)
43    }
44
45    fn is_test_symbol(&self, symbol: &crate::Symbol) -> bool {
46        let name = symbol.name.as_str();
47        match symbol.kind {
48            crate::SymbolKind::Function | crate::SymbolKind::Method => name.starts_with("test_"),
49            crate::SymbolKind::Module => name == "tests" || name == "test",
50            _ => false,
51        }
52    }
53
54    fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
55        // Idris: data → data_body, record → record_body, interface → interface_body
56        let mut c = node.walk();
57        for child in node.children(&mut c) {
58            if matches!(child.kind(), "data_body" | "record_body" | "interface_body") {
59                return Some(child);
60            }
61        }
62        None
63    }
64
65    fn analyze_container_body(
66        &self,
67        body_node: &Node,
68        content: &str,
69        inner_indent: &str,
70    ) -> Option<ContainerBody> {
71        // interface_body starts with `where\n` — skip that first line.
72        // data_body and record_body start directly with content.
73        match body_node.kind() {
74            "interface_body" => {
75                crate::body::analyze_keyword_end_body(body_node, content, inner_indent)
76            }
77            _ => crate::body::analyze_end_body(body_node, content, inner_indent),
78        }
79    }
80}
81
82impl LanguageSymbols for Idris {}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use crate::validate_unused_kinds_audit;
88
89    #[test]
90    fn unused_node_kinds_audit() {
91        #[rustfmt::skip]
92        let documented_unused: &[&str] = &[
93            // Expression nodes
94            "exp_else", "exp_with", "exp_lambda", "exp_lambda_case",
95            "exp_list_comprehension", "lambda_exp", "lambda_args",
96            // Type-related
97            "type_signature", "type_parens", "type_braces", "type_var", "forall",
98            // Body nodes
99            "parameters_body", "namespace_body", "mutual_body", "data_body",
100            "record_body", "interface_body", "implementation_body",
101            // Interface and module
102            "interface_name", "module",
103            // Function body — used in complexity.scm, not tags.scm
104            "function",
105            // Operators
106            "operator", "qualified_operator", "qualified_dot_operators", "dot_operator",
107            "ticked_operator", "tuple_operator",
108            // Qualified names
109            "qualified_loname", "qualified_caname",
110            // Other constructs
111            "constructor", "statement", "declarations",
112            "with", "with_pat", "with_arg",
113            // Pragmas
114            "pragma_export", "pragma_foreign", "pragma_foreign_impl", "pragma_transform",
115            // control flow — not extracted as symbols
116            "exp_if",
117            "exp_case",
118            "import",
119        ];
120        validate_unused_kinds_audit(&Idris, documented_unused)
121            .expect("Idris unused node kinds audit failed");
122    }
123}