Skip to main content

lean_ctx/core/
language_capabilities.rs

1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2pub enum LanguageId {
3    Rust,
4    TypeScript,
5    JavaScript,
6    Python,
7    Go,
8    Java,
9    C,
10    Cpp,
11    Ruby,
12    CSharp,
13    Kotlin,
14    Swift,
15    Php,
16    Bash,
17    Dart,
18    Scala,
19    Elixir,
20    Zig,
21    Vue,
22    Svelte,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct LanguageCapabilities {
27    pub deps_edges: bool,
28    pub deep_queries: bool,
29    pub import_resolver: bool,
30}
31
32impl LanguageId {
33    pub fn id_str(&self) -> &'static str {
34        match self {
35            LanguageId::Rust => "rust",
36            LanguageId::TypeScript => "typescript",
37            LanguageId::JavaScript => "javascript",
38            LanguageId::Python => "python",
39            LanguageId::Go => "go",
40            LanguageId::Java => "java",
41            LanguageId::C => "c",
42            LanguageId::Cpp => "cpp",
43            LanguageId::Ruby => "ruby",
44            LanguageId::CSharp => "csharp",
45            LanguageId::Kotlin => "kotlin",
46            LanguageId::Swift => "swift",
47            LanguageId::Php => "php",
48            LanguageId::Bash => "bash",
49            LanguageId::Dart => "dart",
50            LanguageId::Scala => "scala",
51            LanguageId::Elixir => "elixir",
52            LanguageId::Zig => "zig",
53            LanguageId::Vue => "vue",
54            LanguageId::Svelte => "svelte",
55        }
56    }
57}
58
59pub fn capabilities(lang: LanguageId) -> LanguageCapabilities {
60    match lang {
61        // tree-sitter backed (deep_queries + resolver can be meaningful)
62        LanguageId::Rust
63        | LanguageId::TypeScript
64        | LanguageId::JavaScript
65        | LanguageId::Python
66        | LanguageId::Go
67        | LanguageId::Java
68        | LanguageId::C
69        | LanguageId::Cpp
70        | LanguageId::Ruby
71        | LanguageId::CSharp
72        | LanguageId::Kotlin
73        | LanguageId::Swift
74        | LanguageId::Php
75        | LanguageId::Bash
76        | LanguageId::Dart
77        | LanguageId::Scala
78        | LanguageId::Elixir
79        | LanguageId::Zig => LanguageCapabilities {
80            deps_edges: true,
81            deep_queries: true,
82            import_resolver: true,
83        },
84        // templating languages: we can extract deps edges, but no deep_queries/resolver.
85        LanguageId::Vue | LanguageId::Svelte => LanguageCapabilities {
86            deps_edges: true,
87            deep_queries: false,
88            import_resolver: false,
89        },
90    }
91}
92
93pub fn language_for_ext(ext: &str) -> Option<LanguageId> {
94    let e = ext.trim().trim_start_matches('.').to_lowercase();
95    match e.as_str() {
96        "rs" => Some(LanguageId::Rust),
97        "ts" | "tsx" => Some(LanguageId::TypeScript),
98        "js" | "jsx" => Some(LanguageId::JavaScript),
99        "py" => Some(LanguageId::Python),
100        "go" => Some(LanguageId::Go),
101        "java" => Some(LanguageId::Java),
102        "c" | "h" => Some(LanguageId::C),
103        "cpp" | "cc" | "cxx" | "hpp" | "hxx" | "hh" => Some(LanguageId::Cpp),
104        "rb" => Some(LanguageId::Ruby),
105        "cs" => Some(LanguageId::CSharp),
106        "kt" | "kts" => Some(LanguageId::Kotlin),
107        "swift" => Some(LanguageId::Swift),
108        "php" => Some(LanguageId::Php),
109        "sh" | "bash" => Some(LanguageId::Bash),
110        "dart" => Some(LanguageId::Dart),
111        "scala" | "sc" => Some(LanguageId::Scala),
112        "ex" | "exs" => Some(LanguageId::Elixir),
113        "zig" => Some(LanguageId::Zig),
114        "vue" => Some(LanguageId::Vue),
115        "svelte" => Some(LanguageId::Svelte),
116        _ => None,
117    }
118}
119
120pub fn language_for_path(path: &str) -> Option<LanguageId> {
121    std::path::Path::new(path)
122        .extension()
123        .and_then(|e| e.to_str())
124        .and_then(language_for_ext)
125}
126
127pub fn is_indexable_ext(ext: &str) -> bool {
128    language_for_ext(ext).is_some()
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn ext_mapping_basic() {
137        assert_eq!(language_for_ext("rs"), Some(LanguageId::Rust));
138        assert_eq!(language_for_ext(".tsx"), Some(LanguageId::TypeScript));
139        assert_eq!(language_for_ext("JS"), Some(LanguageId::JavaScript));
140        assert_eq!(language_for_ext("hxx"), Some(LanguageId::Cpp));
141        assert_eq!(language_for_ext("exs"), Some(LanguageId::Elixir));
142        assert_eq!(language_for_ext("unknown"), None);
143    }
144
145    #[test]
146    fn indexable_ext_true_for_known() {
147        assert!(is_indexable_ext("rs"));
148        assert!(is_indexable_ext("vue"));
149        assert!(!is_indexable_ext("md"));
150    }
151
152    #[test]
153    fn caps_are_deterministic() {
154        let c1 = capabilities(LanguageId::Rust);
155        let c2 = capabilities(LanguageId::Rust);
156        assert_eq!(c1, c2);
157        assert!(c1.deps_edges);
158    }
159}