1use once_cell::sync::Lazy;
2use rustc_hash::FxHashMap;
3use std::path::Path;
4
5pub static EXTENSION_TO_LANGUAGE: Lazy<FxHashMap<&'static str, &'static str>> = Lazy::new(|| {
6 let entries: &[(&str, &str)] = &[
7 (".py", "python"),
8 (".pyw", "python"),
9 (".pyi", "python"),
10 (".js", "javascript"),
11 (".mjs", "javascript"),
12 (".cjs", "javascript"),
13 (".jsx", "jsx"),
14 (".ts", "typescript"),
15 (".tsx", "tsx"),
16 (".mts", "typescript"),
17 (".cts", "typescript"),
18 (".json", "json"),
19 (".yaml", "yaml"),
20 (".yml", "yaml"),
21 (".toml", "toml"),
22 (".md", "markdown"),
23 (".markdown", "markdown"),
24 (".html", "html"),
25 (".htm", "html"),
26 (".css", "css"),
27 (".scss", "scss"),
28 (".less", "less"),
29 (".xml", "xml"),
30 (".svg", "xml"),
31 (".sh", "bash"),
32 (".bash", "bash"),
33 (".zsh", "zsh"),
34 (".fish", "fish"),
35 (".ksh", "bash"),
36 (".ps1", "powershell"),
37 (".psm1", "powershell"),
38 (".psd1", "powershell"),
39 (".bat", "batch"),
40 (".cmd", "batch"),
41 (".c", "c"),
42 (".h", "c"),
43 (".cpp", "cpp"),
44 (".cc", "cpp"),
45 (".cxx", "cpp"),
46 (".hpp", "cpp"),
47 (".hh", "cpp"),
48 (".hxx", "cpp"),
49 (".cs", "csharp"),
50 (".fs", "fsharp"),
51 (".fsi", "fsharp"),
52 (".fsx", "fsharp"),
53 (".java", "java"),
54 (".kt", "kotlin"),
55 (".kts", "kotlin"),
56 (".scala", "scala"),
57 (".sc", "scala"),
58 (".go", "go"),
59 (".rs", "rust"),
60 (".rb", "ruby"),
61 (".rake", "ruby"),
62 (".gemspec", "ruby"),
63 (".php", "php"),
64 (".swift", "swift"),
65 (".m", "objectivec"),
66 (".mm", "objectivec"),
67 (".r", "r"),
68 (".lua", "lua"),
69 (".pl", "perl"),
70 (".pm", "perl"),
71 (".ex", "elixir"),
72 (".exs", "elixir"),
73 (".erl", "erlang"),
74 (".hrl", "erlang"),
75 (".hs", "haskell"),
76 (".lhs", "haskell"),
77 (".ml", "ocaml"),
78 (".mli", "ocaml"),
79 (".clj", "clojure"),
80 (".cljs", "clojure"),
81 (".cljc", "clojure"),
82 (".sql", "sql"),
83 (".graphql", "graphql"),
84 (".gql", "graphql"),
85 (".proto", "protobuf"),
86 (".dockerfile", "dockerfile"),
87 (".tf", "terraform"),
88 (".hcl", "hcl"),
89 (".vim", "vim"),
90 (".el", "elisp"),
91 (".lisp", "lisp"),
92 (".scm", "scheme"),
93 (".rkt", "racket"),
94 (".zig", "zig"),
95 (".nim", "nim"),
96 (".v", "v"),
97 (".sv", "systemverilog"),
98 (".vhd", "vhdl"),
99 (".vhdl", "vhdl"),
100 (".d", "d"),
101 (".dart", "dart"),
102 (".groovy", "groovy"),
103 (".gradle", "groovy"),
104 (".jl", "julia"),
105 (".ini", "ini"),
106 (".cfg", "ini"),
107 (".conf", "ini"),
108 (".properties", "properties"),
109 (".env", "dotenv"),
110 (".ada", "ada"),
111 (".pas", "pascal"),
112 (".f90", "fortran"),
113 (".f95", "fortran"),
114 (".cob", "cobol"),
115 (".asm", "asm"),
116 (".s", "asm"),
117 (".c++", "cpp"),
118 (".h++", "cpp"),
119 (".ipp", "cpp"),
120 (".tpp", "cpp"),
121 (".phtml", "php"),
122 (".php3", "php"),
123 (".php4", "php"),
124 (".php5", "php"),
125 (".php7", "php"),
126 (".phps", "php"),
127 (".adoc", "asciidoc"),
128 (".editorconfig", "editorconfig"),
129 (".tex", "latex"),
130 (".latex", "latex"),
131 (".rst", "rst"),
132 (".txt", "text"),
133 (".log", "text"),
134 (".diff", "diff"),
135 (".patch", "diff"),
136 (".vue", "vue"),
137 (".svelte", "svelte"),
138 (".sty", "latex"),
139 (".cls", "latex"),
140 (".bst", "latex"),
141 (".dtx", "latex"),
142 (".bib", "bibtex"),
143 (".nix", "nix"),
144 (".prisma", "prisma"),
145 (".bzl", "bazel"),
146 (".j2", "jinja"),
147 (".jinja", "jinja"),
148 (".jinja2", "jinja"),
149 ];
150
151 let mut map = FxHashMap::with_capacity_and_hasher(entries.len(), Default::default());
152 for &(ext, lang) in entries {
153 map.insert(ext, lang);
154 }
155 map
156});
157
158pub static FILENAME_TO_LANGUAGE: Lazy<FxHashMap<&'static str, &'static str>> = Lazy::new(|| {
159 let entries: &[(&str, &str)] = &[
160 ("makefile", "makefile"),
161 ("gnumakefile", "makefile"),
162 ("dockerfile", "dockerfile"),
163 ("containerfile", "dockerfile"),
164 ("vagrantfile", "ruby"),
165 ("gemfile", "ruby"),
166 ("rakefile", "ruby"),
167 ("guardfile", "ruby"),
168 ("brewfile", "ruby"),
169 ("podfile", "ruby"),
170 ("cmakelists.txt", "cmake"),
171 ("justfile", "just"),
172 (".bashrc", "bash"),
173 (".bash_profile", "bash"),
174 (".bash_aliases", "bash"),
175 (".zshrc", "zsh"),
176 (".zshenv", "zsh"),
177 (".zprofile", "zsh"),
178 (".profile", "bash"),
179 (".gitconfig", "gitconfig"),
180 (".gitattributes", "gitattributes"),
181 (".gitignore", "gitignore"),
182 (".dockerignore", "gitignore"),
183 (".diffctxignore", "gitignore"),
184 (".npmrc", "ini"),
185 (".yarnrc", "yaml"),
186 (".prettierrc", "json"),
187 (".eslintrc", "json"),
188 ("package.json", "json"),
189 ("tsconfig.json", "json"),
190 ("composer.json", "json"),
191 ("cargo.toml", "toml"),
192 ("pyproject.toml", "toml"),
193 ("go.mod", "gomod"),
194 ("go.sum", "gosum"),
195 ("requirements.txt", "text"),
196 ("pipfile", "toml"),
197 ("procfile", "text"),
198 ("jenkinsfile", "groovy"),
199 ("build", "bazel"),
200 ("build.bazel", "bazel"),
201 ("workspace", "bazel"),
202 ("workspace.bazel", "bazel"),
203 ("flake.lock", "json"),
204 ];
205
206 let mut map = FxHashMap::with_capacity_and_hasher(entries.len(), Default::default());
207 for &(name, lang) in entries {
208 map.insert(name, lang);
209 }
210 map
211});
212
213pub fn get_language_for_file(path: &str) -> Option<&'static str> {
214 let p = Path::new(path);
215
216 if let Some(name) = p.file_name() {
217 let name_lower = name.to_string_lossy().to_lowercase();
218 if let Some(&lang) = FILENAME_TO_LANGUAGE.get(name_lower.as_str()) {
219 return Some(lang);
220 }
221 }
222
223 if let Some(ext) = p.extension() {
224 let ext_lower = format!(".{}", ext.to_string_lossy().to_lowercase());
225 if let Some(&lang) = EXTENSION_TO_LANGUAGE.get(ext_lower.as_str()) {
226 return Some(lang);
227 }
228 }
229
230 if let Some(name) = p.file_name() {
231 let name_lower = name.to_string_lossy().to_lowercase();
232 if name_lower.starts_with("dockerfile") {
233 return Some("dockerfile");
234 }
235 }
236
237 None
238}