Detailed changes
@@ -100,7 +100,6 @@ name = "ai"
version = "0.1.0"
dependencies = [
"anyhow",
- "assets",
"chrono",
"collections",
"editor",
@@ -220,15 +219,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16"
-[[package]]
-name = "assets"
-version = "0.1.0"
-dependencies = [
- "anyhow",
- "gpui",
- "rust-embed",
-]
-
[[package]]
name = "async-broadcast"
version = "0.4.1"
@@ -1429,7 +1419,6 @@ name = "copilot_button"
version = "0.1.0"
dependencies = [
"anyhow",
- "assets",
"context_menu",
"copilot",
"editor",
@@ -6151,7 +6140,6 @@ name = "settings"
version = "0.1.0"
dependencies = [
"anyhow",
- "assets",
"collections",
"fs",
"futures 0.3.28",
@@ -6160,6 +6148,7 @@ dependencies = [
"lazy_static",
"postage",
"pretty_assertions",
+ "rust-embed",
"schemars",
"serde",
"serde_derive",
@@ -7404,8 +7393,8 @@ dependencies = [
[[package]]
name = "tree-sitter-elixir"
-version = "0.19.0"
-source = "git+https://github.com/elixir-lang/tree-sitter-elixir?rev=05e3631c6a0701c1fa518b0fee7be95a2ceef5e2#05e3631c6a0701c1fa518b0fee7be95a2ceef5e2"
+version = "0.1.0"
+source = "git+https://github.com/elixir-lang/tree-sitter-elixir?rev=4ba9dab6e2602960d95b2b625f3386c27e08084e#4ba9dab6e2602960d95b2b625f3386c27e08084e"
dependencies = [
"cc",
"tree-sitter",
@@ -7801,6 +7790,7 @@ dependencies = [
"lazy_static",
"log",
"rand 0.8.5",
+ "rust-embed",
"serde",
"serde_json",
"smol",
@@ -7871,7 +7861,6 @@ name = "vim"
version = "0.1.0"
dependencies = [
"anyhow",
- "assets",
"async-compat",
"async-trait",
"collections",
@@ -8699,7 +8688,6 @@ name = "workspace"
version = "0.1.0"
dependencies = [
"anyhow",
- "assets",
"async-recursion 1.0.4",
"bincode",
"call",
@@ -8799,7 +8787,6 @@ dependencies = [
"activity_indicator",
"ai",
"anyhow",
- "assets",
"async-compression",
"async-recursion 0.3.2",
"async-tar",
@@ -2,7 +2,6 @@
members = [
"crates/activity_indicator",
"crates/ai",
- "crates/assets",
"crates/auto_update",
"crates/breadcrumbs",
"crates/call",
@@ -88,6 +87,7 @@ parking_lot = { version = "0.11.1" }
postage = { version = "0.5", features = ["futures-traits"] }
rand = { version = "0.8.5" }
regex = { version = "1.5" }
+rust-embed = { version = "6.3", features = ["include-exclude"] }
schemars = { version = "0.8" }
serde = { version = "1.0", features = ["derive", "rc"] }
serde_derive = { version = "1.0", features = ["deserialize_in_place"] }
@@ -116,3 +116,4 @@ split-debuginfo = "unpacked"
[profile.release]
debug = true
+lto = "thin"
@@ -1,6 +1,6 @@
# syntax = docker/dockerfile:1.2
-FROM rust:1.65-bullseye as builder
+FROM rust:1.70-bullseye as builder
WORKDIR app
COPY . .
@@ -1,7 +1,7 @@
-// Zed settings
+// Folder-specific settings
//
-// For information on how to configure Zed, see the Zed
-// documentation: https://zed.dev/docs/configuring-zed
+// For a full list of overridable settings, and general information on folder-specific settings, see the documentation:
+// https://docs.zed.dev/configuration/configuring-zed#folder-specific-settings
//
// To see all of Zed's default settings without changing your
// custom settings, run the `open default settings` command
@@ -9,7 +9,6 @@ path = "src/ai.rs"
doctest = false
[dependencies]
-assets = { path = "../assets"}
collections = { path = "../collections"}
editor = { path = "../editor" }
fs = { path = "../fs" }
@@ -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"] }
@@ -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");
-}
@@ -9,7 +9,6 @@ path = "src/copilot_button.rs"
doctest = false
[dependencies]
-assets = { path = "../assets" }
copilot = { path = "../copilot" }
editor = { path = "../editor" }
fs = { path = "../fs" }
@@ -315,9 +315,7 @@ async fn configure_disabled_globs(
let settings_editor = workspace
.update(&mut cx, |_, cx| {
create_and_open_local_file(&paths::SETTINGS, cx, || {
- settings::initial_user_settings_content(&assets::Assets)
- .as_ref()
- .into()
+ settings::initial_user_settings_content().as_ref().into()
})
})?
.await?
@@ -2253,7 +2253,7 @@ impl BufferSnapshot {
}
pub fn outline(&self, theme: Option<&SyntaxTheme>) -> Option<Outline<Anchor>> {
- self.outline_items_containing(0..self.len(), theme)
+ self.outline_items_containing(0..self.len(), true, theme)
.map(Outline::new)
}
@@ -2265,6 +2265,7 @@ impl BufferSnapshot {
let position = position.to_offset(self);
let mut items = self.outline_items_containing(
position.saturating_sub(1)..self.len().min(position + 1),
+ false,
theme,
)?;
let mut prev_depth = None;
@@ -2279,6 +2280,7 @@ impl BufferSnapshot {
fn outline_items_containing(
&self,
range: Range<usize>,
+ include_extra_context: bool,
theme: Option<&SyntaxTheme>,
) -> Option<Vec<OutlineItem<Anchor>>> {
let mut matches = self.syntax.matches(range.clone(), &self.text, |grammar| {
@@ -2313,7 +2315,10 @@ impl BufferSnapshot {
let node_is_name;
if capture.index == config.name_capture_ix {
node_is_name = true;
- } else if Some(capture.index) == config.context_capture_ix {
+ } else if Some(capture.index) == config.context_capture_ix
+ || (Some(capture.index) == config.extra_context_capture_ix
+ && include_extra_context)
+ {
node_is_name = false;
} else {
continue;
@@ -2340,10 +2345,12 @@ impl BufferSnapshot {
buffer_ranges.first().unwrap().0.start..buffer_ranges.last().unwrap().0.end,
true,
);
+ let mut last_buffer_range_end = 0;
for (buffer_range, is_name) in buffer_ranges {
- if !text.is_empty() {
+ if !text.is_empty() && buffer_range.start > last_buffer_range_end {
text.push(' ');
}
+ last_buffer_range_end = buffer_range.end;
if is_name {
let mut start = text.len();
let end = start + buffer_range.len();
@@ -592,6 +592,52 @@ async fn test_outline_nodes_with_newlines(cx: &mut gpui::TestAppContext) {
);
}
+#[gpui::test]
+async fn test_outline_with_extra_context(cx: &mut gpui::TestAppContext) {
+ let language = javascript_lang()
+ .with_outline_query(
+ r#"
+ (function_declaration
+ "function" @context
+ name: (_) @name
+ parameters: (formal_parameters
+ "(" @context.extra
+ ")" @context.extra)) @item
+ "#,
+ )
+ .unwrap();
+
+ let text = r#"
+ function a() {}
+ function b(c) {}
+ "#
+ .unindent();
+
+ let buffer = cx.add_model(|cx| Buffer::new(0, text, cx).with_language(Arc::new(language), cx));
+ let snapshot = buffer.read_with(cx, |buffer, _| buffer.snapshot());
+
+ // extra context nodes are included in the outline.
+ let outline = snapshot.outline(None).unwrap();
+ assert_eq!(
+ outline
+ .items
+ .iter()
+ .map(|item| (item.text.as_str(), item.depth))
+ .collect::<Vec<_>>(),
+ &[("function a()", 0), ("function b( )", 0),]
+ );
+
+ // extra context nodes do not appear in breadcrumbs.
+ let symbols = snapshot.symbols_containing(3, None).unwrap();
+ assert_eq!(
+ symbols
+ .iter()
+ .map(|item| (item.text.as_str(), item.depth))
+ .collect::<Vec<_>>(),
+ &[("function a", 0)]
+ );
+}
+
#[gpui::test]
async fn test_symbols_containing(cx: &mut gpui::TestAppContext) {
let text = r#"
@@ -34,7 +34,7 @@ use std::{
fmt::Debug,
hash::Hash,
mem,
- ops::Range,
+ ops::{Not, Range},
path::{Path, PathBuf},
str,
sync::{
@@ -455,6 +455,7 @@ struct OutlineConfig {
item_capture_ix: u32,
name_capture_ix: u32,
context_capture_ix: Option<u32>,
+ extra_context_capture_ix: Option<u32>,
}
struct InjectionConfig {
@@ -500,6 +501,7 @@ struct AvailableLanguage {
grammar: tree_sitter::Language,
lsp_adapters: Vec<Arc<dyn LspAdapter>>,
get_queries: fn(&str) -> LanguageQueries,
+ loaded: bool,
}
pub struct LanguageRegistry {
@@ -527,6 +529,7 @@ struct LanguageRegistryState {
subscription: (watch::Sender<()>, watch::Receiver<()>),
theme: Option<Arc<Theme>>,
version: usize,
+ reload_count: usize,
}
pub struct PendingLanguageServer {
@@ -547,6 +550,7 @@ impl LanguageRegistry {
subscription: watch::channel(),
theme: Default::default(),
version: 0,
+ reload_count: 0,
}),
language_server_download_dir: None,
lsp_binary_statuses_tx,
@@ -566,6 +570,14 @@ impl LanguageRegistry {
self.executor = Some(executor);
}
+ /// Clear out all of the loaded languages and reload them from scratch.
+ ///
+ /// This is useful in development, when queries have changed.
+ #[cfg(debug_assertions)]
+ pub fn reload(&self) {
+ self.state.write().reload();
+ }
+
pub fn register(
&self,
path: &'static str,
@@ -582,6 +594,7 @@ impl LanguageRegistry {
grammar,
lsp_adapters,
get_queries,
+ loaded: false,
});
}
@@ -590,7 +603,7 @@ impl LanguageRegistry {
let mut result = state
.available_languages
.iter()
- .map(|l| l.config.name.to_string())
+ .filter_map(|l| l.loaded.not().then_some(l.config.name.to_string()))
.chain(state.languages.iter().map(|l| l.config.name.to_string()))
.collect::<Vec<_>>();
result.sort_unstable_by_key(|language_name| language_name.to_lowercase());
@@ -603,6 +616,7 @@ impl LanguageRegistry {
state
.available_languages
.iter()
+ .filter(|l| !l.loaded)
.flat_map(|l| l.lsp_adapters.clone())
.chain(
state
@@ -639,10 +653,17 @@ impl LanguageRegistry {
self.state.read().subscription.1.clone()
}
+ /// The number of times that the registry has been changed,
+ /// by adding languages or reloading.
pub fn version(&self) -> usize {
self.state.read().version
}
+ /// The number of times that the registry has been reloaded.
+ pub fn reload_count(&self) -> usize {
+ self.state.read().reload_count
+ }
+
pub fn set_theme(&self, theme: Arc<Theme>) {
let mut state = self.state.write();
state.theme = Some(theme.clone());
@@ -721,7 +742,7 @@ impl LanguageRegistry {
if let Some(language) = state
.available_languages
.iter()
- .find(|l| callback(&l.config))
+ .find(|l| !l.loaded && callback(&l.config))
.cloned()
{
let txs = state
@@ -743,9 +764,7 @@ impl LanguageRegistry {
let language = Arc::new(language);
let mut state = this.state.write();
state.add(language.clone());
- state
- .available_languages
- .retain(|language| language.id != id);
+ state.mark_language_loaded(id);
if let Some(mut txs) = state.loading_languages.remove(&id) {
for tx in txs.drain(..) {
let _ = tx.send(Ok(language.clone()));
@@ -753,10 +772,9 @@ impl LanguageRegistry {
}
}
Err(err) => {
+ log::error!("failed to load language {name} - {err}");
let mut state = this.state.write();
- state
- .available_languages
- .retain(|language| language.id != id);
+ state.mark_language_loaded(id);
if let Some(mut txs) = state.loading_languages.remove(&id) {
for tx in txs.drain(..) {
let _ = tx.send(Err(anyhow!(
@@ -905,6 +923,28 @@ impl LanguageRegistryState {
self.version += 1;
*self.subscription.0.borrow_mut() = ();
}
+
+ #[cfg(debug_assertions)]
+ fn reload(&mut self) {
+ self.languages.clear();
+ self.version += 1;
+ self.reload_count += 1;
+ for language in &mut self.available_languages {
+ language.loaded = false;
+ }
+ *self.subscription.0.borrow_mut() = ();
+ }
+
+ /// Mark the given language a having been loaded, so that the
+ /// language registry won't try to load it again.
+ fn mark_language_loaded(&mut self, id: AvailableLanguageId) {
+ for language in &mut self.available_languages {
+ if language.id == id {
+ language.loaded = true;
+ break;
+ }
+ }
+ }
}
#[cfg(any(test, feature = "test-support"))]
@@ -1021,34 +1061,22 @@ impl Language {
pub fn with_queries(mut self, queries: LanguageQueries) -> Result<Self> {
if let Some(query) = queries.highlights {
- self = self
- .with_highlights_query(query.as_ref())
- .expect("failed to evaluate highlights query");
+ self = self.with_highlights_query(query.as_ref())?;
}
if let Some(query) = queries.brackets {
- self = self
- .with_brackets_query(query.as_ref())
- .expect("failed to load brackets query");
+ self = self.with_brackets_query(query.as_ref())?;
}
if let Some(query) = queries.indents {
- self = self
- .with_indents_query(query.as_ref())
- .expect("failed to load indents query");
+ self = self.with_indents_query(query.as_ref())?;
}
if let Some(query) = queries.outline {
- self = self
- .with_outline_query(query.as_ref())
- .expect("failed to load outline query");
+ self = self.with_outline_query(query.as_ref())?;
}
if let Some(query) = queries.injections {
- self = self
- .with_injection_query(query.as_ref())
- .expect("failed to load injection query");
+ self = self.with_injection_query(query.as_ref())?;
}
if let Some(query) = queries.overrides {
- self = self
- .with_override_query(query.as_ref())
- .expect("failed to load override query");
+ self = self.with_override_query(query.as_ref())?;
}
Ok(self)
}
@@ -1064,12 +1092,14 @@ impl Language {
let mut item_capture_ix = None;
let mut name_capture_ix = None;
let mut context_capture_ix = None;
+ let mut extra_context_capture_ix = None;
get_capture_indices(
&query,
&mut [
("item", &mut item_capture_ix),
("name", &mut name_capture_ix),
("context", &mut context_capture_ix),
+ ("context.extra", &mut extra_context_capture_ix),
],
);
if let Some((item_capture_ix, name_capture_ix)) = item_capture_ix.zip(name_capture_ix) {
@@ -1078,6 +1108,7 @@ impl Language {
item_capture_ix,
name_capture_ix,
context_capture_ix,
+ extra_context_capture_ix,
});
}
Ok(self)
@@ -523,7 +523,7 @@ impl Project {
_subscriptions: vec![
cx.observe_global::<SettingsStore, _>(Self::on_settings_changed)
],
- _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx),
+ _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
_maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx),
active_entry: None,
languages,
@@ -592,7 +592,7 @@ impl Project {
active_entry: None,
collaborators: Default::default(),
join_project_response_message_id: response.message_id,
- _maintain_buffer_languages: Self::maintain_buffer_languages(&languages, cx),
+ _maintain_buffer_languages: Self::maintain_buffer_languages(languages.clone(), cx),
_maintain_workspace_config: Self::maintain_workspace_config(languages.clone(), cx),
languages,
user_store: user_store.clone(),
@@ -2238,13 +2238,34 @@ impl Project {
}
fn maintain_buffer_languages(
- languages: &LanguageRegistry,
+ languages: Arc<LanguageRegistry>,
cx: &mut ModelContext<Project>,
) -> Task<()> {
let mut subscription = languages.subscribe();
+ let mut prev_reload_count = languages.reload_count();
cx.spawn_weak(|project, mut cx| async move {
while let Some(()) = subscription.next().await {
if let Some(project) = project.upgrade(&cx) {
+ // If the language registry has been reloaded, then remove and
+ // re-assign the languages on all open buffers.
+ let reload_count = languages.reload_count();
+ if reload_count > prev_reload_count {
+ prev_reload_count = reload_count;
+ project.update(&mut cx, |this, cx| {
+ let buffers = this
+ .opened_buffers
+ .values()
+ .filter_map(|b| b.upgrade(cx))
+ .collect::<Vec<_>>();
+ for buffer in buffers {
+ if let Some(f) = File::from_dyn(buffer.read(cx).file()).cloned() {
+ this.unregister_buffer_from_language_servers(&buffer, &f, cx);
+ buffer.update(cx, |buffer, cx| buffer.set_language(None, cx));
+ }
+ }
+ });
+ }
+
project.update(&mut cx, |project, cx| {
let mut plain_text_buffers = Vec::new();
let mut buffers_with_unknown_injections = Vec::new();
@@ -12,7 +12,6 @@ doctest = false
test-support = ["gpui/test-support", "fs/test-support"]
[dependencies]
-assets = { path = "../assets" }
collections = { path = "../collections" }
gpui = { path = "../gpui" }
sqlez = { path = "../sqlez" }
@@ -25,6 +24,7 @@ futures.workspace = true
json_comments = "0.2"
lazy_static.workspace = true
postage.workspace = true
+rust-embed.workspace = true
schemars.workspace = true
serde.workspace = true
serde_derive.workspace = true
@@ -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 assets::Assets;
use collections::BTreeMap;
use gpui::{keymap_matcher::Binding, AppContext};
use schemars::{
@@ -10,11 +9,11 @@ use schemars::{
};
use serde::Deserialize;
use serde_json::{value::RawValue, Value};
-use util::ResultExt;
+use util::{asset_str, ResultExt};
#[derive(Deserialize, Default, Clone, JsonSchema)]
#[serde(transparent)]
-pub struct KeymapFileContent(Vec<KeymapBlock>);
+pub struct KeymapFile(Vec<KeymapBlock>);
#[derive(Deserialize, Default, Clone, JsonSchema)]
pub struct KeymapBlock {
@@ -40,11 +39,10 @@ impl JsonSchema for KeymapAction {
#[derive(Deserialize)]
struct ActionWithData(Box<str>, Box<RawValue>);
-impl KeymapFileContent {
+impl KeymapFile {
pub fn load_asset(asset_path: &str, cx: &mut AppContext) -> Result<()> {
- let content = Assets::get(asset_path).unwrap().data;
- let content_str = std::str::from_utf8(content.as_ref()).unwrap();
- Self::parse(content_str)?.add_to_cx(cx)
+ let content = asset_str::<SettingsAssets>(asset_path);
+ Self::parse(content.as_ref())?.add_to_cx(cx)
}
pub fn parse(content: &str) -> Result<Self> {
@@ -83,40 +81,40 @@ impl KeymapFileContent {
}
Ok(())
}
-}
-pub fn keymap_file_json_schema(action_names: &[&'static str]) -> serde_json::Value {
- let mut root_schema = SchemaSettings::draft07()
- .with(|settings| settings.option_add_null_type = false)
- .into_generator()
- .into_root_schema_for::<KeymapFileContent>();
+ pub fn generate_json_schema(action_names: &[&'static str]) -> serde_json::Value {
+ let mut root_schema = SchemaSettings::draft07()
+ .with(|settings| settings.option_add_null_type = false)
+ .into_generator()
+ .into_root_schema_for::<KeymapFile>();
- let action_schema = Schema::Object(SchemaObject {
- subschemas: Some(Box::new(SubschemaValidation {
- one_of: Some(vec![
- Schema::Object(SchemaObject {
- instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
- enum_values: Some(
- action_names
- .iter()
- .map(|name| Value::String(name.to_string()))
- .collect(),
- ),
- ..Default::default()
- }),
- Schema::Object(SchemaObject {
- instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))),
- ..Default::default()
- }),
- ]),
+ let action_schema = Schema::Object(SchemaObject {
+ subschemas: Some(Box::new(SubschemaValidation {
+ one_of: Some(vec![
+ Schema::Object(SchemaObject {
+ instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
+ enum_values: Some(
+ action_names
+ .iter()
+ .map(|name| Value::String(name.to_string()))
+ .collect(),
+ ),
+ ..Default::default()
+ }),
+ Schema::Object(SchemaObject {
+ instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))),
+ ..Default::default()
+ }),
+ ]),
+ ..Default::default()
+ })),
..Default::default()
- })),
- ..Default::default()
- });
+ });
- root_schema
- .definitions
- .insert("KeymapAction".to_owned(), action_schema);
+ root_schema
+ .definitions
+ .insert("KeymapAction".to_owned(), action_schema);
- serde_json::to_value(root_schema).unwrap()
+ serde_json::to_value(root_schema).unwrap()
+ }
}
@@ -2,31 +2,37 @@ mod keymap_file;
mod settings_file;
mod settings_store;
-use gpui::AssetSource;
-pub use keymap_file::{keymap_file_json_schema, KeymapFileContent};
+use rust_embed::RustEmbed;
+use std::{borrow::Cow, str};
+use util::asset_str;
+
+pub use keymap_file::KeymapFile;
pub use settings_file::*;
pub use settings_store::{Setting, SettingsJsonSchemaParams, SettingsStore};
-use std::{borrow::Cow, str};
-pub const DEFAULT_SETTINGS_ASSET_PATH: &str = "settings/default.json";
-const INITIAL_USER_SETTINGS_ASSET_PATH: &str = "settings/initial_user_settings.json";
-const INITIAL_LOCAL_SETTINGS_ASSET_PATH: &str = "settings/initial_local_settings.json";
+#[derive(RustEmbed)]
+#[folder = "../../assets"]
+#[include = "settings/*"]
+#[include = "keymaps/*"]
+#[exclude = "*.DS_Store"]
+pub struct SettingsAssets;
pub fn default_settings() -> Cow<'static, str> {
- asset_str(&assets::Assets, DEFAULT_SETTINGS_ASSET_PATH)
+ asset_str::<SettingsAssets>("settings/default.json")
+}
+
+pub fn default_keymap() -> Cow<'static, str> {
+ asset_str::<SettingsAssets>("keymaps/default.json")
}
-pub fn initial_user_settings_content(assets: &dyn AssetSource) -> Cow<'_, str> {
- asset_str(assets, INITIAL_USER_SETTINGS_ASSET_PATH)
+pub fn vim_keymap() -> Cow<'static, str> {
+ asset_str::<SettingsAssets>("keymaps/vim.json")
}
-pub fn initial_local_settings_content(assets: &dyn AssetSource) -> Cow<'_, str> {
- asset_str(assets, INITIAL_LOCAL_SETTINGS_ASSET_PATH)
+pub fn initial_user_settings_content() -> Cow<'static, str> {
+ asset_str::<SettingsAssets>("settings/initial_user_settings.json")
}
-fn asset_str<'a>(assets: &'a dyn AssetSource, path: &str) -> Cow<'a, str> {
- match assets.load(path).unwrap() {
- 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")
}
@@ -1,6 +1,5 @@
use crate::{settings_store::SettingsStore, Setting};
use anyhow::Result;
-use assets::Assets;
use fs::Fs;
use futures::{channel::mpsc, StreamExt};
use gpui::{executor::Background, AppContext};
@@ -111,7 +110,7 @@ async fn load_settings(fs: &Arc<dyn Fs>) -> Result<String> {
Err(err) => {
if let Some(e) = err.downcast_ref::<std::io::Error>() {
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);
@@ -21,6 +21,7 @@ isahc.workspace = true
smol.workspace = true
url = "2.2"
rand.workspace = true
+rust-embed.workspace = true
tempdir = { workspace = true, optional = true }
serde.workspace = true
serde_json.workspace = true
@@ -7,6 +7,7 @@ pub mod paths;
pub mod test;
use std::{
+ borrow::Cow,
cmp::{self, Ordering},
ops::{AddAssign, Range, RangeInclusive},
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
// https://github.com/rust-lang/rust/issues/87800
// Remove when this ship in Rust 1.66 or 1.67
@@ -24,7 +24,6 @@ nvim-rs = { git = "https://github.com/KillTheMule/nvim-rs", branch = "master", f
tokio = { version = "1.15", "optional" = true }
serde_json.workspace = true
-assets = { path = "../assets" }
collections = { path = "../collections" }
command_palette = { path = "../command_palette" }
editor = { path = "../editor" }
@@ -27,7 +27,7 @@ impl<'a> VimTestContext<'a> {
cx.update_global(|store: &mut SettingsStore, cx| {
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
@@ -19,7 +19,6 @@ test-support = [
]
[dependencies]
-assets = { path = "../assets" }
db = { path = "../db" }
call = { path = "../call" }
client = { path = "../client" }
@@ -17,7 +17,6 @@ path = "src/main.rs"
[dependencies]
activity_indicator = { path = "../activity_indicator" }
-assets = { path = "../assets" }
auto_update = { path = "../auto_update" }
breadcrumbs = { path = "../breadcrumbs" }
call = { path = "../call" }
@@ -107,7 +106,7 @@ tree-sitter = "0.20"
tree-sitter-c = "0.20.1"
tree-sitter-cpp = "0.20.0"
tree-sitter-css = { git = "https://github.com/tree-sitter/tree-sitter-css", rev = "769203d0f9abe1a9a691ac2b9fe4bb4397a73c51" }
-tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "05e3631c6a0701c1fa518b0fee7be95a2ceef5e2" }
+tree-sitter-elixir = { git = "https://github.com/elixir-lang/tree-sitter-elixir", rev = "4ba9dab6e2602960d95b2b625f3386c27e08084e" }
tree-sitter-embedded-template = "0.20.0"
tree-sitter-go = { git = "https://github.com/tree-sitter/tree-sitter-go", rev = "aeb2f33b366fd78d5789ff104956ce23508b85db" }
tree-sitter-json = { git = "https://github.com/tree-sitter/tree-sitter-json", rev = "40a81c01a40ac48744e0c8ccabbaba1920441199" }
@@ -1,3 +1,5 @@
+use std::process::Command;
+
fn main() {
println!("cargo:rustc-env=MACOSX_DEPLOYMENT_TARGET=10.15.7");
@@ -21,4 +23,32 @@ fn main() {
// Register exported Objective-C selectors, protocols, etc
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");
}
@@ -4,6 +4,10 @@ use rust_embed::RustEmbed;
#[derive(RustEmbed)]
#[folder = "../../assets"]
+#[include = "fonts/**/*"]
+#[include = "icons/**/*"]
+#[include = "themes/**/*"]
+#[include = "*.md"]
#[exclude = "*.DS_Store"]
pub struct Assets;
@@ -3,6 +3,7 @@ pub use language::*;
use node_runtime::NodeRuntime;
use rust_embed::RustEmbed;
use std::{borrow::Cow, str, sync::Arc};
+use util::asset_str;
mod c;
mod elixir;
@@ -179,10 +180,7 @@ fn load_query(name: &str, filename_prefix: &str) -> Option<Cow<'static, str>> {
for path in LanguageDir::iter() {
if let Some(remainder) = path.strip_prefix(name) {
if remainder.starts_with(filename_prefix) {
- let contents = match LanguageDir::get(path.as_ref()).unwrap().data {
- Cow::Borrowed(s) => Cow::Borrowed(str::from_utf8(s).unwrap()),
- Cow::Owned(s) => Cow::Owned(String::from_utf8(s).unwrap()),
- };
+ let contents = asset_str::<LanguageDir>(path.as_ref());
match &mut result {
None => result = Some(contents),
Some(r) => r.to_mut().push_str(contents.as_ref()),
@@ -1,20 +1,5 @@
["when" "and" "or" "not" "in" "not in" "fn" "do" "end" "catch" "rescue" "after" "else"] @keyword
-(unary_operator
- operator: "@" @comment.doc
- operand: (call
- target: (identifier) @comment.doc.__attribute__
- (arguments
- [
- (string) @comment.doc
- (charlist) @comment.doc
- (sigil
- quoted_start: _ @comment.doc
- quoted_end: _ @comment.doc) @comment.doc
- (boolean) @comment.doc
- ]))
- (#match? @comment.doc.__attribute__ "^(moduledoc|typedoc|doc)$"))
-
(unary_operator
operator: "&"
operand: (integer) @operator)
@@ -84,6 +69,11 @@
quoted_start: _ @string.special
quoted_end: _ @string.special) @string.special
+(
+ (identifier) @comment.unused
+ (#match? @comment.unused "^_")
+)
+
(call
target: [
(identifier) @function
@@ -99,17 +89,12 @@
(binary_operator
left: (identifier) @function
operator: "when")
+ (binary_operator
+ operator: "|>"
+ right: (identifier))
])
(#match? @keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp)$"))
-(call
- target: (identifier) @keyword
- (arguments
- (binary_operator
- operator: "|>"
- right: (identifier)))
- (#match? @keyword "^(def|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp|defp)$"))
-
(binary_operator
operator: "|>"
right: (identifier) @function)
@@ -127,10 +112,18 @@
(#match? @constant.builtin "^(__MODULE__|__DIR__|__ENV__|__CALLER__|__STACKTRACE__)$")
)
-(
- (identifier) @comment.unused
- (#match? @comment.unused "^_")
-)
+(unary_operator
+ operator: "@" @comment.doc
+ operand: (call
+ target: (identifier) @__attribute__ @comment.doc
+ (arguments
+ [
+ (string)
+ (charlist)
+ (sigil)
+ (boolean)
+ ] @comment.doc))
+ (#match? @__attribute__ "^(moduledoc|typedoc|doc)$"))
(comment) @comment
@@ -1,6 +1,4 @@
-[
- (call)
-] @indent
+(call) @indent
(_ "[" "]" @end) @indent
(_ "{" "}" @end) @indent
@@ -8,9 +8,19 @@
(arguments
[
(identifier) @name
- (call target: (identifier) @name)
+ (call
+ target: (identifier) @name
+ (arguments
+ "(" @context.extra
+ _* @context.extra
+ ")" @context.extra))
(binary_operator
- left: (call target: (identifier) @name)
+ left: (call
+ target: (identifier) @name
+ (arguments
+ "(" @context.extra
+ _* @context.extra
+ ")" @context.extra))
operator: "when")
])
(#match? @context "^(def|defp|defdelegate|defguard|defguardp|defmacro|defmacrop|defn|defnp)$")) @item
@@ -6,7 +6,7 @@ use gpui::AppContext;
use language::{LanguageRegistry, LanguageServerBinary, LanguageServerName, LspAdapter};
use node_runtime::NodeRuntime;
use serde_json::json;
-use settings::{keymap_file_json_schema, SettingsJsonSchemaParams, SettingsStore};
+use settings::{KeymapFile, SettingsJsonSchemaParams, SettingsStore};
use smol::fs;
use staff_mode::StaffMode;
use std::{
@@ -143,7 +143,7 @@ impl LspAdapter for JsonLspAdapter {
},
{
"fileMatch": [schema_file_match(&paths::KEYMAP)],
- "schema": keymap_file_json_schema(&action_names),
+ "schema": KeymapFile::generate_json_schema(&action_names),
}
]
}
@@ -14,11 +14,11 @@
(list_marker_parenthesis)
] @punctuation.list_marker
-[
- (indented_code_block)
- (fenced_code_block)
- (code_span)
-] @text.literal
+(code_span) @text.literal
+
+(fenced_code_block
+ (info_string
+ (language) @text.literal))
(link_destination) @link_uri
(link_text) @link_text
@@ -327,10 +327,10 @@ mod tests {
.map(|item| (item.text.as_str(), item.depth))
.collect::<Vec<_>>(),
&[
- ("function a ( )", 0),
- ("async function a2 ( )", 1),
+ ("function a()", 0),
+ ("async function a2()", 1),
("let b", 0),
- ("function getB ( )", 0),
+ ("function getB()", 0),
("const d", 0),
]
);
@@ -2,7 +2,6 @@
#![allow(non_snake_case)]
use anyhow::{anyhow, Context, Result};
-use assets::Assets;
use backtrace::Backtrace;
use cli::{
ipc::{self, IpcSender},
@@ -58,7 +57,8 @@ use staff_mode::StaffMode;
use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt};
use workspace::{item::ItemHandle, notifications::NotifyResultExt, AppState, Workspace};
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() {
@@ -160,6 +160,8 @@ fn main() {
ai::init(cx);
cx.spawn(|cx| watch_themes(fs.clone(), cx)).detach();
+ cx.spawn(|_| watch_languages(fs.clone(), languages.clone()))
+ .detach();
languages.set_theme(theme::current(cx).clone());
cx.observe_global::<SettingsStore, _>({
@@ -660,11 +662,30 @@ async fn watch_themes(fs: Arc<dyn Fs>, mut cx: AsyncAppContext) -> Option<()> {
Some(())
}
+#[cfg(debug_assertions)]
+async fn watch_languages(fs: Arc<dyn Fs>, languages: Arc<LanguageRegistry>) -> Option<()> {
+ let mut events = fs
+ .watch(
+ "crates/zed/src/languages".as_ref(),
+ Duration::from_millis(100),
+ )
+ .await;
+ while (events.next().await).is_some() {
+ languages.reload();
+ }
+ Some(())
+}
+
#[cfg(not(debug_assertions))]
async fn watch_themes(_fs: Arc<dyn Fs>, _cx: AsyncAppContext) -> Option<()> {
None
}
+#[cfg(not(debug_assertions))]
+async fn watch_languages(_: Arc<dyn Fs>, _: Arc<LanguageRegistry>) -> Option<()> {
+ None
+}
+
fn connect_to_cli(
server_name: &str,
) -> Result<(mpsc::Receiver<CliRequest>, IpcSender<CliResponse>)> {
@@ -1,7 +1,9 @@
+pub mod assets;
pub mod languages;
pub mod menus;
#[cfg(any(test, feature = "test-support"))]
pub mod test;
+
use ai::AssistantPanel;
use anyhow::Context;
use assets::Assets;
@@ -31,12 +33,11 @@ use project_panel::ProjectPanel;
use search::{BufferSearchBar, ProjectSearchBar};
use serde::Deserialize;
use serde_json::to_string_pretty;
-use settings::{
- initial_local_settings_content, KeymapFileContent, SettingsStore, DEFAULT_SETTINGS_ASSET_PATH,
-};
+use settings::{initial_local_settings_content, KeymapFile, SettingsStore};
use std::{borrow::Cow, str, sync::Arc};
use terminal_view::terminal_panel::{self, TerminalPanel};
use util::{
+ asset_str,
channel::ReleaseChannel,
paths::{self, LOCAL_SETTINGS_RELATIVE_PATH},
ResultExt,
@@ -150,7 +151,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
move |workspace: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext<Workspace>| {
open_bundled_file(
workspace,
- "licenses.md",
+ asset_str::<Assets>("licenses.md"),
"Open Source License Attribution",
"Markdown",
cx,
@@ -170,9 +171,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
cx.add_action(
move |_: &mut Workspace, _: &OpenSettings, cx: &mut ViewContext<Workspace>| {
create_and_open_local_file(&paths::SETTINGS, cx, || {
- settings::initial_user_settings_content(&Assets)
- .as_ref()
- .into()
+ settings::initial_user_settings_content().as_ref().into()
})
.detach_and_log_err(cx);
},
@@ -182,7 +181,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext<Workspace>| {
open_bundled_file(
workspace,
- "keymaps/default.json",
+ settings::default_keymap(),
"Default Key Bindings",
"JSON",
cx,
@@ -195,7 +194,7 @@ pub fn init(app_state: &Arc<AppState>, cx: &mut gpui::AppContext) {
cx: &mut ViewContext<Workspace>| {
open_bundled_file(
workspace,
- DEFAULT_SETTINGS_ASSET_PATH,
+ settings::default_settings(),
"Default Settings",
"JSON",
cx,
@@ -532,11 +531,11 @@ fn open_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Workspace>) {
pub fn load_default_keymap(cx: &mut AppContext) {
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() {
- KeymapFileContent::load_asset(asset_path, cx).unwrap();
+ KeymapFile::load_asset(asset_path, cx).unwrap();
}
}
@@ -547,7 +546,7 @@ pub fn handle_keymap_file_changes(
cx.spawn(move |mut cx| async move {
let mut settings_subscription = None;
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.clear_bindings();
load_default_keymap(cx);
@@ -624,11 +623,7 @@ fn open_local_settings_file(
if let Some(buffer) = editor.buffer().read(cx).as_singleton() {
if buffer.read(cx).is_empty() {
buffer.update(cx, |buffer, cx| {
- buffer.edit(
- [(0..0, initial_local_settings_content(&Assets))],
- None,
- cx,
- )
+ buffer.edit([(0..0, initial_local_settings_content())], None, cx)
});
}
}
@@ -704,7 +699,7 @@ fn open_telemetry_log_file(workspace: &mut Workspace, cx: &mut ViewContext<Works
fn open_bundled_file(
workspace: &mut Workspace,
- asset_path: &'static str,
+ text: Cow<'static, str>,
title: &'static str,
language: &'static str,
cx: &mut ViewContext<Workspace>,
@@ -716,13 +711,9 @@ fn open_bundled_file(
.update(&mut cx, |workspace, cx| {
workspace.with_local_workspace(cx, |workspace, cx| {
let project = workspace.project();
- let buffer = project.update(cx, |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();
+ let buffer = project.update(cx, move |project, cx| {
project
- .create_buffer(text, language, cx)
+ .create_buffer(text.as_ref(), language, cx)
.expect("creating buffers on a local workspace always succeeds")
});
let buffer = cx.add_model(|cx| {
@@ -0,0 +1,4 @@
+[toolchain]
+channel = "1.70"
+components = [ "rustfmt" ]
+targets = [ "x86_64-apple-darwin", "aarch64-apple-darwin", "wasm32-wasi" ]