normalize_languages/
elisp.rs1use crate::{ContainerBody, Import, Language, LanguageSymbols, Visibility};
4use tree_sitter::Node;
5
6pub struct Elisp;
8
9impl Language for Elisp {
10 fn name(&self) -> &'static str {
11 "Emacs Lisp"
12 }
13 fn extensions(&self) -> &'static [&'static str] {
14 &["el", "elc"]
15 }
16 fn grammar_name(&self) -> &'static str {
17 "elisp"
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() != "list" {
26 return Vec::new();
27 }
28
29 let text = &content[node.byte_range()];
30 let line = node.start_position().row + 1;
31
32 if let Some(rest) = text.strip_prefix("(require ") {
33 let module = rest
34 .split(|c: char| c.is_whitespace() || c == ')')
35 .next()
36 .map(|s| s.trim_matches('\''))
37 .unwrap_or("")
38 .to_string();
39
40 if !module.is_empty() {
41 return vec![Import {
42 module,
43 names: Vec::new(),
44 alias: None,
45 is_wildcard: false,
46 is_relative: false,
47 line,
48 }];
49 }
50 }
51
52 Vec::new()
53 }
54
55 fn format_import(&self, import: &Import, _names: Option<&[&str]>) -> String {
56 format!("(require '{})", import.module)
58 }
59
60 fn get_visibility(&self, node: &Node, content: &str) -> Visibility {
61 let text = &content[node.byte_range()];
62 if text.contains("--") {
63 Visibility::Private
64 } else {
65 Visibility::Public
66 }
67 }
68
69 fn is_test_symbol(&self, symbol: &crate::Symbol) -> bool {
70 let name = symbol.name.as_str();
71 match symbol.kind {
72 crate::SymbolKind::Function | crate::SymbolKind::Method => name.starts_with("test_"),
73 crate::SymbolKind::Module => name == "tests" || name == "test",
74 _ => false,
75 }
76 }
77
78 fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
79 Some(*node)
81 }
82 fn analyze_container_body(
83 &self,
84 body_node: &Node,
85 content: &str,
86 inner_indent: &str,
87 ) -> Option<ContainerBody> {
88 crate::body::analyze_paren_body(body_node, content, inner_indent)
89 }
90
91 fn node_name<'a>(&self, _node: &Node, _content: &'a str) -> Option<&'a str> {
92 None
93 }
94}
95
96impl LanguageSymbols for Elisp {}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use crate::validate_unused_kinds_audit;
102
103 #[test]
104 fn unused_node_kinds_audit() {
105 #[rustfmt::skip]
106 let documented_unused: &[&str] = &[];
107 validate_unused_kinds_audit(&Elisp, documented_unused)
108 .expect("Emacs Lisp unused node kinds audit failed");
109 }
110}