1use std::{path::Path, str};
2
3use collections::HashMap;
4
5use gpui::{AppContext, AssetSource, Global, SharedString};
6use serde_derive::Deserialize;
7use settings::Settings;
8use theme::ThemeSettings;
9use util::{maybe, paths::PathExt};
10
11#[derive(Deserialize, Debug)]
12pub struct FileIcons {
13 stems: HashMap<String, String>,
14 suffixes: HashMap<String, String>,
15}
16
17impl Global for FileIcons {}
18
19pub const FILE_TYPES_ASSET: &str = "icons/file_icons/file_types.json";
20
21pub fn init(assets: impl AssetSource, cx: &mut AppContext) {
22 cx.set_global(FileIcons::new(assets))
23}
24
25impl FileIcons {
26 pub fn get(cx: &AppContext) -> &Self {
27 cx.global::<FileIcons>()
28 }
29
30 pub fn new(assets: impl AssetSource) -> Self {
31 assets
32 .load(FILE_TYPES_ASSET)
33 .ok()
34 .flatten()
35 .and_then(|file| serde_json::from_str::<FileIcons>(str::from_utf8(&file).unwrap()).ok())
36 .unwrap_or_else(|| FileIcons {
37 stems: HashMap::default(),
38 suffixes: HashMap::default(),
39 })
40 }
41
42 pub fn get_icon(path: &Path, cx: &AppContext) -> Option<SharedString> {
43 let this = cx.try_global::<Self>()?;
44
45 // TODO: Associate a type with the languages and have the file's language
46 // override these associations
47 maybe!({
48 let suffix = path.icon_stem_or_suffix()?;
49
50 if let Some(type_str) = this.stems.get(suffix) {
51 return this.get_icon_for_type(type_str, cx);
52 }
53
54 this.suffixes
55 .get(suffix)
56 .and_then(|type_str| this.get_icon_for_type(type_str, cx))
57 })
58 .or_else(|| this.get_icon_for_type("default", cx))
59 }
60
61 pub fn get_icon_for_type(&self, typ: &str, cx: &AppContext) -> Option<SharedString> {
62 let theme_settings = ThemeSettings::get_global(cx);
63
64 theme_settings
65 .active_icon_theme
66 .file_icons
67 .get(typ)
68 .map(|icon_definition| icon_definition.path.clone())
69 }
70
71 pub fn get_folder_icon(expanded: bool, cx: &AppContext) -> Option<SharedString> {
72 let icon_theme = &ThemeSettings::get_global(cx).active_icon_theme;
73
74 if expanded {
75 icon_theme.directory_icons.expanded.clone()
76 } else {
77 icon_theme.directory_icons.collapsed.clone()
78 }
79 }
80
81 pub fn get_chevron_icon(expanded: bool, cx: &AppContext) -> Option<SharedString> {
82 let icon_theme = &ThemeSettings::get_global(cx).active_icon_theme;
83
84 if expanded {
85 icon_theme.chevron_icons.expanded.clone()
86 } else {
87 icon_theme.chevron_icons.collapsed.clone()
88 }
89 }
90}