Eliminate assets crate (#2575)

Previously, all static assets were embedded into Zed using a single
`RustEmbed` struct called `Assets`, which was in a crate of its own,
also called `assets`. Many crates depended on this crate. The problem
with this situation was that changing *any* static asset file caused
almost every crate in the codebase to need recompilation (because of
recursive dependencies on `assets`).

Now, most of the assets are embedded only into the top-level `zed`
crate. A few assets (such as settings JSON files and AI prompts) are
needed in lower-level crates such as `settings` and `ai`. For these,
I've created separate `RustEmbed` structs in those specific crates,
which embed those specific sub-folders of `assets`.

The result is that now, when you change a theme file, the only crate
that needs to recompile is `zed`.

Release Notes:

- N/A
This commit is contained in:
Max Brunsfeld 2023-06-06 12:00:44 -07:00 committed by GitHub
commit 17560cc5b0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 142 additions and 160 deletions

18
Cargo.lock generated
View file

@ -100,12 +100,12 @@ name = "ai"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assets",
"collections", "collections",
"editor", "editor",
"futures 0.3.28", "futures 0.3.28",
"gpui", "gpui",
"isahc", "isahc",
"rust-embed",
"serde", "serde",
"serde_json", "serde_json",
"util", "util",
@ -210,15 +210,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
[[package]]
name = "assets"
version = "0.1.0"
dependencies = [
"anyhow",
"gpui",
"rust-embed",
]
[[package]] [[package]]
name = "async-broadcast" name = "async-broadcast"
version = "0.4.1" version = "0.4.1"
@ -1402,7 +1393,6 @@ name = "copilot_button"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assets",
"context_menu", "context_menu",
"copilot", "copilot",
"editor", "editor",
@ -6114,7 +6104,6 @@ name = "settings"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assets",
"collections", "collections",
"fs", "fs",
"futures 0.3.28", "futures 0.3.28",
@ -6123,6 +6112,7 @@ dependencies = [
"lazy_static", "lazy_static",
"postage", "postage",
"pretty_assertions", "pretty_assertions",
"rust-embed",
"schemars", "schemars",
"serde", "serde",
"serde_derive", "serde_derive",
@ -7749,6 +7739,7 @@ dependencies = [
"lazy_static", "lazy_static",
"log", "log",
"rand 0.8.5", "rand 0.8.5",
"rust-embed",
"serde", "serde",
"serde_json", "serde_json",
"smol", "smol",
@ -7819,7 +7810,6 @@ name = "vim"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assets",
"async-compat", "async-compat",
"async-trait", "async-trait",
"collections", "collections",
@ -8647,7 +8637,6 @@ name = "workspace"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"assets",
"async-recursion 1.0.4", "async-recursion 1.0.4",
"bincode", "bincode",
"call", "call",
@ -8747,7 +8736,6 @@ dependencies = [
"activity_indicator", "activity_indicator",
"ai", "ai",
"anyhow", "anyhow",
"assets",
"async-compression", "async-compression",
"async-recursion 0.3.2", "async-recursion 0.3.2",
"async-tar", "async-tar",

View file

@ -2,7 +2,6 @@
members = [ members = [
"crates/activity_indicator", "crates/activity_indicator",
"crates/ai", "crates/ai",
"crates/assets",
"crates/auto_update", "crates/auto_update",
"crates/breadcrumbs", "crates/breadcrumbs",
"crates/call", "crates/call",
@ -88,6 +87,7 @@ parking_lot = { version = "0.11.1" }
postage = { version = "0.5", features = ["futures-traits"] } postage = { version = "0.5", features = ["futures-traits"] }
rand = { version = "0.8.5" } rand = { version = "0.8.5" }
regex = { version = "1.5" } regex = { version = "1.5" }
rust-embed = { version = "6.3", features = ["include-exclude"] }
schemars = { version = "0.8" } schemars = { version = "0.8" }
serde = { version = "1.0", features = ["derive", "rc"] } serde = { version = "1.0", features = ["derive", "rc"] }
serde_derive = { version = "1.0", features = ["deserialize_in_place"] } serde_derive = { version = "1.0", features = ["deserialize_in_place"] }

View file

@ -9,12 +9,12 @@ path = "src/ai.rs"
doctest = false doctest = false
[dependencies] [dependencies]
assets = { path = "../assets"}
collections = { path = "../collections"} collections = { path = "../collections"}
editor = { path = "../editor" } editor = { path = "../editor" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
util = { path = "../util" } util = { path = "../util" }
rust-embed.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
anyhow.workspace = true anyhow.workspace = true

View file

@ -1,5 +1,4 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use assets::Assets;
use collections::HashMap; use collections::HashMap;
use editor::Editor; use editor::Editor;
use futures::AsyncBufReadExt; use futures::AsyncBufReadExt;
@ -16,6 +15,14 @@ use std::{io, sync::Arc};
use util::channel::{ReleaseChannel, RELEASE_CHANNEL}; use util::channel::{ReleaseChannel, RELEASE_CHANNEL};
use util::{ResultExt, TryFutureExt}; use util::{ResultExt, TryFutureExt};
use rust_embed::RustEmbed;
use std::str;
#[derive(RustEmbed)]
#[folder = "../../assets/contexts"]
#[exclude = "*.DS_Store"]
pub struct ContextAssets;
actions!(ai, [Assist]); actions!(ai, [Assist]);
// Data types for chat completion requests // Data types for chat completion requests
@ -173,7 +180,7 @@ impl Assistant {
let assist_task = cx.spawn(|_, mut cx| { let assist_task = cx.spawn(|_, mut cx| {
async move { async move {
// TODO: We should have a get_string method on assets. This is repateated elsewhere. // TODO: We should have a get_string method on assets. This is repateated elsewhere.
let content = Assets::get("contexts/system.zmd").unwrap(); let content = ContextAssets::get("system.zmd").unwrap();
let mut system_message = std::str::from_utf8(content.data.as_ref()) let mut system_message = std::str::from_utf8(content.data.as_ref())
.unwrap() .unwrap()
.to_string(); .to_string();

View file

@ -1,14 +0,0 @@
[package]
name = "assets"
version = "0.1.0"
edition = "2021"
publish = false
[lib]
path = "src/assets.rs"
doctest = false
[dependencies]
gpui = { path = "../gpui" }
anyhow.workspace = true
rust-embed = { version = "6.3", features = ["include-exclude"] }

View file

@ -1,29 +0,0 @@
use std::process::Command;
fn main() {
let output = Command::new("npm")
.current_dir("../../styles")
.args(["install", "--no-save"])
.output()
.expect("failed to run npm");
if !output.status.success() {
panic!(
"failed to install theme dependencies {}",
String::from_utf8_lossy(&output.stderr)
);
}
let output = Command::new("npm")
.current_dir("../../styles")
.args(["run", "build"])
.output()
.expect("failed to run npm");
if !output.status.success() {
panic!(
"build script failed {}",
String::from_utf8_lossy(&output.stderr)
);
}
println!("cargo:rerun-if-changed=../../styles/src");
}

View file

@ -9,7 +9,6 @@ path = "src/copilot_button.rs"
doctest = false doctest = false
[dependencies] [dependencies]
assets = { path = "../assets" }
copilot = { path = "../copilot" } copilot = { path = "../copilot" }
editor = { path = "../editor" } editor = { path = "../editor" }
fs = { path = "../fs" } fs = { path = "../fs" }

View file

@ -315,9 +315,7 @@ async fn configure_disabled_globs(
let settings_editor = workspace let settings_editor = workspace
.update(&mut cx, |_, cx| { .update(&mut cx, |_, cx| {
create_and_open_local_file(&paths::SETTINGS, cx, || { create_and_open_local_file(&paths::SETTINGS, cx, || {
settings::initial_user_settings_content(&assets::Assets) settings::initial_user_settings_content().as_ref().into()
.as_ref()
.into()
}) })
})? })?
.await? .await?

View file

@ -12,7 +12,6 @@ doctest = false
test-support = ["gpui/test-support", "fs/test-support"] test-support = ["gpui/test-support", "fs/test-support"]
[dependencies] [dependencies]
assets = { path = "../assets" }
collections = { path = "../collections" } collections = { path = "../collections" }
gpui = { path = "../gpui" } gpui = { path = "../gpui" }
sqlez = { path = "../sqlez" } sqlez = { path = "../sqlez" }
@ -25,6 +24,7 @@ futures.workspace = true
json_comments = "0.2" json_comments = "0.2"
lazy_static.workspace = true lazy_static.workspace = true
postage.workspace = true postage.workspace = true
rust-embed.workspace = true
schemars.workspace = true schemars.workspace = true
serde.workspace = true serde.workspace = true
serde_derive.workspace = true serde_derive.workspace = true

View file

@ -1,6 +1,5 @@
use crate::settings_store::parse_json_with_comments; use crate::{settings_store::parse_json_with_comments, SettingsAssets};
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use assets::Assets;
use collections::BTreeMap; use collections::BTreeMap;
use gpui::{keymap_matcher::Binding, AppContext}; use gpui::{keymap_matcher::Binding, AppContext};
use schemars::{ use schemars::{
@ -10,11 +9,11 @@ use schemars::{
}; };
use serde::Deserialize; use serde::Deserialize;
use serde_json::{value::RawValue, Value}; use serde_json::{value::RawValue, Value};
use util::ResultExt; use util::{asset_str, ResultExt};
#[derive(Deserialize, Default, Clone, JsonSchema)] #[derive(Deserialize, Default, Clone, JsonSchema)]
#[serde(transparent)] #[serde(transparent)]
pub struct KeymapFileContent(Vec<KeymapBlock>); pub struct KeymapFile(Vec<KeymapBlock>);
#[derive(Deserialize, Default, Clone, JsonSchema)] #[derive(Deserialize, Default, Clone, JsonSchema)]
pub struct KeymapBlock { pub struct KeymapBlock {
@ -40,11 +39,10 @@ impl JsonSchema for KeymapAction {
#[derive(Deserialize)] #[derive(Deserialize)]
struct ActionWithData(Box<str>, Box<RawValue>); struct ActionWithData(Box<str>, Box<RawValue>);
impl KeymapFileContent { impl KeymapFile {
pub fn load_asset(asset_path: &str, cx: &mut AppContext) -> Result<()> { pub fn load_asset(asset_path: &str, cx: &mut AppContext) -> Result<()> {
let content = Assets::get(asset_path).unwrap().data; let content = asset_str::<SettingsAssets>(asset_path);
let content_str = std::str::from_utf8(content.as_ref()).unwrap(); Self::parse(content.as_ref())?.add_to_cx(cx)
Self::parse(content_str)?.add_to_cx(cx)
} }
pub fn parse(content: &str) -> Result<Self> { pub fn parse(content: &str) -> Result<Self> {
@ -83,40 +81,40 @@ impl KeymapFileContent {
} }
Ok(()) Ok(())
} }
}
pub fn keymap_file_json_schema(action_names: &[&'static str]) -> serde_json::Value { pub fn generate_json_schema(action_names: &[&'static str]) -> serde_json::Value {
let mut root_schema = SchemaSettings::draft07() let mut root_schema = SchemaSettings::draft07()
.with(|settings| settings.option_add_null_type = false) .with(|settings| settings.option_add_null_type = false)
.into_generator() .into_generator()
.into_root_schema_for::<KeymapFileContent>(); .into_root_schema_for::<KeymapFile>();
let action_schema = Schema::Object(SchemaObject { let action_schema = Schema::Object(SchemaObject {
subschemas: Some(Box::new(SubschemaValidation { subschemas: Some(Box::new(SubschemaValidation {
one_of: Some(vec![ one_of: Some(vec![
Schema::Object(SchemaObject { Schema::Object(SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
enum_values: Some( enum_values: Some(
action_names action_names
.iter() .iter()
.map(|name| Value::String(name.to_string())) .map(|name| Value::String(name.to_string()))
.collect(), .collect(),
), ),
..Default::default() ..Default::default()
}), }),
Schema::Object(SchemaObject { Schema::Object(SchemaObject {
instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))), instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))),
..Default::default() ..Default::default()
}), }),
]), ]),
..Default::default()
})),
..Default::default() ..Default::default()
})), });
..Default::default()
});
root_schema root_schema
.definitions .definitions
.insert("KeymapAction".to_owned(), action_schema); .insert("KeymapAction".to_owned(), action_schema);
serde_json::to_value(root_schema).unwrap() serde_json::to_value(root_schema).unwrap()
}
} }

View file

@ -2,31 +2,37 @@ mod keymap_file;
mod settings_file; mod settings_file;
mod settings_store; mod settings_store;
use gpui::AssetSource; use rust_embed::RustEmbed;
pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; use std::{borrow::Cow, str};
use util::asset_str;
pub use keymap_file::KeymapFile;
pub use settings_file::*; pub use settings_file::*;
pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore}; pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore};
use std::{borrow::Cow, str};
pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json"; #[derive(RustEmbed)]
const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json"; #[folder = "../../assets"]
const INITIAL_LOCAL_SETTINGS_ASSET_PATH: &str = "settings/initial_local_settings.json"; #[include = "settings/*"]
#[include = "keymaps/*"]
#[exclude = "*.DS_Store"]
pub struct SettingsAssets;
pub fn default_settings() -> Cow<'static, str> { pub fn default_settings() -> Cow<'static, str> {
asset_str(&assets::Assets, DEFAULT_SETTINGS_ASSET_PATH) asset_str::<SettingsAssets>("settings/default.json")
} }
pub fn initial_user_settings_content(assets: &dyn AssetSource) -> Cow<'_, str> { pub fn default_keymap() -> Cow<'static, str> {
asset_str(assets, INITIAL_USER_SETTINGS_ASSET_PATH) asset_str::<SettingsAssets>("keymaps/default.json")
} }
pub fn initial_local_settings_content(assets: &dyn AssetSource) -> Cow<'_, str> { pub fn vim_keymap() -> Cow<'static, str> {
asset_str(assets, INITIAL_LOCAL_SETTINGS_ASSET_PATH) asset_str::<SettingsAssets>("keymaps/vim.json")
} }
fn asset_str<'a>(assets: &'a dyn AssetSource, path: &str) -> Cow<'a, str> { pub fn initial_user_settings_content() -> Cow<'static, str> {
match assets.load(path).unwrap() { asset_str::<SettingsAssets>("settings/initial_user_settings.json")
Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()), }
Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()),
} pub fn initial_local_settings_content() -> Cow<'static, str> {
asset_str::<SettingsAssets>("settings/initial_local_settings.json")
} }

