normalize_languages/
commonlisp.rs1use crate::{ContainerBody, Import, Language, LanguageSymbols};
4use tree_sitter::Node;
5
6pub struct CommonLisp;
8
9impl Language for CommonLisp {
10 fn name(&self) -> &'static str {
11 "Common Lisp"
12 }
13 fn extensions(&self) -> &'static [&'static str] {
14 &["lisp", "lsp", "cl", "asd"]
15 }
16 fn grammar_name(&self) -> &'static str {
17 "commonlisp"
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_lit" {
26 return Vec::new();
27 }
28
29 let text = &content[node.byte_range()];
30 let line = node.start_position().row + 1;
31
32 for prefix in &["(require ", "(use-package ", "(ql:quickload "] {
33 if let Some(rest) = text.strip_prefix(prefix) {
34 let module = rest
35 .split(|c: char| c.is_whitespace() || c == ')')
36 .next()
37 .map(|s| s.trim_matches(|c| c == '\'' || c == ':' || c == '"'))
38 .unwrap_or("")
39 .to_string();
40
41 if !module.is_empty() {
42 return vec![Import {
43 module,
44 names: Vec::new(),
45 alias: None,
46 is_wildcard: false,
47 is_relative: false,
48 line,
49 }];
50 }
51 }
52 }
53
54 Vec::new()
55 }
56
57 fn format_import(&self, import: &Import, names: Option<&[&str]>) -> String {
58 let names_to_use: Vec<&str> = names
60 .map(|n| n.to_vec())
61 .unwrap_or_else(|| import.names.iter().map(|s| s.as_str()).collect());
62 if names_to_use.is_empty() {
63 format!("(use-package :{})", import.module)
64 } else {
65 let symbols: Vec<String> = names_to_use.iter().map(|n| format!("#:{}", n)).collect();
66 format!(
67 "(use-package :{} (:import-from {}))",
68 import.module,
69 symbols.join(" ")
70 )
71 }
72 }
73
74 fn is_test_symbol(&self, symbol: &crate::Symbol) -> bool {
75 let name = symbol.name.as_str();
76 match symbol.kind {
77 crate::SymbolKind::Function | crate::SymbolKind::Method => name.starts_with("test_"),
78 crate::SymbolKind::Module => name == "tests" || name == "test",
79 _ => false,
80 }
81 }
82
83 fn container_body<'a>(&self, node: &'a Node<'a>) -> Option<Node<'a>> {
84 Some(*node)
86 }
87 fn analyze_container_body(
88 &self,
89 body_node: &Node,
90 content: &str,
91 inner_indent: &str,
92 ) -> Option<ContainerBody> {
93 crate::body::analyze_paren_body(body_node, content, inner_indent)
94 }
95
96 fn node_name<'a>(&self, _node: &Node, _content: &'a str) -> Option<&'a str> {
97 None
98 }
99}
100
101impl LanguageSymbols for CommonLisp {}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use crate::validate_unused_kinds_audit;
107
108 #[test]
109 fn unused_node_kinds_audit() {
110 #[rustfmt::skip]
111 let documented_unused: &[&str] = &[
112 "accumulation_clause", "condition_clause", "do_clause", "for_clause",
114 "for_clause_word", "loop_clause", "loop_macro", "repeat_clause",
115 "termination_clause", "while_clause", "with_clause",
116 "format_directive_type", "format_modifiers", "format_prefix_parameters",
118 "format_specifier",
119 "block_comment",
121 ];
122 validate_unused_kinds_audit(&CommonLisp, documented_unused)
123 .expect("Common Lisp unused node kinds audit failed");
124 }
125}