Skip to main content

normalize_languages/
asciidoc.rs

1//! AsciiDoc language support.
2
3use crate::{Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6/// AsciiDoc language support.
7pub struct AsciiDoc;
8
9impl Language for AsciiDoc {
10    fn name(&self) -> &'static str {
11        "AsciiDoc"
12    }
13    fn extensions(&self) -> &'static [&'static str] {
14        &["adoc", "asciidoc", "asc"]
15    }
16    fn grammar_name(&self) -> &'static str {
17        "asciidoc"
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() != "block_macro" {
26            return Vec::new();
27        }
28
29        let text = &content[node.byte_range()];
30        // Only include macros are imports
31        if !text.starts_with("include::") {
32            return Vec::new();
33        }
34
35        vec![Import {
36            module: text.trim().to_string(),
37            names: Vec::new(),
38            alias: None,
39            is_wildcard: false,
40            is_relative: false,
41            line: node.start_position().row + 1,
42        }]
43    }
44
45    fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
46        import.module.clone()
47    }
48
49    fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
50        node.child_by_field_name("content")
51    }
52
53    fn node_name<'a>(&self, node: &Node, content: &'a str) -> Option<&'a str> {
54        // For sections, the title is typically the first line
55        let text = &content[node.byte_range()];
56        let first_line = text.lines().next()?;
57        // Strip section markers (=, ==, etc.)
58        let name = first_line.trim().trim_start_matches('=').trim();
59        if !name.is_empty() { Some(name) } else { None }
60    }
61}
62
63impl LanguageSymbols for AsciiDoc {}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use crate::validate_unused_kinds_audit;
69
70    #[test]
71    fn unused_node_kinds_audit() {
72        #[rustfmt::skip]
73        let documented_unused: &[&str] = &[
74            // Block types - not symbols
75            "literal_block", "listing_block", "open_block", "quoted_block",
76            "passthrough_block", "delimited_block", "table_block", "ntable_block",
77            "ident_block", "quoted_md_block",
78            // Block markers and bodies
79            "block_comment", "block_comment_start_marker", "block_comment_end_marker",
80            "quoted_block_marker", "quoted_block_md_marker", "passthrough_block_marker",
81            "open_block_marker", "table_block_marker", "ntable_block_marker",
82            "literal_block_marker", "literal_block_body",
83            "listing_block_start_marker", "listing_block_end_marker", "listing_block_body",
84            "delimited_block_start_marker", "delimited_block_end_marker",
85            // Block elements and titles
86            "block_title", "block_title_marker", "block_element",
87            "block_macro_name", "block_macro_attr",
88            // Other content
89            "body", "ident_block_line", "admonition_important",
90            // structural node, not extracted as symbols
91            "section_block",
92            "block_macro",
93        ];
94        validate_unused_kinds_audit(&AsciiDoc, documented_unused)
95            .expect("AsciiDoc unused node kinds audit failed");
96    }
97}