View file

@ -1,6 +1,5 @@
use crate::{settings_store::SettingsStore, Setting}; use crate::{settings_store::SettingsStore, Setting};
use anyhow::Result; use anyhow::Result;
use assets::Assets;
use fs::Fs; use fs::Fs;
use futures::{channel::mpsc, StreamExt}; use futures::{channel::mpsc, StreamExt};
use gpui::{executor::Background, AppContext}; use gpui::{executor::Background, AppContext};
@ -111,7 +110,7 @@ async fn load_settings(fs: &Arc<dyn Fs>) -> Result<String> {
Err(err) => { Err(err) => {
if let Some(e) = err.downcast_ref::<std::io::Error>() { if let Some(e) = err.downcast_ref::<std::io::Error>() {
if e.kind() == ErrorKind::NotFound { if e.kind() == ErrorKind::NotFound {
return Ok(crate::initial_user_settings_content(&Assets).to_string()); return Ok(crate::initial_user_settings_content().to_string());
} }
} }
return Err(err); return Err(err);

View file

@ -21,6 +21,7 @@ isahc.workspace = true
smol.workspace = true smol.workspace = true
url = "2.2" url = "2.2"
rand.workspace = true rand.workspace = true
rust-embed.workspace = true
tempdir = { workspace = true, optional = true } tempdir = { workspace = true, optional = true }
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true

View file

@ -7,6 +7,7 @@ pub mod paths;
pub mod test; pub mod test;
use std::{ use std::{
borrow::Cow,
cmp::{self, Ordering}, cmp::{self, Ordering},
ops::{AddAssign, Range, RangeInclusive}, ops::{AddAssign, Range, RangeInclusive},
panic::Location, panic::Location,
@ -284,6 +285,14 @@ impl<T: Rng> Iterator for RandomCharIter<T> {
} }
} }
/// Get an embedded file as a string.
pub fn asset_str<A: rust_embed::RustEmbed>(path: &str) -> Cow<'static, str> {
match A::get(path).unwrap().data {
Cow::Borrowed(bytes) => Cow::Borrowed(std::str::from_utf8(bytes).unwrap()),
Cow::Owned(bytes) => Cow::Owned(String::from_utf8(bytes).unwrap()),
}
}
// copy unstable standard feature option unzip // copy unstable standard feature option unzip
// https://github.com/rust-lang/rust/issues/87800 // https://github.com/rust-lang/rust/issues/87800
// Remove when this ship in Rust 1.66 or 1.67 // Remove when this ship in Rust 1.66 or 1.67

View file

@ -24,7 +24,6 @@ nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "master", f
tokio = { version = "1.15", "optional" = true } tokio = { version = "1.15", "optional" = true }
serde_json.workspace = true serde_json.workspace = true
assets = { path = "../assets" }
collections = { path = "../collections" } collections = { path = "../collections" }
command_palette = { path = "../command_palette" } command_palette = { path = "../command_palette" }
editor = { path = "../editor" } editor = { path = "../editor" }

View file

@ -27,7 +27,7 @@ impl<'a> VimTestContext<'a> {
cx.update_global(|store: &mut SettingsStore, cx| { cx.update_global(|store: &mut SettingsStore, cx| {
store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled)); store.update_user_settings::<VimModeSetting>(cx, |s| *s = Some(enabled));
}); });
settings::KeymapFileContent::load_asset("keymaps/vim.json", cx).unwrap(); settings::KeymapFile::load_asset("keymaps/vim.json", cx).unwrap();
}); });
// Setup search toolbars and keypress hook // Setup search toolbars and keypress hook

