Allow icon themes to provide their own file associations (#24926)
This PR adds the ability for icon themes to provide their own file associations. The old `file_types.json` that was previously used to make these associations has been removed in favor of storing them on the default theme. Icon themes have two new fields on them: - `file_stems`: A mapping of file stems to icon keys. - `file_suffixes`: A mapping of file suffixes to icon keys. These mappings produce icon keys which can then be used in `file_icons` to associate them to a particular icon: ```json { "file_stems": { "Makefile": "make" }, "file_suffixes": { "idr": "idris" }, "file_icons": { "idris": { "path": "./icons/idris.svg" }, "make": { "path": "./icons/make.svg" } } } ``` When loading an icon theme, the `file_stems` and `file_icons` fields will be merged with the ones from the base icon theme, with the values from the icon theme being loaded overriding ones in the base theme. Release Notes: - Added the ability for icon themes to provide their own file associations.
This commit is contained in:
parent
f2776099ab
commit
e60123bbdc
@ -43,10 +43,6 @@ Zed's default icon theme consists of icons that are hand-designed to fit togethe
|
||||
|
||||
We do not accept PRs for file icons that are just an off-the-shelf SVG taken from somewhere else.
|
||||
|
||||
### File icon associations
|
||||
|
||||
We will happily accept PRs that add new file icon associations to [`file_types.json`](assets/icons/file_icons/file_types.json) to allow them to be targeted by [icon themes](https://zed.dev/docs/extensions/icon-themes).
|
||||
|
||||
### Adding new icons to the Zed icon theme
|
||||
|
||||
If you would like to add a new icon to the Zed icon theme, [open a Discussion](https://github.com/zed-industries/zed/discussions/new?category=ux-and-design) and we can work with you on getting an icon designed and added to Zed.
|
||||
|
4
Cargo.lock
generated
4
Cargo.lock
generated
@ -4704,11 +4704,8 @@ dependencies = [
|
||||
name = "file_icons"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"collections",
|
||||
"gpui",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"settings",
|
||||
"theme",
|
||||
"util",
|
||||
@ -16680,7 +16677,6 @@ dependencies = [
|
||||
"feature_flags",
|
||||
"feedback",
|
||||
"file_finder",
|
||||
"file_icons",
|
||||
"fs",
|
||||
"futures 0.3.31",
|
||||
"git",
|
||||
|
@ -1,260 +0,0 @@
|
||||
{
|
||||
"stems": {
|
||||
"Dockerfile": "docker",
|
||||
"Podfile": "ruby",
|
||||
"Procfile": "heroku"
|
||||
},
|
||||
"suffixes": {
|
||||
"COMMIT_EDITMSG": "vcs",
|
||||
"EDIT_DESCRIPTION": "vcs",
|
||||
"Emakefile": "erlang",
|
||||
"MERGE_MSG": "vcs",
|
||||
"NOTES_EDITMSG": "vcs",
|
||||
"R": "r",
|
||||
"TAG_EDITMSG": "vcs",
|
||||
"aac": "audio",
|
||||
"accdb": "storage",
|
||||
"app.src": "erlang",
|
||||
"astro": "astro",
|
||||
"avi": "video",
|
||||
"avif": "image",
|
||||
"bak": "backup",
|
||||
"bash": "terminal",
|
||||
"bash_aliases": "terminal",
|
||||
"bash_logout": "terminal",
|
||||
"bash_profile": "terminal",
|
||||
"bashrc": "terminal",
|
||||
"bicep": "bicep",
|
||||
"bmp": "image",
|
||||
"c": "c",
|
||||
"c++": "cpp",
|
||||
"cc": "cpp",
|
||||
"cjs": "javascript",
|
||||
"cjsx": "react",
|
||||
"coffee": "coffeescript",
|
||||
"conf": "settings",
|
||||
"cpp": "cpp",
|
||||
"cr": "crystal",
|
||||
"cs": "csharp",
|
||||
"csproj": "csproj",
|
||||
"css": "css",
|
||||
"csv": "storage",
|
||||
"cts": "typescript",
|
||||
"ctsx": "react",
|
||||
"cue": "cue",
|
||||
"cxx": "cpp",
|
||||
"dart": "dart",
|
||||
"dat": "storage",
|
||||
"db": "storage",
|
||||
"dbf": "storage",
|
||||
"diff": "diff",
|
||||
"dll": "storage",
|
||||
"doc": "document",
|
||||
"docx": "document",
|
||||
"ecr": "crystal",
|
||||
"eex": "elixir",
|
||||
"elm": "elm",
|
||||
"erl": "erlang",
|
||||
"escript": "erlang",
|
||||
"eslint.config.cjs": "eslint",
|
||||
"eslint.config.cts": "eslint",
|
||||
"eslint.config.js": "eslint",
|
||||
"eslint.config.mjs": "eslint",
|
||||
"eslint.config.mts": "eslint",
|
||||
"eslint.config.ts": "eslint",
|
||||
"eslintrc": "eslint",
|
||||
"eslintrc.js": "eslint",
|
||||
"eslintrc.json": "eslint",
|
||||
"ex": "elixir",
|
||||
"exs": "elixir",
|
||||
"fish": "terminal",
|
||||
"flac": "audio",
|
||||
"fmp": "storage",
|
||||
"fp7": "storage",
|
||||
"frm": "storage",
|
||||
"fs": "fsharp",
|
||||
"fsproj": "fsproj",
|
||||
"gdb": "storage",
|
||||
"gif": "image",
|
||||
"gitattributes": "vcs",
|
||||
"gitignore": "vcs",
|
||||
"gitkeep": "vcs",
|
||||
"gitlab-ci.yml": "gitlab",
|
||||
"gitmodules": "vcs",
|
||||
"gleam": "gleam",
|
||||
"go": "go",
|
||||
"gql": "graphql",
|
||||
"graphql": "graphql",
|
||||
"graphqls": "graphql",
|
||||
"h": "c",
|
||||
"handlebars": "code",
|
||||
"hbs": "template",
|
||||
"hcl": "hcl",
|
||||
"heex": "elixir",
|
||||
"heic": "image",
|
||||
"heif": "image",
|
||||
"hh": "cpp",
|
||||
"hpp": "cpp",
|
||||
"hrl": "erlang",
|
||||
"hs": "haskell",
|
||||
"htm": "html",
|
||||
"html": "html",
|
||||
"hxx": "cpp",
|
||||
"ib": "storage",
|
||||
"ico": "image",
|
||||
"ini": "settings",
|
||||
"inl": "cpp",
|
||||
"j2k": "image",
|
||||
"java": "java",
|
||||
"jfif": "image",
|
||||
"jl": "julia",
|
||||
"jp2": "image",
|
||||
"jpeg": "image",
|
||||
"jpg": "image",
|
||||
"js": "javascript",
|
||||
"json": "json",
|
||||
"jsonc": "storage",
|
||||
"jsx": "react",
|
||||
"jxl": "image",
|
||||
"kt": "kotlin",
|
||||
"ldf": "storage",
|
||||
"lock": "lock",
|
||||
"lockb": "bun",
|
||||
"log": "log",
|
||||
"lua": "lua",
|
||||
"luau": "luau",
|
||||
"m4a": "audio",
|
||||
"m4v": "video",
|
||||
"markdown": "markdown",
|
||||
"md": "markdown",
|
||||
"mdb": "storage",
|
||||
"mdf": "storage",
|
||||
"mdx": "document",
|
||||
"metadata": "code",
|
||||
"metal": "metal",
|
||||
"mjs": "javascript",
|
||||
"mjsx": "react",
|
||||
"mka": "audio",
|
||||
"mkv": "video",
|
||||
"ml": "ocaml",
|
||||
"mli": "ocaml",
|
||||
"mod": "go",
|
||||
"mov": "video",
|
||||
"mp3": "audio",
|
||||
"mp4": "video",
|
||||
"mts": "typescript",
|
||||
"mtsx": "react",
|
||||
"myd": "storage",
|
||||
"myi": "storage",
|
||||
"nim": "nim",
|
||||
"nix": "nix",
|
||||
"nu": "terminal",
|
||||
"odp": "document",
|
||||
"ods": "document",
|
||||
"odt": "document",
|
||||
"ogg": "audio",
|
||||
"opus": "audio",
|
||||
"otf": "font",
|
||||
"pcss": "css",
|
||||
"pdb": "storage",
|
||||
"pdf": "document",
|
||||
"php": "php",
|
||||
"plist": "template",
|
||||
"png": "image",
|
||||
"postcss": "css",
|
||||
"ppt": "document",
|
||||
"pptx": "document",
|
||||
"prettier.config.cjs": "prettier",
|
||||
"prettier.config.js": "prettier",
|
||||
"prettier.config.mjs": "prettier",
|
||||
"prettierignore": "prettier",
|
||||
"prettierrc": "prettier",
|
||||
"prettierrc.cjs": "prettier",
|
||||
"prettierrc.js": "prettier",
|
||||
"prettierrc.json": "prettier",
|
||||
"prettierrc.json5": "prettier",
|
||||
"prettierrc.mjs": "prettier",
|
||||
"prettierrc.toml": "prettier",
|
||||
"prettierrc.yaml": "prettier",
|
||||
"prettierrc.yml": "prettier",
|
||||
"prisma": "prisma",
|
||||
"profile": "terminal",
|
||||
"ps1": "terminal",
|
||||
"psd": "image",
|
||||
"py": "python",
|
||||
"qoi": "image",
|
||||
"r": "r",
|
||||
"rb": "ruby",
|
||||
"rebar.config": "erlang",
|
||||
"rkt": "code",
|
||||
"roc": "roc",
|
||||
"rs": "rust",
|
||||
"rtf": "document",
|
||||
"sass": "sass",
|
||||
"sav": "storage",
|
||||
"sc": "scala",
|
||||
"scala": "scala",
|
||||
"scm": "code",
|
||||
"scss": "sass",
|
||||
"sdf": "storage",
|
||||
"sh": "terminal",
|
||||
"sln": "vs_sln",
|
||||
"sol": "solidity",
|
||||
"sql": "storage",
|
||||
"sqlite": "storage",
|
||||
"stylelint.config.cjs": "stylelint",
|
||||
"stylelint.config.js": "stylelint",
|
||||
"stylelint.config.mjs": "stylelint",
|
||||
"stylelintignore": "stylelint",
|
||||
"stylelintrc": "stylelint",
|
||||
"stylelintrc.cjs": "stylelint",
|
||||
"stylelintrc.js": "stylelint",
|
||||
"stylelintrc.json": "stylelint",
|
||||
"stylelintrc.mjs": "stylelint",
|
||||
"stylelintrc.yaml": "stylelint",
|
||||
"stylelintrc.yml": "stylelint",
|
||||
"suo": "vs_suo",
|
||||
"svelte": "svelte",
|
||||
"svg": "image",
|
||||
"swift": "swift",
|
||||
"tcl": "tcl",
|
||||
"tf": "terraform",
|
||||
"tfvars": "terraform",
|
||||
"tiff": "image",
|
||||
"toml": "toml",
|
||||
"ts": "typescript",
|
||||
"tsv": "storage",
|
||||
"tsx": "react",
|
||||
"ttf": "font",
|
||||
"txt": "document",
|
||||
"v": "v",
|
||||
"vbproj": "vbproj",
|
||||
"vsh": "v",
|
||||
"vue": "vue",
|
||||
"vv": "v",
|
||||
"wav": "audio",
|
||||
"webm": "video",
|
||||
"webp": "image",
|
||||
"wma": "audio",
|
||||
"wmv": "video",
|
||||
"woff": "font",
|
||||
"woff2": "font",
|
||||
"work": "go",
|
||||
"wv": "audio",
|
||||
"xls": "document",
|
||||
"xlsx": "document",
|
||||
"xml": "template",
|
||||
"xrl": "erlang",
|
||||
"yaml": "settings",
|
||||
"yml": "settings",
|
||||
"yrl": "erlang",
|
||||
"zig": "zig",
|
||||
"zlogin": "terminal",
|
||||
"zsh": "terminal",
|
||||
"zsh_aliases": "terminal",
|
||||
"zsh_histfile": "terminal",
|
||||
"zsh_profile": "terminal",
|
||||
"zshenv": "terminal",
|
||||
"zshrc": "terminal"
|
||||
}
|
||||
}
|
@ -13,11 +13,8 @@ path = "src/file_icons.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
collections.workspace = true
|
||||
gpui.workspace = true
|
||||
serde.workspace = true
|
||||
serde_derive.workspace = true
|
||||
serde_json.workspace = true
|
||||
settings.workspace = true
|
||||
theme.workspace = true
|
||||
util.workspace = true
|
||||
|
@ -1,52 +1,33 @@
|
||||
use std::sync::Arc;
|
||||
use std::{path::Path, str};
|
||||
|
||||
use collections::HashMap;
|
||||
|
||||
use gpui::{App, AssetSource, Global, SharedString};
|
||||
use serde_derive::Deserialize;
|
||||
use gpui::{App, SharedString};
|
||||
use settings::Settings;
|
||||
use theme::{IconTheme, ThemeRegistry, ThemeSettings};
|
||||
use util::paths::PathExt;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct FileIcons {
|
||||
stems: HashMap<String, String>,
|
||||
suffixes: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Global for FileIcons {}
|
||||
|
||||
pub const FILE_TYPES_ASSET: &str = "icons/file_icons/file_types.json";
|
||||
|
||||
pub fn init(assets: impl AssetSource, cx: &mut App) {
|
||||
cx.set_global(FileIcons::new(assets))
|
||||
icon_theme: Arc<IconTheme>,
|
||||
}
|
||||
|
||||
impl FileIcons {
|
||||
pub fn get(cx: &App) -> &Self {
|
||||
cx.global::<FileIcons>()
|
||||
}
|
||||
pub fn get(cx: &App) -> Self {
|
||||
let theme_settings = ThemeSettings::get_global(cx);
|
||||
|
||||
pub fn new(assets: impl AssetSource) -> Self {
|
||||
assets
|
||||
.load(FILE_TYPES_ASSET)
|
||||
.ok()
|
||||
.flatten()
|
||||
.and_then(|file| serde_json::from_str::<FileIcons>(str::from_utf8(&file).unwrap()).ok())
|
||||
.unwrap_or_else(|| FileIcons {
|
||||
stems: HashMap::default(),
|
||||
suffixes: HashMap::default(),
|
||||
})
|
||||
Self {
|
||||
icon_theme: theme_settings.active_icon_theme.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_icon(path: &Path, cx: &App) -> Option<SharedString> {
|
||||
let this = cx.try_global::<Self>()?;
|
||||
let this = Self::get(cx);
|
||||
|
||||
let get_icon_from_suffix = |suffix: &str| -> Option<SharedString> {
|
||||
this.stems
|
||||
this.icon_theme
|
||||
.file_stems
|
||||
.get(suffix)
|
||||
.or_else(|| this.suffixes.get(suffix))
|
||||
.or_else(|| this.icon_theme.file_suffixes.get(suffix))
|
||||
.and_then(|typ| this.get_icon_for_type(typ, cx))
|
||||
};
|
||||
// TODO: Associate a type with the languages and have the file's language
|
||||
|
@ -28,12 +28,12 @@ use file_icons::FileIcons;
|
||||
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, anchored, deferred, div, point, px, size, uniform_list, Action, AnyElement, App,
|
||||
AppContext as _, AssetSource, AsyncWindowContext, Bounds, ClipboardItem, Context, DismissEvent,
|
||||
Div, ElementId, Entity, EventEmitter, FocusHandle, Focusable, HighlightStyle,
|
||||
InteractiveElement, IntoElement, KeyContext, ListHorizontalSizingBehavior, ListSizingBehavior,
|
||||
MouseButton, MouseDownEvent, ParentElement, Pixels, Point, Render, ScrollStrategy,
|
||||
SharedString, Stateful, StatefulInteractiveElement as _, Styled, Subscription, Task,
|
||||
UniformListScrollHandle, WeakEntity, Window,
|
||||
AppContext as _, AsyncWindowContext, Bounds, ClipboardItem, Context, DismissEvent, Div,
|
||||
ElementId, Entity, EventEmitter, FocusHandle, Focusable, HighlightStyle, InteractiveElement,
|
||||
IntoElement, KeyContext, ListHorizontalSizingBehavior, ListSizingBehavior, MouseButton,
|
||||
MouseDownEvent, ParentElement, Pixels, Point, Render, ScrollStrategy, SharedString, Stateful,
|
||||
StatefulInteractiveElement as _, Styled, Subscription, Task, UniformListScrollHandle,
|
||||
WeakEntity, Window,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::{BufferId, BufferSnapshot, OffsetRangeExt, OutlineItem};
|
||||
@ -664,9 +664,8 @@ pub fn init_settings(cx: &mut App) {
|
||||
OutlinePanelSettings::register(cx);
|
||||
}
|
||||
|
||||
pub fn init(assets: impl AssetSource, cx: &mut App) {
|
||||
pub fn init(cx: &mut App) {
|
||||
init_settings(cx);
|
||||
file_icons::init(assets, cx);
|
||||
|
||||
cx.observe_new(|workspace: &mut Workspace, _, _| {
|
||||
workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
|
||||
@ -6641,7 +6640,7 @@ outline: struct OutlineEntryExcerpt
|
||||
workspace::init_settings(cx);
|
||||
Project::init_settings(cx);
|
||||
project_search::init(cx);
|
||||
super::init((), cx);
|
||||
super::init(cx);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,8 @@ use file_icons::FileIcons;
|
||||
use git::status::GitSummary;
|
||||
use gpui::{
|
||||
actions, anchored, deferred, div, impl_actions, point, px, size, uniform_list, Action,
|
||||
AnyElement, App, AssetSource, AsyncWindowContext, Bounds, ClipboardItem, Context, DismissEvent,
|
||||
Div, DragMoveEvent, Entity, EventEmitter, ExternalPaths, FocusHandle, Focusable, Hsla,
|
||||
AnyElement, App, AsyncWindowContext, Bounds, ClipboardItem, Context, DismissEvent, Div,
|
||||
DragMoveEvent, Entity, EventEmitter, ExternalPaths, FocusHandle, Focusable, Hsla,
|
||||
InteractiveElement, KeyContext, ListHorizontalSizingBehavior, ListSizingBehavior, MouseButton,
|
||||
MouseDownEvent, ParentElement, Pixels, Point, PromptLevel, Render, ScrollStrategy, Stateful,
|
||||
Styled, Subscription, Task, UniformListScrollHandle, WeakEntity, Window,
|
||||
@ -225,9 +225,8 @@ pub fn init_settings(cx: &mut App) {
|
||||
ProjectPanelSettings::register(cx);
|
||||
}
|
||||
|
||||
pub fn init(assets: impl AssetSource, cx: &mut App) {
|
||||
pub fn init(cx: &mut App) {
|
||||
init_settings(cx);
|
||||
file_icons::init(assets, cx);
|
||||
|
||||
cx.observe_new(|workspace: &mut Workspace, _, _| {
|
||||
workspace.register_action(|workspace, _: &ToggleFocus, window, cx| {
|
||||
@ -9484,7 +9483,7 @@ mod tests {
|
||||
theme::init(theme::LoadThemes::JustBase, cx);
|
||||
language::init(cx);
|
||||
editor::init_settings(cx);
|
||||
crate::init((), cx);
|
||||
crate::init(cx);
|
||||
workspace::init_settings(cx);
|
||||
client::init_settings(cx);
|
||||
Project::init_settings(cx);
|
||||
@ -9507,7 +9506,7 @@ mod tests {
|
||||
init_settings(cx);
|
||||
language::init(cx);
|
||||
editor::init(cx);
|
||||
crate::init((), cx);
|
||||
crate::init(cx);
|
||||
workspace::init(app_state.clone(), cx);
|
||||
Project::init_settings(cx);
|
||||
|
||||
|
@ -455,7 +455,6 @@ mod tests {
|
||||
pub(crate) fn init_test(cx: &mut TestAppContext) -> Arc<AppState> {
|
||||
cx.update(|cx| {
|
||||
let state = AppState::test(cx);
|
||||
file_icons::init((), cx);
|
||||
language::init(cx);
|
||||
crate::init(cx);
|
||||
editor::init(cx);
|
||||
|
@ -1,3 +1,5 @@
|
||||
use std::sync::{Arc, LazyLock};
|
||||
|
||||
use collections::HashMap;
|
||||
use gpui::SharedString;
|
||||
|
||||
@ -28,7 +30,11 @@ pub struct IconTheme {
|
||||
pub directory_icons: DirectoryIcons,
|
||||
/// The icons used for chevrons.
|
||||
pub chevron_icons: ChevronIcons,
|
||||
/// The mapping of file types to icon definitions.
|
||||
/// The mapping of file stems to their associated icon keys.
|
||||
pub file_stems: HashMap<String, String>,
|
||||
/// The mapping of file suffixes to their associated icon keys.
|
||||
pub file_suffixes: HashMap<String, String>,
|
||||
/// The mapping of icon keys to icon definitions.
|
||||
pub file_icons: HashMap<String, IconDefinition>,
|
||||
}
|
||||
|
||||
@ -57,6 +63,209 @@ pub struct IconDefinition {
|
||||
pub path: SharedString,
|
||||
}
|
||||
|
||||
const FILE_STEMS_BY_ICON_KEY: &[(&str, &[&str])] = &[
|
||||
("docker", &["Dockerfile"]),
|
||||
("ruby", &["Podfile"]),
|
||||
("heroku", &["Procfile"]),
|
||||
];
|
||||
|
||||
const FILE_SUFFIXES_BY_ICON_KEY: &[(&str, &[&str])] = &[
|
||||
("astro", &["astro"]),
|
||||
(
|
||||
"audio",
|
||||
&[
|
||||
"aac", "flac", "m4a", "mka", "mp3", "ogg", "opus", "wav", "wma", "wv",
|
||||
],
|
||||
),
|
||||
("backup", &["bak"]),
|
||||
("bicep", &["bicep"]),
|
||||
("bun", &["lockb"]),
|
||||
("c", &["c", "h"]),
|
||||
("code", &["handlebars", "metadata", "rkt", "scm"]),
|
||||
("coffeescript", &["coffee"]),
|
||||
(
|
||||
"cpp",
|
||||
&["c++", "cc", "cpp", "cxx", "hh", "hpp", "hxx", "inl"],
|
||||
),
|
||||
("crystal", &["cr", "ecr"]),
|
||||
("csharp", &["cs"]),
|
||||
("csproj", &["csproj"]),
|
||||
("css", &["css", "pcss", "postcss"]),
|
||||
("cue", &["cue"]),
|
||||
("dart", &["dart"]),
|
||||
("diff", &["diff"]),
|
||||
(
|
||||
"document",
|
||||
&[
|
||||
"doc", "docx", "mdx", "odp", "ods", "odt", "pdf", "ppt", "pptx", "rtf", "txt", "xls",
|
||||
"xlsx",
|
||||
],
|
||||
),
|
||||
("elixir", &["eex", "ex", "exs", "heex"]),
|
||||
("elm", &["elm"]),
|
||||
(
|
||||
"erlang",
|
||||
&[
|
||||
"Emakefile",
|
||||
"app.src",
|
||||
"erl",
|
||||
"escript",
|
||||
"hrl",
|
||||
"rebar.config",
|
||||
"xrl",
|
||||
"yrl",
|
||||
],
|
||||
),
|
||||
(
|
||||
"eslint",
|
||||
&[
|
||||
"eslint.config.cjs",
|
||||
"eslint.config.cts",
|
||||
"eslint.config.js",
|
||||
"eslint.config.mjs",
|
||||
"eslint.config.mts",
|
||||
"eslint.config.ts",
|
||||
"eslintrc",
|
||||
"eslintrc.js",
|
||||
"eslintrc.json",
|
||||
],
|
||||
),
|
||||
("font", &["otf", "ttf", "woff", "woff2"]),
|
||||
("fsharp", &["fs"]),
|
||||
("fsproj", &["fsproj"]),
|
||||
("gitlab", &["gitlab-ci.yml"]),
|
||||
("gleam", &["gleam"]),
|
||||
("go", &["go", "mod", "work"]),
|
||||
("graphql", &["gql", "graphql", "graphqls"]),
|
||||
("haskell", &["hs"]),
|
||||
("hcl", &["hcl"]),
|
||||
("html", &["htm", "html"]),
|
||||
(
|
||||
"image",
|
||||
&[
|
||||
"avif", "bmp", "gif", "heic", "heif", "ico", "j2k", "jfif", "jp2", "jpeg", "jpg",
|
||||
"jxl", "png", "psd", "qoi", "svg", "tiff", "webp",
|
||||
],
|
||||
),
|
||||
("java", &["java"]),
|
||||
("javascript", &["cjs", "js", "mjs"]),
|
||||
("json", &["json"]),
|
||||
("julia", &["jl"]),
|
||||
("kotlin", &["kt"]),
|
||||
("lock", &["lock"]),
|
||||
("log", &["log"]),
|
||||
("lua", &["lua"]),
|
||||
("luau", &["luau"]),
|
||||
("markdown", &["markdown", "md"]),
|
||||
("metal", &["metal"]),
|
||||
("nim", &["nim"]),
|
||||
("nix", &["nix"]),
|
||||
("ocaml", &["ml", "mli"]),
|
||||
("php", &["php"]),
|
||||
(
|
||||
"prettier",
|
||||
&[
|
||||
"prettier.config.cjs",
|
||||
"prettier.config.js",
|
||||
"prettier.config.mjs",
|
||||
"prettierignore",
|
||||
"prettierrc",
|
||||
"prettierrc.cjs",
|
||||
"prettierrc.js",
|
||||
"prettierrc.json",
|
||||
"prettierrc.json5",
|
||||
"prettierrc.mjs",
|
||||
"prettierrc.toml",
|
||||
"prettierrc.yaml",
|
||||
"prettierrc.yml",
|
||||
],
|
||||
),
|
||||
("prisma", &["prisma"]),
|
||||
("python", &["py"]),
|
||||
("r", &["r", "R"]),
|
||||
("react", &["cjsx", "ctsx", "jsx", "mjsx", "mtsx", "tsx"]),
|
||||
("roc", &["roc"]),
|
||||
("ruby", &["rb"]),
|
||||
("rust", &["rs"]),
|
||||
("sass", &["sass", "scss"]),
|
||||
("scala", &["scala", "sc"]),
|
||||
("settings", &["conf", "ini", "yaml", "yml"]),
|
||||
("solidity", &["sol"]),
|
||||
(
|
||||
"storage",
|
||||
&[
|
||||
"accdb", "csv", "dat", "db", "dbf", "dll", "fmp", "fp7", "frm", "gdb", "ib", "jsonc",
|
||||
"ldf", "mdb", "mdf", "myd", "myi", "pdb", "sav", "sdf", "sql", "sqlite", "tsv",
|
||||
],
|
||||
),
|
||||
(
|
||||
"stylelint",
|
||||
&[
|
||||
"stylelint.config.cjs",
|
||||
"stylelint.config.js",
|
||||
"stylelint.config.mjs",
|
||||
"stylelintignore",
|
||||
"stylelintrc",
|
||||
"stylelintrc.cjs",
|
||||
"stylelintrc.js",
|
||||
"stylelintrc.json",
|
||||
"stylelintrc.mjs",
|
||||
"stylelintrc.yaml",
|
||||
"stylelintrc.yml",
|
||||
],
|
||||
),
|
||||
("svelte", &["svelte"]),
|
||||
("swift", &["swift"]),
|
||||
("tcl", &["tcl"]),
|
||||
("template", &["hbs", "plist", "xml"]),
|
||||
(
|
||||
"terminal",
|
||||
&[
|
||||
"bash",
|
||||
"bash_aliases",
|
||||
"bash_logout",
|
||||
"bash_profile",
|
||||
"bashrc",
|
||||
"fish",
|
||||
"nu",
|
||||
"profile",
|
||||
"ps1",
|
||||
"sh",
|
||||
"zlogin",
|
||||
"zsh",
|
||||
"zsh_aliases",
|
||||
"zsh_histfile",
|
||||
"zsh_profile",
|
||||
"zshenv",
|
||||
"zshrc",
|
||||
],
|
||||
),
|
||||
("terraform", &["tf", "tfvars"]),
|
||||
("toml", &["toml"]),
|
||||
("typescript", &["cts", "mts", "ts"]),
|
||||
("v", &["v", "vsh", "vv"]),
|
||||
(
|
||||
"vcs",
|
||||
&[
|
||||
"COMMIT_EDITMSG",
|
||||
"EDIT_DESCRIPTION",
|
||||
"MERGE_MSG",
|
||||
"NOTES_EDITMSG",
|
||||
"TAG_EDITMSG",
|
||||
"gitattributes",
|
||||
"gitignore",
|
||||
"gitkeep",
|
||||
"gitmodules",
|
||||
],
|
||||
),
|
||||
("vbproj", &["vbproj"]),
|
||||
("video", &["avi", "m4v", "mkv", "mov", "mp4", "webm", "wmv"]),
|
||||
("vs_sln", &["sln"]),
|
||||
("vs_suo", &["suo"]),
|
||||
("vue", &["vue"]),
|
||||
("zig", &["zig"]),
|
||||
];
|
||||
|
||||
/// A mapping of a file type identifier to its corresponding icon.
|
||||
const FILE_ICONS: &[(&str, &str)] = &[
|
||||
("astro", "icons/file_icons/astro.svg"),
|
||||
@ -141,12 +350,25 @@ const FILE_ICONS: &[(&str, &str)] = &[
|
||||
("zig", "icons/file_icons/zig.svg"),
|
||||
];
|
||||
|
||||
/// Returns a mapping of file associations to icon keys.
|
||||
fn icon_keys_by_association(
|
||||
associations_by_icon_key: &[(&str, &[&str])],
|
||||
) -> HashMap<String, String> {
|
||||
let mut icon_keys_by_association = HashMap::default();
|
||||
for (icon_key, associations) in associations_by_icon_key {
|
||||
for association in *associations {
|
||||
icon_keys_by_association.insert(association.to_string(), icon_key.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
icon_keys_by_association
|
||||
}
|
||||
|
||||
/// The name of the default icon theme.
|
||||
pub(crate) const DEFAULT_ICON_THEME_NAME: &str = "Zed (Default)";
|
||||
|
||||
/// Returns the default icon theme.
|
||||
pub fn default_icon_theme() -> IconTheme {
|
||||
IconTheme {
|
||||
static DEFAULT_ICON_THEME: LazyLock<Arc<IconTheme>> = LazyLock::new(|| {
|
||||
Arc::new(IconTheme {
|
||||
id: "zed".into(),
|
||||
name: DEFAULT_ICON_THEME_NAME.into(),
|
||||
appearance: Appearance::Dark,
|
||||
@ -158,6 +380,8 @@ pub fn default_icon_theme() -> IconTheme {
|
||||
collapsed: Some("icons/file_icons/chevron_right.svg".into()),
|
||||
expanded: Some("icons/file_icons/chevron_down.svg".into()),
|
||||
},
|
||||
file_stems: icon_keys_by_association(FILE_STEMS_BY_ICON_KEY),
|
||||
file_suffixes: icon_keys_by_association(FILE_SUFFIXES_BY_ICON_KEY),
|
||||
file_icons: HashMap::from_iter(FILE_ICONS.into_iter().map(|(ty, path)| {
|
||||
(
|
||||
ty.to_string(),
|
||||
@ -166,5 +390,10 @@ pub fn default_icon_theme() -> IconTheme {
|
||||
},
|
||||
)
|
||||
})),
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
/// Returns the default icon theme.
|
||||
pub fn default_icon_theme() -> Arc<IconTheme> {
|
||||
DEFAULT_ICON_THEME.clone()
|
||||
}
|
||||
|
@ -23,6 +23,10 @@ pub struct IconThemeContent {
|
||||
#[serde(default)]
|
||||
pub chevron_icons: ChevronIconsContent,
|
||||
#[serde(default)]
|
||||
pub file_stems: HashMap<String, String>,
|
||||
#[serde(default)]
|
||||
pub file_suffixes: HashMap<String, String>,
|
||||
#[serde(default)]
|
||||
pub file_icons: HashMap<String, IconDefinitionContent>,
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,8 @@ use parking_lot::RwLock;
|
||||
use util::ResultExt;
|
||||
|
||||
use crate::{
|
||||
read_icon_theme, read_user_theme, refine_theme_family, Appearance, AppearanceContent,
|
||||
ChevronIcons, DirectoryIcons, IconDefinition, IconTheme, Theme, ThemeFamily,
|
||||
default_icon_theme, read_icon_theme, read_user_theme, refine_theme_family, Appearance,
|
||||
AppearanceContent, ChevronIcons, DirectoryIcons, IconDefinition, IconTheme, Theme, ThemeFamily,
|
||||
ThemeFamilyContent, DEFAULT_ICON_THEME_NAME,
|
||||
};
|
||||
|
||||
@ -80,10 +80,11 @@ impl ThemeRegistry {
|
||||
registry.insert_theme_families([crate::fallback_themes::zed_default_themes()]);
|
||||
|
||||
let default_icon_theme = crate::default_icon_theme();
|
||||
registry.state.write().icon_themes.insert(
|
||||
default_icon_theme.name.clone(),
|
||||
Arc::new(default_icon_theme),
|
||||
);
|
||||
registry
|
||||
.state
|
||||
.write()
|
||||
.icon_themes
|
||||
.insert(default_icon_theme.name.clone(), default_icon_theme);
|
||||
|
||||
registry
|
||||
}
|
||||
@ -263,8 +264,16 @@ impl ThemeRegistry {
|
||||
.into()
|
||||
};
|
||||
|
||||
let default_icon_theme = default_icon_theme();
|
||||
|
||||
let mut state = self.state.write();
|
||||
for icon_theme in icon_theme_family.themes {
|
||||
let mut file_stems = default_icon_theme.file_stems.clone();
|
||||
file_stems.extend(icon_theme.file_stems);
|
||||
|
||||
let mut file_suffixes = default_icon_theme.file_suffixes.clone();
|
||||
file_suffixes.extend(icon_theme.file_suffixes);
|
||||
|
||||
let icon_theme = IconTheme {
|
||||
id: uuid::Uuid::new_v4().to_string(),
|
||||
name: icon_theme.name.into(),
|
||||
@ -280,6 +289,8 @@ impl ThemeRegistry {
|
||||
collapsed: icon_theme.chevron_icons.collapsed.map(resolve_icon_path),
|
||||
expanded: icon_theme.chevron_icons.expanded.map(resolve_icon_path),
|
||||
},
|
||||
file_stems,
|
||||
file_suffixes,
|
||||
file_icons: icon_theme
|
||||
.file_icons
|
||||
.into_iter()
|
||||
|
@ -1,6 +1,5 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use assets::Assets;
|
||||
use editor::test::editor_lsp_test_context::EditorLspTestContext;
|
||||
use gpui::{Context, Entity, SemanticVersion, UpdateGlobal};
|
||||
use search::{project_search::ProjectSearchBar, BufferSearchBar};
|
||||
@ -21,7 +20,7 @@ impl VimTestContext {
|
||||
cx.set_global(settings);
|
||||
release_channel::init(SemanticVersion::default(), cx);
|
||||
command_palette::init(cx);
|
||||
project_panel::init(Assets, cx);
|
||||
project_panel::init(cx);
|
||||
git_ui::init(cx);
|
||||
crate::init(cx);
|
||||
search::init(cx);
|
||||
|
@ -51,7 +51,6 @@ extensions_ui.workspace = true
|
||||
feature_flags.workspace = true
|
||||
feedback.workspace = true
|
||||
file_finder.workspace = true
|
||||
file_icons.workspace = true
|
||||
fs.workspace = true
|
||||
futures.workspace = true
|
||||
git.workspace = true
|
||||
|
@ -488,9 +488,9 @@ fn main() {
|
||||
tab_switcher::init(cx);
|
||||
outline::init(cx);
|
||||
project_symbols::init(cx);
|
||||
project_panel::init(Assets, cx);
|
||||
project_panel::init(cx);
|
||||
git_ui::git_panel::init(cx);
|
||||
outline_panel::init(Assets, cx);
|
||||
outline_panel::init(cx);
|
||||
component_preview::init(cx);
|
||||
tasks_ui::init(cx);
|
||||
snippets_ui::init(cx);
|
||||
@ -555,7 +555,6 @@ fn main() {
|
||||
load_user_themes_in_background(fs.clone(), cx);
|
||||
watch_themes(fs.clone(), cx);
|
||||
watch_languages(fs.clone(), app_state.languages.clone(), cx);
|
||||
watch_file_types(fs.clone(), cx);
|
||||
|
||||
cx.set_menus(app_menus());
|
||||
initialize_workspace(app_state.clone(), prompt_builder, cx);
|
||||
@ -1158,35 +1157,3 @@ fn watch_languages(fs: Arc<dyn fs::Fs>, languages: Arc<LanguageRegistry>, cx: &m
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn watch_languages(_fs: Arc<dyn fs::Fs>, _languages: Arc<LanguageRegistry>, _cx: &mut App) {}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
fn watch_file_types(fs: Arc<dyn fs::Fs>, cx: &mut App) {
|
||||
use std::time::Duration;
|
||||
|
||||
use file_icons::FileIcons;
|
||||
use gpui::UpdateGlobal;
|
||||
|
||||
let path = {
|
||||
let p = Path::new("assets").join(file_icons::FILE_TYPES_ASSET);
|
||||
let Ok(full_path) = p.canonicalize() else {
|
||||
return;
|
||||
};
|
||||
full_path
|
||||
};
|
||||
|
||||
cx.spawn(|cx| async move {
|
||||
let (mut events, _) = fs.watch(path.as_path(), Duration::from_millis(100)).await;
|
||||
while (events.next().await).is_some() {
|
||||
cx.update(|cx| {
|
||||
FileIcons::update_global(cx, |file_types, _cx| {
|
||||
*file_types = file_icons::FileIcons::new(Assets);
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
fn watch_file_types(_fs: Arc<dyn fs::Fs>, _cx: &mut App) {}
|
||||
|
@ -4198,8 +4198,8 @@ mod tests {
|
||||
editor::init(cx);
|
||||
collab_ui::init(&app_state, cx);
|
||||
git_ui::init(cx);
|
||||
project_panel::init((), cx);
|
||||
outline_panel::init((), cx);
|
||||
project_panel::init(cx);
|
||||
outline_panel::init(cx);
|
||||
terminal_view::init(cx);
|
||||
copilot::copilot_chat::init(
|
||||
app_state.fs.clone(),
|
||||
|
@ -8,7 +8,7 @@ extend-exclude = [
|
||||
".mailmap",
|
||||
|
||||
# File suffixes aren't typos.
|
||||
"assets/icons/file_icons/file_types.json",
|
||||
"crates/theme/src/icon_theme.rs",
|
||||
"crates/extensions_ui/src/extension_suggest.rs",
|
||||
|
||||
# Some countries codes are flagged as typos.
|
||||
|
Loading…
Reference in New Issue
Block a user