Skip to main content

normalize_languages/
json.rs

1//! JSON language support.
2
3use crate::{Language, LanguageSymbols};
4use tree_sitter::Node;
5
6/// JSON language support.
7pub struct Json;
8
9impl Language for Json {
10    fn name(&self) -> &'static str {
11        "JSON"
12    }
13    fn extensions(&self) -> &'static [&'static str] {
14        &["json", "jsonc"]
15    }
16    fn grammar_name(&self) -> &'static str {
17        "json"
18    }
19
20    fn as_symbols(&self) -> Option<&dyn LanguageSymbols> {
21        Some(self)
22    }
23
24    fn refine_kind(
25        &self,
26        node: &Node,
27        _content: &str,
28        tag_kind: crate::SymbolKind,
29    ) -> crate::SymbolKind {
30        // Pairs with object values act as containers (sections/namespaces)
31        if node.kind() == "pair"
32            && let Some(value) = node.child_by_field_name("value")
33            && value.kind() == "object"
34        {
35            return crate::SymbolKind::Module;
36        }
37        tag_kind
38    }
39
40    fn node_name<'a>(&self, node: &Node, content: &'a str) -> Option<&'a str> {
41        // For pair nodes, extract the key string content
42        if node.kind() == "pair"
43            && let Some(key) = node.child_by_field_name("key")
44        {
45            let mut cursor = key.walk();
46            for child in key.children(&mut cursor) {
47                if child.kind() == "string_content" {
48                    return Some(&content[child.byte_range()]);
49                }
50            }
51        }
52        None
53    }
54
55    fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
56        if node.kind() == "pair"
57            && let Some(value) = node.child_by_field_name("value")
58            && value.kind() == "object"
59        {
60            return Some(value);
61        }
62        None
63    }
64
65    fn build_signature(&self, node: &Node, content: &str) -> String {
66        if node.kind() == "pair"
67            && let Some(key) = self.node_name(node, content)
68        {
69            if let Some(value) = node.child_by_field_name("value") {
70                return match value.kind() {
71                    "object" => format!("{}: {{}}", key),
72                    "array" => format!("{}: []", key),
73                    _ => {
74                        let val_text = &content[value.byte_range()];
75                        if val_text.len() > 40 {
76                            format!("{}: {}…", key, &val_text[..37])
77                        } else {
78                            format!("{}: {}", key, val_text)
79                        }
80                    }
81                };
82            }
83            return key.to_string();
84        }
85        content[node.byte_range()]
86            .lines()
87            .next()
88            .unwrap_or("")
89            .trim()
90            .to_string()
91    }
92}
93
94impl LanguageSymbols for Json {}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99    use crate::validate_unused_kinds_audit;
100
101    #[test]
102    fn unused_node_kinds_audit() {
103        // JSON has no "interesting" unused kinds matching our patterns
104        let documented_unused: &[&str] = &[];
105        validate_unused_kinds_audit(&Json, documented_unused)
106            .expect("JSON unused node kinds audit failed");
107    }
108}