View file

@ -19,7 +19,6 @@ test-support = [
] ]
[dependencies] [dependencies]
assets = { path = "../assets" }
db = { path = "../db" } db = { path = "../db" }
call = { path = "../call" } call = { path = "../call" }
client = { path = "../client" } client = { path = "../client" }

View file

@ -17,7 +17,6 @@ path = "src/main.rs"
[dependencies] [dependencies]
activity_indicator = { path = "../activity_indicator" } activity_indicator = { path = "../activity_indicator" }
assets = { path = "../assets" }
auto_update = { path = "../auto_update" } auto_update = { path = "../auto_update" }
breadcrumbs = { path = "../breadcrumbs" } breadcrumbs = { path = "../breadcrumbs" }
call = { path = "../call" } call = { path = "../call" }

View file

@ -1,3 +1,5 @@
use std::process::Command;
fn main() { fn main() {
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7"); println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7");
@ -21,4 +23,32 @@ fn main() {
// Register exported Objective-C selectors, protocols, etc // Register exported Objective-C selectors, protocols, etc
println!("cargo:rustc-link-arg=-Wl,-ObjC"); println!("cargo:rustc-link-arg=-Wl,-ObjC");
// Install dependencies for theme-generation
let output = Command::new("npm")
.current_dir("../../styles")
.args(["install", "--no-save"])
.output()
.expect("failed to run npm");
if !output.status.success() {
panic!(
"failed to install theme dependencies {}",
String::from_utf8_lossy(&output.stderr)
);
}
// Regenerate themes
let output = Command::new("npm")
.current_dir("../../styles")
.args(["run", "build"])
.output()
.expect("failed to run npm");
if !output.status.success() {
panic!(
"build script failed {}",
String::from_utf8_lossy(&output.stderr)
);
}
println!("cargo:rerun-if-changed=../../styles/src");
} }

