1const std = @import("std");
2const posix = std.posix;
3
4const main = @import("main.zig");
5const Entry = main.Entry;
6const Options = main.Options;
7
8const Icon = @This();
9
10icon: []const u8,
11color: []const u8,
12
13// Entry types
14const directory: Icon = .{ .icon = "", .color = Options.Colors.blue };
15const drive: Icon = .{ .icon = "", .color = Options.Colors.blue };
16const file: Icon = .{ .icon = "", .color = Options.Colors.fg };
17const file_hidden: Icon = .{ .icon = "", .color = Options.Colors.fg };
18const pipe: Icon = .{ .icon = "", .color = Options.Colors.fg };
19const socket: Icon = .{ .icon = "", .color = Options.Colors.fg };
20const symlink: Icon = .{ .icon = "", .color = Options.Colors.fg };
21const symlink_dir: Icon = .{ .icon = "", .color = Options.Colors.blue };
22
23// Broad file types
24const artifact: Icon = .{ .icon = "", .color = "\x1b[38;2;255;165;0m" }; // #FFA500
25const audio: Icon = .{ .icon = "", .color = "\x1b[38;2;72;209;204m" }; // #48D1CC
26const doc: Icon = .{ .icon = "", .color = "\x1b[38;2;255;165;0m" }; // #FFA500
27const executable: Icon = .{ .icon = "", .color = Options.Colors.green };
28const image: Icon = .{ .icon = "", .color = Options.Colors.yellow };
29const playlist: Icon = .{ .icon = "", .color = "\x1b[38;2;72;209;204m" }; // #48D1CC
30const text: Icon = .{ .icon = "", .color = "\x1b[38;2;255;192;203m" }; // #FFC0CB
31const video: Icon = .{ .icon = "", .color = Options.Colors.yellow };
32
33// Filetypes
34const assembly: Icon = .{ .icon = "", .color = Options.Colors.yellow };
35const c: Icon = .{ .icon = "", .color = "\x1b[38;2;127;139;153m" }; // #7F8B99
36const clj: Icon = .{ .icon = "", .color = "\x1b[38;2;88;129;216m" }; // #5881D8
37const cmake: Icon = .{ .icon = "", .color = "\x1b[38;2;33;153;72m" }; // #219948
38const config: Icon = .{ .icon = "", .color = Options.Colors.yellow };
39const cpp: Icon = .{ .icon = "", .color = "\x1b[38;2;0;89;156m" }; // #00599C
40const cs: Icon = .{ .icon = "", .color = "\x1b[38;2;115;85;221m" }; // #7355dd
41const css: Icon = .{ .icon = "", .color = "\x1b[38;2;21;114;182m" }; // #1572B6
42const dart: Icon = .{ .icon = "", .color = "\x1b[38;2;1;117;194m" }; // #0175C2
43const diff: Icon = .{ .icon = "", .color = "\x1b[38;2;50;167;220m" }; // #32A7DC
44const elisp: Icon = .{ .icon = "", .color = "\x1b[38;2;127;90;182m" }; // #7f5ab6
45const erl: Icon = .{ .icon = "", .color = "\x1b[38;2;169;5;51m" }; // #A90533
46const ex: Icon = .{ .icon = "", .color = "\x1b[38;2;110;74;126m" }; // #6E4A7E
47const fennel: Icon = .{ .icon = "", .color = "" };
48const git: Icon = .{ .icon = "", .color = Options.Colors.fg };
49const font: Icon = .{ .icon = "", .color = "\x1b[38;2;50;167;220m" }; // #32A7DC
50const go: Icon = .{ .icon = "", .color = "\x1b[38;2;0;173;216m" }; // #00ADD8
51const h: Icon = .{ .icon = "", .color = "\x1b[38;2;81;154;186m" }; // #519ABA
52const html: Icon = .{ .icon = "", .color = "\x1b[38;2;227;79;38m" }; // #E34F26
53const hs: Icon = .{ .icon = "", .color = "\x1b[38;2;93;79;133m" }; // #5D4F85
54const java: Icon = .{ .icon = "", .color = "\x1b[38;2;0;115;150m" }; // #007396
55const javascript: Icon = .{ .icon = "", .color = "\x1b[38;2;0;187;0m" }; // #00BB00
56const json: Icon = .{ .icon = "", .color = Options.Colors.blue };
57const key: Icon = .{ .icon = "", .color = "\x1b[38;2;255;192;203m" }; // #FFC0CB
58const kt: Icon = .{ .icon = "", .color = "\x1b[38;2;127;82;255m" }; // #7F52FF
59const license: Icon = .{ .icon = "", .color = "" };
60const log: Icon = .{ .icon = "", .color = Options.Colors.red };
61const lua: Icon = .{ .icon = "", .color = "\x1b[38;2;0;0;124m" }; // #00007C
62const makefile: Icon = .{ .icon = "", .color = "\x1b[38;2;227;121;51m" }; // #E37933
63const markdown: Icon = .{ .icon = "", .color = Options.Colors.fg };
64const nim: Icon = .{ .icon = "", .color = "\x1b[38;2;254;233;89m" }; // #FEE959
65const nix: Icon = .{ .icon = "", .color = "\x1b[38;2;127;185;228m" }; // #7fb9e4
66const ocaml: Icon = .{ .icon = "", .color = "\x1b[38;2;241;136;63m" }; // #F1883F
67const package: Icon = .{ .icon = "", .color = "\x1b[38;2;127;185;228m" }; // #7FB9E4
68const pdf: Icon = .{ .icon = "", .color = "\x1b[38;2;127;185;228m" }; // #7FB9E4
69const php: Icon = .{ .icon = "", .color = "\x1b[38;2;119;123;180m" }; // #777BB4
70const pl: Icon = .{ .icon = "", .color = "\x1b[38;2;0;79;120m" }; // #004F78
71const python: Icon = .{ .icon = "", .color = "\x1b[38;2;55;118;171m" }; // #3776AB
72const rb: Icon = .{ .icon = "", .color = "\x1b[38;2;204;52;45m" }; // #CC342D
73const rlang: Icon = .{ .icon = "", .color = "\x1b[38;2;39;109;195m" }; // #276DC3
74const root: Icon = .{ .icon = "", .color = "\x1b[38;2;23;214;240m" }; // #17D6F0
75const rust: Icon = .{ .icon = "", .color = "\x1b[38;2;211;69;22m" }; // #D34516
76const scala: Icon = .{ .icon = "", .color = "\x1b[38;2;220;50;47m" }; // #DC322F
77const script: Icon = .{ .icon = "", .color = Options.Colors.green };
78const sh: Icon = .{ .icon = "", .color = "\x1b[38;2;137;224;81m" }; // #89E051
79const sheet: Icon = .{ .icon = "", .color = "\x1b[38;2;50;167;220m" }; // #32A7DC
80const sql: Icon = .{ .icon = "", .color = "\x1b[38;2;229;101;54m" }; // #E56536
81const swift: Icon = .{ .icon = "", .color = "\x1b[38;2;240;81;56m" }; // #F05138
82const slide: Icon = .{ .icon = "", .color = "\x1b[38;2;50;167;220m" }; // #32A7DC
83const toml: Icon = .{ .icon = "", .color = "\x1b[38;2;156;66;33m" }; // #9C4221
84const typescript: Icon = .{ .icon = "", .color = "\x1b[38;2;49;120;198m" }; // #3178C6
85const xml: Icon = .{ .icon = "", .color = Options.Colors.green };
86const yaml: Icon = .{ .icon = "", .color = "\x1b[38;2;204;25;31m" }; // #CC191F
87const zig: Icon = .{ .icon = "", .color = "\x1b[38;2;247;164;71m" }; // #F7A447
88
89const by_name: std.StaticStringMap(Icon) = .initComptime(.{
90 .{ ".bash_aliases", Icon.sh },
91 .{ ".bash_history", Icon.sh },
92 .{ ".bash_logout", Icon.sh },
93 .{ ".bash_profile", Icon.sh },
94 .{ ".bashrc", Icon.sh },
95 .{ ".gitattributes", Icon.git },
96 .{ ".gitconfig", Icon.git },
97 .{ ".gitignore", Icon.git },
98 .{ ".mailmap", Icon.git },
99 .{ ".rootlogon.C", Icon.root },
100 .{ ".rootrc", Icon.root },
101 .{ ".zshrc", Icon.sh },
102 .{ "build.gradle", Icon.java },
103 .{ "build.gradle.kts", Icon.kt },
104 .{ "build.sbt", Icon.scala },
105 .{ "cabal", Icon.hs },
106 .{ "Cargo.lock", Icon.rust },
107 .{ "Cargo.toml", Icon.rust },
108 .{ "CMakeCache.txt", Icon.cmake },
109 .{ "CMakeLists.txt", Icon.cmake },
110 .{ "composer.json", Icon.php },
111 .{ "composer.lock", Icon.php },
112 .{ "cpanfile", Icon.pl },
113 .{ "csproj", Icon.cs },
114 .{ "DESCRIPTION", Icon.rlang },
115 .{ "flake.lock", Icon.nix },
116 .{ "Gemfile", Icon.rb },
117 .{ "Gemfile.lock", Icon.rb },
118 .{ "GNUMakefile", Icon.makefile },
119 .{ "go.mod", Icon.go },
120 .{ "go.sum", Icon.go },
121 .{ "install_manifest.txt", Icon.cmake },
122 .{ "Makefile", Icon.makefile },
123 .{ "mix.exs", Icon.ex },
124 .{ "package-lock.json", Icon.javascript },
125 .{ "package.json", Icon.javascript },
126 .{ "Package.swift", Icon.swift },
127 .{ "pom.xml", Icon.java },
128 .{ "project.clj", Icon.clj },
129 .{ "pubspec.yaml", Icon.dart },
130 .{ "rebar.config", Icon.erl },
131 .{ "requirements.txt", Icon.python },
132 .{ "stack.yaml", Icon.hs },
133 .{ "tsconfig.json", Icon.typescript },
134});
135
136const by_extension: std.StaticStringMap(Icon) = .initComptime(.{
137 .{ "asm", Icon.assembly },
138 .{ "asn", Icon.script },
139 .{ "avro", Icon.json },
140 .{ "awk", Icon.script },
141 .{ "bash_history", Icon.script },
142 .{ "bash_profile", Icon.script },
143 .{ "bash", Icon.sh },
144 .{ "bashrc", Icon.script },
145 .{ "bmp", Icon.image },
146 .{ "bz2", Icon.artifact },
147 .{ "c", Icon.c },
148 .{ "cc", Icon.cpp },
149 .{ "cfg", Icon.config },
150 .{ "cjs", Icon.javascript },
151 .{ "clj", Icon.clj },
152 .{ "cljs", Icon.clj },
153 .{ "cma", Icon.ocaml },
154 .{ "cmake.in", Icon.cmake },
155 .{ "cmake", Icon.cmake },
156 .{ "cmi", Icon.ocaml },
157 .{ "cmo", Icon.ocaml },
158 .{ "cmt", Icon.ocaml },
159 .{ "cmti", Icon.ocaml },
160 .{ "cmx", Icon.ocaml },
161 .{ "cmxa", Icon.ocaml },
162 .{ "cmxs", Icon.ocaml },
163 .{ "conf", Icon.config },
164 .{ "cpp", Icon.cpp },
165 .{ "cs", Icon.cs },
166 .{ "csh", Icon.script },
167 .{ "css", Icon.css },
168 .{ "csv", Icon.sheet },
169 .{ "cxx", Icon.cpp },
170 .{ "dart", Icon.dart },
171 .{ "deb", Icon.package },
172 .{ "diff", Icon.diff },
173 .{ "doc", Icon.doc },
174 .{ "docx", Icon.doc },
175 .{ "drv", Icon.nix },
176 .{ "editorconf", Icon.config },
177 .{ "el", Icon.elisp },
178 .{ "elf", Icon.script },
179 .{ "eot", Icon.font },
180 .{ "erl", Icon.erl },
181 .{ "ex", Icon.ex },
182 .{ "exs", Icon.ex },
183 .{ "fish", Icon.script },
184 .{ "flac", Icon.audio },
185 .{ "fnl", Icon.fennel },
186 .{ "font", Icon.font },
187 .{ "gdoc", Icon.doc },
188 .{ "gif", Icon.image },
189 .{ "git", Icon.git },
190 .{ "go", Icon.go },
191 .{ "gsheet", Icon.sheet },
192 .{ "gslides", Icon.slide },
193 .{ "gz", Icon.artifact },
194 .{ "h", Icon.h },
195 .{ "hh", Icon.h },
196 .{ "hpp", Icon.h },
197 .{ "hs", Icon.hs },
198 .{ "htm", Icon.html },
199 .{ "html", Icon.html },
200 .{ "hxx", Icon.h },
201 .{ "ico", Icon.image },
202 .{ "in", Icon.text },
203 .{ "ini", Icon.config },
204 .{ "ipynb", Icon.python },
205 .{ "java", Icon.java },
206 .{ "jpeg", Icon.image },
207 .{ "jpg", Icon.image },
208 .{ "js", Icon.javascript },
209 .{ "json", Icon.json },
210 .{ "jsonc", Icon.json },
211 .{ "jsx", Icon.javascript },
212 .{ "key", Icon.key },
213 .{ "ksh", Icon.script },
214 .{ "kt", Icon.kt },
215 .{ "kts", Icon.kt },
216 .{ "LICENSE", Icon.license },
217 .{ "log", Icon.log },
218 .{ "lua", Icon.lua },
219 .{ "m3u", Icon.playlist },
220 .{ "m4a", Icon.audio },
221 .{ "markdown", Icon.markdown },
222 .{ "md", Icon.markdown },
223 .{ "mdk", Icon.markdown },
224 .{ "mjs", Icon.javascript },
225 .{ "mkv", Icon.video },
226 .{ "ml", Icon.ocaml },
227 .{ "mli", Icon.ocaml },
228 .{ "mp3", Icon.audio },
229 .{ "mp4", Icon.video },
230 .{ "nar", Icon.nix },
231 .{ "nim", Icon.nim },
232 .{ "nimble", Icon.nim },
233 .{ "nix", Icon.nix },
234 .{ "odp", Icon.slide },
235 .{ "ods", Icon.sheet },
236 .{ "odt", Icon.doc },
237 .{ "ogg", Icon.audio },
238 .{ "opam", Icon.ocaml },
239 .{ "opus", Icon.audio },
240 .{ "otf", Icon.font },
241 .{ "patch", Icon.diff },
242 .{ "pdf", Icon.pdf },
243 .{ "pem", Icon.key },
244 .{ "php", Icon.php },
245 .{ "pkg", Icon.package },
246 .{ "pl", Icon.pl },
247 .{ "pls", Icon.playlist },
248 .{ "png", Icon.image },
249 .{ "ppt", Icon.slide },
250 .{ "pptm,", Icon.slide },
251 .{ "pptx", Icon.slide },
252 .{ "ps1", Icon.script },
253 .{ "pub", Icon.key },
254 .{ "py", Icon.python },
255 .{ "pyc", Icon.python },
256 .{ "r", Icon.rlang },
257 .{ "rb", Icon.rb },
258 .{ "readme", Icon.markdown },
259 .{ "rmd", Icon.markdown },
260 .{ "root", Icon.root },
261 .{ "rpm", Icon.package },
262 .{ "rs", Icon.rust },
263 .{ "rtf", Icon.text },
264 .{ "s", Icon.assembly },
265 .{ "scala", Icon.scala },
266 .{ "sh", Icon.sh },
267 .{ "shell", Icon.script },
268 .{ "sig", Icon.license },
269 .{ "sql", Icon.sql },
270 .{ "svg", Icon.image },
271 .{ "swift", Icon.swift },
272 .{ "tar", Icon.artifact },
273 .{ "tgz", Icon.artifact },
274 .{ "tiff", Icon.image },
275 .{ "toml", Icon.toml },
276 .{ "ts", Icon.typescript },
277 .{ "tsx", Icon.typescript },
278 .{ "ttc", Icon.font },
279 .{ "ttf", Icon.font },
280 .{ "txt", Icon.text },
281 .{ "vlc", Icon.playlist },
282 .{ "wav", Icon.audio },
283 .{ "webp", Icon.image },
284 .{ "wma", Icon.audio },
285 .{ "woff", Icon.font },
286 .{ "woff2", Icon.font },
287 .{ "xls", Icon.sheet },
288 .{ "xlsv", Icon.sheet },
289 .{ "xlsx", Icon.sheet },
290 .{ "xml", Icon.xml },
291 .{ "yaml", Icon.yaml },
292 .{ "yml", Icon.json },
293 .{ "zig", Icon.zig },
294 .{ "zip", Icon.artifact },
295 .{ "zon", Icon.zig },
296 .{ "zsh-theme", Icon.script },
297 .{ "zsh", Icon.script },
298 .{ "zshrc", Icon.script },
299});
300
301pub fn get(entry: Entry) Icon {
302 // 1. By name
303 // 2. By type
304 // 3. By extension
305 if (by_name.get(entry.name)) |icon| return icon;
306
307 switch (entry.kind) {
308 .block_device => return drive,
309 .character_device => return drive,
310 .directory => return directory,
311 .file => {
312 const ext = std.fs.path.extension(entry.name);
313 if (ext.len > 0) {
314 const ft = ext[1..];
315 // First try exact match for performance
316 if (by_extension.get(ft)) |icon| return icon;
317
318 // Then try case-insensitive match
319 var buf: [32]u8 = undefined;
320 if (ft.len < buf.len) {
321 const lowercase_ext = std.ascii.lowerString(&buf, ft);
322 if (by_extension.get(lowercase_ext)) |icon| return icon;
323 }
324 }
325
326 if (entry.isExecutable()) {
327 return executable;
328 }
329 return file;
330 },
331 .named_pipe => return pipe,
332 .sym_link => {
333 if (posix.S.ISDIR(entry.statx.mode)) {
334 return symlink_dir;
335 }
336 return symlink;
337 },
338 .unix_domain_socket => return pipe,
339 else => return file,
340 }
341}