View file

@ -4,6 +4,10 @@ use rust_embed::RustEmbed;
#[derive(RustEmbed)] #[derive(RustEmbed)]
#[folder = "../../assets"] #[folder = "../../assets"]
#[include = "fonts/**/*"]
#[include = "icons/**/*"]
#[include = "themes/**/*"]
#[include = "*.md"]
#[exclude = "*.DS_Store"] #[exclude = "*.DS_Store"]
pub struct Assets; pub struct Assets;

View file

@ -3,6 +3,7 @@ pub use language::*;
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use rust_embed::RustEmbed; use rust_embed::RustEmbed;
use std::{borrow::Cow, str, sync::Arc}; use std::{borrow::Cow, str, sync::Arc};
use util::asset_str;
mod c; mod c;
mod elixir; mod elixir;
@ -179,10 +180,7 @@ fn load_query(name: &str, filename_prefix: &str) -> Option<Cow<'static, str>> {
for path in LanguageDir::iter() { for path in LanguageDir::iter() {
if let Some(remainder) = path.strip_prefix(name) { if let Some(remainder) = path.strip_prefix(name) {
if remainder.starts_with(filename_prefix) { if remainder.starts_with(filename_prefix) {
let contents = match LanguageDir::get(path.as_ref()).unwrap().data { let contents = asset_str::<LanguageDir>(path.as_ref());
Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()),
Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()),
};
match &mut result { match &mut result {
None => result = Some(contents), None => result = Some(contents),
Some(r) => r.to_mut().push_str(contents.as_ref()), Some(r) => r.to_mut().push_str(contents.as_ref()),

View file

@ -6,7 +6,7 @@ use gpui::AppContext;
use language::{LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter}; use language::{LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter};
use node_runtime::NodeRuntime; use node_runtime::NodeRuntime;
use serde_json::json; use serde_json::json;
use settings::{keymap_file_json_schema, SettingsJsonSchemaParams, SettingsStore}; use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
use smol::fs; use smol::fs;
use staff_mode::StaffMode; use staff_mode::StaffMode;
use std::{ use std::{
@ -143,7 +143,7 @@ impl LspAdapter for JsonLspAdapter {
}, },
{ {
"fileMatch": [schema_file_match(&paths::KEYMAP)], "fileMatch": [schema_file_match(&paths::KEYMAP)],
"schema": keymap_file_json_schema(&action_names), "schema": KeymapFile::generate_json_schema(&action_names),
} }
] ]
} }

View file

@ -2,7 +2,6 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use anyhow::{anyhow, Context, Result}; use anyhow::{anyhow, Context, Result};
use assets::Assets;
use backtrace::Backtrace; use backtrace::Backtrace;
use cli::{ use cli::{
ipc::{self, IpcSender}, ipc::{self, IpcSender},
@ -58,7 +57,8 @@ use staff_mode::StaffMode;
use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt}; use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt};
use workspace::{item::ItemHandle, notifications::NotifyResultExt, AppState, Workspace}; use workspace::{item::ItemHandle, notifications::NotifyResultExt, AppState, Workspace};
use zed::{ use zed::{
self, build_window_options, handle_keymap_file_changes, initialize_workspace, languages, menus, assets::Assets, build_window_options, handle_keymap_file_changes, initialize_workspace,
languages, menus,
}; };
fn main() { fn main() {

View file

@ -1,7 +1,9 @@
pub mod assets;
pub mod languages; pub mod languages;
pub mod menus; pub mod menus;
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
pub mod test; pub mod test;
use anyhow::Context; use anyhow::Context;
use assets::Assets; use assets::Assets;
use breadcrumbs::Breadcrumbs; use breadcrumbs::Breadcrumbs;
@ -30,12 +32,11 @@ use project_panel::ProjectPanel;
use search::{BufferSearchBar, ProjectSearchBar}; use search::{BufferSearchBar, ProjectSearchBar};
use serde::Deserialize; use serde::Deserialize;
use serde_json::to_string_pretty; use serde_json::to_string_pretty;
use settings::{ use settings::{initial_local_settings_content, KeymapFile, SettingsStore};
initial_local_settings_content, KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH,
};
use std::{borrow::Cow, str, sync::Arc}; use std::{borrow::Cow, str, sync::Arc};
use terminal_view::terminal_panel::{self, TerminalPanel}; use terminal_view::terminal_panel::{self, TerminalPanel};
use util::{ use util::{
asset_str,
channel::ReleaseChannel, channel::ReleaseChannel,
paths::{self, LOCAL_SETTINGS_RELATIVE_PATH}, paths::{self, LOCAL_SETTINGS_RELATIVE_PATH},
ResultExt, ResultExt,
@ -149,7 +150,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
move |workspace: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext<Workspace>| { move |workspace: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext<Workspace>| {
open_bundled_file( open_bundled_file(
workspace, workspace,
"licenses.md", asset_str::<Assets>("licenses.md"),
"Open Source License Attribution", "Open Source License Attribution",
"Markdown", "Markdown",
cx, cx,
@ -169,9 +170,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
cx.add_action( cx.add_action(
move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| { move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| {
create_and_open_local_file(&paths::SETTINGS, cx, || { create_and_open_local_file(&paths::SETTINGS, cx, || {
settings::initial_user_settings_content(&Assets) settings::initial_user_settings_content().as_ref().into()
.as_ref()
.into()
}) })
.detach_and_log_err(cx); .detach_and_log_err(cx);
}, },
@ -181,7 +180,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext<Workspace>| { move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext<Workspace>| {
open_bundled_file( open_bundled_file(
workspace, workspace,
"keymaps/default.json", settings::default_keymap(),
"Default Key Bindings", "Default Key Bindings",
"JSON", "JSON",
cx, cx,
@ -194,7 +193,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
cx: &mut ViewContext<Workspace>| { cx: &mut ViewContext<Workspace>| {
open_bundled_file( open_bundled_file(
workspace, workspace,
DEFAULT_SETTINGS_ASSET_PATH, settings::default_settings(),
"Default Settings", "Default Settings",
"JSON", "JSON",
cx, cx,
@ -521,11 +520,11 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
pub fn load_default_keymap(cx: &mut AppContext) { pub fn load_default_keymap(cx: &mut AppContext) {
for path in ["keymaps/default.json", "keymaps/vim.json"] { for path in ["keymaps/default.json", "keymaps/vim.json"] {
KeymapFileContent::load_asset(path, cx).unwrap(); KeymapFile::load_asset(path, cx).unwrap();
} }
if let Some(asset_path) = settings::get::<BaseKeymap>(cx).asset_path() { if let Some(asset_path) = settings::get::<BaseKeymap>(cx).asset_path() {
KeymapFileContent::load_asset(asset_path, cx).unwrap(); KeymapFile::load_asset(asset_path, cx).unwrap();
} }
} }
@ -536,7 +535,7 @@ pub fn handle_keymap_file_changes(
cx.spawn(move |mut cx| async move { cx.spawn(move |mut cx| async move {
let mut settings_subscription = None; let mut settings_subscription = None;
while let Some(user_keymap_content) = user_keymap_file_rx.next().await { while let Some(user_keymap_content) = user_keymap_file_rx.next().await {
if let Ok(keymap_content) = KeymapFileContent::parse(&user_keymap_content) { if let Ok(keymap_content) = KeymapFile::parse(&user_keymap_content) {
cx.update(|cx| { cx.update(|cx| {
cx.clear_bindings(); cx.clear_bindings();
load_default_keymap(cx); load_default_keymap(cx);
@ -613,11 +612,7 @@ fn open_local_settings_file(
if let Some(buffer) = editor.buffer().read(cx).as_singleton() { if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
if buffer.read(cx).is_empty() { if buffer.read(cx).is_empty() {
buffer.update(cx, |buffer, cx| { buffer.update(cx, |buffer, cx| {
buffer.edit( buffer.edit([(0..0, initial_local_settings_content())], None, cx)
[(0..0, initial_local_settings_content(&Assets))],
None,
cx,
)
}); });
} }
} }
@ -693,7 +688,7 @@ fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Works
fn open_bundled_file( fn open_bundled_file(
workspace: &mut Workspace, workspace: &mut Workspace,
asset_path: &'static str, text: Cow<'static, str>,
title: &'static str, title: &'static str,
language: &'static str, language: &'static str,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
@ -705,13 +700,9 @@ fn open_bundled_file(
.update(&mut cx, |workspace, cx| { .update(&mut cx, |workspace, cx| {
workspace.with_local_workspace(cx, |workspace, cx| { workspace.with_local_workspace(cx, |workspace, cx| {
let project = workspace.project(); let project = workspace.project();
let buffer = project.update(cx, |project, cx| { let buffer = project.update(cx, move |project, cx| {
let text = Assets::get(asset_path)
.map(|f| f.data)
.unwrap_or_else(|| Cow::Borrowed(b"File not found"));
let text = str::from_utf8(text.as_ref()).unwrap();
project project
.create_buffer(text, language, cx) .create_buffer(text.as_ref(), language, cx)
.expect("creating buffers on a local workspace always succeeds") .expect("creating buffers on a local workspace always succeeds")
}); });
let buffer = cx.add_model(|cx| { let buffer = cx.add_model(|cx| {