From b9110c9268bd2af83fff1c55b4bc9a2da11844f3 Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 3 Mar 2023 13:10:08 -0800 Subject: [PATCH 01/93] Increase reconnect timeout Co-Authored-By: Antonio Scandurra --- crates/call/src/room.rs | 2 +- crates/collab/src/rpc.rs | 2 +- crates/rpc/src/peer.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs index 06680c1dcb..901ae08fc0 100644 --- a/crates/call/src/room.rs +++ b/crates/call/src/room.rs @@ -20,7 +20,7 @@ use project::Project; use std::{mem, sync::Arc, time::Duration}; use util::{post_inc, ResultExt, TryFutureExt}; -pub const RECONNECT_TIMEOUT: Duration = client::RECEIVE_TIMEOUT; +pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); #[derive(Clone, Debug, PartialEq, Eq)] pub enum Event { diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 01d24024eb..710ddcb890 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -57,7 +57,7 @@ use tokio::sync::watch; use tower::ServiceBuilder; use tracing::{info_span, instrument, Instrument}; -pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(5); +pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30); pub const CLEANUP_TIMEOUT: Duration = Duration::from_secs(10); lazy_static! { diff --git a/crates/rpc/src/peer.rs b/crates/rpc/src/peer.rs index 4740120fc4..0df87fd92d 100644 --- a/crates/rpc/src/peer.rs +++ b/crates/rpc/src/peer.rs @@ -114,7 +114,7 @@ pub struct ConnectionState { const KEEPALIVE_INTERVAL: Duration = Duration::from_secs(1); const WRITE_TIMEOUT: Duration = Duration::from_secs(2); -pub const RECEIVE_TIMEOUT: Duration = Duration::from_secs(5); +pub const RECEIVE_TIMEOUT: Duration = Duration::from_secs(10); impl Peer { pub fn new(epoch: u32) -> Arc { From 9401ef223d97f57ce33252675ac169b353213767 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 21 Feb 2023 18:16:47 -0800 Subject: [PATCH 02/93] Add welcome crate and associated types --- Cargo.lock | 15 +++++++++++ Cargo.toml | 1 + crates/welcome/Cargo.toml | 21 +++++++++++++++ crates/welcome/src/welcome.rs | 51 +++++++++++++++++++++++++++++++++++ crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 1 + 6 files changed, 90 insertions(+) create mode 100644 crates/welcome/Cargo.toml create mode 100644 crates/welcome/src/welcome.rs diff --git a/Cargo.lock b/Cargo.lock index 2e7997458e..607f47b5cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8013,6 +8013,20 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" +[[package]] +name = "welcome" +version = "0.1.0" +dependencies = [ + "anyhow", + "gpui", + "log", + "project", + "settings", + "theme", + "util", + "workspace", +] + [[package]] name = "wepoll-ffi" version = "0.1.2" @@ -8459,6 +8473,7 @@ dependencies = [ "util", "uuid 1.2.2", "vim", + "welcome", "workspace", ] diff --git a/Cargo.toml b/Cargo.toml index c74a76ccce..feb80633c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,6 +58,7 @@ members = [ "crates/util", "crates/vim", "crates/workspace", + "crates/welcome", "crates/zed", ] default-members = ["crates/zed"] diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml new file mode 100644 index 0000000000..6ac312c37f --- /dev/null +++ b/crates/welcome/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "welcome" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/welcome.rs" + +[features] +test-support = [] + +[dependencies] +anyhow = "1.0.38" +log = "0.4" +gpui = { path = "../gpui" } +project = { path = "../project" } +settings = { path = "../settings" } +theme = { path = "../theme" } +util = { path = "../util" } +workspace = { path = "../workspace" } \ No newline at end of file diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs new file mode 100644 index 0000000000..1deede7611 --- /dev/null +++ b/crates/welcome/src/welcome.rs @@ -0,0 +1,51 @@ +use gpui::{ + actions, + elements::{Flex, Label, ParentElement}, + Element, Entity, MutableAppContext, View, +}; +use settings::Settings; +use workspace::{item::Item, Workspace}; + +actions!(welcome, [ShowWelcome]); + +pub fn init(cx: &mut MutableAppContext) { + cx.add_action(|workspace: &mut Workspace, _: &ShowWelcome, cx| { + let welcome_page = cx.add_view(|_cx| WelcomePage); + workspace.add_item(Box::new(welcome_page), cx) + }) +} + +struct WelcomePage; + +impl Entity for WelcomePage { + type Event = (); +} + +impl View for WelcomePage { + fn ui_name() -> &'static str { + "WelcomePage" + } + + fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox { + let theme = &cx.global::().theme; + Label::new("Welcome page", theme.editor.hover_popover.prose.clone()).boxed() + } +} + +impl Item for WelcomePage { + fn tab_content( + &self, + _detail: Option, + style: &theme::Tab, + _cx: &gpui::AppContext, + ) -> gpui::ElementBox { + Flex::row() + .with_child( + Label::new("Welcome to Zed!", style.label.clone()) + .aligned() + .contained() + .boxed(), + ) + .boxed() + } +} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 19c9a3d727..d3b6e4810f 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -58,6 +58,7 @@ theme_testbench = { path = "../theme_testbench" } util = { path = "../util" } vim = { path = "../vim" } workspace = { path = "../workspace" } +welcome = { path = "../welcome" } anyhow = "1.0.38" async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] } async-tar = "0.4.2" diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index b9e3ed550b..cc930774d5 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -189,6 +189,7 @@ fn main() { zed::init(&app_state, cx); collab_ui::init(app_state.clone(), cx); feedback::init(app_state.clone(), cx); + welcome::init(cx); cx.set_menus(menus::menus()); From a0637a769ca0fde5af883c4004a7e164fd71e8ee Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 22 Feb 2023 12:01:59 -0800 Subject: [PATCH 03/93] WIP --- crates/welcome/src/welcome.rs | 34 ++++++++++++++++++++++++------- crates/workspace/src/workspace.rs | 7 ++++--- crates/zed/src/main.rs | 4 ++-- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 1deede7611..face85c4b6 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,15 +1,13 @@ use gpui::{ - actions, - elements::{Flex, Label, ParentElement}, + color::Color, + elements::{Flex, Label, ParentElement, Svg}, Element, Entity, MutableAppContext, View, }; use settings::Settings; -use workspace::{item::Item, Workspace}; - -actions!(welcome, [ShowWelcome]); +use workspace::{item::Item, Welcome, Workspace}; pub fn init(cx: &mut MutableAppContext) { - cx.add_action(|workspace: &mut Workspace, _: &ShowWelcome, cx| { + cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { let welcome_page = cx.add_view(|_cx| WelcomePage); workspace.add_item(Box::new(welcome_page), cx) }) @@ -28,7 +26,29 @@ impl View for WelcomePage { fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox { let theme = &cx.global::().theme; - Label::new("Welcome page", theme.editor.hover_popover.prose.clone()).boxed() + + Flex::new(gpui::Axis::Vertical) + .with_children([ + Flex::new(gpui::Axis::Horizontal) + .with_children([ + Svg::new("icons/terminal_16.svg") + .with_color(Color::red()) + .constrained() + .with_width(100.) + .with_height(100.) + .aligned() + .contained() + .boxed(), + Label::new("Zed", theme.editor.hover_popover.prose.clone()).boxed(), + ]) + .boxed(), + Label::new( + "Code at the speed of thought", + theme.editor.hover_popover.prose.clone(), + ) + .boxed(), + ]) + .boxed() } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index b9d80e7150..31960fbc1e 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -118,7 +118,8 @@ actions!( NewTerminal, NewSearch, Feedback, - Restart + Restart, + Welcome ] ); @@ -198,7 +199,7 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { }); cx.add_global_action({ let app_state = Arc::downgrade(&app_state); - move |_: &NewFile, cx: &mut MutableAppContext| { + move |_: &Welcome, cx: &mut MutableAppContext| { if let Some(app_state) = app_state.upgrade() { open_new(&app_state, cx).detach(); } @@ -2865,7 +2866,7 @@ pub fn open_new(app_state: &Arc, cx: &mut MutableAppContext) -> Task<( workspace.update(&mut cx, |_, cx| { if opened_paths.is_empty() { - cx.dispatch_action(NewFile); + cx.dispatch_action(Welcome); } }) }) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index cc930774d5..298e8ef8b9 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -43,7 +43,7 @@ use theme::ThemeRegistry; use util::StaffMode; use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt}; use workspace::{ - self, item::ItemHandle, notifications::NotifyResultExt, AppState, NewFile, OpenPaths, Workspace, + self, item::ItemHandle, notifications::NotifyResultExt, AppState, OpenPaths, Welcome, Workspace, }; use zed::{self, build_window_options, initialize_workspace, languages, menus}; @@ -260,7 +260,7 @@ async fn restore_or_create_workspace(mut cx: AsyncAppContext) { }); } else { cx.update(|cx| { - cx.dispatch_global_action(NewFile); + cx.dispatch_global_action(Welcome); }); } } From 416c79307695dc164ef2262a64f49c784a49fcc1 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 24 Feb 2023 14:31:59 -0800 Subject: [PATCH 04/93] Start on welcome experience settings --- Cargo.lock | 1 + crates/gpui/src/app.rs | 2 +- crates/settings/src/settings.rs | 180 ++++++++++++++++++++++++--- crates/settings/src/settings_file.rs | 50 ++------ crates/theme/src/theme.rs | 15 +++ crates/welcome/Cargo.toml | 1 + crates/welcome/src/welcome.rs | 107 ++++++++++++++-- styles/src/styleTree/app.ts | 2 + styles/src/styleTree/welcome.ts | 34 +++++ 9 files changed, 326 insertions(+), 66 deletions(-) create mode 100644 styles/src/styleTree/welcome.ts diff --git a/Cargo.lock b/Cargo.lock index 607f47b5cf..90da178c7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8018,6 +8018,7 @@ name = "welcome" version = "0.1.0" dependencies = [ "anyhow", + "editor", "gpui", "log", "project", diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 31563010b7..0397032de8 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -5086,7 +5086,7 @@ impl From> for AnyWeakModelHandle { } } -#[derive(Debug)] +#[derive(Debug, Copy)] pub struct WeakViewHandle { window_id: usize, view_id: usize, diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 6b51b06c9c..229e11ff3a 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -66,9 +66,18 @@ impl TelemetrySettings { pub fn metrics(&self) -> bool { self.metrics.unwrap() } + pub fn diagnostics(&self) -> bool { self.diagnostics.unwrap() } + + pub fn set_metrics(&mut self, value: bool) { + self.metrics = Some(value); + } + + pub fn set_diagnostics(&mut self, value: bool) { + self.diagnostics = Some(value); + } } #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] @@ -679,7 +688,7 @@ pub fn settings_file_json_schema( /// Expects the key to be unquoted, and the value to be valid JSON /// (e.g. values should be unquoted for numbers and bools, quoted for strings) -pub fn write_top_level_setting( +pub fn write_settings_key( mut settings_content: String, top_level_key: &str, new_val: &str, @@ -786,11 +795,160 @@ pub fn parse_json_with_comments(content: &str) -> Result )?) } +pub fn update_settings_file( + old_text: String, + old_file_content: SettingsFileContent, + update: impl FnOnce(&mut SettingsFileContent), +) -> String { + let mut new_file_content = old_file_content.clone(); + update(&mut new_file_content); + + let old_json = to_json_object(old_file_content); + let new_json = to_json_object(new_file_content); + + // Find changed fields + let mut diffs = vec![]; + for (key, old_value) in old_json.iter() { + let new_value = new_json.get(key).unwrap(); + if old_value != new_value { + if matches!( + new_value, + &Value::Null | &Value::Object(_) | &Value::Array(_) + ) { + unimplemented!("We only support updating basic values at the top level"); + } + + let new_json = serde_json::to_string_pretty(new_value) + .expect("Could not serialize new json field to string"); + + diffs.push((key, new_json)); + } + } + + let mut new_text = old_text; + for (key, new_value) in diffs { + new_text = write_settings_key(new_text, key, &new_value) + } + new_text +} + +fn to_json_object(settings_file: SettingsFileContent) -> serde_json::Map { + let tmp = serde_json::to_value(settings_file).unwrap(); + match tmp { + Value::Object(map) => map, + _ => unreachable!("SettingsFileContent represents a JSON map"), + } +} + #[cfg(test)] mod tests { - use crate::write_top_level_setting; + use super::*; use unindent::Unindent; + fn assert_new_settings, S2: Into>( + old_json: S1, + update: fn(&mut SettingsFileContent), + expected_new_json: S2, + ) { + let old_json = old_json.into(); + let old_content: SettingsFileContent = serde_json::from_str(&old_json).unwrap(); + let new_json = update_settings_file(old_json, old_content, update); + assert_eq!(new_json, expected_new_json.into()); + } + + #[test] + fn test_update_telemetry_setting_multiple_fields() { + assert_new_settings( + r#"{ + "telemetry": { + "metrics": false, + "diagnostics": false + } + }"# + .unindent(), + |settings| { + settings.telemetry.set_diagnostics(true); + settings.telemetry.set_metrics(true); + }, + r#"{ + "telemetry": { + "metrics": true, + "diagnostics": true + } + }"# + .unindent(), + ); + } + + #[test] + fn test_update_telemetry_setting_weird_formatting() { + assert_new_settings( + r#"{ + "telemetry": { "metrics": false, "diagnostics": true } + }"# + .unindent(), + |settings| settings.telemetry.set_diagnostics(false), + r#"{ + "telemetry": { "metrics": false, "diagnostics": false } + }"# + .unindent(), + ); + } + + #[test] + fn test_update_telemetry_setting_other_fields() { + assert_new_settings( + r#"{ + "telemetry": { + "metrics": false, + "diagnostics": true + } + }"# + .unindent(), + |settings| settings.telemetry.set_diagnostics(false), + r#"{ + "telemetry": { + "metrics": false, + "diagnostics": false + } + }"# + .unindent(), + ); + } + + #[test] + fn test_update_telemetry_setting_pre_existing() { + assert_new_settings( + r#"{ + "telemetry": { + "diagnostics": true + } + }"# + .unindent(), + |settings| settings.telemetry.set_diagnostics(false), + r#"{ + "telemetry": { + "diagnostics": false + } + }"# + .unindent(), + ); + } + + #[test] + fn test_update_telemetry_setting() { + assert_new_settings( + "{}", + |settings| settings.telemetry.set_diagnostics(true), + r#"{ + "telemetry": { + "diagnostics": true + } + }"# + .unindent(), + ); + } + #[test] fn test_write_theme_into_settings_with_theme() { let settings = r#" @@ -807,8 +965,7 @@ mod tests { "# .unindent(); - let settings_after_theme = - write_top_level_setting(settings, "theme", "\"summerfruit-light\""); + let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } @@ -828,8 +985,7 @@ mod tests { "# .unindent(); - let settings_after_theme = - write_top_level_setting(settings, "theme", "\"summerfruit-light\""); + let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } @@ -845,8 +1001,7 @@ mod tests { "# .unindent(); - let settings_after_theme = - write_top_level_setting(settings, "theme", "\"summerfruit-light\""); + let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } @@ -856,8 +1011,7 @@ mod tests { let settings = r#"{ "a": "", "ok": true }"#.to_string(); let new_settings = r#"{ "theme": "summerfruit-light", "a": "", "ok": true }"#; - let settings_after_theme = - write_top_level_setting(settings, "theme", "\"summerfruit-light\""); + let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } @@ -867,8 +1021,7 @@ mod tests { let settings = r#" { "a": "", "ok": true }"#.to_string(); let new_settings = r#" { "theme": "summerfruit-light", "a": "", "ok": true }"#; - let settings_after_theme = - write_top_level_setting(settings, "theme", "\"summerfruit-light\""); + let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } @@ -890,8 +1043,7 @@ mod tests { "# .unindent(); - let settings_after_theme = - write_top_level_setting(settings, "theme", "\"summerfruit-light\""); + let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); assert_eq!(settings_after_theme, new_settings) } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 506ebc8c3d..575e9499d3 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -1,8 +1,7 @@ -use crate::{watched_json::WatchedJsonFile, write_top_level_setting, SettingsFileContent}; +use crate::{update_settings_file, watched_json::WatchedJsonFile, SettingsFileContent}; use anyhow::Result; use fs::Fs; use gpui::MutableAppContext; -use serde_json::Value; use std::{path::Path, sync::Arc}; // TODO: Switch SettingsFile to open a worktree and buffer for synchronization @@ -27,57 +26,24 @@ impl SettingsFile { } } - pub fn update(cx: &mut MutableAppContext, update: impl FnOnce(&mut SettingsFileContent)) { + pub fn update( + cx: &mut MutableAppContext, + update: impl 'static + Send + FnOnce(&mut SettingsFileContent), + ) { let this = cx.global::(); let current_file_content = this.settings_file_content.current(); - let mut new_file_content = current_file_content.clone(); - - update(&mut new_file_content); let fs = this.fs.clone(); let path = this.path.clone(); cx.background() .spawn(async move { - // Unwrap safety: These values are all guarnteed to be well formed, and we know - // that they will deserialize to our settings object. All of the following unwraps - // are therefore safe. - let tmp = serde_json::to_value(current_file_content).unwrap(); - let old_json = tmp.as_object().unwrap(); + let old_text = fs.load(path).await?; - let new_tmp = serde_json::to_value(new_file_content).unwrap(); - let new_json = new_tmp.as_object().unwrap(); + let new_text = update_settings_file(old_text, current_file_content, update); - // Find changed fields - let mut diffs = vec![]; - for (key, old_value) in old_json.iter() { - let new_value = new_json.get(key).unwrap(); - if old_value != new_value { - if matches!( - new_value, - &Value::Null | &Value::Object(_) | &Value::Array(_) - ) { - unimplemented!( - "We only support updating basic values at the top level" - ); - } - - let new_json = serde_json::to_string_pretty(new_value) - .expect("Could not serialize new json field to string"); - - diffs.push((key, new_json)); - } - } - - // Have diffs, rewrite the settings file now. - let mut content = fs.load(path).await?; - - for (key, new_value) in diffs { - content = write_top_level_setting(content, key, &new_value) - } - - fs.atomic_write(path.to_path_buf(), content).await?; + fs.atomic_write(path.to_path_buf(), new_text).await?; Ok(()) as Result<()> }) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 484c542ede..49f2d982ba 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -37,6 +37,7 @@ pub struct Theme { pub tooltip: TooltipStyle, pub terminal: TerminalStyle, pub feedback: FeedbackStyle, + pub welcome: WelcomeStyle, pub color_scheme: ColorScheme, } @@ -850,6 +851,20 @@ pub struct FeedbackStyle { pub link_text_hover: ContainedText, } +#[derive(Clone, Deserialize, Default)] +pub struct WelcomeStyle { + pub checkbox: CheckboxStyle, +} + +#[derive(Clone, Deserialize, Default)] +pub struct CheckboxStyle { + pub width: f32, + pub height: f32, + pub unchecked: ContainerStyle, + pub checked: ContainerStyle, + pub hovered: ContainerStyle, +} + #[derive(Clone, Deserialize, Default)] pub struct ColorScheme { pub name: String, diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index 6ac312c37f..3e450e2312 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -13,6 +13,7 @@ test-support = [] [dependencies] anyhow = "1.0.38" log = "0.4" +editor = { path = "../editor" } gpui = { path = "../gpui" } project = { path = "../project" } settings = { path = "../settings" } diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index face85c4b6..385a8a5f00 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,19 +1,22 @@ use gpui::{ color::Color, - elements::{Flex, Label, ParentElement, Svg}, - Element, Entity, MutableAppContext, View, + elements::{Empty, Flex, Label, MouseEventHandler, ParentElement, Svg}, + Element, ElementBox, Entity, MutableAppContext, RenderContext, Subscription, View, ViewContext, }; -use settings::Settings; +use settings::{settings_file::SettingsFile, Settings, SettingsFileContent}; +use theme::CheckboxStyle; use workspace::{item::Item, Welcome, Workspace}; pub fn init(cx: &mut MutableAppContext) { cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { - let welcome_page = cx.add_view(|_cx| WelcomePage); + let welcome_page = cx.add_view(WelcomePage::new); workspace.add_item(Box::new(welcome_page), cx) }) } -struct WelcomePage; +struct WelcomePage { + _settings_subscription: Subscription, +} impl Entity for WelcomePage { type Event = (); @@ -24,12 +27,21 @@ impl View for WelcomePage { "WelcomePage" } - fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox { - let theme = &cx.global::().theme; + fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox { + let settings = cx.global::(); + let theme = settings.theme.clone(); - Flex::new(gpui::Axis::Vertical) + let (diagnostics, metrics) = { + let telemetry = settings.telemetry(); + (telemetry.diagnostics(), telemetry.metrics()) + }; + + enum Metrics {} + enum Diagnostics {} + + Flex::column() .with_children([ - Flex::new(gpui::Axis::Horizontal) + Flex::row() .with_children([ Svg::new("icons/terminal_16.svg") .with_color(Color::red()) @@ -47,11 +59,88 @@ impl View for WelcomePage { theme.editor.hover_popover.prose.clone(), ) .boxed(), + Flex::row() + .with_children([ + self.render_settings_checkbox::( + &theme.welcome.checkbox, + metrics, + cx, + |content, checked| { + content.telemetry.set_metrics(checked); + }, + ), + Label::new( + "Do you want to send telemetry?", + theme.editor.hover_popover.prose.clone(), + ) + .boxed(), + ]) + .boxed(), + Flex::row() + .with_children([ + self.render_settings_checkbox::( + &theme.welcome.checkbox, + diagnostics, + cx, + |content, checked| content.telemetry.set_diagnostics(checked), + ), + Label::new( + "Send crash reports", + theme.editor.hover_popover.prose.clone(), + ) + .boxed(), + ]) + .boxed(), ]) + .aligned() .boxed() } } +impl WelcomePage { + fn new(cx: &mut ViewContext) -> Self { + let handle = cx.weak_handle(); + + let settings_subscription = cx.observe_global::(move |cx| { + if let Some(handle) = handle.upgrade(cx) { + handle.update(cx, |_, cx| cx.notify()) + } + }); + + WelcomePage { + _settings_subscription: settings_subscription, + } + } + + fn render_settings_checkbox( + &self, + style: &CheckboxStyle, + checked: bool, + cx: &mut RenderContext, + set_value: fn(&mut SettingsFileContent, checked: bool) -> (), + ) -> ElementBox { + MouseEventHandler::::new(0, cx, |state, _| { + Empty::new() + .constrained() + .with_width(style.width) + .with_height(style.height) + .contained() + .with_style(if checked { + style.checked + } else if state.hovered() { + style.hovered + } else { + style.unchecked + }) + .boxed() + }) + .on_click(gpui::MouseButton::Left, move |_, cx| { + SettingsFile::update(cx, move |content| set_value(content, !checked)) + }) + .boxed() + } +} + impl Item for WelcomePage { fn tab_content( &self, diff --git a/styles/src/styleTree/app.ts b/styles/src/styleTree/app.ts index dc57468df6..423ce37d48 100644 --- a/styles/src/styleTree/app.ts +++ b/styles/src/styleTree/app.ts @@ -20,6 +20,7 @@ import contactList from "./contactList" import incomingCallNotification from "./incomingCallNotification" import { ColorScheme } from "../themes/common/colorScheme" import feedback from "./feedback" +import welcome from "./welcome" export default function app(colorScheme: ColorScheme): Object { return { @@ -33,6 +34,7 @@ export default function app(colorScheme: ColorScheme): Object { incomingCallNotification: incomingCallNotification(colorScheme), picker: picker(colorScheme), workspace: workspace(colorScheme), + welcome: welcome(colorScheme), contextMenu: contextMenu(colorScheme), editor: editor(colorScheme), projectDiagnostics: projectDiagnostics(colorScheme), diff --git a/styles/src/styleTree/welcome.ts b/styles/src/styleTree/welcome.ts new file mode 100644 index 0000000000..f1325514cd --- /dev/null +++ b/styles/src/styleTree/welcome.ts @@ -0,0 +1,34 @@ + +import { ColorScheme } from "../themes/common/colorScheme"; +import { border } from "./components"; + +export default function welcome(colorScheme: ColorScheme) { + let layer = colorScheme.highest; + + // TODO + let checkbox_base = { + background: colorScheme.ramps.red(0.5).hex(), + cornerRadius: 8, + padding: { + left: 8, + right: 8, + top: 4, + bottom: 4, + }, + shadow: colorScheme.popoverShadow, + border: border(layer), + margin: { + left: -8, + }, + }; + + return { + checkbox: { + width: 9, + height: 9, + unchecked: checkbox_base, + checked: checkbox_base, + hovered: checkbox_base + } + } +} \ No newline at end of file From 50586812ecdbca1f8fa538a708122dafd52bba9c Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 24 Feb 2023 14:41:16 -0800 Subject: [PATCH 05/93] Make generate licenses quieter --- script/generate-licenses | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/generate-licenses b/script/generate-licenses index 8a41f55c02..14c9d4c79f 100755 --- a/script/generate-licenses +++ b/script/generate-licenses @@ -10,7 +10,7 @@ echo -e "# ###### THEME LICENSES ######\n" >> $OUTPUT_FILE echo "Generating theme licenses" cd styles -npm ci +npm --silent ci npm run --silent build-licenses >> $OUTPUT_FILE cd .. From 86e21015922487c20cacba5f93ecf588142a89c8 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 24 Feb 2023 16:43:34 -0800 Subject: [PATCH 06/93] Added the ability to nested values to the settings file, while preserving user formatting co-authored-by: max --- crates/settings/src/settings.rs | 477 ++++++++++++++++++++------------ 1 file changed, 293 insertions(+), 184 deletions(-) diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 229e11ff3a..501a88c42e 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -18,7 +18,7 @@ use sqlez::{ bindable::{Bind, Column, StaticColumnCount}, statement::Statement, }; -use std::{collections::HashMap, fmt::Write as _, num::NonZeroU32, str, sync::Arc}; +use std::{collections::HashMap, num::NonZeroU32, str, sync::Arc}; use theme::{Theme, ThemeRegistry}; use tree_sitter::Query; use util::ResultExt as _; @@ -686,13 +686,25 @@ pub fn settings_file_json_schema( serde_json::to_value(root_schema).unwrap() } +fn merge(target: &mut T, value: Option) { + if let Some(value) = value { + *target = value; + } +} + +pub fn parse_json_with_comments(content: &str) -> Result { + Ok(serde_json::from_reader( + json_comments::CommentSettings::c_style().strip_comments(content.as_bytes()), + )?) +} + /// Expects the key to be unquoted, and the value to be valid JSON /// (e.g. values should be unquoted for numbers and bools, quoted for strings) -pub fn write_settings_key( - mut settings_content: String, - top_level_key: &str, - new_val: &str, -) -> String { +pub fn write_settings_key( + settings_content: &mut String, + key_path: &[&str], + new_value: &T, +) { let mut parser = tree_sitter::Parser::new(); parser.set_language(tree_sitter_json::language()).unwrap(); let tree = parser.parse(&settings_content, None).unwrap(); @@ -702,56 +714,64 @@ pub fn write_settings_key( let query = Query::new( tree_sitter_json::language(), " - (document - (object - (pair - key: (string) @key - value: (_) @value))) - ", + (pair + key: (string) @key + value: (_) @value) + ", ) .unwrap(); + let mut depth = 0; let mut first_key_start = None; - let mut existing_value_range = None; + let mut existing_value_range = 0..settings_content.len(); let matches = cursor.matches(&query, tree.root_node(), settings_content.as_bytes()); for mat in matches { if mat.captures.len() != 2 { continue; } - let key = mat.captures[0]; - let value = mat.captures[1]; + let key_range = mat.captures[0].node.byte_range(); + let value_range = mat.captures[1].node.byte_range(); - first_key_start.get_or_insert_with(|| key.node.start_byte()); + if key_range.start > existing_value_range.end { + break; + } - if let Some(key_text) = settings_content.get(key.node.byte_range()) { - if key_text == format!("\"{top_level_key}\"") { - existing_value_range = Some(value.node.byte_range()); + first_key_start.get_or_insert_with(|| key_range.start); + + let found_key = settings_content + .get(key_range.clone()) + .map(|key_text| key_text == format!("\"{}\"", key_path[depth])) + .unwrap_or(false); + + if found_key { + existing_value_range = value_range; + depth += 1; + + if depth == key_path.len() { break; + } else { + first_key_start = None; } } } - match (first_key_start, existing_value_range) { - (None, None) => { - // No document, create a new object and overwrite - settings_content.clear(); - write!( - settings_content, - "{{\n \"{}\": {new_val}\n}}\n", - top_level_key - ) - .unwrap(); + // We found the exact key we want, insert the new value + if depth == key_path.len() { + let new_val = serde_json::to_string_pretty(new_value) + .expect("Could not serialize new json field to string"); + settings_content.replace_range(existing_value_range, &new_val); + } else { + // We have key paths, construct the sub objects + let new_key = key_path[depth]; + + // We don't have the key, construct the nested objects + let mut new_value = serde_json::to_value(new_value).unwrap(); + for key in key_path[(depth + 1)..].iter().rev() { + new_value = serde_json::json!({ key.to_string(): new_value }); } - (_, Some(existing_value_range)) => { - // Existing theme key, overwrite - settings_content.replace_range(existing_value_range, &new_val); - } - - (Some(first_key_start), None) => { - // No existing theme key, but other settings. Prepend new theme settings and - // match style of first key + if let Some(first_key_start) = first_key_start { let mut row = 0; let mut column = 0; for (ix, char) in settings_content.char_indices() { @@ -766,70 +786,118 @@ pub fn write_settings_key( } } - let content = format!(r#""{top_level_key}": {new_val},"#); - settings_content.insert_str(first_key_start, &content); - if row > 0 { + let new_val = to_pretty_json(&new_value, column, column); + let content = format!(r#""{new_key}": {new_val},"#); + settings_content.insert_str(first_key_start, &content); + settings_content.insert_str( first_key_start + content.len(), &format!("\n{:width$}", ' ', width = column), ) } else { - settings_content.insert_str(first_key_start + content.len(), " ") + let new_val = serde_json::to_string(&new_value).unwrap(); + let mut content = format!(r#""{new_key}": {new_val},"#); + content.push(' '); + settings_content.insert_str(first_key_start, &content); + } + } else { + new_value = serde_json::json!({ new_key.to_string(): new_value }); + let indent_prefix_len = 4 * depth; + let new_val = to_pretty_json(&new_value, 4, indent_prefix_len); + + settings_content.replace_range(existing_value_range, &new_val); + if depth == 0 { + settings_content.push('\n'); } } } - - settings_content } -fn merge(target: &mut T, value: Option) { - if let Some(value) = value { - *target = value; +fn to_pretty_json( + value: &serde_json::Value, + indent_size: usize, + indent_prefix_len: usize, +) -> String { + const SPACES: [u8; 32] = [b' '; 32]; + + debug_assert!(indent_size <= SPACES.len()); + debug_assert!(indent_prefix_len <= SPACES.len()); + + let mut output = Vec::new(); + let mut ser = serde_json::Serializer::with_formatter( + &mut output, + serde_json::ser::PrettyFormatter::with_indent(&SPACES[0..indent_size.min(SPACES.len())]), + ); + + value.serialize(&mut ser).unwrap(); + let text = String::from_utf8(output).unwrap(); + + let mut adjusted_text = String::new(); + for (i, line) in text.split('\n').enumerate() { + if i > 0 { + adjusted_text.push_str(str::from_utf8(&SPACES[0..indent_prefix_len]).unwrap()); + } + adjusted_text.push_str(line); + adjusted_text.push('\n'); } -} - -pub fn parse_json_with_comments(content: &str) -> Result { - Ok(serde_json::from_reader( - json_comments::CommentSettings::c_style().strip_comments(content.as_bytes()), - )?) + adjusted_text.pop(); + adjusted_text } pub fn update_settings_file( - old_text: String, + mut text: String, old_file_content: SettingsFileContent, update: impl FnOnce(&mut SettingsFileContent), ) -> String { let mut new_file_content = old_file_content.clone(); + update(&mut new_file_content); - let old_json = to_json_object(old_file_content); - let new_json = to_json_object(new_file_content); + let old_object = to_json_object(old_file_content); + let new_object = to_json_object(new_file_content); - // Find changed fields - let mut diffs = vec![]; - for (key, old_value) in old_json.iter() { - let new_value = new_json.get(key).unwrap(); - if old_value != new_value { - if matches!( - new_value, - &Value::Null | &Value::Object(_) | &Value::Array(_) - ) { - unimplemented!("We only support updating basic values at the top level"); + fn apply_changes_to_json_text( + old_object: &serde_json::Map, + new_object: &serde_json::Map, + current_key_path: Vec<&str>, + json_text: &mut String, + ) { + for (key, old_value) in old_object.iter() { + // We know that these two are from the same shape of object, so we can just unwrap + let new_value = new_object.get(key).unwrap(); + if old_value != new_value { + match new_value { + Value::Bool(_) | Value::Number(_) | Value::String(_) => { + let mut key_path = current_key_path.clone(); + key_path.push(key); + write_settings_key(json_text, &key_path, &new_value); + } + Value::Object(new_sub_object) => { + let mut key_path = current_key_path.clone(); + key_path.push(key); + if let Value::Object(old_sub_object) = old_value { + apply_changes_to_json_text( + old_sub_object, + new_sub_object, + key_path, + json_text, + ); + } else { + unimplemented!("This function doesn't support changing values from simple values to objects yet"); + } + } + Value::Null | Value::Array(_) => { + unimplemented!("We only support objects and simple values"); + } + } } - - let new_json = serde_json::to_string_pretty(new_value) - .expect("Could not serialize new json field to string"); - - diffs.push((key, new_json)); } } - let mut new_text = old_text; - for (key, new_value) in diffs { - new_text = write_settings_key(new_text, key, &new_value) - } - new_text + apply_changes_to_json_text(&old_object, &new_object, vec![], &mut text); + + text } fn to_json_object(settings_file: SettingsFileContent) -> serde_json::Map { @@ -851,7 +919,7 @@ mod tests { expected_new_json: S2, ) { let old_json = old_json.into(); - let old_content: SettingsFileContent = serde_json::from_str(&old_json).unwrap(); + let old_content: SettingsFileContent = serde_json::from_str(&old_json).unwrap_or_default(); let new_json = update_settings_file(old_json, old_content, update); assert_eq!(new_json, expected_new_json.into()); } @@ -859,23 +927,27 @@ mod tests { #[test] fn test_update_telemetry_setting_multiple_fields() { assert_new_settings( - r#"{ - "telemetry": { - "metrics": false, - "diagnostics": false + r#" + { + "telemetry": { + "metrics": false, + "diagnostics": false + } } - }"# + "# .unindent(), |settings| { settings.telemetry.set_diagnostics(true); settings.telemetry.set_metrics(true); }, - r#"{ - "telemetry": { - "metrics": true, - "diagnostics": true + r#" + { + "telemetry": { + "metrics": true, + "diagnostics": true + } } - }"# + "# .unindent(), ); } @@ -898,20 +970,45 @@ mod tests { #[test] fn test_update_telemetry_setting_other_fields() { assert_new_settings( - r#"{ - "telemetry": { - "metrics": false, - "diagnostics": true + r#" + { + "telemetry": { + "metrics": false, + "diagnostics": true + } } - }"# + "# .unindent(), |settings| settings.telemetry.set_diagnostics(false), - r#"{ - "telemetry": { - "metrics": false, - "diagnostics": false + r#" + { + "telemetry": { + "metrics": false, + "diagnostics": false + } } - }"# + "# + .unindent(), + ); + } + + #[test] + fn test_update_telemetry_setting_empty_telemetry() { + assert_new_settings( + r#" + { + "telemetry": {} + } + "# + .unindent(), + |settings| settings.telemetry.set_diagnostics(false), + r#" + { + "telemetry": { + "diagnostics": false + } + } + "# .unindent(), ); } @@ -919,18 +1016,22 @@ mod tests { #[test] fn test_update_telemetry_setting_pre_existing() { assert_new_settings( - r#"{ - "telemetry": { - "diagnostics": true + r#" + { + "telemetry": { + "diagnostics": true + } } - }"# + "# .unindent(), |settings| settings.telemetry.set_diagnostics(false), - r#"{ - "telemetry": { - "diagnostics": false + r#" + { + "telemetry": { + "diagnostics": false + } } - }"# + "# .unindent(), ); } @@ -940,111 +1041,119 @@ mod tests { assert_new_settings( "{}", |settings| settings.telemetry.set_diagnostics(true), - r#"{ - "telemetry": { - "diagnostics": true + r#" + { + "telemetry": { + "diagnostics": true + } } - }"# + "# + .unindent(), + ); + } + + #[test] + fn test_update_object_empty_doc() { + assert_new_settings( + "", + |settings| settings.telemetry.set_diagnostics(true), + r#" + { + "telemetry": { + "diagnostics": true + } + } + "# .unindent(), ); } #[test] fn test_write_theme_into_settings_with_theme() { - let settings = r#" - { - "theme": "One Dark" - } - "# - .unindent(); - - let new_settings = r#" - { - "theme": "summerfruit-light" - } - "# - .unindent(); - - let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); - - assert_eq!(settings_after_theme, new_settings) + assert_new_settings( + r#" + { + "theme": "One Dark" + } + "# + .unindent(), + |settings| settings.theme = Some("summerfruit-light".to_string()), + r#" + { + "theme": "summerfruit-light" + } + "# + .unindent(), + ); } #[test] fn test_write_theme_into_empty_settings() { - let settings = r#" - { - } - "# - .unindent(); - - let new_settings = r#" - { - "theme": "summerfruit-light" - } - "# - .unindent(); - - let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); - - assert_eq!(settings_after_theme, new_settings) + assert_new_settings( + r#" + { + } + "# + .unindent(), + |settings| settings.theme = Some("summerfruit-light".to_string()), + r#" + { + "theme": "summerfruit-light" + } + "# + .unindent(), + ); } #[test] - fn test_write_theme_into_no_settings() { - let settings = "".to_string(); - - let new_settings = r#" - { - "theme": "summerfruit-light" - } - "# - .unindent(); - - let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); - - assert_eq!(settings_after_theme, new_settings) + fn write_key_no_document() { + assert_new_settings( + "", + |settings| settings.theme = Some("summerfruit-light".to_string()), + r#" + { + "theme": "summerfruit-light" + } + "# + .unindent(), + ); } #[test] fn test_write_theme_into_single_line_settings_without_theme() { - let settings = r#"{ "a": "", "ok": true }"#.to_string(); - let new_settings = r#"{ "theme": "summerfruit-light", "a": "", "ok": true }"#; - - let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); - - assert_eq!(settings_after_theme, new_settings) + assert_new_settings( + r#"{ "a": "", "ok": true }"#, + |settings| settings.theme = Some("summerfruit-light".to_string()), + r#"{ "theme": "summerfruit-light", "a": "", "ok": true }"#, + ); } #[test] fn test_write_theme_pre_object_whitespace() { - let settings = r#" { "a": "", "ok": true }"#.to_string(); - let new_settings = r#" { "theme": "summerfruit-light", "a": "", "ok": true }"#; - - let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); - - assert_eq!(settings_after_theme, new_settings) + assert_new_settings( + r#" { "a": "", "ok": true }"#, + |settings| settings.theme = Some("summerfruit-light".to_string()), + r#" { "theme": "summerfruit-light", "a": "", "ok": true }"#.unindent(), + ); } #[test] fn test_write_theme_into_multi_line_settings_without_theme() { - let settings = r#" - { - "a": "b" - } - "# - .unindent(); - - let new_settings = r#" - { - "theme": "summerfruit-light", - "a": "b" - } - "# - .unindent(); - - let settings_after_theme = write_settings_key(settings, "theme", "\"summerfruit-light\""); - - assert_eq!(settings_after_theme, new_settings) + assert_new_settings( + r#" + { + "a": "b" + } + "# + .unindent(), + |settings| settings.theme = Some("summerfruit-light".to_string()), + r#" + { + "theme": "summerfruit-light", + "a": "b" + } + "# + .unindent(), + ); } } From 118435a348e76ebcb43b3b0f8c6a04c5b3b66256 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 24 Feb 2023 17:04:31 -0800 Subject: [PATCH 07/93] Added basic styling for checkboxes, yay Co-authored-by: Max --- crates/theme/src/theme.rs | 3 ++- crates/welcome/src/welcome.rs | 14 ++++++++++---- styles/src/styleTree/welcome.ts | 33 +++++++++++++++++++++++++++------ 3 files changed, 39 insertions(+), 11 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 49f2d982ba..87b9d9845a 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -860,9 +860,10 @@ pub struct WelcomeStyle { pub struct CheckboxStyle { pub width: f32, pub height: f32, - pub unchecked: ContainerStyle, + pub default: ContainerStyle, pub checked: ContainerStyle, pub hovered: ContainerStyle, + pub hovered_and_checked: ContainerStyle, } #[derive(Clone, Deserialize, Default)] diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 385a8a5f00..6985b70069 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -126,11 +126,17 @@ impl WelcomePage { .with_height(style.height) .contained() .with_style(if checked { - style.checked - } else if state.hovered() { - style.hovered + if state.hovered() { + style.hovered_and_checked + } else { + style.checked + } } else { - style.unchecked + if state.hovered() { + style.hovered + } else { + style.default + } }) .boxed() }) diff --git a/styles/src/styleTree/welcome.ts b/styles/src/styleTree/welcome.ts index f1325514cd..6085782afd 100644 --- a/styles/src/styleTree/welcome.ts +++ b/styles/src/styleTree/welcome.ts @@ -6,9 +6,8 @@ export default function welcome(colorScheme: ColorScheme) { let layer = colorScheme.highest; // TODO - let checkbox_base = { - background: colorScheme.ramps.red(0.5).hex(), - cornerRadius: 8, + let checkboxBase = { + cornerRadius: 4, padding: { left: 8, right: 8, @@ -26,9 +25,31 @@ export default function welcome(colorScheme: ColorScheme) { checkbox: { width: 9, height: 9, - unchecked: checkbox_base, - checked: checkbox_base, - hovered: checkbox_base + default: { + ...checkboxBase, + background: colorScheme.ramps.blue(0.5).hex(), + }, + checked: { + ...checkboxBase, + background: colorScheme.ramps.red(0.5).hex(), + }, + hovered: { + ...checkboxBase, + background: colorScheme.ramps.blue(0.5).hex(), + + border: { + color: colorScheme.ramps.green(0.5).hex(), + width: 1, + } + }, + hoveredAndChecked: { + ...checkboxBase, + background: colorScheme.ramps.red(0.5).hex(), + border: { + color: colorScheme.ramps.green(0.5).hex(), + width: 1, + } + } } } } \ No newline at end of file From 7d7053b990419edc1772c8a370bb80976455dee0 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 28 Feb 2023 19:20:21 -0800 Subject: [PATCH 08/93] Move to using stateless --- Cargo.lock | 1 + assets/settings/default.json | 2 +- crates/db/src/db.rs | 3 ++- crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 14 +++++++++++--- crates/zed/src/zed.rs | 25 ++++++++++++++++++++++++- 6 files changed, 40 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 90da178c7b..c8124fcc3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8395,6 +8395,7 @@ dependencies = [ "command_palette", "context_menu", "ctor", + "db", "diagnostics", "easy-parallel", "editor", diff --git a/assets/settings/default.json b/assets/settings/default.json index 2a5e05b401..90c47478f3 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -50,7 +50,7 @@ // "default_dock_anchor": "right" // 3. Position the dock full screen over the entire workspace" // "default_dock_anchor": "expanded" - "default_dock_anchor": "right", + "default_dock_anchor": "bottom", // Whether or not to remove any trailing whitespace from lines of a buffer // before saving it. "remove_trailing_whitespace_on_save": true, diff --git a/crates/db/src/db.rs b/crates/db/src/db.rs index 20f2300d89..f4d0dc1a46 100644 --- a/crates/db/src/db.rs +++ b/crates/db/src/db.rs @@ -39,7 +39,8 @@ const FALLBACK_DB_NAME: &'static str = "FALLBACK_MEMORY_DB"; const DB_FILE_NAME: &'static str = "db.sqlite"; lazy_static::lazy_static! { - static ref ZED_STATELESS: bool = std::env::var("ZED_STATELESS").map_or(false, |v| !v.is_empty()); + // !!!!!!! CHANGE BACK TO DEFAULT FALSE BEFORE SHIPPING + static ref ZED_STATELESS: bool = std::env::var("ZED_STATELESS").map_or(true, |v| !v.is_empty()); static ref DB_FILE_OPERATIONS: Mutex<()> = Mutex::new(()); pub static ref BACKUP_DB_PATH: RwLock> = RwLock::new(None); pub static ref ALL_FILE_DB_FAILED: AtomicBool = AtomicBool::new(false); diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index d3b6e4810f..68b04c7e2f 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -29,6 +29,7 @@ context_menu = { path = "../context_menu" } client = { path = "../client" } clock = { path = "../clock" } diagnostics = { path = "../diagnostics" } +db = { path = "../db" } editor = { path = "../editor" } feedback = { path = "../feedback" } file_finder = { path = "../file_finder" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 298e8ef8b9..a9625bf78e 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -13,6 +13,7 @@ use client::{ http::{self, HttpClient}, UserStore, ZED_APP_VERSION, ZED_SECRET_CLIENT_TOKEN, }; +use db::kvp::KEY_VALUE_STORE; use futures::{ channel::{mpsc, oneshot}, FutureExt, SinkExt, StreamExt, @@ -43,9 +44,12 @@ use theme::ThemeRegistry; use util::StaffMode; use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt}; use workspace::{ - self, item::ItemHandle, notifications::NotifyResultExt, AppState, OpenPaths, Welcome, Workspace, + self, item::ItemHandle, notifications::NotifyResultExt, AppState, NewFile, OpenPaths, Workspace, +}; +use zed::{ + self, build_window_options, initialize_workspace, languages, menus, WelcomeExperience, + FIRST_OPEN, }; -use zed::{self, build_window_options, initialize_workspace, languages, menus}; fn main() { let http = http::client(); @@ -258,9 +262,13 @@ async fn restore_or_create_workspace(mut cx: AsyncAppContext) { paths: location.paths().as_ref().clone(), }) }); + } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { + cx.update(|cx| { + cx.dispatch_global_action(WelcomeExperience); + }); } else { cx.update(|cx| { - cx.dispatch_global_action(Welcome); + cx.dispatch_global_action(NewFile); }); } } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 2fbac3613e..75cdf0e687 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -8,6 +8,7 @@ use breadcrumbs::Breadcrumbs; pub use client; use collab_ui::{CollabTitlebarItem, ToggleContactsMenu}; use collections::VecDeque; +use db::kvp::KEY_VALUE_STORE; pub use editor; use editor::{Editor, MultiBuffer}; @@ -34,7 +35,9 @@ use std::{borrow::Cow, env, path::Path, str, sync::Arc}; use util::{channel::ReleaseChannel, paths, ResultExt, StaffMode}; use uuid::Uuid; pub use workspace; -use workspace::{sidebar::SidebarSide, AppState, Restart, Workspace}; +use workspace::{sidebar::SidebarSide, AppState, Restart, Welcome, Workspace}; + +pub const FIRST_OPEN: &str = "first_open"; #[derive(Deserialize, Clone, PartialEq)] pub struct OpenBrowser { @@ -67,6 +70,7 @@ actions!( ResetBufferFontSize, InstallCommandLineInterface, ResetDatabase, + WelcomeExperience ] ); @@ -252,6 +256,25 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }, ); + cx.add_global_action(|_: &WelcomeExperience, cx| { + if !matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { + return; //noop, in case someone fires this from the command palette + } + + // Make a workspace, set it up with an open bottom dock and the welcome page + + cx.dispatch_global_action(Welcome); + + cx.background() + .spawn(async move { + KEY_VALUE_STORE + .write_kvp(FIRST_OPEN.to_string(), "false".to_string()) + .await + .log_err(); + }) + .detach(); + }); + activity_indicator::init(cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); settings::KeymapFileContent::load_defaults(cx); From 5210be95feeb75ae801d90cbf354e2dd8c95ddd9 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 28 Feb 2023 22:11:58 -0800 Subject: [PATCH 09/93] Added welcome experience sketch Made toolbar hideable --- crates/db/src/db.rs | 11 +++++++++ crates/welcome/src/welcome.rs | 8 +++++-- crates/workspace/src/dock.rs | 40 +++++++++++++++++++------------ crates/workspace/src/item.rs | 8 +++++++ crates/workspace/src/pane.rs | 7 +++--- crates/workspace/src/toolbar.rs | 12 ++++++++++ crates/workspace/src/workspace.rs | 29 +++++++++++----------- crates/zed/src/zed.rs | 37 +++++++++++++++------------- styles/src/styleTree/workspace.ts | 2 +- 9 files changed, 103 insertions(+), 51 deletions(-) diff --git a/crates/db/src/db.rs b/crates/db/src/db.rs index f4d0dc1a46..989dcf0af5 100644 --- a/crates/db/src/db.rs +++ b/crates/db/src/db.rs @@ -4,6 +4,7 @@ pub mod query; // Re-export pub use anyhow; use anyhow::Context; +use gpui::MutableAppContext; pub use indoc::indoc; pub use lazy_static; use parking_lot::{Mutex, RwLock}; @@ -17,6 +18,7 @@ use sqlez::domain::Migrator; use sqlez::thread_safe_connection::ThreadSafeConnection; use sqlez_macros::sql; use std::fs::create_dir_all; +use std::future::Future; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; @@ -237,6 +239,15 @@ macro_rules! define_connection { }; } +pub fn write_and_log(cx: &mut MutableAppContext, db_write: impl FnOnce() -> F + Send + 'static) +where + F: Future> + Send, +{ + cx.background() + .spawn(async move { db_write().await.log_err() }) + .detach() +} + #[cfg(test)] mod tests { use std::{fs, thread}; diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 6985b70069..5a166218ce 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -14,7 +14,7 @@ pub fn init(cx: &mut MutableAppContext) { }) } -struct WelcomePage { +pub struct WelcomePage { _settings_subscription: Subscription, } @@ -98,7 +98,7 @@ impl View for WelcomePage { } impl WelcomePage { - fn new(cx: &mut ViewContext) -> Self { + pub fn new(cx: &mut ViewContext) -> Self { let handle = cx.weak_handle(); let settings_subscription = cx.observe_global::(move |cx| { @@ -163,4 +163,8 @@ impl Item for WelcomePage { ) .boxed() } + + fn show_toolbar(&self) -> bool { + false + } } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 2bd8808281..4281c04649 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -39,20 +39,24 @@ impl_internal_actions!(dock, [MoveDock, AddDefaultItemToDock]); pub fn init(cx: &mut MutableAppContext) { cx.add_action(Dock::focus_dock); cx.add_action(Dock::hide_dock); - cx.add_action(Dock::move_dock); + cx.add_action( + |workspace: &mut Workspace, &MoveDock(dock_anchor), cx: &mut ViewContext| { + Dock::move_dock(workspace, dock_anchor, true, cx); + }, + ); cx.add_action( |workspace: &mut Workspace, _: &AnchorDockRight, cx: &mut ViewContext| { - Dock::move_dock(workspace, &MoveDock(DockAnchor::Right), cx) + Dock::move_dock(workspace, DockAnchor::Right, true, cx); }, ); cx.add_action( |workspace: &mut Workspace, _: &AnchorDockBottom, cx: &mut ViewContext| { - Dock::move_dock(workspace, &MoveDock(DockAnchor::Bottom), cx) + Dock::move_dock(workspace, DockAnchor::Bottom, true, cx) }, ); cx.add_action( |workspace: &mut Workspace, _: &ExpandDock, cx: &mut ViewContext| { - Dock::move_dock(workspace, &MoveDock(DockAnchor::Expanded), cx) + Dock::move_dock(workspace, DockAnchor::Expanded, true, cx) }, ); cx.add_action( @@ -215,6 +219,7 @@ impl Dock { pub(crate) fn set_dock_position( workspace: &mut Workspace, new_position: DockPosition, + focus: bool, cx: &mut ViewContext, ) { workspace.dock.position = new_position; @@ -235,19 +240,23 @@ impl Dock { let pane = workspace.dock.pane.clone(); if pane.read(cx).items().next().is_none() { if let Some(item_to_add) = (workspace.dock.default_item_factory)(workspace, cx) { - Pane::add_item(workspace, &pane, item_to_add, true, true, None, cx); + Pane::add_item(workspace, &pane, item_to_add, focus, focus, None, cx); } else { workspace.dock.position = workspace.dock.position.hide(); } } else { - cx.focus(pane); + if focus { + cx.focus(pane); + } } } else if let Some(last_active_center_pane) = workspace .last_active_center_pane .as_ref() .and_then(|pane| pane.upgrade(cx)) { - cx.focus(last_active_center_pane); + if focus { + cx.focus(last_active_center_pane); + } } cx.emit(crate::Event::DockAnchorChanged); workspace.serialize_workspace(cx); @@ -255,11 +264,11 @@ impl Dock { } pub fn hide(workspace: &mut Workspace, cx: &mut ViewContext) { - Self::set_dock_position(workspace, workspace.dock.position.hide(), cx); + Self::set_dock_position(workspace, workspace.dock.position.hide(), true, cx); } - pub fn show(workspace: &mut Workspace, cx: &mut ViewContext) { - Self::set_dock_position(workspace, workspace.dock.position.show(), cx); + pub fn show(workspace: &mut Workspace, focus: bool, cx: &mut ViewContext) { + Self::set_dock_position(workspace, workspace.dock.position.show(), focus, cx); } pub fn hide_on_sidebar_shown( @@ -275,19 +284,20 @@ impl Dock { } fn focus_dock(workspace: &mut Workspace, _: &FocusDock, cx: &mut ViewContext) { - Self::set_dock_position(workspace, workspace.dock.position.show(), cx); + Self::set_dock_position(workspace, workspace.dock.position.show(), true, cx); } fn hide_dock(workspace: &mut Workspace, _: &HideDock, cx: &mut ViewContext) { - Self::set_dock_position(workspace, workspace.dock.position.hide(), cx); + Self::set_dock_position(workspace, workspace.dock.position.hide(), true, cx); } - fn move_dock( + pub fn move_dock( workspace: &mut Workspace, - &MoveDock(new_anchor): &MoveDock, + new_anchor: DockAnchor, + focus: bool, cx: &mut ViewContext, ) { - Self::set_dock_position(workspace, DockPosition::Shown(new_anchor), cx); + Self::set_dock_position(workspace, DockPosition::Shown(new_anchor), focus, cx); } pub fn render( diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index a80b9f8d83..b55c9942f8 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -151,6 +151,9 @@ pub trait Item: View { "deserialize() must be implemented if serialized_item_kind() returns Some(_)" ) } + fn show_toolbar(&self) -> bool { + true + } } pub trait ItemHandle: 'static + fmt::Debug { @@ -213,6 +216,7 @@ pub trait ItemHandle: 'static + fmt::Debug { fn breadcrumb_location(&self, cx: &AppContext) -> ToolbarItemLocation; fn breadcrumbs(&self, theme: &Theme, cx: &AppContext) -> Option>; fn serialized_item_kind(&self) -> Option<&'static str>; + fn show_toolbar(&self, cx: &AppContext) -> bool; } pub trait WeakItemHandle { @@ -591,6 +595,10 @@ impl ItemHandle for ViewHandle { fn serialized_item_kind(&self) -> Option<&'static str> { T::serialized_item_kind() } + + fn show_toolbar(&self, cx: &AppContext) -> bool { + self.read(cx).show_toolbar() + } } impl From> for AnyViewHandle { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 98fcac664c..235df0202d 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1485,11 +1485,12 @@ impl View for Pane { cx, { let toolbar = self.toolbar.clone(); + let toolbar_hidden = toolbar.read(cx).hidden(); move |_, cx| { Flex::column() - .with_child( - ChildView::new(&toolbar, cx).expanded().boxed(), - ) + .with_children((!toolbar_hidden).then(|| { + ChildView::new(&toolbar, cx).expanded().boxed() + })) .with_child( ChildView::new(active_item, cx) .flex(1., true) diff --git a/crates/workspace/src/toolbar.rs b/crates/workspace/src/toolbar.rs index 7443f19003..df10db91a0 100644 --- a/crates/workspace/src/toolbar.rs +++ b/crates/workspace/src/toolbar.rs @@ -42,6 +42,7 @@ pub enum ToolbarItemLocation { pub struct Toolbar { active_pane_item: Option>, + hidden: bool, pane: WeakViewHandle, items: Vec<(Box, ToolbarItemLocation)>, } @@ -211,6 +212,7 @@ impl Toolbar { active_pane_item: None, pane, items: Default::default(), + hidden: false, } } @@ -243,6 +245,12 @@ impl Toolbar { cx: &mut ViewContext, ) { self.active_pane_item = pane_item.map(|item| item.boxed_clone()); + self.hidden = self + .active_pane_item + .as_ref() + .map(|item| !item.show_toolbar(cx)) + .unwrap_or(false); + for (toolbar_item, current_location) in self.items.iter_mut() { let new_location = toolbar_item.set_active_pane_item(pane_item, cx); if new_location != *current_location { @@ -257,6 +265,10 @@ impl Toolbar { .iter() .find_map(|(item, _)| item.to_any().downcast()) } + + pub fn hidden(&self) -> bool { + self.hidden + } } impl ToolbarItemViewHandle for ViewHandle { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 31960fbc1e..442d28579f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -197,20 +197,12 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { } } }); - cx.add_global_action({ - let app_state = Arc::downgrade(&app_state); - move |_: &Welcome, cx: &mut MutableAppContext| { - if let Some(app_state) = app_state.upgrade() { - open_new(&app_state, cx).detach(); - } - } - }); cx.add_global_action({ let app_state = Arc::downgrade(&app_state); move |_: &NewWindow, cx: &mut MutableAppContext| { if let Some(app_state) = app_state.upgrade() { - open_new(&app_state, cx).detach(); + open_new(&app_state, cx, |_, cx| cx.dispatch_action(NewFile)).detach(); } } }); @@ -1514,7 +1506,7 @@ impl Workspace { self.active_item_path_changed(cx); if &pane == self.dock_pane() { - Dock::show(self, cx); + Dock::show(self, true, cx); } else { self.last_active_center_pane = Some(pane.downgrade()); if self.dock.is_anchored_at(DockAnchor::Expanded) { @@ -2527,7 +2519,12 @@ impl Workspace { // the focus the dock generates start generating alternating // focus due to the deferred execution each triggering each other cx.after_window_update(move |workspace, cx| { - Dock::set_dock_position(workspace, serialized_workspace.dock_position, cx); + Dock::set_dock_position( + workspace, + serialized_workspace.dock_position, + true, + cx, + ); }); cx.notify(); @@ -2859,14 +2856,18 @@ pub fn open_paths( }) } -pub fn open_new(app_state: &Arc, cx: &mut MutableAppContext) -> Task<()> { +pub fn open_new( + app_state: &Arc, + cx: &mut MutableAppContext, + init: impl FnOnce(&mut Workspace, &mut ViewContext) + 'static, +) -> Task<()> { let task = Workspace::new_local(Vec::new(), app_state.clone(), cx); cx.spawn(|mut cx| async move { let (workspace, opened_paths) = task.await; - workspace.update(&mut cx, |_, cx| { + workspace.update(&mut cx, |workspace, cx| { if opened_paths.is_empty() { - cx.dispatch_action(Welcome); + init(workspace, cx) } }) }) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 75cdf0e687..5d13d41bba 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -35,7 +35,7 @@ use std::{borrow::Cow, env, path::Path, str, sync::Arc}; use util::{channel::ReleaseChannel, paths, ResultExt, StaffMode}; use uuid::Uuid; pub use workspace; -use workspace::{sidebar::SidebarSide, AppState, Restart, Welcome, Workspace}; +use workspace::{dock::Dock, open_new, sidebar::SidebarSide, AppState, Restart, Workspace}; pub const FIRST_OPEN: &str = "first_open"; @@ -256,23 +256,27 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }, ); - cx.add_global_action(|_: &WelcomeExperience, cx| { - if !matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { - return; //noop, in case someone fires this from the command palette - } + cx.add_global_action({ + let app_state = app_state.clone(); + move |_: &WelcomeExperience, cx| { + if !matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { + return; //noop, in case someone fires this from the command palette + } - // Make a workspace, set it up with an open bottom dock and the welcome page - - cx.dispatch_global_action(Welcome); - - cx.background() - .spawn(async move { - KEY_VALUE_STORE - .write_kvp(FIRST_OPEN.to_string(), "false".to_string()) - .await - .log_err(); + open_new(&app_state, cx, |workspace, cx| { + workspace.toggle_sidebar(SidebarSide::Left, cx); + let welcome_page = cx.add_view(|cx| welcome::WelcomePage::new(cx)); + workspace.add_item(Box::new(welcome_page.clone()), cx); + Dock::move_dock(workspace, settings::DockAnchor::Bottom, false, cx); + cx.focus(welcome_page); + cx.notify(); }) .detach(); + + db::write_and_log(cx, || { + KEY_VALUE_STORE.write_kvp(FIRST_OPEN.to_string(), "false".to_string()) + }); + } }); activity_indicator::init(cx); @@ -881,7 +885,8 @@ mod tests { #[gpui::test] async fn test_new_empty_workspace(cx: &mut TestAppContext) { let app_state = init(cx); - cx.update(|cx| open_new(&app_state, cx)).await; + cx.update(|cx| open_new(&app_state, cx, |_, cx| cx.dispatch_action(NewFile))) + .await; let window_id = *cx.window_ids().first().unwrap(); let workspace = cx.root_view::(window_id).unwrap(); diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index f9f49e3c7d..c758e0227e 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -248,7 +248,7 @@ export default function workspace(colorScheme: ColorScheme) { }, dock: { initialSizeRight: 640, - initialSizeBottom: 480, + initialSizeBottom: 300, wash_color: withOpacity(background(colorScheme.highest), 0.5), panel: { border: border(colorScheme.middle), From 62aeb6b8b390ffe5c547c544726f5813778854e7 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 28 Feb 2023 22:33:12 -0800 Subject: [PATCH 10/93] Added background to welcome page --- crates/welcome/src/welcome.rs | 115 ++++++++++++++++++++-------------- 1 file changed, 68 insertions(+), 47 deletions(-) diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 5a166218ce..afb38385a4 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,6 +1,7 @@ use gpui::{ color::Color, - elements::{Empty, Flex, Label, MouseEventHandler, ParentElement, Svg}, + elements::{Canvas, Empty, Flex, Label, MouseEventHandler, ParentElement, Stack, Svg}, + geometry::rect::RectF, Element, ElementBox, Entity, MutableAppContext, RenderContext, Subscription, View, ViewContext, }; use settings::{settings_file::SettingsFile, Settings, SettingsFileContent}; @@ -39,60 +40,80 @@ impl View for WelcomePage { enum Metrics {} enum Diagnostics {} - Flex::column() - .with_children([ - Flex::row() - .with_children([ - Svg::new("icons/terminal_16.svg") - .with_color(Color::red()) - .constrained() - .with_width(100.) - .with_height(100.) - .aligned() - .contained() - .boxed(), - Label::new("Zed", theme.editor.hover_popover.prose.clone()).boxed(), - ]) - .boxed(), - Label::new( - "Code at the speed of thought", - theme.editor.hover_popover.prose.clone(), - ) + let background = theme.editor.background; + + Stack::new() + .with_child( + Canvas::new(move |bounds, visible_bounds, cx| { + let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); + + cx.paint_layer(Some(visible_bounds), |cx| { + cx.scene.push_quad(gpui::Quad { + bounds: RectF::new(bounds.origin(), bounds.size()), + background: Some(background), + ..Default::default() + }) + }) + }) .boxed(), - Flex::row() + ) + .with_child( + Flex::column() .with_children([ - self.render_settings_checkbox::( - &theme.welcome.checkbox, - metrics, - cx, - |content, checked| { - content.telemetry.set_metrics(checked); - }, - ), + Flex::row() + .with_children([ + Svg::new("icons/terminal_16.svg") + .with_color(Color::red()) + .constrained() + .with_width(100.) + .with_height(100.) + .aligned() + .contained() + .boxed(), + Label::new("Zed", theme.editor.hover_popover.prose.clone()).boxed(), + ]) + .boxed(), Label::new( - "Do you want to send telemetry?", + "Code at the speed of thought", theme.editor.hover_popover.prose.clone(), ) .boxed(), + Flex::row() + .with_children([ + self.render_settings_checkbox::( + &theme.welcome.checkbox, + metrics, + cx, + |content, checked| { + content.telemetry.set_metrics(checked); + }, + ), + Label::new( + "Do you want to send telemetry?", + theme.editor.hover_popover.prose.clone(), + ) + .boxed(), + ]) + .boxed(), + Flex::row() + .with_children([ + self.render_settings_checkbox::( + &theme.welcome.checkbox, + diagnostics, + cx, + |content, checked| content.telemetry.set_diagnostics(checked), + ), + Label::new( + "Send crash reports", + theme.editor.hover_popover.prose.clone(), + ) + .boxed(), + ]) + .boxed(), ]) + .aligned() .boxed(), - Flex::row() - .with_children([ - self.render_settings_checkbox::( - &theme.welcome.checkbox, - diagnostics, - cx, - |content, checked| content.telemetry.set_diagnostics(checked), - ), - Label::new( - "Send crash reports", - theme.editor.hover_popover.prose.clone(), - ) - .boxed(), - ]) - .boxed(), - ]) - .aligned() + ) .boxed() } } From 9dee2ca2be3606f994cba67f1a097a454268bf4b Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 1 Mar 2023 20:42:01 -0800 Subject: [PATCH 11/93] WIP --- crates/project_panel/src/project_panel.rs | 182 ++++++++++++++----- crates/terminal_view/src/terminal_element.rs | 2 +- crates/welcome/src/welcome.rs | 12 +- 3 files changed, 143 insertions(+), 53 deletions(-) diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 4b3c5b7bc5..1cefc558f0 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -5,9 +5,11 @@ use futures::stream::StreamExt; use gpui::{ actions, anyhow::{anyhow, Result}, + color::Color, elements::{ - AnchorCorner, ChildView, ConstrainedBox, ContainerStyle, Empty, Flex, Label, - MouseEventHandler, ParentElement, ScrollTarget, Stack, Svg, UniformList, UniformListState, + AnchorCorner, Canvas, ChildView, ConstrainedBox, ContainerStyle, Empty, Flex, + KeystrokeLabel, Label, MouseEventHandler, ParentElement, ScrollTarget, Stack, Svg, + UniformList, UniformListState, }, geometry::vector::Vector2F, impl_internal_actions, @@ -1262,54 +1264,134 @@ impl View for ProjectPanel { let padding = std::mem::take(&mut container_style.padding); let last_worktree_root_id = self.last_worktree_root_id; - Stack::new() - .with_child( - MouseEventHandler::::new(0, cx, |_, cx| { - UniformList::new( - self.list.clone(), - self.visible_entries - .iter() - .map(|(_, worktree_entries)| worktree_entries.len()) - .sum(), - cx, - move |this, range, items, cx| { - let theme = cx.global::().theme.clone(); - let mut dragged_entry_destination = - this.dragged_entry_destination.clone(); - this.for_each_visible_entry(range, cx, |id, details, cx| { - items.push(Self::render_entry( - id, - details, - &this.filename_editor, - &mut dragged_entry_destination, - &theme.project_panel, - cx, - )); - }); - this.dragged_entry_destination = dragged_entry_destination; - }, - ) - .with_padding_top(padding.top) - .with_padding_bottom(padding.bottom) - .contained() - .with_style(container_style) - .expanded() - .boxed() - }) - .on_down(MouseButton::Right, move |e, cx| { - // When deploying the context menu anywhere below the last project entry, - // act as if the user clicked the root of the last worktree. - if let Some(entry_id) = last_worktree_root_id { - cx.dispatch_action(DeployContextMenu { - entry_id, - position: e.position, - }) - } - }) - .boxed(), - ) - .with_child(ChildView::new(&self.context_menu, cx).boxed()) - .boxed() + let has_worktree = self.visible_entries.len() != 0; + + if has_worktree { + Stack::new() + .with_child( + MouseEventHandler::::new(0, cx, |_, cx| { + UniformList::new( + self.list.clone(), + self.visible_entries + .iter() + .map(|(_, worktree_entries)| worktree_entries.len()) + .sum(), + cx, + move |this, range, items, cx| { + let theme = cx.global::().theme.clone(); + let mut dragged_entry_destination = + this.dragged_entry_destination.clone(); + this.for_each_visible_entry(range, cx, |id, details, cx| { + items.push(Self::render_entry( + id, + details, + &this.filename_editor, + &mut dragged_entry_destination, + &theme.project_panel, + cx, + )); + }); + this.dragged_entry_destination = dragged_entry_destination; + }, + ) + .with_padding_top(padding.top) + .with_padding_bottom(padding.bottom) + .contained() + .with_style(container_style) + .expanded() + .boxed() + }) + .on_down(MouseButton::Right, move |e, cx| { + // When deploying the context menu anywhere below the last project entry, + // act as if the user clicked the root of the last worktree. + if let Some(entry_id) = last_worktree_root_id { + cx.dispatch_action(DeployContextMenu { + entry_id, + position: e.position, + }) + } + }) + .boxed(), + ) + .with_child(ChildView::new(&self.context_menu, cx).boxed()) + .boxed() + } else { + let parent_view_id = cx.handle().id(); + Stack::new() + .with_child( + MouseEventHandler::::new(1, cx, |_, cx| { + Stack::new() + .with_child( + Canvas::new(|bounds, _visible_bounds, cx| { + cx.scene.push_quad(gpui::Quad { + bounds, + background: Some(Color::red()), + ..Default::default() + }) + }) + .boxed(), + ) + .with_child( + MouseEventHandler::::new(2, cx, |state, cx| { + let style = &cx + .global::() + .theme + .search + .option_button + .style_for(state, false); + + let context_menu_item = cx + .global::() + .theme + .context_menu + .clone() + .item + .style_for(state, true) + .clone(); + + Flex::row() + .with_child( + Label::new( + "Open a new project!".to_string(), + context_menu_item.label.clone(), + ) + .contained() + .boxed(), + ) + .with_child({ + KeystrokeLabel::new( + cx.window_id(), + parent_view_id, + Box::new(workspace::Open), + context_menu_item.keystroke.container, + context_menu_item.keystroke.text.clone(), + ) + .flex_float() + .boxed() + }) + .contained() + .with_style(style.container) + .aligned() + .top() + .constrained() + .with_width(100.) + .with_height(20.) + .boxed() + }) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(workspace::Open) + }) + .with_cursor_style(CursorStyle::PointingHand) + .boxed(), + ) + .boxed() + }) + // TODO is this nescessary? + .on_click(MouseButton::Left, |_, cx| cx.focus_parent_view()) + .boxed(), + ) + .boxed() + } } fn keymap_context(&self, _: &AppContext) -> KeymapContext { diff --git a/crates/terminal_view/src/terminal_element.rs b/crates/terminal_view/src/terminal_element.rs index 08ed3ecc2d..5d03d6304e 100644 --- a/crates/terminal_view/src/terminal_element.rs +++ b/crates/terminal_view/src/terminal_element.rs @@ -720,7 +720,7 @@ impl Element for TerminalElement { cx.paint_layer(clip_bounds, |cx| { let origin = bounds.origin() + vec2f(layout.size.cell_width, 0.); - //Elements are ephemeral, only at paint time do we know what could be clicked by a mouse + // Elements are ephemeral, only at paint time do we know what could be clicked by a mouse self.attach_mouse_handlers(origin, self.view.id(), visible_bounds, layout.mode, cx); cx.scene.push_cursor_region(gpui::CursorRegion { diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index afb38385a4..54898f92c8 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -2,7 +2,8 @@ use gpui::{ color::Color, elements::{Canvas, Empty, Flex, Label, MouseEventHandler, ParentElement, Stack, Svg}, geometry::rect::RectF, - Element, ElementBox, Entity, MutableAppContext, RenderContext, Subscription, View, ViewContext, + Element, ElementBox, Entity, MouseRegion, MutableAppContext, RenderContext, Subscription, View, + ViewContext, }; use settings::{settings_file::SettingsFile, Settings, SettingsFileContent}; use theme::CheckboxStyle; @@ -29,6 +30,7 @@ impl View for WelcomePage { } fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> ElementBox { + let self_handle = cx.handle(); let settings = cx.global::(); let theme = settings.theme.clone(); @@ -44,6 +46,7 @@ impl View for WelcomePage { Stack::new() .with_child( + // TODO: Can this be moved into the pane? Canvas::new(move |bounds, visible_bounds, cx| { let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); @@ -53,7 +56,12 @@ impl View for WelcomePage { background: Some(background), ..Default::default() }) - }) + }); + + cx.scene.push_mouse_region( + MouseRegion::new::(self_handle.id(), 0, visible_bounds) + .on_down(gpui::MouseButton::Left, |_, cx| cx.focus_parent_view()), + ); }) .boxed(), ) From f89f33347de1289c0d78cf9cb1b68fe5b1199765 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 6 Mar 2023 09:58:07 -0800 Subject: [PATCH 12/93] Added CTA buttons to welcome experience Co-authored-by: Nathan --- Cargo.lock | 1 + crates/theme/src/theme.rs | 1 + crates/welcome/Cargo.toml | 1 + crates/welcome/src/welcome.rs | 36 ++++++++++- styles/src/styleTree/welcome.ts | 111 +++++++++++++++++++------------- 5 files changed, 102 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8124fcc3a..c25656bfa9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8024,6 +8024,7 @@ dependencies = [ "project", "settings", "theme", + "theme_selector", "util", "workspace", ] diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 87b9d9845a..ded5c54f83 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -854,6 +854,7 @@ pub struct FeedbackStyle { #[derive(Clone, Deserialize, Default)] pub struct WelcomeStyle { pub checkbox: CheckboxStyle, + pub button: Interactive, } #[derive(Clone, Deserialize, Default)] diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index 3e450e2312..e1231ad5f6 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -18,5 +18,6 @@ gpui = { path = "../gpui" } project = { path = "../project" } settings = { path = "../settings" } theme = { path = "../theme" } +theme_selector = { path = "../theme_selector" } util = { path = "../util" } workspace = { path = "../workspace" } \ No newline at end of file diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 54898f92c8..7bf6c26302 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,12 +1,14 @@ +use std::borrow::Cow; + use gpui::{ color::Color, elements::{Canvas, Empty, Flex, Label, MouseEventHandler, ParentElement, Stack, Svg}, geometry::rect::RectF, - Element, ElementBox, Entity, MouseRegion, MutableAppContext, RenderContext, Subscription, View, - ViewContext, + Action, Element, ElementBox, Entity, MouseButton, MouseRegion, MutableAppContext, + RenderContext, Subscription, View, ViewContext, }; use settings::{settings_file::SettingsFile, Settings, SettingsFileContent}; -use theme::CheckboxStyle; +use theme::{CheckboxStyle, ContainedText, Interactive}; use workspace::{item::Item, Welcome, Workspace}; pub fn init(cx: &mut MutableAppContext) { @@ -86,6 +88,8 @@ impl View for WelcomePage { theme.editor.hover_popover.prose.clone(), ) .boxed(), + self.render_cta_button(2, "Choose a theme", theme_selector::Toggle, cx), + self.render_cta_button(3, "Choose a keymap", theme_selector::Toggle, cx), Flex::row() .with_children([ self.render_settings_checkbox::( @@ -141,6 +145,32 @@ impl WelcomePage { } } + fn render_cta_button( + &self, + region_id: usize, + label: L, + action: A, + cx: &mut RenderContext, + ) -> ElementBox + where + L: Into>, + A: 'static + Action + Clone, + { + let theme = cx.global::().theme.clone(); + MouseEventHandler::::new(region_id, cx, |state, _| { + let style = theme.welcome.button.style_for(state, false); + Label::new(label, style.text.clone()) + .contained() + .with_style(style.container) + .boxed() + }) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(action.clone()) + }) + .aligned() + .boxed() + } + fn render_settings_checkbox( &self, style: &CheckboxStyle, diff --git a/styles/src/styleTree/welcome.ts b/styles/src/styleTree/welcome.ts index 6085782afd..114ff0b7df 100644 --- a/styles/src/styleTree/welcome.ts +++ b/styles/src/styleTree/welcome.ts @@ -1,55 +1,76 @@ import { ColorScheme } from "../themes/common/colorScheme"; -import { border } from "./components"; +import { border, background, text } from "./components"; + export default function welcome(colorScheme: ColorScheme) { - let layer = colorScheme.highest; + let layer = colorScheme.highest; - // TODO - let checkboxBase = { - cornerRadius: 4, - padding: { - left: 8, - right: 8, - top: 4, - bottom: 4, - }, - shadow: colorScheme.popoverShadow, - border: border(layer), - margin: { - left: -8, - }, - }; + // TODO + let checkboxBase = { + cornerRadius: 4, + padding: { + left: 8, + right: 8, + top: 4, + bottom: 4, + }, + shadow: colorScheme.popoverShadow, + border: border(layer), + margin: { + left: -8, + }, + }; - return { - checkbox: { - width: 9, - height: 9, - default: { - ...checkboxBase, - background: colorScheme.ramps.blue(0.5).hex(), - }, - checked: { - ...checkboxBase, - background: colorScheme.ramps.red(0.5).hex(), - }, - hovered: { - ...checkboxBase, - background: colorScheme.ramps.blue(0.5).hex(), + return { + button: { + background: background(layer), + border: border(layer), + cornerRadius: 6, + margin: { + top: 1, + }, + padding: { + top: 1, + bottom: 1, + left: 7, + right: 7, + }, + ...text(layer, "sans", "variant", { size: "xs" }), + hover: { + ...text(layer, "sans", "hovered", { size: "xs" }), + background: background(layer, "hovered"), + border: border(layer, "hovered"), + }, + }, + checkbox: { + width: 9, + height: 9, + default: { + ...checkboxBase, + background: colorScheme.ramps.blue(0.5).hex(), + }, + checked: { + ...checkboxBase, + background: colorScheme.ramps.red(0.5).hex(), + }, + hovered: { + ...checkboxBase, + background: colorScheme.ramps.blue(0.5).hex(), - border: { - color: colorScheme.ramps.green(0.5).hex(), - width: 1, - } - }, - hoveredAndChecked: { - ...checkboxBase, - background: colorScheme.ramps.red(0.5).hex(), - border: { - color: colorScheme.ramps.green(0.5).hex(), - width: 1, - } - } + border: { + color: colorScheme.ramps.green(0.5).hex(), + width: 1, } + }, + hoveredAndChecked: { + ...checkboxBase, + background: colorScheme.ramps.red(0.5).hex(), + border: { + color: colorScheme.ramps.green(0.5).hex(), + width: 1, + } + } } + } } \ No newline at end of file From 4c179875ab5b120a871bfb853aa6969fa8779478 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 6 Mar 2023 10:39:35 -0800 Subject: [PATCH 13/93] Add png image loading to gpui add zed logo into welcome experience Co-authored-by: Nathan --- assets/images/zed-logo-90x90.png | Bin 0 -> 12413 bytes crates/collab_ui/src/collab_titlebar_item.rs | 2 +- crates/collab_ui/src/contact_finder.rs | 2 +- crates/collab_ui/src/contact_list.rs | 6 +- .../src/incoming_call_notification.rs | 2 +- crates/collab_ui/src/notifications.rs | 2 +- .../src/project_shared_notification.rs | 2 +- crates/gpui/src/assets.rs | 21 ++++++- crates/gpui/src/elements/image.rs | 56 +++++++++++++----- crates/project_panel/src/project_panel.rs | 2 +- crates/welcome/src/welcome.rs | 14 ++--- 11 files changed, 76 insertions(+), 33 deletions(-) create mode 100644 assets/images/zed-logo-90x90.png diff --git a/assets/images/zed-logo-90x90.png b/assets/images/zed-logo-90x90.png new file mode 100644 index 0000000000000000000000000000000000000000..17f92d2c1afbb8459fd31bfdc7ace50a412ce0a8 GIT binary patch literal 12413 zcmZ{K2UJthvUcbK0tyP!QKU%;5G2%qq9P(iKza!!v;dLNds7hU9i${k@6x4%C^hsF ziZp=$0i^f#)R72F8#@>P@G>+ZiA+m#jn;o!IEK4qcc*AV4ML7baKs)<>J$9SJV?=iDbt)KYzEuONsoZlqIlq7V-#d zsY1oPtmVj|mZI1n#t3_(D!6 z8X|uyi1-MKDU@s6dxmE;>C zc0pX`J@smE(jXZ-6!7F0?Nh~jABD``Cv(<(r2GD&F$VDPX6RGe7>kEgF&q>Y_bYDl zJ$V&FVIi#%!SXEffmO8fOKBwg+B46X+AfAWA;aq2w*LBPx;NrIPZWmkIg@@4?tij3 z_|*BXex!Eh*&yluO;3`h2v;<4$d_ea>)n)uc^YeE#NzMoj!NIO(r(t?(+THqf4gAo z*!NBQmx>|TpSyoR86U0Y_1vW!c_8=L!x-jQ<{1`se)Dg8ZBrdP9j6|vdb3|j2dED6 zOwh{QkaV(pZSv)|F89`f9c5sx{HQH^nku2({JJ+k1epp%SFdzKhY*#%o_T8dj@ zUSit?JWn@M-9f+Cd>?-jKNg=JZ`t$s6&U7U_@p}9^No2v<7mz((pK<5=HT{0`lnBy zLO(UU%KoIwWs+mK?BDY|K{a7OOyx6GgmS8?=AibF>xk;epOM+2HD}6tplqm6UfCC& zk%3{*SSeoAIjU~_CLq-x9BkI_mv z5~M2T@blZ0Be=t7l(B5Fh;x{4ykMlRWbb)W4l_Enph3I#%~3g<@xTk92BLIUyJ^p} ztOL~P70D0|Wb?B=C-Tvndm;DAFW{2;qUiL3G??@WsYZ|rsTGwmqb>6(ljXfqu#C+F z{D<(aI4yGtSPHfunDg`Z*9zU`Vy9w=xz8fh{KJmxqWB^KopPLR9ATVPTxYJ9W{pkZ zn&$}p2n57g$!AH~XNzG=*2GNW)^N;y27Pk+y81s11Zu6T+#L$cyDKr3#pT`>&ue$f zKbyaWx%7Uv?yKpqnlyv#Pbjy2y7Rz?1<{|dI_0olunt%!1zrL#OHDDTn6|8Ku;Z2vY>M?6 zN}vC;(){D%+nE)O4ozL>{U|xvO&O5vktYOYC#fgqv9crtqysi%UCXEuiEb4~1GB7< z4v07E5h|}axB2OuQL`)yT$R*{eWe-|gu;}~ZgFCR8a{eWv9GW%nQY9_qZw~8tKZ&! zIQURl@GJLMU>BG5lfgCU(|6X}Q}YcnL>c}%ee0BtlzMlNLhBnVA z(rHCuGx3U=eR;2P#e)?jfZj&Oi#K{dO5W~#5E^qkCLw0v3riOLi|QAkFILaDA}((& zGE_?8OpD(ZKd9kS6?E`4botfkesw&29Qi(!JdC#VA)f0KU25!0B}b)(*wZ|c*VDo! zUiH2EU)J0#b^DXSi^H#*QuIJ-7>(4QC>M#8Xckl>_=I0B&E)5n+Cq+<>KffU5@r(M zhvx4G-;HCE^0h|e@Co{#Pi6Y}?Tz#i^=h7ar~D#1hWCBrZl{$CE}C1q!#AP14>gevH47UamD4#j!w+maS)wGFar@m*VGYRWG)74kB)2$=(OX?UUv+1k z=ZZGY_cJs2Xj>-2Bi2J_Wg255(!v82-bx}lCJ7`Fq>Nw?GMcFSSZRG`X5rZLJCQ3_ znIg$~g=~3C@0HQ#x_vBRNZ@ebQSa}h#|%t&a15>&xAqlOVr*lISN|XfmZyDxT=8C( z+ke>mq{83KKp6&1l8BE_@HXPp^*G__MD_SF879;h+E=gROW+^j58;1`5A+)Hw6es? zs^By6E>!D!>tWcIIbnEvPK6R|T3iGCNZd6(BmrzMI$dQsg8j7rX*$6-!6o$oTsCp; z81}9=qNLdXUdw35KY6%pwKH&3LuQZ2ugHHi{p43n!{TKBxAc;huYLuyIkiO=*xH%c z8CI_^hiOHwMWwSvEp9GU`dRsFGhm;&gNw_vRNGD4TC5E=fyJNb6A_S_o4wJAcVE5N z^l)K{6Q?MS1>wV(qk~VVd?MB&SXDw*HWd{jYs9?7vFw#{HD}*;c6#Xad9+d483Hnr z?vJ*#u58xUbgYK^ovSgn9`51`8zoLLD^vX;87=Pjx4%zntF9M>88bwg?0$3J7d=BR z-o^4$=@^xcU*UJ*KB;hn_Q$nw73@qIxK_C%i6xRx!)~bAV*VcP}z`I&oO-vZ(X5vz4*t_sxI6 z72qI*PVJJc3pI#^-@RkqbETt^jDd}T5|iI8`!_KcuTJ$Z`%Vq^js~}) zNGr+6a=aP+-0!rSe#*@yY@DHzS0jc(LBuXoU9V*Yy1M*Yh1>jo$JBZ08WPxwfW(*_ z)reSHM<1}u_c1`N1wQSNj85tmWbJKj7egKfPqcg&le{JVV%ESgn&v7xM)DHqb2Snu zx<0qj0^ovBQnNNbeRy5N8p3pyELBthkFMz(0Ad1C0MRu?03f_R(f*4jxTbmk!w<0m z5dT950DxRy0RTcY;lDXmXrlkne}(TGBnPimYHYN0opn`SOPM3=g-k6FW-uXl`*(j8 z05a}U*Q7nn*_6ZG-p;{E%3b!sKQyGS>A!5?1CD>FIKyQh=&Go5C?Jq94sju2A>juQ z3Jwkq8Ki}!l*Y3c|AJrN$v&`lc77)X1iHDo3AurU5J)Sah@_+>P*@ZwDk^xbA?W1c z;B4wH=-|Zp&q4lcoM$j6bEM5XXB&hA$KP>H%@8imvJW2oHT3V}pME;qSpLV7gVVpl zx(*QdR{|6f5(fTnFqpf|{{j0e`6uijasAVr%-_MJ6cG0BkT55wYquaGGXF5}|MC9g z-aqv4DjE35<>Br0*eL!9vCS-d!pq|u$itBC7|bt%?nwuU#|HP z`xDkqUDu?DOaTg4noV!2wI0)17&rTuHwpKelJVaZy+^Jr`%;(w#t#93`>n(KA4!-O zQ$Cnjw2q{iSX}O2t}lgleZtR#$%8sd+WXPcA5rMla2kgY&^7V9ILP~Xu~#9- ze^y{;;ktF;qIBGyptL@rpAmQXIT_;x_r}%#&k9`#=G zpuBgxbv{Iv>M8Kb`#xfHg(=j!B+HkDjPnl|_zFp8`4AkOmA9)B)&ou|X^;6~auHna zi>EA0%yRgRMZuJA+PO)pJDqRTqX_H$PuSyI{i?Ei-fxwVrG7$Hs8m&TRaJFp_j;Oa={w;^|*eYMR;9^b*TdtkpmDiY&S0sEf ztzC^`ua+V|Gw+~}&dow9C?(gc1jjH()t%?rP@T9ltevV^vyZ}qE{wyDSd`Di{!BH? z2?jJimoXRs%rIgGC}wyCSjW?lB>|;xtW<%TkP=AVxz{wV{|98{bl@OWYCzMzDim=? zxP`xieVqFKNNcy;ep%lv+yskR7EE9Mya$ZrD`OcDP66*HF;vJ>HqM{L;;t@KHc8D6ioFQNAKbl+?UNt_#US}CvC=`UyW zh@PLME{KK9w0evGR4I2RB(ZLggr;tve9@~UK2HioPwm4%-MPQGzd{K~v>KRnLCYw% zi^HttPwi1w-0k)a>54pcjJVfms5M!%gYH?k6eXRU3iovQG%+`~`q!G&*3{aq_r2J& z(0#HIE$8+_XORWmR~58KKwyp$ky?@#`K}M=W*uIm!)zd1ZG}<1Pds3a{LanINC{;D z*0+^i4)srUt#Z!h_wQsfH>k}2CZ=$9WuJGtTic@hkd;2sy=Nj~j$8O~Kw!ztQ}3}q zX9edf2I;0xaZzD;ma!^zD(KJhZ9NWz%1N)G_9i)LJjOkSlPDZ)rV)Z8zk}I$^h@CG zkR{Cb(xwB@u;IWDt8fxW6AeWryJ6zu4ArE4D znUt4G=|DcaL`?D0mEg`3eyR~(^pO#1T!Q7cn$af*rP)zp3I|uvSI~0BmUwi*BdTf5 z+V2Kmz}JDt7!mO(Yn_z_>1xy+LtF%#xx7}%@5pS>|ITxrb7Pk`S_nKJZ3+-vP*6qB zTeuPMCt=PWQ(u-mjxCL;e7!QmGXU)OhaKDmC_ecY$)D@=F2jI$x zCMk69W+Q)JV`b*2-RNAlJAZ7{nqRpbnQhI|{b+KEq5{Ex6D1#+_rq6B?hbb9M9h+v zhTwnYve~j~_TqG3c`c_DN^6_r8%w;+J@lKa`*q@Nz@X%q2N&+F@GD z>WZvP!BV-2%=as`%somavAS*LOy-y7o_mHPspYuK5TFmp;GVn}prA%ydoIow5{n^F zQv8)suvTj|AnP=lFKMtcoLlDnx~vIoe^8U%QS=KVegiG*uVo-uw58-%swv7Qb#b_H zGHxiyp}Ndc8L_q!UOLvE_6_c1d>12jF;&JQ=e4=?;7?+RtFm3ypap`!476|)=97`M zu`t$#@iyK>P(;n$>UV#^lVl?BONYnC+*1{566@ou@Vx=BH|R$&K4E)-B~=5noa$Ng zojMe6f4rQjBu*WK5yHkBVmt=kZZ(Jv^L3B%cPd{xj;%=;eCDD9V1D|si=6btdViBK z-&(PR)Nr9!#I80T^4~C*4PVo-URYF`1b{*Ylm$-4LEof>od9#0l^*+L>lB+c>@GU>2`zdOeVOG%18RwJ^wGWLZC%(S!d0rTobnr)#sFM-*s_B$ zh!6vP@hfe$2~gkZG>sJF{tCc6qrBMH7OC0$X2K7aMyH zK`KmY;zf*u<;v`N=nE0g zX1XRU^Hfmrt0?GY#hsH4-L5uF!8F~uGE#7-WMIb{47%8R?JWdI-J{(8o?%CH^@gba zHB_etYRRFTsF3l=CZH@QHLh1m**hYD5a;uV?IS~L+D!VUbX>Pim<2aJTz?6BDSaOx;MIi+XN&N1n95phD)EQ&J2>SosX|xQqwrw;F2~j8uZ?LGo)4np;K1y}bb`as%i6E6LiCk7F7|jbwIOQ9RT<$(zN;H}d45+s*$G#$-r zxXSlQuTxle=_t=vVu!$<)YD$=H`{V>%?UiXTr5~Wk3f(La%$DGI}F#b zw3{&yEF-Y=RBatrgRhfqRu?Za#CSMy^Gs&}rW6|Q^qC$M>JcGqj2G8uHH=9xomeV) z&Ug9U+BgAx1^d1%y$imP&)}@oMAGx-7oA8GfzW1rg5AS`($$uG<(d6t6G($=(9=-| z0(}l81D}IIn6G>Dw{>7{X1}X~TGX_`PSLLF=~&hJ!8nr0m~by=2_85(e9-}(IyRBMv zWU$U8hvDWh`q#Myvk!)WGKz@z->cNU5J90z9)5rk7f zZhojD_&9uK&?UN!Ib9?GOlqlm(|UJhFz`lCU=PiwyATXYE3J^ZDN~J_U+gefv-yee zS>rCNzR~Z89-B?pvx7@jiThes8^I7sJIpz`SnCFrXkvEFUVkIIlkaoc)}4f)43c}N zr#InMHci@ptk8d_Zs=}3X>#|j-9(J`t?Y}x39>OWA+BSnz7k_iF{rm~dWn}#ZI<>W z#%TI*NAEOP3428n-@U+E4VHncBgOWz_K^4XX9ox38OwJ@=)MtRT)ul9OFGSH3KFF? zW>)ib^|$6|lIi=2z`wf&>Zzf=?^7qOD4=v&t0W>4rhM$#&8QRu<439?5ZXZ|yPtsN2sR#J0-R^? zvnuB(XVBTNPC&_w(_shINkxrIG38II{8axxGcW1ALFdHDlIHN>>OX}DfU;4>*xWrv zi}m|grXJ}hew>%eV}KqV>V5@T?Oj2SclA3#OoX57xAm^{PKozH?c8Z<^MuI*MAS@e zq)p3y;PWKq^Etj$8OY#wYJS+s9Dky*f9?%=+SWjdI^*%OgW9g~f&$3`$q8|J=~&AB zSHf{)A@3-Y`}VLDdn4wmyvlfmw>5v-)@#&|9tu|6aPE&@F{a2pKcy4MVcDH8PW%eLN`i+9bEMeG{ic4cHJA{2GQQwj-Fu z-)?~?`O~Xw?0H=3mtD~J=uM0Z>-4mH_=Kni^&iQ5$;Ingeg2k`_q!513(XZeA#e_q z^vKAB23yKKZ?uVlZzN`yQbI09=bl!&`f$UG<}UFK8T$#)elfUz%DIdfpfBn-vFQ9x zV&ZPJ&)|p6ns1qXg8rhC2VOxb=cA$spVAGY>6sSfJ2ZoV+f7T$*7ilKA%LzB(m(2@!(Bw)RRbNmR zdn+YPs}ITTLAZZmb3lPlJ zO%4^xc1IJpw2&_qjMrN$@%p2zcGPy;^;tB`COJ8j&|F;F>CG6jUxcEb8x!~tI|my_ zMm)Lg`Nblu!ETp;*-lW!rh@VOUCpHD#-~A^Z27Ve$BZ#E^e&SEdsaZ6RR5Yxg{})9 z&yT6nsRTI5$E#$6hEurjWiF)b@uR6X{5*Kk4uZ|lj|aKIomW7Jzv>fNe!UeZ=eWA=g9;L(Binr8!3Wc*Va4^At;97pI+KB)m~ zc!;~NJcu$W0&ZZ1;BgEYun2ufL#B%`@ACZ-P9JAt5Rt{1a00x?eXzWWHM>2pW>jP` z4b<35|KrCR*?}ugjR?z5k7G}oK8ezD&OLl)zXDauD00CWOn)UEmBMM&)Pm#}p&a|B zI#$AGfE2Y|K2XtAaDS%qP`S5yH;V1`#EvEn?2Y0KivDsg{sKXnynxl6BwE>4yK&kY z94TlrQ5x1*EY)%vRPBx9520doM@orKu$!s)OAbi5ByQbZ)KMfe!$**(170MS`HZMR zeBxT}q#nxgJ#Kk)rpQlNmn|pQ;=e&4T)t+4HZ?HUoBl;8xlSef5bYxR^3|;xkp^G4 zCp@jM{X9qq-X2{v11wDV<&Y_LhD9)c zo+8wJRk9R=^3jywW!qeQAq+atXaO${b%1M4{Nn)hF9WgD`W_CmlD>KdSH&~~Y~u=Z zgLjh|acj`dJ);NA$v~CWAX$RG;=MfP>P-bL_WqSe2b4U^3ny{{XntXkIL*opAc416 z-#d*xUbM|fvPitTEcOQjP45Ba-Ir{?{tcYlG)d;Y>+yYa^jra#Nv37 zX1A+x@iM^S?}SLg(?3Vg&4t!Up;%RXS$(F%0uMBCU~VYM51wv$*#=ZPo>6!tk?PM^Ul<83+@1`^Vt_~ zD|OdkT68YC?_BUK=XgrBv_-E5Eeh6(W)3Q5H7euE@1{af@t$cV=}@y6b{83>tNN!* zC3y~CrvEmH(@zx zGM9{;z%hmtF8}D|gMdRr855Mq=()NDzJdNCz8ziaOpFn4$WCnUC#wBjo)?AeC<3wR zNlR@i9VD1Jov3%kcy11Q>z*!B4v4DiU1gIE&~RjBxGsXMPTRd+$P)r}$Kt|fV#lsL z%8!f>O=o4|WNH?G+Rb^`o1S}HNxe+hdD!l=bAe{?xD@QVOq-9HnbLf*Li7f8r-9N( zh^%l{!Y96Zy^K{#F>iqDv&TY75oB>}VNsmmYW)i!@iK-@ccG|mqwfogZj6I^aH&de*iy|3X1v_#dhPN%CYD75 zvbLnnSCSqZr#Z&yhx+0Od$8hZUfCsnqv=qmqrbE*bH*F@Cin~8w=Mx^#-d^q93$cH zAc+kJuO40J(*p2M_Ack_LrVMfFpUY{S0G?L6EwG(Dr(2$hIjOgCUR$3nR_+&)jHNp*1MA&UBW9I)pc;r$5R`0k{4BZ zswM^EFmrT4kWONacQuzTCPB(w{K_F~JT7R?^K|+{X;ZOa&Y?}q1b-(Lw4dNa7+x6V zKUNCit4WkSn7YV%ya>-Q)z?Yg=@}gDrt8%RW*=G8oKENq3P<3}$0*t5O^x{+S7qZM zJ-Cjjh)chuiy~NAbulbzG{(=;>$d!L4IYAJiYLO!AA87~kB*65j95}R3-HN^jB4`y zO!QV}8_9H=jJqQ%6Lwy}9^V-?YQ>I^voz%AX%?{WA=`9{&Z1lOKhf#gylDgv+k~BeJUc*xOpL!N*K_Zv1?3466Dy z8I{Hu#nvJxbfyT~^Sg08lMrC|DouNa5}9$f^o2zQCHw;9+<$BXs=gh zsHA7Ih+n=RwM@CO?$>i0dRS!M|7*m}t1lT;-WNi?v>ARg^1M3)`oiy!y_>p6`ETze z($}TD;GGXxSWU&Ia+$a_9DP@AqKPF(0rOiY@>@y>F(pKxF~Fm3P#k;xtxGkhlAfJl zL+175s0_A}TC#V(SW)%Yp)>)86DT(-D4853dbU`1&mQYS7 z9H1lYWk9F7xXUAvUP6cienPSbzox~%aXlk_=PZ_L`iGc;`pS6cVtN5co#}wthMsg< zft{6hOYgbDOd7<6O{D7t|A+-}CSUo!ai?4{aY3C2wq2fC`3v>rRm>&G)(8w%tdv!a zMP7~8TYz0Lz=LjP9Zes!H;jK>rx%|ic*bR9U$$KSORy!e-+eXNC)C))2B+rR1F>w# zUP(V$>BU)&GxSs<3)EQ8*|W{w37IgO(Bo`wLA+g9e(Jq96Nj4ekuT+0PsP>}IR7b6 zV+7-he0y}5T^hMWRnNH6eSRxuC_5exl!TNWrAHZHiuY5l-#9xOe>NTA>mr8?-SW>fk;zc!n&1?I7 zKCHM;=5!Q^uVyTwDztx%t(cY;+2+gQ-K%ghX66_-tm8F2rDjk> zE&eqxs=TY1eoSt28-JBWSk+nK=(Ic-i}j^iMlZ#F#+GUPZTPT44pfZ#q+jl@p-#15 zPsL#wj1INVtTlnpP4;@0N>Ad;SH342aEwCu+UbH2O%-6f@x;hFk=dglWc95=dz-PH}X2FL5v zso+P&*_%r}ys$Ty^VL7^c5H*)P05p~QkBA{=iMe}_ZuRH6h9fyV$=8gn&YsFg?)yr z%blJe@d6JG9t31*$-vb`V;855(rS>hort<)U?gq7ZBqvOJ;XRt7*n%P7WcTT{Q4E+ z7`yBWxnQa=AV?4Sl3y{(e3zNB$w2RwQ^#7ImRvz|b(*K$K_OY_Ome+3YZ1y534;uv zRSW%CQKurzkd(ZiMU{T$qY6$2%ITNGBC@Uwkh*XsS(nj8ev}qEmvuG=A((Y_{Q&so z5PaI5d?dGgT*d3FEADJJnIF`qc1g>+_gJ-|LaXVHY%1vZLl!kVz2~M&#{O7tDOc_8 zs7pkiD}RXRg+2to&2^QGz}fCp3we!^!eCVEMbhpvM{lh9F>Pt0ic{gBS}pdyu~Q7s z6_7I7ulN0MC3C&R&efaA^>^9a-B0VYRgL|8^qnzz9}$~hOLU7Exe*? zIj@f`KJ&PCe;nJ{SkQ?ZF1`7@vrhDd@h5hEnEMg*W`plkLS?hK+44cr-q>}+8scp` zDZ^OSKRsnQtbaA259L-`zpiNBr1@3<8kIBHXfWrZ;mYY|dg-#*qb^>qS#JgryikAW zhqPWj2zZ>wIvwjFB^hBgiGKDV@Tj4>b_S;l*?P!8fz%IEHFg(^ zm$JyI;>lfw6{fHu)d)-uWN~y2ZgN#}@6x7iVbWPQMKD(4bXq=aY+@pTk+}0D8(%RI*J~{vZ3keWzBIJX13$QJd7g$ehp{AC~i3 z&N*F|ekkFa4?;}E@+4ii&U3EI(Yi{W#q1wkD34JEy%aLZgR7>lt41iTyqZwZi*$hRI~_R>ox+>?(i5&%|Hm>`3j(&)_rv WubU>a9)CZGQhKiXtn?`~@c#k#=|!0U literal 0 HcmV?d00001 diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index f9f5738ad2..a16c31595f 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -823,7 +823,7 @@ impl CollabTitlebarItem { avatar_style: AvatarStyle, background_color: Color, ) -> ElementBox { - Image::new(avatar) + Image::from_data(avatar) .with_style(avatar_style.image) .aligned() .contained() diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs index 98f70e83f0..cb55669ab0 100644 --- a/crates/collab_ui/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -128,7 +128,7 @@ impl PickerDelegate for ContactFinder { .style_for(mouse_state, selected); Flex::row() .with_children(user.avatar.clone().map(|avatar| { - Image::new(avatar) + Image::from_data(avatar) .with_style(theme.contact_finder.contact_avatar) .aligned() .left() diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index 3bb036d336..203d130b5b 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -726,7 +726,7 @@ impl ContactList { ) -> ElementBox { Flex::row() .with_children(user.avatar.clone().map(|avatar| { - Image::new(avatar) + Image::from_data(avatar) .with_style(theme.contact_avatar) .aligned() .left() @@ -1080,7 +1080,7 @@ impl ContactList { }; Stack::new() .with_child( - Image::new(avatar) + Image::from_data(avatar) .with_style(theme.contact_avatar) .aligned() .left() @@ -1173,7 +1173,7 @@ impl ContactList { let mut row = Flex::row() .with_children(user.avatar.clone().map(|avatar| { - Image::new(avatar) + Image::from_data(avatar) .with_style(theme.contact_avatar) .aligned() .left() diff --git a/crates/collab_ui/src/incoming_call_notification.rs b/crates/collab_ui/src/incoming_call_notification.rs index a0f54abe38..6fb0278218 100644 --- a/crates/collab_ui/src/incoming_call_notification.rs +++ b/crates/collab_ui/src/incoming_call_notification.rs @@ -108,7 +108,7 @@ impl IncomingCallNotification { .unwrap_or(&default_project); Flex::row() .with_children(self.call.calling_user.avatar.clone().map(|avatar| { - Image::new(avatar) + Image::from_data(avatar) .with_style(theme.caller_avatar) .aligned() .boxed() diff --git a/crates/collab_ui/src/notifications.rs b/crates/collab_ui/src/notifications.rs index 06b6cf2a90..21c2d2c218 100644 --- a/crates/collab_ui/src/notifications.rs +++ b/crates/collab_ui/src/notifications.rs @@ -24,7 +24,7 @@ pub fn render_user_notification( .with_child( Flex::row() .with_children(user.avatar.clone().map(|avatar| { - Image::new(avatar) + Image::from_data(avatar) .with_style(theme.header_avatar) .aligned() .constrained() diff --git a/crates/collab_ui/src/project_shared_notification.rs b/crates/collab_ui/src/project_shared_notification.rs index edf0354eec..b24f3492da 100644 --- a/crates/collab_ui/src/project_shared_notification.rs +++ b/crates/collab_ui/src/project_shared_notification.rs @@ -108,7 +108,7 @@ impl ProjectSharedNotification { let theme = &cx.global::().theme.project_shared_notification; Flex::row() .with_children(self.owner.avatar.clone().map(|avatar| { - Image::new(avatar) + Image::from_data(avatar) .with_style(theme.owner_avatar) .aligned() .boxed() diff --git a/crates/gpui/src/assets.rs b/crates/gpui/src/assets.rs index ac0d72dee9..2170d215af 100644 --- a/crates/gpui/src/assets.rs +++ b/crates/gpui/src/assets.rs @@ -1,5 +1,8 @@ use anyhow::{anyhow, Result}; -use std::{borrow::Cow, cell::RefCell, collections::HashMap}; +use image::ImageFormat; +use std::{borrow::Cow, cell::RefCell, collections::HashMap, sync::Arc}; + +use crate::ImageData; pub trait AssetSource: 'static + Send + Sync { fn load(&self, path: &str) -> Result>; @@ -22,6 +25,7 @@ impl AssetSource for () { pub struct AssetCache { source: Box, svgs: RefCell>, + pngs: RefCell>>, } impl AssetCache { @@ -29,6 +33,7 @@ impl AssetCache { Self { source: Box::new(source), svgs: RefCell::new(HashMap::new()), + pngs: RefCell::new(HashMap::new()), } } @@ -43,4 +48,18 @@ impl AssetCache { Ok(svg) } } + + pub fn png(&self, path: &str) -> Result> { + let mut pngs = self.pngs.borrow_mut(); + if let Some(png) = pngs.get(path) { + Ok(png.clone()) + } else { + let bytes = self.source.load(path)?; + let image = ImageData::new( + image::load_from_memory_with_format(&bytes, ImageFormat::Png)?.into_bgra8(), + ); + pngs.insert(path.to_string(), image.clone()); + Ok(image) + } + } } diff --git a/crates/gpui/src/elements/image.rs b/crates/gpui/src/elements/image.rs index 37cb01ace8..cc49308e15 100644 --- a/crates/gpui/src/elements/image.rs +++ b/crates/gpui/src/elements/image.rs @@ -11,8 +11,13 @@ use crate::{ use serde::Deserialize; use std::{ops::Range, sync::Arc}; +enum ImageSource { + Path(&'static str), + Data(Arc), +} + pub struct Image { - data: Arc, + source: ImageSource, style: ImageStyle, } @@ -31,9 +36,16 @@ pub struct ImageStyle { } impl Image { - pub fn new(data: Arc) -> Self { + pub fn new(asset_path: &'static str) -> Self { Self { - data, + source: ImageSource::Path(asset_path), + style: Default::default(), + } + } + + pub fn from_data(data: Arc) -> Self { + Self { + source: ImageSource::Data(data), style: Default::default(), } } @@ -45,39 +57,53 @@ impl Image { } impl Element for Image { - type LayoutState = (); + type LayoutState = Option>; type PaintState = (); fn layout( &mut self, constraint: SizeConstraint, - _: &mut LayoutContext, + cx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { + let data = match &self.source { + ImageSource::Path(path) => match cx.asset_cache.png(path) { + Ok(data) => data, + Err(error) => { + log::error!("could not load image: {}", error); + return (Vector2F::zero(), None); + } + }, + ImageSource::Data(data) => data.clone(), + }; + let desired_size = vec2f( self.style.width.unwrap_or_else(|| constraint.max.x()), self.style.height.unwrap_or_else(|| constraint.max.y()), ); let size = constrain_size_preserving_aspect_ratio( constraint.constrain(desired_size), - self.data.size().to_f32(), + data.size().to_f32(), ); - (size, ()) + + (size, Some(data)) } fn paint( &mut self, bounds: RectF, _: RectF, - _: &mut Self::LayoutState, + layout: &mut Self::LayoutState, cx: &mut PaintContext, ) -> Self::PaintState { - cx.scene.push_image(scene::Image { - bounds, - border: self.style.border, - corner_radius: self.style.corner_radius, - grayscale: self.style.grayscale, - data: self.data.clone(), - }); + if let Some(data) = layout { + cx.scene.push_image(scene::Image { + bounds, + border: self.style.border, + corner_radius: self.style.corner_radius, + grayscale: self.style.grayscale, + data: data.clone(), + }); + } } fn rect_for_text_range( diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 1cefc558f0..9df6581d20 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1325,7 +1325,7 @@ impl View for ProjectPanel { Canvas::new(|bounds, _visible_bounds, cx| { cx.scene.push_quad(gpui::Quad { bounds, - background: Some(Color::red()), + background: Some(Color::transparent_black()), ..Default::default() }) }) diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 7bf6c26302..232fd1bcb7 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,14 +1,13 @@ use std::borrow::Cow; use gpui::{ - color::Color, - elements::{Canvas, Empty, Flex, Label, MouseEventHandler, ParentElement, Stack, Svg}, + elements::{Canvas, Empty, Flex, Image, Label, MouseEventHandler, ParentElement, Stack}, geometry::rect::RectF, Action, Element, ElementBox, Entity, MouseButton, MouseRegion, MutableAppContext, RenderContext, Subscription, View, ViewContext, }; use settings::{settings_file::SettingsFile, Settings, SettingsFileContent}; -use theme::{CheckboxStyle, ContainedText, Interactive}; +use theme::CheckboxStyle; use workspace::{item::Item, Welcome, Workspace}; pub fn init(cx: &mut MutableAppContext) { @@ -72,15 +71,14 @@ impl View for WelcomePage { .with_children([ Flex::row() .with_children([ - Svg::new("icons/terminal_16.svg") - .with_color(Color::red()) + Image::new("images/zed-logo-90x90.png") .constrained() - .with_width(100.) - .with_height(100.) + .with_width(90.) + .with_height(90.) .aligned() .contained() .boxed(), - Label::new("Zed", theme.editor.hover_popover.prose.clone()).boxed(), + // Label::new("Zed", theme.editor.hover_popover.prose.clone()).boxed(), ]) .boxed(), Label::new( From 4a8527478df335c27bf1cd601676d82fb73e535f Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 6 Mar 2023 11:51:58 -0800 Subject: [PATCH 14/93] Add child item alignment to flex implementation Fix checkbox styling co-authored-by: Nathan --- crates/gpui/src/elements/flex.rs | 32 ++++++++++++++++++++++++- crates/theme/src/theme.rs | 2 ++ crates/welcome/src/welcome.rs | 16 +++++++++---- styles/src/styleTree/welcome.ts | 41 +++++++++++++++++++++----------- 4 files changed, 72 insertions(+), 19 deletions(-) diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs index ce595222f3..b142726994 100644 --- a/crates/gpui/src/elements/flex.rs +++ b/crates/gpui/src/elements/flex.rs @@ -22,6 +22,7 @@ pub struct Flex { axis: Axis, children: Vec, scroll_state: Option<(ElementStateHandle>, usize)>, + child_alignment: f32, } impl Flex { @@ -30,6 +31,7 @@ impl Flex { axis, children: Default::default(), scroll_state: None, + child_alignment: -1., } } @@ -41,6 +43,15 @@ impl Flex { Self::new(Axis::Vertical) } + /// Render children centered relative to the cross-axis of the parent flex. + /// + /// If this is a flex row, children will be centered vertically. If this is a + /// flex column, children will be centered horizontally. + pub fn align_children_center(mut self) -> Self { + self.child_alignment = 0.; + self + } + pub fn scrollable( mut self, element_id: usize, @@ -309,7 +320,26 @@ impl Element for Flex { } } - child.paint(child_origin, visible_bounds, cx); + // We use the child_alignment f32 to determine a point along the cross axis of the + // overall flex element and each child. We then align these points. So 0 would center + // each child relative to the overall height/width of the flex. -1 puts children at + // the start. 1 puts children at the end. + let cross_axis = self.axis.invert(); + let my_center = bounds.size().along(cross_axis) / 2.; + let my_target = my_center + my_center * self.child_alignment; + + let child_center = child.size().along(cross_axis) / 2.; + let child_target = child_center + child_center * self.child_alignment; + + let mut aligned_child_origin = child_origin; + match self.axis { + Axis::Horizontal => aligned_child_origin + .set_y(aligned_child_origin.y() - (child_target - my_target)), + Axis::Vertical => aligned_child_origin + .set_x(aligned_child_origin.x() - (child_target - my_target)), + } + + child.paint(aligned_child_origin, visible_bounds, cx); match self.axis { Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0), diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index ded5c54f83..9442f08d24 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -859,6 +859,8 @@ pub struct WelcomeStyle { #[derive(Clone, Deserialize, Default)] pub struct CheckboxStyle { + pub icon: String, + pub icon_color: Color, pub width: f32, pub height: f32, pub default: ContainerStyle, diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 232fd1bcb7..f787a6cf24 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use gpui::{ - elements::{Canvas, Empty, Flex, Image, Label, MouseEventHandler, ParentElement, Stack}, + elements::{Canvas, Empty, Flex, Image, Label, MouseEventHandler, ParentElement, Stack, Svg}, geometry::rect::RectF, Action, Element, ElementBox, Entity, MouseButton, MouseRegion, MutableAppContext, RenderContext, Subscription, View, ViewContext, @@ -104,6 +104,7 @@ impl View for WelcomePage { ) .boxed(), ]) + .align_children_center() .boxed(), Flex::row() .with_children([ @@ -119,9 +120,9 @@ impl View for WelcomePage { ) .boxed(), ]) + .align_children_center() .boxed(), ]) - .aligned() .boxed(), ) .boxed() @@ -177,8 +178,15 @@ impl WelcomePage { set_value: fn(&mut SettingsFileContent, checked: bool) -> (), ) -> ElementBox { MouseEventHandler::::new(0, cx, |state, _| { - Empty::new() - .constrained() + let indicator = if checked { + Svg::new(style.icon.clone()) + .with_color(style.icon_color) + .constrained() + } else { + Empty::new().constrained() + }; + + indicator .with_width(style.width) .with_height(style.height) .contained() diff --git a/styles/src/styleTree/welcome.ts b/styles/src/styleTree/welcome.ts index 114ff0b7df..02e3c11f91 100644 --- a/styles/src/styleTree/welcome.ts +++ b/styles/src/styleTree/welcome.ts @@ -1,6 +1,6 @@ import { ColorScheme } from "../themes/common/colorScheme"; -import { border, background, text } from "./components"; +import { border, background, foreground, text } from "./components"; export default function welcome(colorScheme: ColorScheme) { @@ -10,15 +10,18 @@ export default function welcome(colorScheme: ColorScheme) { let checkboxBase = { cornerRadius: 4, padding: { - left: 8, - right: 8, - top: 4, - bottom: 4, + left: 3, + right: 3, + top: 3, + bottom: 3, }, shadow: colorScheme.popoverShadow, border: border(layer), margin: { - left: -8, + left: 8, + right: 8, + top: 5, + bottom: 5 }, }; @@ -44,30 +47,40 @@ export default function welcome(colorScheme: ColorScheme) { }, }, checkbox: { - width: 9, - height: 9, + width: 12, + height: 12, + icon: "icons/check_12.svg", + iconColor: foreground(layer, "on"), default: { ...checkboxBase, - background: colorScheme.ramps.blue(0.5).hex(), + background: background(layer, "default"), + border: { + color: foreground(layer, "hovered"), + width: 1, + } }, checked: { ...checkboxBase, - background: colorScheme.ramps.red(0.5).hex(), + background: background(layer, "hovered"), + border: { + color: foreground(layer, "hovered"), + width: 1, + } }, hovered: { ...checkboxBase, - background: colorScheme.ramps.blue(0.5).hex(), + background: background(layer, "hovered"), border: { - color: colorScheme.ramps.green(0.5).hex(), + color: foreground(layer, "hovered"), width: 1, } }, hoveredAndChecked: { ...checkboxBase, - background: colorScheme.ramps.red(0.5).hex(), + background: background(layer, "hovered"), border: { - color: colorScheme.ramps.green(0.5).hex(), + color: foreground(layer, "hovered"), width: 1, } } From b74553455f7712e2cf3e9fc738fdc0dc882685ef Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 6 Mar 2023 14:14:36 -0800 Subject: [PATCH 15/93] Add an element to pane to take care of wiring initial mouse handlers --- crates/gpui/src/elements/flex.rs | 28 +++--- crates/welcome/src/welcome.rs | 156 ++++++++++++++----------------- crates/workspace/src/pane.rs | 91 +++++++++++++++++- 3 files changed, 175 insertions(+), 100 deletions(-) diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs index b142726994..5df283bfee 100644 --- a/crates/gpui/src/elements/flex.rs +++ b/crates/gpui/src/elements/flex.rs @@ -324,20 +324,24 @@ impl Element for Flex { // overall flex element and each child. We then align these points. So 0 would center // each child relative to the overall height/width of the flex. -1 puts children at // the start. 1 puts children at the end. - let cross_axis = self.axis.invert(); - let my_center = bounds.size().along(cross_axis) / 2.; - let my_target = my_center + my_center * self.child_alignment; + let aligned_child_origin = { + let cross_axis = self.axis.invert(); + let my_center = bounds.size().along(cross_axis) / 2.; + let my_target = my_center + my_center * self.child_alignment; - let child_center = child.size().along(cross_axis) / 2.; - let child_target = child_center + child_center * self.child_alignment; + let child_center = child.size().along(cross_axis) / 2.; + let child_target = child_center + child_center * self.child_alignment; - let mut aligned_child_origin = child_origin; - match self.axis { - Axis::Horizontal => aligned_child_origin - .set_y(aligned_child_origin.y() - (child_target - my_target)), - Axis::Vertical => aligned_child_origin - .set_x(aligned_child_origin.x() - (child_target - my_target)), - } + let mut aligned_child_origin = child_origin; + match self.axis { + Axis::Horizontal => aligned_child_origin + .set_y(aligned_child_origin.y() - (child_target - my_target)), + Axis::Vertical => aligned_child_origin + .set_x(aligned_child_origin.x() - (child_target - my_target)), + } + + aligned_child_origin + }; child.paint(aligned_child_origin, visible_bounds, cx); diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index f787a6cf24..f1d59ce7a1 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,14 +1,13 @@ use std::borrow::Cow; use gpui::{ - elements::{Canvas, Empty, Flex, Image, Label, MouseEventHandler, ParentElement, Stack, Svg}, - geometry::rect::RectF, - Action, Element, ElementBox, Entity, MouseButton, MouseRegion, MutableAppContext, - RenderContext, Subscription, View, ViewContext, + elements::{Empty, Flex, Image, Label, MouseEventHandler, ParentElement, Svg}, + Action, Element, ElementBox, Entity, MouseButton, MutableAppContext, RenderContext, + Subscription, View, ViewContext, }; use settings::{settings_file::SettingsFile, Settings, SettingsFileContent}; use theme::CheckboxStyle; -use workspace::{item::Item, Welcome, Workspace}; +use workspace::{item::Item, PaneBackdrop, Welcome, Workspace, WorkspaceId}; pub fn init(cx: &mut MutableAppContext) { cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { @@ -43,89 +42,67 @@ impl View for WelcomePage { enum Metrics {} enum Diagnostics {} - let background = theme.editor.background; - - Stack::new() - .with_child( - // TODO: Can this be moved into the pane? - Canvas::new(move |bounds, visible_bounds, cx| { - let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); - - cx.paint_layer(Some(visible_bounds), |cx| { - cx.scene.push_quad(gpui::Quad { - bounds: RectF::new(bounds.origin(), bounds.size()), - background: Some(background), - ..Default::default() - }) - }); - - cx.scene.push_mouse_region( - MouseRegion::new::(self_handle.id(), 0, visible_bounds) - .on_down(gpui::MouseButton::Left, |_, cx| cx.focus_parent_view()), - ); - }) - .boxed(), - ) - .with_child( - Flex::column() - .with_children([ - Flex::row() - .with_children([ - Image::new("images/zed-logo-90x90.png") - .constrained() - .with_width(90.) - .with_height(90.) - .aligned() - .contained() - .boxed(), - // Label::new("Zed", theme.editor.hover_popover.prose.clone()).boxed(), - ]) - .boxed(), - Label::new( - "Code at the speed of thought", - theme.editor.hover_popover.prose.clone(), - ) + PaneBackdrop::new( + self_handle.id(), + Flex::column() + .with_children([ + Flex::row() + .with_children([ + Image::new("images/zed-logo-90x90.png") + .constrained() + .with_width(90.) + .with_height(90.) + .aligned() + .contained() + .boxed(), + // Label::new("Zed", theme.editor.hover_popover.prose.clone()).boxed(), + ]) .boxed(), - self.render_cta_button(2, "Choose a theme", theme_selector::Toggle, cx), - self.render_cta_button(3, "Choose a keymap", theme_selector::Toggle, cx), - Flex::row() - .with_children([ - self.render_settings_checkbox::( - &theme.welcome.checkbox, - metrics, - cx, - |content, checked| { - content.telemetry.set_metrics(checked); - }, - ), - Label::new( - "Do you want to send telemetry?", - theme.editor.hover_popover.prose.clone(), - ) - .boxed(), - ]) - .align_children_center() - .boxed(), - Flex::row() - .with_children([ - self.render_settings_checkbox::( - &theme.welcome.checkbox, - diagnostics, - cx, - |content, checked| content.telemetry.set_diagnostics(checked), - ), - Label::new( - "Send crash reports", - theme.editor.hover_popover.prose.clone(), - ) - .boxed(), - ]) - .align_children_center() - .boxed(), - ]) + Label::new( + "Code at the speed of thought", + theme.editor.hover_popover.prose.clone(), + ) .boxed(), - ) - .boxed() + self.render_cta_button(2, "Choose a theme", theme_selector::Toggle, cx), + self.render_cta_button(3, "Choose a keymap", theme_selector::Toggle, cx), + Flex::row() + .with_children([ + self.render_settings_checkbox::( + &theme.welcome.checkbox, + metrics, + cx, + |content, checked| { + content.telemetry.set_metrics(checked); + }, + ), + Label::new( + "Do you want to send telemetry?", + theme.editor.hover_popover.prose.clone(), + ) + .boxed(), + ]) + .align_children_center() + .boxed(), + Flex::row() + .with_children([ + self.render_settings_checkbox::( + &theme.welcome.checkbox, + diagnostics, + cx, + |content, checked| content.telemetry.set_diagnostics(checked), + ), + Label::new( + "Send crash reports", + theme.editor.hover_popover.prose.clone(), + ) + .boxed(), + ]) + .align_children_center() + .boxed(), + ]) + .boxed(), + ) + .boxed() } } @@ -232,4 +209,11 @@ impl Item for WelcomePage { fn show_toolbar(&self) -> bool { false } + fn clone_on_split( + &self, + _workspace_id: WorkspaceId, + cx: &mut ViewContext, + ) -> Option { + Some(WelcomePage::new(cx)) + } } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 235df0202d..fe33996961 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -24,8 +24,8 @@ use gpui::{ keymap_matcher::KeymapContext, platform::{CursorStyle, NavigationDirection}, Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext, - ModelHandle, MouseButton, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View, - ViewContext, ViewHandle, WeakViewHandle, + ModelHandle, MouseButton, MouseRegion, MutableAppContext, PromptLevel, Quad, RenderContext, + Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use project::{Project, ProjectEntryId, ProjectPath}; use serde::Deserialize; @@ -1706,6 +1706,93 @@ impl NavHistory { } } +pub struct PaneBackdrop { + child_view: usize, + child: ElementBox, +} +impl PaneBackdrop { + pub fn new(pane_item_view: usize, child: ElementBox) -> Self { + PaneBackdrop { + child, + child_view: pane_item_view, + } + } +} + +impl Element for PaneBackdrop { + type LayoutState = (); + + type PaintState = (); + + fn layout( + &mut self, + constraint: gpui::SizeConstraint, + cx: &mut gpui::LayoutContext, + ) -> (Vector2F, Self::LayoutState) { + let size = self.child.layout(constraint, cx); + (size, ()) + } + + fn paint( + &mut self, + bounds: RectF, + visible_bounds: RectF, + _: &mut Self::LayoutState, + cx: &mut gpui::PaintContext, + ) -> Self::PaintState { + let background = cx.global::().theme.editor.background; + + let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); + + cx.scene.push_quad(gpui::Quad { + bounds: RectF::new(bounds.origin(), bounds.size()), + background: Some(background), + ..Default::default() + }); + + let child_view_id = self.child_view; + cx.scene.push_mouse_region( + MouseRegion::new::(child_view_id, 0, visible_bounds).on_down( + gpui::MouseButton::Left, + move |_, cx| { + let window_id = cx.window_id; + cx.focus(window_id, Some(child_view_id)) + }, + ), + ); + + cx.paint_layer(Some(bounds), |cx| { + self.child.paint(bounds.origin(), visible_bounds, cx) + }) + } + + fn rect_for_text_range( + &self, + range_utf16: std::ops::Range, + _bounds: RectF, + _visible_bounds: RectF, + _layout: &Self::LayoutState, + _paint: &Self::PaintState, + cx: &gpui::MeasurementContext, + ) -> Option { + self.child.rect_for_text_range(range_utf16, cx) + } + + fn debug( + &self, + _bounds: RectF, + _layout: &Self::LayoutState, + _paint: &Self::PaintState, + cx: &gpui::DebugContext, + ) -> serde_json::Value { + gpui::json::json!({ + "type": "Pane Back Drop", + "view": self.child_view, + "child": self.child.debug(cx), + }) + } +} + #[cfg(test)] mod tests { use std::sync::Arc; From 020a0965b0cd0a1d8dcf5c89ee61a9f44320afa3 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 6 Mar 2023 14:16:55 -0800 Subject: [PATCH 16/93] WIP --- crates/welcome/src/welcome.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index f1d59ce7a1..dcc72357ed 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -100,6 +100,9 @@ impl View for WelcomePage { .align_children_center() .boxed(), ]) + .aligned() + .constrained() + .with_max_width(300.) .boxed(), ) .boxed() From ba652fc0331cf1c4cc6c161133647ffa533e5d46 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 6 Mar 2023 16:28:23 -0800 Subject: [PATCH 17/93] Finish basic welcome experience --- crates/theme/src/theme.rs | 8 +- crates/welcome/src/welcome.rs | 135 +++++++++++++-------------- styles/src/styleTree/components.ts | 2 +- styles/src/styleTree/hoverPopover.ts | 78 ++++++++-------- styles/src/styleTree/welcome.ts | 59 ++++++------ 5 files changed, 143 insertions(+), 139 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 9442f08d24..43a10978d6 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -853,14 +853,18 @@ pub struct FeedbackStyle { #[derive(Clone, Deserialize, Default)] pub struct WelcomeStyle { + pub page_width: f32, + pub logo_subheading: ContainedText, pub checkbox: CheckboxStyle, pub button: Interactive, } #[derive(Clone, Deserialize, Default)] pub struct CheckboxStyle { - pub icon: String, - pub icon_color: Color, + pub check_icon: String, + pub check_icon_color: Color, + pub label: ContainedText, + pub container: ContainerStyle, pub width: f32, pub height: f32, pub default: ContainerStyle, diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index dcc72357ed..c24e0b5b24 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -34,6 +34,8 @@ impl View for WelcomePage { let settings = cx.global::(); let theme = settings.theme.clone(); + let width = theme.welcome.page_width; + let (diagnostics, metrics) = { let telemetry = settings.telemetry(); (telemetry.diagnostics(), telemetry.metrics()) @@ -46,63 +48,42 @@ impl View for WelcomePage { self_handle.id(), Flex::column() .with_children([ - Flex::row() - .with_children([ - Image::new("images/zed-logo-90x90.png") - .constrained() - .with_width(90.) - .with_height(90.) - .aligned() - .contained() - .boxed(), - // Label::new("Zed", theme.editor.hover_popover.prose.clone()).boxed(), - ]) + Image::new("images/zed-logo-90x90.png") + .constrained() + .with_width(90.) + .with_height(90.) + .aligned() + .contained() + .aligned() .boxed(), Label::new( "Code at the speed of thought", - theme.editor.hover_popover.prose.clone(), + theme.welcome.logo_subheading.text.clone(), ) + .aligned() + .contained() + .with_style(theme.welcome.logo_subheading.container) .boxed(), - self.render_cta_button(2, "Choose a theme", theme_selector::Toggle, cx), - self.render_cta_button(3, "Choose a keymap", theme_selector::Toggle, cx), - Flex::row() - .with_children([ - self.render_settings_checkbox::( - &theme.welcome.checkbox, - metrics, - cx, - |content, checked| { - content.telemetry.set_metrics(checked); - }, - ), - Label::new( - "Do you want to send telemetry?", - theme.editor.hover_popover.prose.clone(), - ) - .boxed(), - ]) - .align_children_center() - .boxed(), - Flex::row() - .with_children([ - self.render_settings_checkbox::( - &theme.welcome.checkbox, - diagnostics, - cx, - |content, checked| content.telemetry.set_diagnostics(checked), - ), - Label::new( - "Send crash reports", - theme.editor.hover_popover.prose.clone(), - ) - .boxed(), - ]) - .align_children_center() - .boxed(), + self.render_cta_button(2, "Choose a theme", theme_selector::Toggle, width, cx), + self.render_cta_button(3, "Choose a keymap", theme_selector::Toggle, width, cx), + self.render_settings_checkbox::( + "Do you want to send telemetry?", + &theme.welcome.checkbox, + metrics, + cx, + |content, checked| content.telemetry.set_metrics(checked), + ), + self.render_settings_checkbox::( + "Send crash reports", + &theme.welcome.checkbox, + diagnostics, + cx, + |content, checked| content.telemetry.set_diagnostics(checked), + ), ]) - .aligned() .constrained() - .with_max_width(300.) + .with_max_width(width) + .aligned() .boxed(), ) .boxed() @@ -129,6 +110,7 @@ impl WelcomePage { region_id: usize, label: L, action: A, + width: f32, cx: &mut RenderContext, ) -> ElementBox where @@ -139,19 +121,23 @@ impl WelcomePage { MouseEventHandler::::new(region_id, cx, |state, _| { let style = theme.welcome.button.style_for(state, false); Label::new(label, style.text.clone()) + .aligned() .contained() .with_style(style.container) + .constrained() + .with_width(width) .boxed() }) .on_click(MouseButton::Left, move |_, cx| { cx.dispatch_action(action.clone()) }) - .aligned() + .with_cursor_style(gpui::CursorStyle::PointingHand) .boxed() } fn render_settings_checkbox( &self, + label: &'static str, style: &CheckboxStyle, checked: bool, cx: &mut RenderContext, @@ -159,35 +145,44 @@ impl WelcomePage { ) -> ElementBox { MouseEventHandler::::new(0, cx, |state, _| { let indicator = if checked { - Svg::new(style.icon.clone()) - .with_color(style.icon_color) + Svg::new(style.check_icon.clone()) + .with_color(style.check_icon_color) .constrained() } else { Empty::new().constrained() }; - indicator - .with_width(style.width) - .with_height(style.height) - .contained() - .with_style(if checked { - if state.hovered() { - style.hovered_and_checked - } else { - style.checked - } - } else { - if state.hovered() { - style.hovered - } else { - style.default - } - }) + Flex::row() + .with_children([ + indicator + .with_width(style.width) + .with_height(style.height) + .contained() + .with_style(if checked { + if state.hovered() { + style.hovered_and_checked + } else { + style.checked + } + } else { + if state.hovered() { + style.hovered + } else { + style.default + } + }) + .boxed(), + Label::new(label, style.label.text.clone()).contained().with_style(style.label.container).boxed(), + ]) + .align_children_center() .boxed() }) .on_click(gpui::MouseButton::Left, move |_, cx| { SettingsFile::update(cx, move |content| set_value(content, !checked)) }) + .with_cursor_style(gpui::CursorStyle::PointingHand) + .contained() + .with_style(style.container) .boxed() } } diff --git a/styles/src/styleTree/components.ts b/styles/src/styleTree/components.ts index edbced8323..3f69df981e 100644 --- a/styles/src/styleTree/components.ts +++ b/styles/src/styleTree/components.ts @@ -93,7 +93,7 @@ interface Text { underline?: boolean } -interface TextProperties { +export interface TextProperties { size?: keyof typeof fontSizes weight?: FontWeight underline?: boolean diff --git a/styles/src/styleTree/hoverPopover.ts b/styles/src/styleTree/hoverPopover.ts index 032c53112b..a2a4467d7c 100644 --- a/styles/src/styleTree/hoverPopover.ts +++ b/styles/src/styleTree/hoverPopover.ts @@ -2,44 +2,44 @@ import { ColorScheme } from "../themes/common/colorScheme" import { background, border, text } from "./components" export default function HoverPopover(colorScheme: ColorScheme) { - let layer = colorScheme.middle - let baseContainer = { - background: background(layer), - cornerRadius: 8, - padding: { - left: 8, - right: 8, - top: 4, - bottom: 4, - }, - shadow: colorScheme.popoverShadow, - border: border(layer), - margin: { - left: -8, - }, - } + let layer = colorScheme.middle + let baseContainer = { + background: background(layer), + cornerRadius: 8, + padding: { + left: 8, + right: 8, + top: 4, + bottom: 4, + }, + shadow: colorScheme.popoverShadow, + border: border(layer), + margin: { + left: -8, + }, + } - return { - container: baseContainer, - infoContainer: { - ...baseContainer, - background: background(layer, "accent"), - border: border(layer, "accent"), - }, - warningContainer: { - ...baseContainer, - background: background(layer, "warning"), - border: border(layer, "warning"), - }, - errorContainer: { - ...baseContainer, - background: background(layer, "negative"), - border: border(layer, "negative"), - }, - block_style: { - padding: { top: 4 }, - }, - prose: text(layer, "sans", { size: "sm" }), - highlight: colorScheme.ramps.neutral(0.5).alpha(0.2).hex(), // TODO: blend was used here. Replace with something better - } + return { + container: baseContainer, + infoContainer: { + ...baseContainer, + background: background(layer, "accent"), + border: border(layer, "accent"), + }, + warningContainer: { + ...baseContainer, + background: background(layer, "warning"), + border: border(layer, "warning"), + }, + errorContainer: { + ...baseContainer, + background: background(layer, "negative"), + border: border(layer, "negative"), + }, + block_style: { + padding: { top: 4 }, + }, + prose: text(layer, "sans", { size: "sm" }), + highlight: colorScheme.ramps.neutral(0.5).alpha(0.2).hex(), // TODO: blend was used here. Replace with something better + } } diff --git a/styles/src/styleTree/welcome.ts b/styles/src/styleTree/welcome.ts index 02e3c11f91..bfd67cec8d 100644 --- a/styles/src/styleTree/welcome.ts +++ b/styles/src/styleTree/welcome.ts @@ -1,12 +1,11 @@ import { ColorScheme } from "../themes/common/colorScheme"; -import { border, background, foreground, text } from "./components"; +import { border, background, foreground, text, TextProperties } from "./components"; export default function welcome(colorScheme: ColorScheme) { let layer = colorScheme.highest; - // TODO let checkboxBase = { cornerRadius: 4, padding: { @@ -18,20 +17,30 @@ export default function welcome(colorScheme: ColorScheme) { shadow: colorScheme.popoverShadow, border: border(layer), margin: { - left: 8, right: 8, top: 5, bottom: 5 }, }; + + let interactive_text_size: TextProperties = { size: "md" } return { + pageWidth: 450, + logoSubheading: { + ...text(layer, "sans", { size: "lg" }), + margin: { + top: 10, + bottom: 7, + }, + }, button: { background: background(layer), - border: border(layer), - cornerRadius: 6, + border: border(layer, "active"), + cornerRadius: 4, margin: { - top: 1, + top: 8, + bottom: 7 }, padding: { top: 1, @@ -39,50 +48,46 @@ export default function welcome(colorScheme: ColorScheme) { left: 7, right: 7, }, - ...text(layer, "sans", "variant", { size: "xs" }), + ...text(layer, "sans", "hovered", interactive_text_size), hover: { - ...text(layer, "sans", "hovered", { size: "xs" }), + ...text(layer, "sans", "hovered", interactive_text_size), background: background(layer, "hovered"), border: border(layer, "hovered"), }, }, checkbox: { + label: { + ...text(layer, "sans", interactive_text_size), + // Also supports margin, container, border, etc. + }, + container: { + margin: { + top: 5, + }, + }, width: 12, height: 12, - icon: "icons/check_12.svg", - iconColor: foreground(layer, "on"), + checkIcon: "icons/check_12.svg", + checkIconColor: foreground(layer, "on"), default: { ...checkboxBase, background: background(layer, "default"), - border: { - color: foreground(layer, "hovered"), - width: 1, - } + border: border(layer, "active") }, checked: { ...checkboxBase, background: background(layer, "hovered"), - border: { - color: foreground(layer, "hovered"), - width: 1, - } + border: border(layer, "active") }, hovered: { ...checkboxBase, background: background(layer, "hovered"), - - border: { - color: foreground(layer, "hovered"), - width: 1, - } + border: border(layer, "hovered") }, hoveredAndChecked: { ...checkboxBase, background: background(layer, "hovered"), - border: { - color: foreground(layer, "hovered"), - width: 1, - } + border: border(layer, "active") } } } From 1f6bd0ea775875d64393093b112140f3c2eedb19 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 6 Mar 2023 16:35:15 -0800 Subject: [PATCH 18/93] Fix edge case where the welcome page might open in the dock if the user's actions race the welcome experience action --- crates/workspace/src/workspace.rs | 17 +++++++++++++++++ crates/zed/src/zed.rs | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 442d28579f..cd2cd6fccb 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1349,6 +1349,23 @@ impl Workspace { pane } + pub fn add_item_to_center( + &mut self, + item: Box, + cx: &mut ViewContext, + ) -> bool { + if let Some(center_pane) = self.last_active_center_pane.clone() { + if let Some(center_pane) = center_pane.upgrade(cx) { + Pane::add_item(self, ¢er_pane, item, true, true, None, cx); + true + } else { + false + } + } else { + false + } + } + pub fn add_item(&mut self, item: Box, cx: &mut ViewContext) { let active_pane = self.active_pane().clone(); Pane::add_item(self, &active_pane, item, true, true, None, cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 5d13d41bba..a9032a264b 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -266,7 +266,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { open_new(&app_state, cx, |workspace, cx| { workspace.toggle_sidebar(SidebarSide::Left, cx); let welcome_page = cx.add_view(|cx| welcome::WelcomePage::new(cx)); - workspace.add_item(Box::new(welcome_page.clone()), cx); + workspace.add_item_to_center(Box::new(welcome_page.clone()), cx); Dock::move_dock(workspace, settings::DockAnchor::Bottom, false, cx); cx.focus(welcome_page); cx.notify(); From 8db7e17ac51266c60fdb610ecad4efc81627210f Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 6 Mar 2023 17:55:58 -0800 Subject: [PATCH 19/93] Move install_cli function to a seperate crate Add install cli button to welcome experience Add toast pop ups for CLI installation status --- Cargo.lock | 14 +++++++ Cargo.toml | 1 + crates/install_cli/Cargo.toml | 18 +++++++++ crates/install_cli/src/install_cli.rs | 56 ++++++++++++++++++++++++++ crates/welcome/Cargo.toml | 1 + crates/welcome/src/welcome.rs | 1 + crates/workspace/Cargo.toml | 1 + crates/workspace/src/notifications.rs | 28 +++++++++---- crates/workspace/src/workspace.rs | 26 +++++++++++- crates/zed/Cargo.toml | 1 + crates/zed/src/menus.rs | 2 +- crates/zed/src/zed.rs | 57 ++------------------------- 12 files changed, 142 insertions(+), 64 deletions(-) create mode 100644 crates/install_cli/Cargo.toml create mode 100644 crates/install_cli/src/install_cli.rs diff --git a/Cargo.lock b/Cargo.lock index c25656bfa9..2b894eff35 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3018,6 +3018,17 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" +[[package]] +name = "install_cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "gpui", + "log", + "smol", + "util", +] + [[package]] name = "instant" version = "0.1.12" @@ -8020,6 +8031,7 @@ dependencies = [ "anyhow", "editor", "gpui", + "install_cli", "log", "project", "settings", @@ -8304,6 +8316,7 @@ dependencies = [ "futures 0.3.25", "gpui", "indoc", + "install_cli", "language", "lazy_static", "log", @@ -8412,6 +8425,7 @@ dependencies = [ "ignore", "image", "indexmap", + "install_cli", "isahc", "journal", "language", diff --git a/Cargo.toml b/Cargo.toml index feb80633c4..8361895146 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ members = [ "crates/go_to_line", "crates/gpui", "crates/gpui_macros", + "crates/install_cli", "crates/journal", "crates/language", "crates/live_kit_client", diff --git a/crates/install_cli/Cargo.toml b/crates/install_cli/Cargo.toml new file mode 100644 index 0000000000..bbbe989920 --- /dev/null +++ b/crates/install_cli/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "install_cli" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/install_cli.rs" + +[features] +test-support = [] + +[dependencies] +smol = "1.2.5" +anyhow = "1.0.38" +log = "0.4" +gpui = { path = "../gpui" } +util = { path = "../util" } diff --git a/crates/install_cli/src/install_cli.rs b/crates/install_cli/src/install_cli.rs new file mode 100644 index 0000000000..06561a28f8 --- /dev/null +++ b/crates/install_cli/src/install_cli.rs @@ -0,0 +1,56 @@ +use std::path::Path; + +use anyhow::{Result, anyhow}; +use gpui::{AsyncAppContext, actions}; +use util::ResultExt; + +actions!(cli, [ Install ]); + + +pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> { + let cli_path = cx.platform().path_for_auxiliary_executable("cli")?; + let link_path = Path::new("/usr/local/bin/zed"); + let bin_dir_path = link_path.parent().unwrap(); + + // Don't re-create symlink if it points to the same CLI binary. + if smol::fs::read_link(link_path).await.ok().as_ref() == Some(&cli_path) { + return Ok(()); + } + + // If the symlink is not there or is outdated, first try replacing it + // without escalating. + smol::fs::remove_file(link_path).await.log_err(); + if smol::fs::unix::symlink(&cli_path, link_path) + .await + .log_err() + .is_some() + { + return Ok(()); + } + + // The symlink could not be created, so use osascript with admin privileges + // to create it. + let status = smol::process::Command::new("osascript") + .args([ + "-e", + &format!( + "do shell script \" \ + mkdir -p \'{}\' && \ + ln -sf \'{}\' \'{}\' \ + \" with administrator privileges", + bin_dir_path.to_string_lossy(), + cli_path.to_string_lossy(), + link_path.to_string_lossy(), + ), + ]) + .stdout(smol::process::Stdio::inherit()) + .stderr(smol::process::Stdio::inherit()) + .output() + .await? + .status; + if status.success() { + Ok(()) + } else { + Err(anyhow!("error running osascript")) + } +} \ No newline at end of file diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index e1231ad5f6..e76636411b 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -15,6 +15,7 @@ anyhow = "1.0.38" log = "0.4" editor = { path = "../editor" } gpui = { path = "../gpui" } +install_cli = { path = "../install_cli" } project = { path = "../project" } settings = { path = "../settings" } theme = { path = "../theme" } diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index c24e0b5b24..c6316c61d5 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -66,6 +66,7 @@ impl View for WelcomePage { .boxed(), self.render_cta_button(2, "Choose a theme", theme_selector::Toggle, width, cx), self.render_cta_button(3, "Choose a keymap", theme_selector::Toggle, width, cx), + self.render_cta_button(4, "Install the CLI", install_cli::Install, width, cx), self.render_settings_checkbox::( "Do you want to send telemetry?", &theme.welcome.checkbox, diff --git a/crates/workspace/Cargo.toml b/crates/workspace/Cargo.toml index fc069fe6c8..2ba7a6cc40 100644 --- a/crates/workspace/Cargo.toml +++ b/crates/workspace/Cargo.toml @@ -27,6 +27,7 @@ context_menu = { path = "../context_menu" } drag_and_drop = { path = "../drag_and_drop" } fs = { path = "../fs" } gpui = { path = "../gpui" } +install_cli = { path = "../install_cli" } language = { path = "../language" } menu = { path = "../menu" } project = { path = "../project" } diff --git a/crates/workspace/src/notifications.rs b/crates/workspace/src/notifications.rs index 141a345382..76f46f83c5 100644 --- a/crates/workspace/src/notifications.rs +++ b/crates/workspace/src/notifications.rs @@ -122,6 +122,8 @@ impl Workspace { pub mod simple_message_notification { + use std::borrow::Cow; + use gpui::{ actions, elements::{Flex, MouseEventHandler, Padding, ParentElement, Svg, Text}, @@ -153,9 +155,9 @@ pub mod simple_message_notification { } pub struct MessageNotification { - message: String, + message: Cow<'static, str>, click_action: Option>, - click_message: Option, + click_message: Option>, } pub enum MessageNotificationEvent { @@ -167,23 +169,23 @@ pub mod simple_message_notification { } impl MessageNotification { - pub fn new_message>(message: S) -> MessageNotification { + pub fn new_message>>(message: S) -> MessageNotification { Self { - message: message.as_ref().to_string(), + message: message.into(), click_action: None, click_message: None, } } - pub fn new, A: Action, S2: AsRef>( + pub fn new>, A: Action, S2: Into>>( message: S1, click_action: A, click_message: S2, ) -> Self { Self { - message: message.as_ref().to_string(), + message: message.into(), click_action: Some(Box::new(click_action) as Box), - click_message: Some(click_message.as_ref().to_string()), + click_message: Some(click_message.into()), } } @@ -210,6 +212,8 @@ pub mod simple_message_notification { let click_message = self.click_message.as_ref().map(|message| message.clone()); let message = self.message.clone(); + let has_click_action = click_action.is_some(); + MouseEventHandler::::new(0, cx, |state, cx| { Flex::column() .with_child( @@ -243,6 +247,7 @@ pub mod simple_message_notification { .on_click(MouseButton::Left, move |_, cx| { cx.dispatch_action(CancelMessageNotification) }) + .with_cursor_style(CursorStyle::PointingHand) .aligned() .constrained() .with_height( @@ -272,12 +277,19 @@ pub mod simple_message_notification { .contained() .boxed() }) - .with_cursor_style(CursorStyle::PointingHand) + // Since we're not using a proper overlay, we have to capture these extra events + .on_down(MouseButton::Left, |_, _| {}) + .on_up(MouseButton::Left, |_, _| {}) .on_click(MouseButton::Left, move |_, cx| { if let Some(click_action) = click_action.as_ref() { cx.dispatch_any_action(click_action.boxed_clone()) } }) + .with_cursor_style(if has_click_action { + CursorStyle::PointingHand + } else { + CursorStyle::Arrow + }) .boxed() } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index cd2cd6fccb..234c40c69d 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -17,7 +17,7 @@ mod toolbar; pub use smallvec; -use anyhow::{anyhow, Result}; +use anyhow::{anyhow, Result, Context}; use call::ActiveCall; use client::{ proto::{self, PeerId}, @@ -65,7 +65,7 @@ use crate::{ }; use lazy_static::lazy_static; use log::{error, warn}; -use notifications::NotificationHandle; +use notifications::{NotificationHandle, NotifyResultExt}; pub use pane::*; pub use pane_group::*; use persistence::{model::SerializedItem, DB}; @@ -267,6 +267,28 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { }) }, ); + + cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| { + cx.spawn(|workspace, mut cx| async move { + let err = install_cli::install_cli(&cx).await.context("Failed to create CLI symlink"); + + cx.update(|cx| { + workspace.update(cx, |workspace, cx| { + if matches!(err, Err(_)) { + err.notify_err(workspace, cx); + } else { + workspace.show_notification(1, cx, |cx| { + cx.add_view(|_| MessageNotification::new_message("Successfully installed the 'zed' binary")) + }); + } + }) + }) + + }).detach(); + + + + }); let client = &app_state.client; client.add_view_request_handler(Workspace::handle_follow); diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 68b04c7e2f..8589af77e0 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -39,6 +39,7 @@ fsevent = { path = "../fsevent" } fuzzy = { path = "../fuzzy" } go_to_line = { path = "../go_to_line" } gpui = { path = "../gpui" } +install_cli = { path = "../install_cli" } journal = { path = "../journal" } language = { path = "../language" } lsp = { path = "../lsp" } diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index bb519c7a95..9381a90907 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -19,7 +19,7 @@ pub fn menus() -> Vec> { MenuItem::action("Select Theme", theme_selector::Toggle), ], }), - MenuItem::action("Install CLI", super::InstallCommandLineInterface), + MenuItem::action("Install CLI", install_cli::Install), MenuItem::separator(), MenuItem::action("Hide Zed", super::Hide), MenuItem::action("Hide Others", super::HideOthers), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index a9032a264b..71e4b48db3 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -2,7 +2,7 @@ pub mod languages; pub mod menus; #[cfg(any(test, feature = "test-support"))] pub mod test; -use anyhow::{anyhow, Context, Result}; +use anyhow::Context; use assets::Assets; use breadcrumbs::Breadcrumbs; pub use client; @@ -21,7 +21,7 @@ use gpui::{ geometry::vector::vec2f, impl_actions, platform::{WindowBounds, WindowOptions}, - AssetSource, AsyncAppContext, Platform, PromptLevel, TitlebarOptions, ViewContext, WindowKind, + AssetSource, Platform, PromptLevel, TitlebarOptions, ViewContext, WindowKind, }; use language::Rope; pub use lsp; @@ -68,7 +68,6 @@ actions!( IncreaseBufferFontSize, DecreaseBufferFontSize, ResetBufferFontSize, - InstallCommandLineInterface, ResetDatabase, WelcomeExperience ] @@ -144,8 +143,8 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { cx.refresh_windows(); }); }); - cx.add_global_action(move |_: &InstallCommandLineInterface, cx| { - cx.spawn(|cx| async move { install_cli(&cx).await.context("error creating CLI symlink") }) + cx.add_global_action(move |_: &install_cli::Install, cx| { + cx.spawn(|cx| async move { install_cli::install_cli(&cx).await.context("error creating CLI symlink") }) .detach_and_log_err(cx); }); cx.add_action({ @@ -505,54 +504,6 @@ fn about(_: &mut Workspace, _: &About, cx: &mut gpui::ViewContext) { ); } -async fn install_cli(cx: &AsyncAppContext) -> Result<()> { - let cli_path = cx.platform().path_for_auxiliary_executable("cli")?; - let link_path = Path::new("/usr/local/bin/zed"); - let bin_dir_path = link_path.parent().unwrap(); - - // Don't re-create symlink if it points to the same CLI binary. - if smol::fs::read_link(link_path).await.ok().as_ref() == Some(&cli_path) { - return Ok(()); - } - - // If the symlink is not there or is outdated, first try replacing it - // without escalating. - smol::fs::remove_file(link_path).await.log_err(); - if smol::fs::unix::symlink(&cli_path, link_path) - .await - .log_err() - .is_some() - { - return Ok(()); - } - - // The symlink could not be created, so use osascript with admin privileges - // to create it. - let status = smol::process::Command::new("osascript") - .args([ - "-e", - &format!( - "do shell script \" \ - mkdir -p \'{}\' && \ - ln -sf \'{}\' \'{}\' \ - \" with administrator privileges", - bin_dir_path.to_string_lossy(), - cli_path.to_string_lossy(), - link_path.to_string_lossy(), - ), - ]) - .stdout(smol::process::Stdio::inherit()) - .stderr(smol::process::Stdio::inherit()) - .output() - .await? - .status; - if status.success() { - Ok(()) - } else { - Err(anyhow!("error running osascript")) - } -} - fn open_config_file( path: &'static Path, app_state: Arc, From 3b31f10c6f5c636a23dd172813be64f632298739 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Mon, 6 Mar 2023 18:36:18 -0800 Subject: [PATCH 20/93] Made the theme picker sort from dark to light Added a layer into 'ConstrainedBox' to clip it 's children Made the welcome experience responsive to small and large sizes --- crates/gpui/src/elements/constrained_box.rs | 4 +++- crates/picker/src/picker.rs | 5 ++++- crates/theme_selector/src/theme_selector.rs | 1 - crates/welcome/src/welcome.rs | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/gpui/src/elements/constrained_box.rs b/crates/gpui/src/elements/constrained_box.rs index 2e232c6197..e4d51f5730 100644 --- a/crates/gpui/src/elements/constrained_box.rs +++ b/crates/gpui/src/elements/constrained_box.rs @@ -153,7 +153,9 @@ impl Element for ConstrainedBox { _: &mut Self::LayoutState, cx: &mut PaintContext, ) -> Self::PaintState { - self.child.paint(bounds.origin(), visible_bounds, cx); + cx.paint_layer(Some(visible_bounds), |cx| { + self.child.paint(bounds.origin(), visible_bounds, cx); + }) } fn rect_for_text_range( diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index e4d062d575..fe4b75dbef 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -102,7 +102,10 @@ impl View for Picker { .read(cx) .render_match(ix, state, ix == selected_ix, cx) }) - .on_down(MouseButton::Left, move |_, cx| { + // Capture mouse events + .on_down(MouseButton::Left, |_, _| {}) + .on_up(MouseButton::Left, |_, _| {}) + .on_click(MouseButton::Left, move |_, cx| { cx.dispatch_action(SelectIndex(ix)) }) .with_cursor_style(CursorStyle::PointingHand) diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index d999730a0d..0930bf0484 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -50,7 +50,6 @@ impl ThemeSelector { theme_names.sort_unstable_by(|a, b| { a.is_light .cmp(&b.is_light) - .reverse() .then(a.name.cmp(&b.name)) }); let matches = theme_names diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index c6316c61d5..dc864a3ad1 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -84,6 +84,7 @@ impl View for WelcomePage { ]) .constrained() .with_max_width(width) + .contained().with_uniform_padding(10.) .aligned() .boxed(), ) @@ -126,7 +127,7 @@ impl WelcomePage { .contained() .with_style(style.container) .constrained() - .with_width(width) + .with_max_width(width) .boxed() }) .on_click(MouseButton::Left, move |_, cx| { From 1e5aff9e5167038b28ebd040a48d3a3c858ac73b Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 7 Mar 2023 12:23:18 -0500 Subject: [PATCH 21/93] Update collab integration test to new reconnect timeout --- crates/collab/src/tests/integration_tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 1ab78ac310..28266824e3 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -829,7 +829,7 @@ async fn test_server_restarts( // Users A and B reconnect to the call. User C has troubles reconnecting, so it leaves the room. client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending())); - deterministic.advance_clock(RECEIVE_TIMEOUT); + deterministic.advance_clock(RECONNECT_TIMEOUT); assert_eq!( room_participants(&room_a, cx_a), RoomParticipants { @@ -1001,7 +1001,7 @@ async fn test_server_restarts( client_a.override_establish_connection(|_, cx| cx.spawn(|_| future::pending())); client_b.override_establish_connection(|_, cx| cx.spawn(|_| future::pending())); client_c.override_establish_connection(|_, cx| cx.spawn(|_| future::pending())); - deterministic.advance_clock(RECEIVE_TIMEOUT); + deterministic.advance_clock(RECONNECT_TIMEOUT); assert_eq!( room_participants(&room_a, cx_a), RoomParticipants { From 19fc14320997a29ad3af2f5d89fe0b339dffebb1 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 7 Mar 2023 12:19:51 -0800 Subject: [PATCH 22/93] Add base keymap setting Format all files Co-Authored-by: Nathan --- assets/keymaps/atom.json | 68 ++++++++++++++++++ assets/keymaps/jetbrains.json | 78 +++++++++++++++++++++ assets/keymaps/sublime_text.json | 60 ++++++++++++++++ crates/install_cli/src/install_cli.rs | 9 ++- crates/settings/src/keymap_file.rs | 6 +- crates/settings/src/settings.rs | 24 +++++++ crates/settings/src/watched_json.rs | 31 ++++++-- crates/theme_selector/src/theme_selector.rs | 6 +- crates/welcome/src/welcome.rs | 8 ++- crates/workspace/src/workspace.rs | 23 +++--- crates/zed/src/main.rs | 13 ++-- crates/zed/src/zed.rs | 10 ++- 12 files changed, 301 insertions(+), 35 deletions(-) create mode 100644 assets/keymaps/atom.json create mode 100644 assets/keymaps/jetbrains.json create mode 100644 assets/keymaps/sublime_text.json diff --git a/assets/keymaps/atom.json b/assets/keymaps/atom.json new file mode 100644 index 0000000000..766c46c133 --- /dev/null +++ b/assets/keymaps/atom.json @@ -0,0 +1,68 @@ +[ + { + "bindings": { + "cmd-k cmd-p": "workspace::ActivatePreviousPane", + "cmd-k cmd-n": "workspace::ActivateNextPane" + } + }, + { + "context": "Editor", + "bindings": { + "cmd-b": "editor::GoToDefinition", + "cmd-<": "editor::ScrollCursorCenter", + "cmd-g": [ + "editor::SelectNext", + { + "replace_newest": true + } + ], + "ctrl-shift-down": "editor::AddSelectionBelow", + "ctrl-shift-up": "editor::AddSelectionAbove", + "cmd-shift-backspace": "editor::DeleteToBeginningOfLine" + } + }, + { + "context": "Editor && mode == full", + "bindings": { + "cmd-r": "outline::Toggle" + } + }, + { + "context": "BufferSearchBar", + "bindings": { + "cmd-f3": "search::SelectNextMatch", + "cmd-shift-f3": "search::SelectPrevMatch" + } + }, + { + "context": "Workspace", + "bindings": { + "cmd-\\": "workspace::ToggleLeftSidebar", + "cmd-k cmd-b": "workspace::ToggleLeftSidebar", + "cmd-t": "file_finder::Toggle", + "cmd-shift-r": "project_symbols::Toggle" + } + }, + { + "context": "Pane", + "bindings": { + "alt-cmd-/": "search::ToggleRegex", + "ctrl-0": "project_panel::ToggleFocus" + } + }, + { + "context": "ProjectPanel", + "bindings": { + "ctrl-[": "project_panel::CollapseSelectedEntry", + "ctrl-b": "project_panel::CollapseSelectedEntry", + "h": "project_panel::CollapseSelectedEntry", + "ctrl-]": "project_panel::ExpandSelectedEntry", + "ctrl-f": "project_panel::ExpandSelectedEntry", + "ctrl-shift-c": "project_panel::CopyPath" + } + }, + { + "context": "Dock", + "bindings": {} + } +] \ No newline at end of file diff --git a/assets/keymaps/jetbrains.json b/assets/keymaps/jetbrains.json new file mode 100644 index 0000000000..2e6e5e77e6 --- /dev/null +++ b/assets/keymaps/jetbrains.json @@ -0,0 +1,78 @@ +[ + { + "bindings": { + "cmd-shift-[": "pane::ActivatePrevItem", + "cmd-shift-]": "pane::ActivateNextItem" + } + }, + { + "context": "Editor", + "bindings": { + "ctrl->": "zed::IncreaseBufferFontSize", + "ctrl-<": "zed::DecreaseBufferFontSize", + "cmd-d": "editor::DuplicateLine", + "cmd-pagedown": "editor::MovePageDown", + "cmd-pageup": "editor::MovePageUp", + "ctrl-alt-shift-b": "editor::SelectToPreviousWordStart", + "shift-enter": "editor::NewlineBelow", + "cmd--": "editor::Fold", + "cmd-=": "editor::UnfoldLines", + "alt-shift-g": "editor::SplitSelectionIntoLines", + "ctrl-g": [ + "editor::SelectNext", + { + "replace_newest": false + } + ], + "cmd-/": [ + "editor::ToggleComments", + { + "advance_downwards": true + } + ], + "shift-alt-up": "editor::MoveLineUp", + "shift-alt-down": "editor::MoveLineDown", + "cmd-[": "pane::GoBack", + "cmd-]": "pane::GoForward", + "alt-f7": "editor::FindAllReferences", + "cmd-alt-f7": "editor::FindAllReferences", + "cmd-b": "editor::GoToDefinition", + "cmd-alt-b": "editor::GoToDefinition", + "cmd-shift-b": "editor::GoToTypeDefinition", + "alt-enter": "editor::ToggleCodeActions", + "f2": "editor::GoToDiagnostic", + "cmd-f2": "editor::GoToPrevDiagnostic", + "ctrl-alt-shift-down": "editor::GoToHunk", + "ctrl-alt-shift-up": "editor::GoToPrevHunk", + "cmd-home": "editor::MoveToBeginning", + "cmd-end": "editor::MoveToEnd", + "cmd-shift-home": "editor::SelectToBeginning", + "cmd-shift-end": "editor::SelectToEnd" + } + }, + { + "context": "Editor && mode == full", + "bindings": { + "cmd-f12": "outline::Toggle", + "cmd-7": "outline::Toggle", + "cmd-shift-o": "file_finder::Toggle", + "cmd-l": "go_to_line::Toggle" + } + }, + { + "context": "Workspace", + "bindings": { + "cmd-shift-a": "command_palette::Toggle", + "cmd-alt-o": "project_symbols::Toggle", + "cmd-1": "workspace::ToggleLeftSidebar", + "cmd-6": "diagnostics::Deploy", + "alt-f12": "dock::FocusDock" + } + }, + { + "context": "Dock", + "bindings": { + "alt-f12": "dock::HideDock" + } + } +] \ No newline at end of file diff --git a/assets/keymaps/sublime_text.json b/assets/keymaps/sublime_text.json new file mode 100644 index 0000000000..1d3dd887d7 --- /dev/null +++ b/assets/keymaps/sublime_text.json @@ -0,0 +1,60 @@ +[ + { + "bindings": { + "cmd-shift-[": "pane::ActivatePrevItem", + "cmd-shift-]": "pane::ActivateNextItem", + "ctrl-pagedown": "pane::ActivatePrevItem", + "ctrl-pageup": "pane::ActivateNextItem", + "ctrl-shift-tab": "pane::ActivateNextItem", + "ctrl-tab": "pane::ActivatePrevItem", + "cmd-+": "zed::IncreaseBufferFontSize" + } + }, + { + "context": "Editor", + "bindings": { + "ctrl-shift-up": "editor::AddSelectionAbove", + "ctrl-shift-down": "editor::AddSelectionBelow", + "cmd-shift-space": "editor::SelectAll", + "ctrl-shift-m": "editor::SelectLargerSyntaxNode", + "cmd-shift-a": "editor::SelectLargerSyntaxNode", + "shift-f12": "editor::FindAllReferences", + "alt-cmd-down": "editor::GoToDefinition", + "alt-shift-cmd-down": "editor::FindAllReferences", + "ctrl-.": "editor::GoToHunk", + "ctrl-,": "editor::GoToPrevHunk", + "ctrl-backspace": "editor::DeleteToPreviousWordStart", + "ctrl-delete": "editor::DeleteToNextWordEnd" + } + }, + { + "context": "Editor && mode == full", + "bindings": { + "cmd-r": "outline::Toggle" + } + }, + { + "context": "Pane", + "bindings": { + "f4": "search::SelectNextMatch", + "shift-f4": "search::SelectPrevMatch" + } + }, + { + "context": "Workspace", + "bindings": { + "ctrl-`": "dock::FocusDock", + "cmd-k cmd-b": "workspace::ToggleLeftSidebar", + "cmd-t": "file_finder::Toggle", + "shift-cmd-r": "project_symbols::Toggle", + // Currently busted: https://github.com/zed-industries/feedback/issues/898 + "ctrl-0": "project_panel::ToggleFocus" + } + }, + { + "context": "Dock", + "bindings": { + "ctrl-`": "dock::HideDock" + } + } +] \ No newline at end of file diff --git a/crates/install_cli/src/install_cli.rs b/crates/install_cli/src/install_cli.rs index 06561a28f8..adf50586d7 100644 --- a/crates/install_cli/src/install_cli.rs +++ b/crates/install_cli/src/install_cli.rs @@ -1,11 +1,10 @@ use std::path::Path; -use anyhow::{Result, anyhow}; -use gpui::{AsyncAppContext, actions}; +use anyhow::{anyhow, Result}; +use gpui::{actions, AsyncAppContext}; use util::ResultExt; -actions!(cli, [ Install ]); - +actions!(cli, [Install]); pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> { let cli_path = cx.platform().path_for_auxiliary_executable("cli")?; @@ -53,4 +52,4 @@ pub async fn install_cli(cx: &AsyncAppContext) -> Result<()> { } else { Err(anyhow!("error running osascript")) } -} \ No newline at end of file +} diff --git a/crates/settings/src/keymap_file.rs b/crates/settings/src/keymap_file.rs index 01992d9431..9048719d3f 100644 --- a/crates/settings/src/keymap_file.rs +++ b/crates/settings/src/keymap_file.rs @@ -1,4 +1,4 @@ -use crate::parse_json_with_comments; +use crate::{parse_json_with_comments, Settings}; use anyhow::{Context, Result}; use assets::Assets; use collections::BTreeMap; @@ -45,6 +45,10 @@ impl KeymapFileContent { for path in ["keymaps/default.json", "keymaps/vim.json"] { Self::load(path, cx).unwrap(); } + + if let Some(base_keymap) = cx.global::().base_keymap { + Self::load(base_keymap.asset_path(), cx).log_err(); + } } pub fn load(asset_path: &str, cx: &mut MutableAppContext) -> Result<()> { diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index 501a88c42e..ae67428948 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -24,6 +24,7 @@ use tree_sitter::Query; use util::ResultExt as _; pub use keymap_file::{keymap_file_json_schema, KeymapFileContent}; +pub use watched_json::watch_files; #[derive(Clone)] pub struct Settings { @@ -54,6 +55,24 @@ pub struct Settings { pub telemetry_defaults: TelemetrySettings, pub telemetry_overrides: TelemetrySettings, pub auto_update: bool, + pub base_keymap: Option, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub enum BaseKeymap { + JetBrains, + Sublime, + Atom, +} + +impl BaseKeymap { + pub fn asset_path(&self) -> &str { + match self { + BaseKeymap::JetBrains => "keymaps/jetbrains.json", + BaseKeymap::Sublime => "keymaps/sublime_text.json", + BaseKeymap::Atom => "keymaps/atom.json", + } + } } #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] @@ -326,6 +345,8 @@ pub struct SettingsFileContent { pub telemetry: TelemetrySettings, #[serde(default)] pub auto_update: Option, + #[serde(default)] + pub base_keymap: Option, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] @@ -396,6 +417,7 @@ impl Settings { telemetry_defaults: defaults.telemetry, telemetry_overrides: Default::default(), auto_update: defaults.auto_update.unwrap(), + base_keymap: Default::default(), } } @@ -433,6 +455,7 @@ impl Settings { merge(&mut self.vim_mode, data.vim_mode); merge(&mut self.autosave, data.autosave); merge(&mut self.default_dock_anchor, data.default_dock_anchor); + merge(&mut self.base_keymap, Some(data.base_keymap)); // Ensure terminal font is loaded, so we can request it in terminal_element layout if let Some(terminal_font) = &data.terminal.font_family { @@ -610,6 +633,7 @@ impl Settings { }, telemetry_overrides: Default::default(), auto_update: true, + base_keymap: None, } } diff --git a/crates/settings/src/watched_json.rs b/crates/settings/src/watched_json.rs index e304842aa2..c4cc64cd62 100644 --- a/crates/settings/src/watched_json.rs +++ b/crates/settings/src/watched_json.rs @@ -62,7 +62,18 @@ where } } -pub fn watch_settings_file( +pub fn watch_files( + defaults: Settings, + settings_file: WatchedJsonFile, + theme_registry: Arc, + keymap_file: WatchedJsonFile, + cx: &mut MutableAppContext, +) { + watch_settings_file(defaults, settings_file, theme_registry, cx); + watch_keymap_file(keymap_file, cx); +} + +pub(crate) fn watch_settings_file( defaults: Settings, mut file: WatchedJsonFile, theme_registry: Arc, @@ -77,13 +88,13 @@ pub fn watch_settings_file( .detach(); } -pub fn keymap_updated(content: KeymapFileContent, cx: &mut MutableAppContext) { +fn keymap_updated(content: KeymapFileContent, cx: &mut MutableAppContext) { cx.clear_bindings(); KeymapFileContent::load_defaults(cx); content.add_to_cx(cx).log_err(); } -pub fn settings_updated( +fn settings_updated( defaults: &Settings, content: SettingsFileContent, theme_registry: &Arc, @@ -95,10 +106,20 @@ pub fn settings_updated( cx.refresh_windows(); } -pub fn watch_keymap_file(mut file: WatchedJsonFile, cx: &mut MutableAppContext) { +fn watch_keymap_file(mut file: WatchedJsonFile, cx: &mut MutableAppContext) { cx.spawn(|mut cx| async move { + let mut settings_subscription = None; while let Some(content) = file.0.recv().await { - cx.update(|cx| keymap_updated(content, cx)); + cx.update(|cx| { + let old_base_keymap = cx.global::().base_keymap; + keymap_updated(content.clone(), cx); + settings_subscription = Some(cx.observe_global::(move |cx| { + let settings = cx.global::(); + if settings.base_keymap != old_base_keymap { + keymap_updated(content.clone(), cx); + } + })); + }); } }) .detach(); diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 0930bf0484..ae3278b711 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -47,11 +47,7 @@ impl ThemeSelector { let mut theme_names = registry .list(**cx.default_global::()) .collect::>(); - theme_names.sort_unstable_by(|a, b| { - a.is_light - .cmp(&b.is_light) - .then(a.name.cmp(&b.name)) - }); + theme_names.sort_unstable_by(|a, b| a.is_light.cmp(&b.is_light).then(a.name.cmp(&b.name))); let matches = theme_names .iter() .map(|meta| StringMatch { diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index dc864a3ad1..8bb055dad0 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -84,7 +84,8 @@ impl View for WelcomePage { ]) .constrained() .with_max_width(width) - .contained().with_uniform_padding(10.) + .contained() + .with_uniform_padding(10.) .aligned() .boxed(), ) @@ -174,7 +175,10 @@ impl WelcomePage { } }) .boxed(), - Label::new(label, style.label.text.clone()).contained().with_style(style.label.container).boxed(), + Label::new(label, style.label.text.clone()) + .contained() + .with_style(style.label.container) + .boxed(), ]) .align_children_center() .boxed() diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 234c40c69d..e74bcdc177 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -17,7 +17,7 @@ mod toolbar; pub use smallvec; -use anyhow::{anyhow, Result, Context}; +use anyhow::{anyhow, Context, Result}; use call::ActiveCall; use client::{ proto::{self, PeerId}, @@ -267,27 +267,30 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { }) }, ); - + cx.add_action(|_: &mut Workspace, _: &install_cli::Install, cx| { cx.spawn(|workspace, mut cx| async move { - let err = install_cli::install_cli(&cx).await.context("Failed to create CLI symlink"); - + let err = install_cli::install_cli(&cx) + .await + .context("Failed to create CLI symlink"); + cx.update(|cx| { workspace.update(cx, |workspace, cx| { if matches!(err, Err(_)) { err.notify_err(workspace, cx); } else { workspace.show_notification(1, cx, |cx| { - cx.add_view(|_| MessageNotification::new_message("Successfully installed the 'zed' binary")) + cx.add_view(|_| { + MessageNotification::new_message( + "Successfully installed the `zed` binary", + ) + }) }); } }) }) - - }).detach(); - - - + }) + .detach(); }); let client = &app_state.client; diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index a9625bf78e..8407bd4516 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -38,7 +38,7 @@ use std::{ use terminal_view::{get_working_directory, TerminalView}; use fs::RealFs; -use settings::watched_json::{watch_keymap_file, watch_settings_file, WatchedJsonFile}; +use settings::watched_json::WatchedJsonFile; use theme::ThemeRegistry; #[cfg(debug_assertions)] use util::StaffMode; @@ -123,7 +123,14 @@ fn main() { fs.clone(), )); - watch_settings_file(default_settings, settings_file_content, themes.clone(), cx); + settings::watch_files( + default_settings, + settings_file_content, + themes.clone(), + keymap_file, + cx, + ); + if !stdout_is_a_pty() { upload_previous_panics(http.clone(), cx); } @@ -136,8 +143,6 @@ fn main() { languages::init(languages.clone()); let user_store = cx.add_model(|cx| UserStore::new(client.clone(), http.clone(), cx)); - watch_keymap_file(keymap_file, cx); - cx.set_global(client.clone()); context_menu::init(cx); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 71e4b48db3..2fb956f5b6 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -21,7 +21,7 @@ use gpui::{ geometry::vector::vec2f, impl_actions, platform::{WindowBounds, WindowOptions}, - AssetSource, Platform, PromptLevel, TitlebarOptions, ViewContext, WindowKind, + AssetSource, Platform, PromptLevel, TitlebarOptions, ViewContext, WindowKind, }; use language::Rope; pub use lsp; @@ -144,8 +144,12 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }); }); cx.add_global_action(move |_: &install_cli::Install, cx| { - cx.spawn(|cx| async move { install_cli::install_cli(&cx).await.context("error creating CLI symlink") }) - .detach_and_log_err(cx); + cx.spawn(|cx| async move { + install_cli::install_cli(&cx) + .await + .context("error creating CLI symlink") + }) + .detach_and_log_err(cx); }); cx.add_action({ let app_state = app_state.clone(); From 477453c3962ff26f41a7530f462f42e2cf0323eb Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 7 Mar 2023 13:22:07 -0800 Subject: [PATCH 23/93] Update existing Atelier themes --- styles/src/themes/atelier-cave-dark.ts | 55 ++++++++++++++++ styles/src/themes/atelier-cave-light.ts | 55 ++++++++++++++++ styles/src/themes/atelier-cave.ts | 65 ------------------ styles/src/themes/atelier-sulphurpool-dark.ts | 55 ++++++++++++++++ .../src/themes/atelier-sulphurpool-light.ts | 55 ++++++++++++++++ styles/src/themes/atelier-sulphurpool.ts | 45 ------------- styles/src/themes/common/atelier-common.ts | 66 +++++++++++++++++++ 7 files changed, 286 insertions(+), 110 deletions(-) create mode 100644 styles/src/themes/atelier-cave-dark.ts create mode 100644 styles/src/themes/atelier-cave-light.ts delete mode 100644 styles/src/themes/atelier-cave.ts create mode 100644 styles/src/themes/atelier-sulphurpool-dark.ts create mode 100644 styles/src/themes/atelier-sulphurpool-light.ts delete mode 100644 styles/src/themes/atelier-sulphurpool.ts create mode 100644 styles/src/themes/common/atelier-common.ts diff --git a/styles/src/themes/atelier-cave-dark.ts b/styles/src/themes/atelier-cave-dark.ts new file mode 100644 index 0000000000..6cb8946b7a --- /dev/null +++ b/styles/src/themes/atelier-cave-dark.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Cave Dark`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", + }, + colors: { + base00: "#19171c", + base01: "#26232a", + base02: "#585260", + base03: "#655f6d", + base04: "#7e7887", + base05: "#8b8792", + base06: "#e2dfe7", + base07: "#efecf4", + base08: "#be4678", + base09: "#aa573c", + base0A: "#a06e3b", + base0B: "#2a9292", + base0C: "#398bc6", + base0D: "#576ddb", + base0E: "#955ae7", + base0F: "#bf40bf" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, false, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ]), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-cave-light.ts b/styles/src/themes/atelier-cave-light.ts new file mode 100644 index 0000000000..b05f730f83 --- /dev/null +++ b/styles/src/themes/atelier-cave-light.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Cave Light`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", + }, + colors: { + base00: "#efecf4", + base01: "#e2dfe7", + base02: "#8b8792", + base03: "#7e7887", + base04: "#655f6d", + base05: "#585260", + base06: "#26232a", + base07: "#19171c", + base08: "#be4678", + base09: "#aa573c", + base0A: "#a06e3b", + base0B: "#2a9292", + base0C: "#398bc6", + base0D: "#576ddb", + base0E: "#955ae7", + base0F: "#bf40bf" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, true, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ].reverse()), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-cave.ts b/styles/src/themes/atelier-cave.ts deleted file mode 100644 index 21b846d708..0000000000 --- a/styles/src/themes/atelier-cave.ts +++ /dev/null @@ -1,65 +0,0 @@ -import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" - -const name = "Atelier Cave" - -export const dark = createColorScheme(`${name} Dark`, false, { - neutral: chroma - .scale([ - "#19171c", - "#26232a", - "#585260", - "#655f6d", - "#7e7887", - "#8b8792", - "#e2dfe7", - "#efecf4", - ]) - .domain([0, 0.15, 0.45, 0.6, 0.65, 0.7, 0.85, 1]), - red: colorRamp(chroma("#be4678")), - orange: colorRamp(chroma("#aa573c")), - yellow: colorRamp(chroma("#a06e3b")), - green: colorRamp(chroma("#2a9292")), - cyan: colorRamp(chroma("#398bc6")), - blue: colorRamp(chroma("#576ddb")), - violet: colorRamp(chroma("#955ae7")), - magenta: colorRamp(chroma("#bf40bf")), -}) - -export const light = createColorScheme(`${name} Light`, true, { - neutral: chroma - .scale([ - "#19171c", - "#26232a", - "#585260", - "#655f6d", - "#7e7887", - "#8b8792", - "#e2dfe7", - "#efecf4", - ]) - .correctLightness(), - red: colorRamp(chroma("#be4678")), - orange: colorRamp(chroma("#aa573c")), - yellow: colorRamp(chroma("#a06e3b")), - green: colorRamp(chroma("#2a9292")), - cyan: colorRamp(chroma("#398bc6")), - blue: colorRamp(chroma("#576ddb")), - violet: colorRamp(chroma("#955ae7")), - magenta: colorRamp(chroma("#bf40bf")), -}) - -export const meta: Meta = { - name, - author: "atelierbram", - license: { - SPDX: "MIT", - license_text: { - https_url: "https://atelierbram.mit-license.org/license.txt", - license_checksum: - "f95ce526ef4e7eecf7a832bba0e3451cc1000f9ce63eb01ed6f64f8109f5d0a5", - } - }, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", -} diff --git a/styles/src/themes/atelier-sulphurpool-dark.ts b/styles/src/themes/atelier-sulphurpool-dark.ts new file mode 100644 index 0000000000..2d282deaa2 --- /dev/null +++ b/styles/src/themes/atelier-sulphurpool-dark.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Sulphurpool Dark`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/sulphurpool/", + }, + colors: { + base00: "#202746", + base01: "#293256", + base02: "#5e6687", + base03: "#6b7394", + base04: "#898ea4", + base05: "#979db4", + base06: "#dfe2f1", + base07: "#f5f7ff", + base08: "#c94922", + base09: "#c76b29", + base0A: "#c08b30", + base0B: "#ac9739", + base0C: "#22a2c9", + base0D: "#3d8fd1", + base0E: "#6679cc", + base0F: "#9c637a" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, false, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ]), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-sulphurpool-light.ts b/styles/src/themes/atelier-sulphurpool-light.ts new file mode 100644 index 0000000000..943ceedf2b --- /dev/null +++ b/styles/src/themes/atelier-sulphurpool-light.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Sulphurpool Light`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/sulphurpool/", + }, + colors: { + base00: "#f5f7ff", + base01: "#dfe2f1", + base02: "#979db4", + base03: "#898ea4", + base04: "#6b7394", + base05: "#5e6687", + base06: "#293256", + base07: "#202746", + base08: "#c94922", + base09: "#c76b29", + base0A: "#c08b30", + base0B: "#ac9739", + base0C: "#22a2c9", + base0D: "#3d8fd1", + base0E: "#6679cc", + base0F: "#9c637a" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, true, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ].reverse()), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-sulphurpool.ts b/styles/src/themes/atelier-sulphurpool.ts deleted file mode 100644 index e34bc80238..0000000000 --- a/styles/src/themes/atelier-sulphurpool.ts +++ /dev/null @@ -1,45 +0,0 @@ -import chroma from "chroma-js" -import { Meta } from "./common/colorScheme" -import { colorRamp, createColorScheme } from "./common/ramps" - -const name = "Atelier Sulphurpool" - -const ramps = { - neutral: chroma - .scale([ - "#202746", - "#293256", - "#5e6687", - "#6b7394", - "#898ea4", - "#979db4", - "#dfe2f1", - "#f5f7ff", - ]) - .domain([0, 0.2, 0.38, 0.45, 0.65, 0.7, 0.85, 1]), - red: colorRamp(chroma("#c94922")), - orange: colorRamp(chroma("#c76b29")), - yellow: colorRamp(chroma("#c08b30")), - green: colorRamp(chroma("#ac9739")), - cyan: colorRamp(chroma("#22a2c9")), - blue: colorRamp(chroma("#3d8fd1")), - violet: colorRamp(chroma("#6679cc")), - magenta: colorRamp(chroma("#9c637a")), -} - -export const dark = createColorScheme(`${name} Dark`, false, ramps) -export const light = createColorScheme(`${name} Light`, true, ramps) - -export const meta: Meta = { - name, - author: "atelierbram", - license: { - SPDX: "MIT", - license_text: { - https_url: "https://atelierbram.mit-license.org/license.txt", - license_checksum: - "f95ce526ef4e7eecf7a832bba0e3451cc1000f9ce63eb01ed6f64f8109f5d0a5", - } - }, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/sulphurpool/", -} diff --git a/styles/src/themes/common/atelier-common.ts b/styles/src/themes/common/atelier-common.ts new file mode 100644 index 0000000000..25dda87c61 --- /dev/null +++ b/styles/src/themes/common/atelier-common.ts @@ -0,0 +1,66 @@ +import { License, Meta, ThemeSyntax } from "./colorScheme" + +export interface Variant { + meta: Meta + colors: { + base00: string + base01: string + base02: string + base03: string + base04: string + base05: string + base06: string + base07: string + base08: string + base09: string + base0A: string + base0B: string + base0C: string + base0D: string + base0E: string + base0F: string + } +} + +export const metaCommon: { + author: string + license: License +} = { + author: "Bram de Haan (http://atelierbramdehaan.nl)", + license: { + SPDX: "MIT", + license_text: { + https_url: "https://atelierbram.mit-license.org/license.txt", + license_checksum: + "f95ce526ef4e7eecf7a832bba0e3451cc1000f9ce63eb01ed6f64f8109f5d0a5", + } + }, +} + +export const buildSyntax = (variant: Variant): ThemeSyntax => { + const { colors } = variant + return { + primary: { color: colors.base06 }, + comment: { color: colors.base03 }, + "punctuation.delimiter": { color: colors.base05 }, + "punctuation.bracket": { color: colors.base05 }, + "punctuation.special": { color: colors.base0F }, + "string.special.symbol": { color: colors.base0B }, + operator: { color: colors.base05 }, + function: { color: colors.base0D }, + "function.method": { color: colors.base0D }, + "function.special.definition": { color: colors.base0A }, + string: { color: colors.base0B }, + "string.special": { color: colors.base0F }, + "string.regex": { color: colors.base0C }, + type: { color: colors.base0A }, + number: { color: colors.base09 }, + property: { color: colors.base08 }, + variable: { color: colors.base06 }, + "variable.special": { color: colors.base0E }, + variant: { color: colors.base0A }, + keyword: { color: colors.base0E }, + } +} + +export const name = "Atelier" From e0f9b2b40f8fb8f7af16efa0922a4e162ff9824e Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 7 Mar 2023 13:27:01 -0800 Subject: [PATCH 24/93] Update Atelier Dune --- styles/src/themes/atelier-dune-dark.ts | 55 +++++++++++++++++++++++++ styles/src/themes/atelier-dune-light.ts | 55 +++++++++++++++++++++++++ styles/src/themes/staff/atelier-dune.ts | 35 ---------------- 3 files changed, 110 insertions(+), 35 deletions(-) create mode 100644 styles/src/themes/atelier-dune-dark.ts create mode 100644 styles/src/themes/atelier-dune-light.ts delete mode 100644 styles/src/themes/staff/atelier-dune.ts diff --git a/styles/src/themes/atelier-dune-dark.ts b/styles/src/themes/atelier-dune-dark.ts new file mode 100644 index 0000000000..a03654339f --- /dev/null +++ b/styles/src/themes/atelier-dune-dark.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Dune Dark`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", + }, + colors: { + base00: "#20201d", + base01: "#292824", + base02: "#6e6b5e", + base03: "#7d7a68", + base04: "#999580", + base05: "#a6a28c", + base06: "#e8e4cf", + base07: "#fefbec", + base08: "#d73737", + base09: "#b65611", + base0A: "#ae9513", + base0B: "#60ac39", + base0C: "#1fad83", + base0D: "#6684e1", + base0E: "#b854d4", + base0F: "#d43552" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, false, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ]), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-dune-light.ts b/styles/src/themes/atelier-dune-light.ts new file mode 100644 index 0000000000..da6a89103e --- /dev/null +++ b/styles/src/themes/atelier-dune-light.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Dune Light`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", + }, + colors: { + base00: "#fefbec", + base01: "#e8e4cf", + base02: "#a6a28c", + base03: "#999580", + base04: "#7d7a68", + base05: "#6e6b5e", + base06: "#292824", + base07: "#20201d", + base08: "#d73737", + base09: "#b65611", + base0A: "#ae9513", + base0B: "#60ac39", + base0C: "#1fad83", + base0D: "#6684e1", + base0E: "#b854d4", + base0F: "#d43552" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, true, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ].reverse()), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/staff/atelier-dune.ts b/styles/src/themes/staff/atelier-dune.ts deleted file mode 100644 index bd39098dc3..0000000000 --- a/styles/src/themes/staff/atelier-dune.ts +++ /dev/null @@ -1,35 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Atelier Dune" -const author = "atelierbram" -const url = - "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune/" -const license = { - type: "MIT", - url: "https://github.com/atelierbram/syntax-highlighting/blob/master/LICENSE", -} - -const ramps = { - neutral: chroma.scale([ - "#20201d", - "#292824", - "#6e6b5e", - "#7d7a68", - "#999580", - "#a6a28c", - "#e8e4cf", - "#fefbec", - ]), - red: colorRamp(chroma("#d73737")), - orange: colorRamp(chroma("#b65611")), - yellow: colorRamp(chroma("#ae9513")), - green: colorRamp(chroma("#60ac39")), - cyan: colorRamp(chroma("#1fad83")), - blue: colorRamp(chroma("#6684e1")), - violet: colorRamp(chroma("#b854d4")), - magenta: colorRamp(chroma("#d43552")), -} - -export const dark = createColorScheme(`${name} Dark`, false, ramps) -export const light = createColorScheme(`${name} Light`, true, ramps) From 90296667b0b3c20d5374bc7ba6badeec939bedd8 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 7 Mar 2023 13:35:23 -0800 Subject: [PATCH 25/93] Remove current staff themes --- styles/src/themes/staff/abruzzo.ts | 31 --------- styles/src/themes/staff/atelier-heath.ts | 54 --------------- styles/src/themes/staff/atelier-seaside.ts | 35 ---------- styles/src/themes/staff/ayu-mirage.ts | 31 --------- styles/src/themes/staff/ayu.ts | 52 -------------- styles/src/themes/staff/brushtrees.ts | 73 -------------------- styles/src/themes/staff/dracula.ts | 31 --------- styles/src/themes/staff/monokai.ts | 32 --------- styles/src/themes/staff/nord.ts | 32 --------- styles/src/themes/staff/seti-ui.ts | 32 --------- styles/src/themes/staff/tokyo-night-storm.ts | 32 --------- styles/src/themes/staff/tokyo-night.ts | 53 -------------- styles/src/themes/staff/zenburn.ts | 31 --------- 13 files changed, 519 deletions(-) delete mode 100644 styles/src/themes/staff/abruzzo.ts delete mode 100644 styles/src/themes/staff/atelier-heath.ts delete mode 100644 styles/src/themes/staff/atelier-seaside.ts delete mode 100644 styles/src/themes/staff/ayu-mirage.ts delete mode 100644 styles/src/themes/staff/ayu.ts delete mode 100644 styles/src/themes/staff/brushtrees.ts delete mode 100644 styles/src/themes/staff/dracula.ts delete mode 100644 styles/src/themes/staff/monokai.ts delete mode 100644 styles/src/themes/staff/nord.ts delete mode 100644 styles/src/themes/staff/seti-ui.ts delete mode 100644 styles/src/themes/staff/tokyo-night-storm.ts delete mode 100644 styles/src/themes/staff/tokyo-night.ts delete mode 100644 styles/src/themes/staff/zenburn.ts diff --git a/styles/src/themes/staff/abruzzo.ts b/styles/src/themes/staff/abruzzo.ts deleted file mode 100644 index 43b75f2613..0000000000 --- a/styles/src/themes/staff/abruzzo.ts +++ /dev/null @@ -1,31 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Abruzzo" -const author = "slightknack " -const url = "https://github.com/slightknack" -const license = { - type: "", - url: "", -} - -export const dark = createColorScheme(name, false, { - neutral: chroma.scale([ - "#1b0d05", - "#2c1e18", - "#654035", - "#9d5e4a", - "#b37354", - "#c1825a", - "#dda66e", - "#fbf3e2", - ]), - red: colorRamp(chroma("#e594c4")), - orange: colorRamp(chroma("#d9e87e")), - yellow: colorRamp(chroma("#fd9d83")), - green: colorRamp(chroma("#96adf7")), - cyan: colorRamp(chroma("#fc798f")), - blue: colorRamp(chroma("#BCD0F5")), - violet: colorRamp(chroma("#dac5eb")), - magenta: colorRamp(chroma("#c1a3ef")), -}) diff --git a/styles/src/themes/staff/atelier-heath.ts b/styles/src/themes/staff/atelier-heath.ts deleted file mode 100644 index cd4eb2a1ac..0000000000 --- a/styles/src/themes/staff/atelier-heath.ts +++ /dev/null @@ -1,54 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Atelier Heath" -const author = "atelierbram" -const url = - "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/heath/" -const license = { - type: "MIT", - url: "https://github.com/atelierbram/syntax-highlighting/blob/master/LICENSE", -} - -// `name-[light|dark]`, isLight, color ramps -export const dark = createColorScheme(`${name} Dark`, false, { - neutral: chroma.scale([ - "#1b181b", - "#292329", - "#695d69", - "#776977", - "#9e8f9e", - "#ab9bab", - "#d8cad8", - "#f7f3f7", - ]), - red: colorRamp(chroma("#ca402b")), - orange: colorRamp(chroma("#a65926")), - yellow: colorRamp(chroma("#bb8a35")), - green: colorRamp(chroma("#918b3b")), - cyan: colorRamp(chroma("#159393")), - blue: colorRamp(chroma("#516aec")), - violet: colorRamp(chroma("#7b59c0")), - magenta: colorRamp(chroma("#cc33cc")), -}) - -export const light = createColorScheme(`${name} Light`, true, { - neutral: chroma.scale([ - "#161b1d", - "#1f292e", - "#516d7b", - "#5a7b8c", - "#7195a8", - "#7ea2b4", - "#c1e4f6", - "#ebf8ff", - ]), - red: colorRamp(chroma("#d22d72")), - orange: colorRamp(chroma("#935c25")), - yellow: colorRamp(chroma("#8a8a0f")), - green: colorRamp(chroma("#568c3b")), - cyan: colorRamp(chroma("#2d8f6f")), - blue: colorRamp(chroma("#257fad")), - violet: colorRamp(chroma("#6b6bb8")), - magenta: colorRamp(chroma("#b72dd2")), -}) diff --git a/styles/src/themes/staff/atelier-seaside.ts b/styles/src/themes/staff/atelier-seaside.ts deleted file mode 100644 index 13286e77f9..0000000000 --- a/styles/src/themes/staff/atelier-seaside.ts +++ /dev/null @@ -1,35 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Atelier Seaside" -const author = "atelierbram" -const url = - "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside/" -const license = { - type: "MIT", - url: "https://github.com/atelierbram/syntax-highlighting/blob/master/LICENSE", -} - -const ramps = { - neutral: chroma.scale([ - "#131513", - "#242924", - "#5e6e5e", - "#687d68", - "#809980", - "#8ca68c", - "#cfe8cf", - "#f4fbf4", - ]), - red: colorRamp(chroma("#e6193c")), - orange: colorRamp(chroma("#87711d")), - yellow: colorRamp(chroma("#98981b")), - green: colorRamp(chroma("#29a329")), - cyan: colorRamp(chroma("#1999b3")), - blue: colorRamp(chroma("#3d62f5")), - violet: colorRamp(chroma("#ad2bee")), - magenta: colorRamp(chroma("#e619c3")), -} - -export const dark = createColorScheme(`${name} Dark`, false, ramps) -export const light = createColorScheme(`${name} Light`, true, ramps) diff --git a/styles/src/themes/staff/ayu-mirage.ts b/styles/src/themes/staff/ayu-mirage.ts deleted file mode 100644 index 5b832699b4..0000000000 --- a/styles/src/themes/staff/ayu-mirage.ts +++ /dev/null @@ -1,31 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Ayu" -const author = "Konstantin Pschera " -const url = "https://github.com/ayu-theme/ayu-colors" -const license = { - type: "MIT", - url: "https://github.com/ayu-theme/ayu-colors/blob/master/license", -} - -export const dark = createColorScheme(`${name} Mirage`, false, { - neutral: chroma.scale([ - "#171B24", - "#1F2430", - "#242936", - "#707A8C", - "#8A9199", - "#CCCAC2", - "#D9D7CE", - "#F3F4F5", - ]), - red: colorRamp(chroma("#F28779")), - orange: colorRamp(chroma("#FFAD66")), - yellow: colorRamp(chroma("#FFD173")), - green: colorRamp(chroma("#D5FF80")), - cyan: colorRamp(chroma("#95E6CB")), - blue: colorRamp(chroma("#5CCFE6")), - violet: colorRamp(chroma("#D4BFFF")), - magenta: colorRamp(chroma("#F29E74")), -}) diff --git a/styles/src/themes/staff/ayu.ts b/styles/src/themes/staff/ayu.ts deleted file mode 100644 index 24fcdb951b..0000000000 --- a/styles/src/themes/staff/ayu.ts +++ /dev/null @@ -1,52 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Ayu" -const author = "Konstantin Pschera " -const url = "https://github.com/ayu-theme/ayu-colors" -const license = { - type: "MIT", - url: "https://github.com/ayu-theme/ayu-colors/blob/master/license", -} - -export const dark = createColorScheme(`${name} Dark`, false, { - neutral: chroma.scale([ - "#0F1419", - "#131721", - "#272D38", - "#3E4B59", - "#BFBDB6", - "#E6E1CF", - "#E6E1CF", - "#F3F4F5", - ]), - red: colorRamp(chroma("#F07178")), - orange: colorRamp(chroma("#FF8F40")), - yellow: colorRamp(chroma("#FFB454")), - green: colorRamp(chroma("#B8CC52")), - cyan: colorRamp(chroma("#95E6CB")), - blue: colorRamp(chroma("#59C2FF")), - violet: colorRamp(chroma("#D2A6FF")), - magenta: colorRamp(chroma("#E6B673")), -}) - -export const light = createColorScheme(`${name} Light`, true, { - neutral: chroma.scale([ - "#1A1F29", - "#242936", - "#5C6773", - "#828C99", - "#ABB0B6", - "#F8F9FA", - "#F3F4F5", - "#FAFAFA", - ]), - red: colorRamp(chroma("#F07178")), - orange: colorRamp(chroma("#FA8D3E")), - yellow: colorRamp(chroma("#F2AE49")), - green: colorRamp(chroma("#86B300")), - cyan: colorRamp(chroma("#4CBF99")), - blue: colorRamp(chroma("#36A3D9")), - violet: colorRamp(chroma("#A37ACC")), - magenta: colorRamp(chroma("#E6BA7E")), -}) diff --git a/styles/src/themes/staff/brushtrees.ts b/styles/src/themes/staff/brushtrees.ts deleted file mode 100644 index a17cf92acb..0000000000 --- a/styles/src/themes/staff/brushtrees.ts +++ /dev/null @@ -1,73 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Brush Trees" -const author = "Abraham White " -const url = "https://github.com/WhiteAbeLincoln/base16-brushtrees-scheme" -const license = { - type: "MIT", - url: "https://github.com/WhiteAbeLincoln/base16-brushtrees-scheme/blob/master/LICENSE", -} - -export const dark = createColorScheme(`${name} Dark`, false, { - neutral: chroma.scale([ - "#485867", - "#5A6D7A", - "#6D828E", - "#8299A1", - "#98AFB5", - "#B0C5C8", - "#C9DBDC", - "#E3EFEF", - ]), - red: colorRamp(chroma("#b38686")), - orange: colorRamp(chroma("#d8bba2")), - yellow: colorRamp(chroma("#aab386")), - green: colorRamp(chroma("#87b386")), - cyan: colorRamp(chroma("#86b3b3")), - blue: colorRamp(chroma("#868cb3")), - violet: colorRamp(chroma("#b386b2")), - magenta: colorRamp(chroma("#b39f9f")), -}) - -export const mirage = createColorScheme(`${name} Mirage`, false, { - neutral: chroma.scale([ - "#485867", - "#5A6D7A", - "#6D828E", - "#8299A1", - "#98AFB5", - "#B0C5C8", - "#C9DBDC", - "#E3EFEF", - ]), - red: colorRamp(chroma("#F28779")), - orange: colorRamp(chroma("#FFAD66")), - yellow: colorRamp(chroma("#FFD173")), - green: colorRamp(chroma("#D5FF80")), - cyan: colorRamp(chroma("#95E6CB")), - blue: colorRamp(chroma("#5CCFE6")), - violet: colorRamp(chroma("#D4BFFF")), - magenta: colorRamp(chroma("#F29E74")), -}) - -export const light = createColorScheme(`${name} Light`, true, { - neutral: chroma.scale([ - "#1A1F29", - "#242936", - "#5C6773", - "#828C99", - "#ABB0B6", - "#F8F9FA", - "#F3F4F5", - "#FAFAFA", - ]), - red: colorRamp(chroma("#b38686")), - orange: colorRamp(chroma("#d8bba2")), - yellow: colorRamp(chroma("#aab386")), - green: colorRamp(chroma("#87b386")), - cyan: colorRamp(chroma("#86b3b3")), - blue: colorRamp(chroma("#868cb3")), - violet: colorRamp(chroma("#b386b2")), - magenta: colorRamp(chroma("#b39f9f")), -}) diff --git a/styles/src/themes/staff/dracula.ts b/styles/src/themes/staff/dracula.ts deleted file mode 100644 index acff08d230..0000000000 --- a/styles/src/themes/staff/dracula.ts +++ /dev/null @@ -1,31 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Dracula" -const author = "zenorocha" -const url = "https://github.com/dracula/dracula-theme" -const license = { - type: "MIT", - url: "https://github.com/dracula/dracula-theme/blob/master/LICENSE", -} - -export const dark = createColorScheme(name, false, { - neutral: chroma.scale([ - "#282A36", - "#3a3c4e", - "#4d4f68", - "#626483", - "#62d6e8", - "#e9e9f4", - "#f1f2f8", - "#f8f8f2", - ]), - red: colorRamp(chroma("#ff5555")), - orange: colorRamp(chroma("#ffb86c")), - yellow: colorRamp(chroma("#f1fa8c")), - green: colorRamp(chroma("#50fa7b")), - cyan: colorRamp(chroma("#8be9fd")), - blue: colorRamp(chroma("#6272a4")), - violet: colorRamp(chroma("#bd93f9")), - magenta: colorRamp(chroma("#00f769")), -}) diff --git a/styles/src/themes/staff/monokai.ts b/styles/src/themes/staff/monokai.ts deleted file mode 100644 index 75319e527a..0000000000 --- a/styles/src/themes/staff/monokai.ts +++ /dev/null @@ -1,32 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Monokai" -const author = "Wimer Hazenberg (http://www.monokai.nl)" -const url = "https://base16.netlify.app/previews/base16-monokai.html" -const license = { - type: "?", - url: "?", -} - -// `name-[light|dark]`, isLight, color ramps -export const dark = createColorScheme(name, false, { - neutral: chroma.scale([ - "#272822", - "#383830", - "#49483e", - "#75715e", - "#a59f85", - "#f8f8f2", - "#f5f4f1", - "#f9f8f5", - ]), - red: colorRamp(chroma("#f92672")), - orange: colorRamp(chroma("#fd971f")), - yellow: colorRamp(chroma("#f4bf75")), - green: colorRamp(chroma("#a6e22e")), - cyan: colorRamp(chroma("#a1efe4")), - blue: colorRamp(chroma("#66d9ef")), - violet: colorRamp(chroma("#ae81ff")), - magenta: colorRamp(chroma("#cc6633")), -}) diff --git a/styles/src/themes/staff/nord.ts b/styles/src/themes/staff/nord.ts deleted file mode 100644 index 48f924e7a4..0000000000 --- a/styles/src/themes/staff/nord.ts +++ /dev/null @@ -1,32 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Nord" -const author = "arcticicestudio" -const url = "https://www.nordtheme.com/" -const license = { - type: "MIT", - url: "https://github.com/arcticicestudio/nord/blob/develop/LICENSE.md", -} - -// `name-[light|dark]`, isLight, color ramps -export const dark = createColorScheme(name, false, { - neutral: chroma.scale([ - "#2E3440", - "#3B4252", - "#434C5E", - "#4C566A", - "#D8DEE9", - "#E5E9F0", - "#ECEFF4", - "#8FBCBB", - ]), - red: colorRamp(chroma("#88C0D0")), - orange: colorRamp(chroma("#81A1C1")), - yellow: colorRamp(chroma("#5E81AC")), - green: colorRamp(chroma("#BF616A")), - cyan: colorRamp(chroma("#D08770")), - blue: colorRamp(chroma("#EBCB8B")), - violet: colorRamp(chroma("#A3BE8C")), - magenta: colorRamp(chroma("#B48EAD")), -}) diff --git a/styles/src/themes/staff/seti-ui.ts b/styles/src/themes/staff/seti-ui.ts deleted file mode 100644 index abf6624242..0000000000 --- a/styles/src/themes/staff/seti-ui.ts +++ /dev/null @@ -1,32 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Seti UI" -const author = "jesseweed" -const url = "https://github.com/jesseweed/seti-ui" -const license = { - type: "MIT", - url: "https://github.com/jesseweed/seti-ui/blob/master/LICENSE.md", -} - -// `name-[light|dark]`, isLight, color ramps -export const dark = createColorScheme(name, false, { - neutral: chroma.scale([ - "#151718", - "#262B30", - "#1E2326", - "#41535B", - "#43a5d5", - "#d6d6d6", - "#eeeeee", - "#ffffff", - ]), - red: colorRamp(chroma("#Cd3f45")), - orange: colorRamp(chroma("#db7b55")), - yellow: colorRamp(chroma("#e6cd69")), - green: colorRamp(chroma("#9fca56")), - cyan: colorRamp(chroma("#55dbbe")), - blue: colorRamp(chroma("#55b5db")), - violet: colorRamp(chroma("#a074c4")), - magenta: colorRamp(chroma("#8a553f")), -}) diff --git a/styles/src/themes/staff/tokyo-night-storm.ts b/styles/src/themes/staff/tokyo-night-storm.ts deleted file mode 100644 index 399c526872..0000000000 --- a/styles/src/themes/staff/tokyo-night-storm.ts +++ /dev/null @@ -1,32 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Tokyo Night Storm" -const author = "folke" -const url = "https://github.com/folke/tokyonight.nvim" -const license = { - type: "MIT", - url: "https://github.com/ghifarit53/tokyonight-vim/blob/master/LICENSE", -} - -// `name-[light|dark]`, isLight, color ramps -export const dark = createColorScheme(name, false, { - neutral: chroma.scale([ - "#24283B", - "#16161E", - "#343A52", - "#444B6A", - "#787C99", - "#A9B1D6", - "#CBCCD1", - "#D5D6DB", - ]), - red: colorRamp(chroma("#C0CAF5")), - orange: colorRamp(chroma("#A9B1D6")), - yellow: colorRamp(chroma("#0DB9D7")), - green: colorRamp(chroma("#9ECE6A")), - cyan: colorRamp(chroma("#B4F9F8")), - blue: colorRamp(chroma("#2AC3DE")), - violet: colorRamp(chroma("#BB9AF7")), - magenta: colorRamp(chroma("#F7768E")), -}) diff --git a/styles/src/themes/staff/tokyo-night.ts b/styles/src/themes/staff/tokyo-night.ts deleted file mode 100644 index 267b1fc031..0000000000 --- a/styles/src/themes/staff/tokyo-night.ts +++ /dev/null @@ -1,53 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Tokyo" -const author = "folke" -const url = "https://github.com/folke/tokyonight.nvim" -const license = { - type: "Apache License 2.0", - url: "https://github.com/folke/tokyonight.nvim/blob/main/LICENSE", -} - -// `name-[light|dark]`, isLight, color ramps -export const dark = createColorScheme(`${name} Night`, false, { - neutral: chroma.scale([ - "#1A1B26", - "#16161E", - "#2F3549", - "#444B6A", - "#787C99", - "#A9B1D6", - "#CBCCD1", - "#D5D6DB", - ]), - red: colorRamp(chroma("#C0CAF5")), - orange: colorRamp(chroma("#A9B1D6")), - yellow: colorRamp(chroma("#0DB9D7")), - green: colorRamp(chroma("#9ECE6A")), - cyan: colorRamp(chroma("#B4F9F8")), - blue: colorRamp(chroma("#2AC3DE")), - violet: colorRamp(chroma("#BB9AF7")), - magenta: colorRamp(chroma("#F7768E")), -}) - -export const light = createColorScheme(`${name} Day`, true, { - neutral: chroma.scale([ - "#1A1B26", - "#1A1B26", - "#343B59", - "#4C505E", - "#9699A3", - "#DFE0E5", - "#CBCCD1", - "#D5D6DB", - ]), - red: colorRamp(chroma("#343B58")), - orange: colorRamp(chroma("#965027")), - yellow: colorRamp(chroma("#166775")), - green: colorRamp(chroma("#485E30")), - cyan: colorRamp(chroma("#3E6968")), - blue: colorRamp(chroma("#34548A")), - violet: colorRamp(chroma("#5A4A78")), - magenta: colorRamp(chroma("#8C4351")), -}) diff --git a/styles/src/themes/staff/zenburn.ts b/styles/src/themes/staff/zenburn.ts deleted file mode 100644 index 9457ef1957..0000000000 --- a/styles/src/themes/staff/zenburn.ts +++ /dev/null @@ -1,31 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Zenburn" -const author = "elnawe" -const url = "https://github.com/elnawe/base16-zenburn-scheme" -const license = { - type: "None", - url: "", -} - -export const dark = createColorScheme(name, false, { - neutral: chroma.scale([ - "#383838", - "#404040", - "#606060", - "#6f6f6f", - "#808080", - "#dcdccc", - "#c0c0c0", - "#ffffff", - ]), - red: colorRamp(chroma("#dca3a3")), - orange: colorRamp(chroma("#dfaf8f")), - yellow: colorRamp(chroma("#e0cf9f")), - green: colorRamp(chroma("#5f7f5f")), - cyan: colorRamp(chroma("#93e0e3")), - blue: colorRamp(chroma("#7cb8bb")), - violet: colorRamp(chroma("#dc8cc3")), - magenta: colorRamp(chroma("#000000")), -}) From 4e81513af15918cd42f8c674c4b729a9d154eac8 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 7 Mar 2023 13:35:32 -0800 Subject: [PATCH 26/93] Add more Atelier themes --- styles/src/themes/atelier-dune-dark.ts | 2 +- styles/src/themes/atelier-dune-light.ts | 2 +- styles/src/themes/atelier-estuary-dark.ts | 55 ++++++++++++++++++++++ styles/src/themes/atelier-estuary-light.ts | 55 ++++++++++++++++++++++ 4 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 styles/src/themes/atelier-estuary-dark.ts create mode 100644 styles/src/themes/atelier-estuary-light.ts diff --git a/styles/src/themes/atelier-dune-dark.ts b/styles/src/themes/atelier-dune-dark.ts index a03654339f..b6f90b4c44 100644 --- a/styles/src/themes/atelier-dune-dark.ts +++ b/styles/src/themes/atelier-dune-dark.ts @@ -7,7 +7,7 @@ const variant: Variant = { meta: { name: `${name} Dune Dark`, ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune/", }, colors: { base00: "#20201d", diff --git a/styles/src/themes/atelier-dune-light.ts b/styles/src/themes/atelier-dune-light.ts index da6a89103e..0a95dc66fc 100644 --- a/styles/src/themes/atelier-dune-light.ts +++ b/styles/src/themes/atelier-dune-light.ts @@ -7,7 +7,7 @@ const variant: Variant = { meta: { name: `${name} Dune Light`, ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/cave/", + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/dune/", }, colors: { base00: "#fefbec", diff --git a/styles/src/themes/atelier-estuary-dark.ts b/styles/src/themes/atelier-estuary-dark.ts new file mode 100644 index 0000000000..94ca7aa63b --- /dev/null +++ b/styles/src/themes/atelier-estuary-dark.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Estuary Dark`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/estuary/", + }, + colors: { + base00: "#22221b", + base01: "#302f27", + base02: "#5f5e4e", + base03: "#6c6b5a", + base04: "#878573", + base05: "#929181", + base06: "#e7e6df", + base07: "#f4f3ec", + base08: "#ba6236", + base09: "#ae7313", + base0A: "#a5980d", + base0B: "#7d9726", + base0C: "#5b9d48", + base0D: "#36a166", + base0E: "#5f9182", + base0F: "#9d6c7c" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, false, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ]), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-estuary-light.ts b/styles/src/themes/atelier-estuary-light.ts new file mode 100644 index 0000000000..4dd4b87549 --- /dev/null +++ b/styles/src/themes/atelier-estuary-light.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Estuary Light`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/estuary/", + }, + colors: { + base00: "#f4f3ec", + base01: "#e7e6df", + base02: "#929181", + base03: "#878573", + base04: "#6c6b5a", + base05: "#5f5e4e", + base06: "#302f27", + base07: "#22221b", + base08: "#ba6236", + base09: "#ae7313", + base0A: "#a5980d", + base0B: "#7d9726", + base0C: "#5b9d48", + base0D: "#36a166", + base0E: "#5f9182", + base0F: "#9d6c7c" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, true, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ].reverse()), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta From 84aefb9dcb763836dc79228e8e553a65a9cd7666 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Tue, 7 Mar 2023 13:47:18 -0800 Subject: [PATCH 27/93] Add the rest of the Atelier dark themes --- styles/src/themes/atelier-forest-dark.ts | 55 ++++++++++++++++++++++ styles/src/themes/atelier-heath-dark.ts | 55 ++++++++++++++++++++++ styles/src/themes/atelier-lakeside-dark.ts | 55 ++++++++++++++++++++++ styles/src/themes/atelier-plateau-dark.ts | 55 ++++++++++++++++++++++ styles/src/themes/atelier-savanna-dark.ts | 55 ++++++++++++++++++++++ styles/src/themes/atelier-seaside-dark.ts | 55 ++++++++++++++++++++++ 6 files changed, 330 insertions(+) create mode 100644 styles/src/themes/atelier-forest-dark.ts create mode 100644 styles/src/themes/atelier-heath-dark.ts create mode 100644 styles/src/themes/atelier-lakeside-dark.ts create mode 100644 styles/src/themes/atelier-plateau-dark.ts create mode 100644 styles/src/themes/atelier-savanna-dark.ts create mode 100644 styles/src/themes/atelier-seaside-dark.ts diff --git a/styles/src/themes/atelier-forest-dark.ts b/styles/src/themes/atelier-forest-dark.ts new file mode 100644 index 0000000000..2bfe285a16 --- /dev/null +++ b/styles/src/themes/atelier-forest-dark.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Forest Dark`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/forest/", + }, + colors: { + base00: "#1b1918", + base01: "#2c2421", + base02: "#68615e", + base03: "#766e6b", + base04: "#9c9491", + base05: "#a8a19f", + base06: "#e6e2e0", + base07: "#f1efee", + base08: "#f22c40", + base09: "#df5320", + base0A: "#c38418", + base0B: "#7b9726", + base0C: "#3d97b8", + base0D: "#407ee7", + base0E: "#6666ea", + base0F: "#c33ff3" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, false, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ]), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-heath-dark.ts b/styles/src/themes/atelier-heath-dark.ts new file mode 100644 index 0000000000..9ae14a8903 --- /dev/null +++ b/styles/src/themes/atelier-heath-dark.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Heath Dark`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/heath/", + }, + colors: { + base00: "#1b181b", + base01: "#292329", + base02: "#695d69", + base03: "#776977", + base04: "#9e8f9e", + base05: "#ab9bab", + base06: "#d8cad8", + base07: "#f7f3f7", + base08: "#ca402b", + base09: "#a65926", + base0A: "#bb8a35", + base0B: "#918b3b", + base0C: "#159393", + base0D: "#516aec", + base0E: "#7b59c0", + base0F: "#cc33cc" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, false, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ]), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-lakeside-dark.ts b/styles/src/themes/atelier-lakeside-dark.ts new file mode 100644 index 0000000000..45f5733cf9 --- /dev/null +++ b/styles/src/themes/atelier-lakeside-dark.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Lakeside Dark`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/lakeside/", + }, + colors: { + base00: "#161b1d", + base01: "#1f292e", + base02: "#516d7b", + base03: "#5a7b8c", + base04: "#7195a8", + base05: "#7ea2b4", + base06: "#c1e4f6", + base07: "#ebf8ff", + base08: "#d22d72", + base09: "#935c25", + base0A: "#8a8a0f", + base0B: "#568c3b", + base0C: "#2d8f6f", + base0D: "#257fad", + base0E: "#6b6bb8", + base0F: "#b72dd2" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, false, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ]), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-plateau-dark.ts b/styles/src/themes/atelier-plateau-dark.ts new file mode 100644 index 0000000000..22510730e7 --- /dev/null +++ b/styles/src/themes/atelier-plateau-dark.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Plateau Dark`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/plateau/", + }, + colors: { + base00: "#1b1818", + base01: "#292424", + base02: "#585050", + base03: "#655d5d", + base04: "#7e7777", + base05: "#8a8585", + base06: "#e7dfdf", + base07: "#f4ecec", + base08: "#ca4949", + base09: "#b45a3c", + base0A: "#a06e3b", + base0B: "#4b8b8b", + base0C: "#5485b6", + base0D: "#7272ca", + base0E: "#8464c4", + base0F: "#bd5187" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, false, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ]), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-savanna-dark.ts b/styles/src/themes/atelier-savanna-dark.ts new file mode 100644 index 0000000000..c2fa1efa6d --- /dev/null +++ b/styles/src/themes/atelier-savanna-dark.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Savanna Dark`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/savanna/", + }, + colors: { + base00: "#171c19", + base01: "#232a25", + base02: "#526057", + base03: "#5f6d64", + base04: "#78877d", + base05: "#87928a", + base06: "#dfe7e2", + base07: "#ecf4ee", + base08: "#b16139", + base09: "#9f713c", + base0A: "#a07e3b", + base0B: "#489963", + base0C: "#1c9aa0", + base0D: "#478c90", + base0E: "#55859b", + base0F: "#867469" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, false, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ]), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-seaside-dark.ts b/styles/src/themes/atelier-seaside-dark.ts new file mode 100644 index 0000000000..29bdcfb447 --- /dev/null +++ b/styles/src/themes/atelier-seaside-dark.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Seaside Dark`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/savanna/", + }, + colors: { + base00: "#131513", + base01: "#242924", + base02: "#5e6e5e", + base03: "#687d68", + base04: "#809980", + base05: "#8ca68c", + base06: "#cfe8cf", + base07: "#f4fbf4", + base08: "#e6193c", + base09: "#87711d", + base0A: "#98981b", + base0B: "#29a329", + base0C: "#1999b3", + base0D: "#3d62f5", + base0E: "#ad2bee", + base0F: "#e619c3" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, false, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ]), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta From 5892f1660218fef98214bfc7c4551deceb30a704 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 7 Mar 2023 14:02:42 -0800 Subject: [PATCH 28/93] Add test for base keymap setting --- crates/settings/src/settings_file.rs | 143 ++++++++++++++++++++++++++- 1 file changed, 142 insertions(+), 1 deletion(-) diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 575e9499d3..1b05993bd5 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -54,10 +54,151 @@ impl SettingsFile { #[cfg(test)] mod tests { use super::*; - use crate::{watched_json::watch_settings_file, EditorSettings, Settings, SoftWrap}; + use crate::{ + watch_files, watched_json::watch_settings_file, EditorSettings, Settings, SoftWrap, + }; use fs::FakeFs; + use gpui::{actions, Action}; use theme::ThemeRegistry; + #[gpui::test] + async fn test_base_keymap(cx: &mut gpui::TestAppContext) { + let executor = cx.background(); + let fs = FakeFs::new(executor.clone()); + let font_cache = cx.font_cache(); + + actions!(test, [A, B]); + // From the Atom keymap + actions!(workspace, [ActivatePreviousPane]); + // From the JetBrains keymap + actions!(pane, [ActivatePrevItem]); + + fs.save( + "/settings.json".as_ref(), + &r#" + { + "base_keymap": "Atom" + } + "# + .into(), + Default::default(), + ) + .await + .unwrap(); + + fs.save( + "/keymap.json".as_ref(), + &r#" + [ + { + "bindings": { + "backspace": "test::A" + } + } + ] + "# + .into(), + Default::default(), + ) + .await + .unwrap(); + + let settings_file = + WatchedJsonFile::new(fs.clone(), &executor, "/settings.json".as_ref()).await; + let keymaps_file = + WatchedJsonFile::new(fs.clone(), &executor, "/keymap.json".as_ref()).await; + + let default_settings = cx.read(Settings::test); + + cx.update(|cx| { + cx.add_global_action(|_: &A, _cx| { + }); + cx.add_global_action(|_: &B, _cx| { + }); + cx.add_global_action(|_: &ActivatePreviousPane, _cx| { + }); + cx.add_global_action(|_: &ActivatePrevItem, _cx| { + }); + watch_files( + default_settings, + settings_file, + ThemeRegistry::new((), font_cache), + keymaps_file, + cx, + ) + }); + + cx.foreground().run_until_parked(); + + // Test loading the keymap base at all + cx.update(|cx| { + assert_keybindings_for(cx, vec![("backspace", &A), ("k", &ActivatePreviousPane)], line!()); + }); + + // Test modifying the users keymap, while retaining the base keymap + fs.save( + "/keymap.json".as_ref(), + &r#" + [ + { + "bindings": { + "backspace": "test::B" + } + } + ] + "# + .into(), + Default::default(), + ) + .await + .unwrap(); + + cx.foreground().run_until_parked(); + + cx.update(|cx| { + assert_keybindings_for(cx, vec![("backspace", &B), ("k", &ActivatePreviousPane)], line!()); + }); + + // Test modifying the base, while retaining the users keymap + fs.save( + "/settings.json".as_ref(), + &r#" + { + "base_keymap": "JetBrains" + } + "# + .into(), + Default::default(), + ) + .await + .unwrap(); + + cx.foreground().run_until_parked(); + + cx.update(|cx| { + assert_keybindings_for(cx, vec![("backspace", &B), ("[", &ActivatePrevItem)], line!()); + }); + } + + fn assert_keybindings_for<'a>( + cx: &mut MutableAppContext, + actions: Vec<(&'static str, &'a dyn Action)>, + line: u32, + ) { + + for (key, action) in actions { + // assert that... + assert!(cx.available_actions(0, 0).any(|(_, bound_action, b)| { + // action names match... + bound_action.name() == action.name() + && bound_action.namespace() == action.namespace() + // and key strokes contain the given key + && b.iter() + .any(|binding| binding.keystrokes().iter().any(|k| k.key == key)) + }), "On {} Failed to find {} with keybinding {}", line, action.name(), key); + } + } + #[gpui::test] async fn test_watch_settings_files(cx: &mut gpui::TestAppContext) { let executor = cx.background(); From ab4b3293d16f015e66fe9bfd77a30c8e3a9bb023 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 7 Mar 2023 14:49:05 -0800 Subject: [PATCH 29/93] Fix project panel button and style it Co-authored-by: max --- crates/project/src/worktree.rs | 2 +- crates/project_panel/src/project_panel.rs | 128 ++++++++++------------ crates/settings/src/settings_file.rs | 51 +++++---- crates/theme/src/theme.rs | 1 + crates/workspace/src/workspace.rs | 1 + crates/zed/src/main.rs | 1 - styles/src/styleTree/projectPanel.ts | 33 ++++++ 7 files changed, 126 insertions(+), 91 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 8b622ab607..d53e84f6b1 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -867,7 +867,7 @@ impl LocalWorktree { let old_path = self.entry_for_id(entry_id)?.path.clone(); let new_path = new_path.into(); let abs_old_path = self.absolutize(&old_path); - let abs_new_path = self.absolutize(&new_path); + let abs_new_path = self.absolutize(new_path.as_ref()); let rename = cx.background().spawn({ let fs = self.fs.clone(); let abs_new_path = abs_new_path.clone(); diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 9df6581d20..5de68b12fd 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -5,9 +5,8 @@ use futures::stream::StreamExt; use gpui::{ actions, anyhow::{anyhow, Result}, - color::Color, elements::{ - AnchorCorner, Canvas, ChildView, ConstrainedBox, ContainerStyle, Empty, Flex, + AnchorCorner, ChildView, ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label, MouseEventHandler, ParentElement, ScrollTarget, Stack, Svg, UniformList, UniformListState, }, @@ -15,7 +14,7 @@ use gpui::{ impl_internal_actions, keymap_matcher::KeymapContext, platform::CursorStyle, - AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, MouseButton, + Action, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, MouseButton, MutableAppContext, PromptLevel, RenderContext, Task, View, ViewContext, ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; @@ -29,7 +28,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use theme::ProjectPanelEntry; +use theme::{ContainedText, ProjectPanelEntry}; use unicase::UniCase; use workspace::Workspace; @@ -1317,79 +1316,36 @@ impl View for ProjectPanel { .boxed() } else { let parent_view_id = cx.handle().id(); - Stack::new() + Flex::column() .with_child( - MouseEventHandler::::new(1, cx, |_, cx| { - Stack::new() - .with_child( - Canvas::new(|bounds, _visible_bounds, cx| { - cx.scene.push_quad(gpui::Quad { - bounds, - background: Some(Color::transparent_black()), - ..Default::default() - }) - }) - .boxed(), - ) - .with_child( - MouseEventHandler::::new(2, cx, |state, cx| { - let style = &cx - .global::() - .theme - .search - .option_button - .style_for(state, false); + MouseEventHandler::::new(2, cx, { + let button_style = theme.open_project_button.clone(); + let context_menu_item_style = + cx.global::().theme.context_menu.item.clone(); + move |state, cx| { + let button_style = button_style.style_for(state, false).clone(); + let context_menu_item = + context_menu_item_style.style_for(state, true).clone(); - let context_menu_item = cx - .global::() - .theme - .context_menu - .clone() - .item - .style_for(state, true) - .clone(); - - Flex::row() - .with_child( - Label::new( - "Open a new project!".to_string(), - context_menu_item.label.clone(), - ) - .contained() - .boxed(), - ) - .with_child({ - KeystrokeLabel::new( - cx.window_id(), - parent_view_id, - Box::new(workspace::Open), - context_menu_item.keystroke.container, - context_menu_item.keystroke.text.clone(), - ) - .flex_float() - .boxed() - }) - .contained() - .with_style(style.container) - .aligned() - .top() - .constrained() - .with_width(100.) - .with_height(20.) - .boxed() - }) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_action(workspace::Open) - }) - .with_cursor_style(CursorStyle::PointingHand) - .boxed(), + keystroke_label( + parent_view_id, + "Open a new project", + &button_style, + context_menu_item.keystroke, + workspace::Open, + cx, ) .boxed() + } }) - // TODO is this nescessary? - .on_click(MouseButton::Left, |_, cx| cx.focus_parent_view()) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_action(workspace::Open) + }) + .with_cursor_style(CursorStyle::PointingHand) .boxed(), ) + .contained() + .with_style(container_style) .boxed() } } @@ -1401,6 +1357,38 @@ impl View for ProjectPanel { } } +fn keystroke_label( + view_id: usize, + label_text: &'static str, + label_style: &ContainedText, + keystroke_style: ContainedText, + action: A, + cx: &mut RenderContext, +) -> Container +where + A: Action, +{ + Flex::row() + .with_child( + Label::new(label_text, label_style.text.clone()) + .contained() + .boxed(), + ) + .with_child({ + KeystrokeLabel::new( + cx.window_id(), + view_id, + Box::new(action), + keystroke_style.container, + keystroke_style.text.clone(), + ) + .flex_float() + .boxed() + }) + .contained() + .with_style(label_style.container) +} + impl Entity for ProjectPanel { type Event = Event; } diff --git a/crates/settings/src/settings_file.rs b/crates/settings/src/settings_file.rs index 1b05993bd5..50638205c5 100644 --- a/crates/settings/src/settings_file.rs +++ b/crates/settings/src/settings_file.rs @@ -111,14 +111,10 @@ mod tests { let default_settings = cx.read(Settings::test); cx.update(|cx| { - cx.add_global_action(|_: &A, _cx| { - }); - cx.add_global_action(|_: &B, _cx| { - }); - cx.add_global_action(|_: &ActivatePreviousPane, _cx| { - }); - cx.add_global_action(|_: &ActivatePrevItem, _cx| { - }); + cx.add_global_action(|_: &A, _cx| {}); + cx.add_global_action(|_: &B, _cx| {}); + cx.add_global_action(|_: &ActivatePreviousPane, _cx| {}); + cx.add_global_action(|_: &ActivatePrevItem, _cx| {}); watch_files( default_settings, settings_file, @@ -132,7 +128,11 @@ mod tests { // Test loading the keymap base at all cx.update(|cx| { - assert_keybindings_for(cx, vec![("backspace", &A), ("k", &ActivatePreviousPane)], line!()); + assert_keybindings_for( + cx, + vec![("backspace", &A), ("k", &ActivatePreviousPane)], + line!(), + ); }); // Test modifying the users keymap, while retaining the base keymap @@ -152,13 +152,17 @@ mod tests { ) .await .unwrap(); - + cx.foreground().run_until_parked(); cx.update(|cx| { - assert_keybindings_for(cx, vec![("backspace", &B), ("k", &ActivatePreviousPane)], line!()); + assert_keybindings_for( + cx, + vec![("backspace", &B), ("k", &ActivatePreviousPane)], + line!(), + ); }); - + // Test modifying the base, while retaining the users keymap fs.save( "/settings.json".as_ref(), @@ -172,11 +176,15 @@ mod tests { ) .await .unwrap(); - + cx.foreground().run_until_parked(); cx.update(|cx| { - assert_keybindings_for(cx, vec![("backspace", &B), ("[", &ActivatePrevItem)], line!()); + assert_keybindings_for( + cx, + vec![("backspace", &B), ("[", &ActivatePrevItem)], + line!(), + ); }); } @@ -185,17 +193,22 @@ mod tests { actions: Vec<(&'static str, &'a dyn Action)>, line: u32, ) { - for (key, action) in actions { // assert that... - assert!(cx.available_actions(0, 0).any(|(_, bound_action, b)| { - // action names match... - bound_action.name() == action.name() + assert!( + cx.available_actions(0, 0).any(|(_, bound_action, b)| { + // action names match... + bound_action.name() == action.name() && bound_action.namespace() == action.namespace() // and key strokes contain the given key && b.iter() .any(|binding| binding.keystrokes().iter().any(|k| k.key == key)) - }), "On {} Failed to find {} with keybinding {}", line, action.name(), key); + }), + "On {} Failed to find {} with keybinding {}", + line, + action.name(), + key + ); } } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 43a10978d6..e7252fbf61 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -346,6 +346,7 @@ pub struct ProjectPanel { pub cut_entry: Interactive, pub filename_editor: FieldEditor, pub indent_width: f32, + pub open_project_button: Interactive, } #[derive(Clone, Debug, Deserialize, Default)] diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e74bcdc177..f987d00ef1 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2815,6 +2815,7 @@ fn open(_: &Open, cx: &mut MutableAppContext) { directories: true, multiple: true, }); + cx.spawn(|mut cx| async move { if let Some(paths) = paths.recv().await.flatten() { cx.update(|cx| cx.dispatch_global_action(OpenPaths { paths })); diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 8407bd4516..1b794b3857 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -237,7 +237,6 @@ fn main() { let app_state = app_state.clone(); async move { while let Some(paths) = open_paths_rx.next().await { - log::error!("OPEN PATHS FROM HANDLE"); cx.update(|cx| workspace::open_paths(&paths, &app_state, cx)) .detach(); } diff --git a/styles/src/styleTree/projectPanel.ts b/styles/src/styleTree/projectPanel.ts index 90e0c82f5b..2601a12691 100644 --- a/styles/src/styleTree/projectPanel.ts +++ b/styles/src/styleTree/projectPanel.ts @@ -29,6 +29,39 @@ export default function projectPanel(colorScheme: ColorScheme) { } return { + openProjectButton: { + ...text(layer, "mono", "active", { size: "sm" }), + background: background(layer, "on"), + cornerRadius: 6, + border: border(layer, "on"), + margin: { + top: 20, + left: 10, + right: 10 + }, + padding: { + bottom: 2, + left: 10, + right: 10, + top: 2, + }, + active: { + ...text(layer, "mono", "on", "inverted"), + background: background(layer, "on", "inverted"), + border: border(layer, "on", "inverted"), + }, + clicked: { + ...text(layer, "mono", "on", "pressed"), + background: background(layer, "on", "pressed"), + border: border(layer, "on", "pressed"), + }, + hover: { + ...text(layer, "mono", "on", "hovered"), + background: background(layer, "on", "hovered"), + border: border(layer, "on", "hovered"), + }, + + }, background: background(layer), padding: { left: 12, right: 12, top: 6, bottom: 6 }, indentWidth: 8, From d173b1d4120cd5313104150e19ca0a9d31f2df35 Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 7 Mar 2023 18:56:03 -0500 Subject: [PATCH 30/93] Update db followers table when user leaves a project Co-Authored-By: Nathan Sobo --- crates/collab/src/db.rs | 43 ++++-- crates/collab/src/rpc.rs | 4 +- crates/collab/src/tests/integration_tests.rs | 123 +++++++++++++++--- crates/gpui/src/app/test_app_context.rs | 15 ++- .../workspace/src/dock/toggle_dock_button.rs | 2 +- 5 files changed, 156 insertions(+), 31 deletions(-) diff --git a/crates/collab/src/db.rs b/crates/collab/src/db.rs index c4ff2e3918..1d0fc377ab 100644 --- a/crates/collab/src/db.rs +++ b/crates/collab/src/db.rs @@ -1757,17 +1757,14 @@ impl Database { .add(follower::Column::ProjectId.eq(project_id)) .add( follower::Column::LeaderConnectionServerId - .eq(leader_connection.owner_id) - .and(follower::Column::LeaderConnectionId.eq(leader_connection.id)), + .eq(leader_connection.owner_id), ) + .add(follower::Column::LeaderConnectionId.eq(leader_connection.id)) .add( follower::Column::FollowerConnectionServerId - .eq(follower_connection.owner_id) - .and( - follower::Column::FollowerConnectionId - .eq(follower_connection.id), - ), - ), + .eq(follower_connection.owner_id), + ) + .add(follower::Column::FollowerConnectionId.eq(follower_connection.id)), ) .exec(&*tx) .await?; @@ -2560,7 +2557,7 @@ impl Database { &self, project_id: ProjectId, connection: ConnectionId, - ) -> Result> { + ) -> Result> { let room_id = self.room_id_for_project(project_id).await?; self.room_transaction(room_id, |tx| async move { let result = project_collaborator::Entity::delete_many() @@ -2592,13 +2589,39 @@ impl Database { .map(|collaborator| collaborator.connection()) .collect(); + follower::Entity::delete_many() + .filter( + Condition::any() + .add( + Condition::all() + .add(follower::Column::ProjectId.eq(project_id)) + .add( + follower::Column::LeaderConnectionServerId + .eq(connection.owner_id), + ) + .add(follower::Column::LeaderConnectionId.eq(connection.id)), + ) + .add( + Condition::all() + .add(follower::Column::ProjectId.eq(project_id)) + .add( + follower::Column::FollowerConnectionServerId + .eq(connection.owner_id), + ) + .add(follower::Column::FollowerConnectionId.eq(connection.id)), + ), + ) + .exec(&*tx) + .await?; + + let room = self.get_room(project.room_id, &tx).await?; let left_project = LeftProject { id: project_id, host_user_id: project.host_user_id, host_connection_id: project.host_connection()?, connection_ids, }; - Ok(left_project) + Ok((room, left_project)) }) .await } diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 710ddcb890..d13487440f 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -1408,7 +1408,7 @@ async fn leave_project(request: proto::LeaveProject, session: Session) -> Result let sender_id = session.connection_id; let project_id = ProjectId::from_proto(request.project_id); - let project = session + let (room, project) = &*session .db() .await .leave_project(project_id, sender_id) @@ -1419,7 +1419,9 @@ async fn leave_project(request: proto::LeaveProject, session: Session) -> Result host_connection_id = %project.host_connection_id, "leave project" ); + project_left(&project, &session); + room_updated(&room, &session.peer); Ok(()) } diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 28266824e3..4f027af758 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -5792,11 +5792,12 @@ async fn test_contact_requests( } #[gpui::test(iterations = 10)] -async fn test_following( +async fn test_basic_following( deterministic: Arc, cx_a: &mut TestAppContext, cx_b: &mut TestAppContext, cx_c: &mut TestAppContext, + cx_d: &mut TestAppContext, ) { deterministic.forbid_parking(); cx_a.update(editor::init); @@ -5806,11 +5807,14 @@ async fn test_following( let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; let client_c = server.create_client(cx_c, "user_c").await; + let client_d = server.create_client(cx_d, "user_d").await; server - .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) - .await; - server - .make_contacts(&mut [(&client_a, cx_a), (&client_c, cx_c)]) + .create_room(&mut [ + (&client_a, cx_a), + (&client_b, cx_b), + (&client_c, cx_c), + (&client_d, cx_d), + ]) .await; let active_call_a = cx_a.read(ActiveCall::global); let active_call_b = cx_b.read(ActiveCall::global); @@ -5877,6 +5881,7 @@ async fn test_following( let peer_id_a = client_a.peer_id().unwrap(); let peer_id_b = client_b.peer_id().unwrap(); let peer_id_c = client_c.peer_id().unwrap(); + let peer_id_d = client_d.peer_id().unwrap(); // Client A updates their selections in those editors editor_a1.update(cx_a, |editor, cx| { @@ -5896,25 +5901,15 @@ async fn test_following( .await .unwrap(); - // Client A invites client C to the call. - active_call_a - .update(cx_a, |call, cx| { - call.invite(client_c.current_user_id(cx_c).to_proto(), None, cx) - }) - .await - .unwrap(); cx_c.foreground().run_until_parked(); let active_call_c = cx_c.read(ActiveCall::global); - active_call_c - .update(cx_c, |call, cx| call.accept_incoming(cx)) - .await - .unwrap(); let project_c = client_c.build_remote_project(project_id, cx_c).await; let workspace_c = client_c.build_workspace(&project_c, cx_c); active_call_c .update(cx_c, |call, cx| call.set_location(Some(&project_c), cx)) .await .unwrap(); + drop(project_c); // Client C also follows client A. workspace_c @@ -5926,12 +5921,23 @@ async fn test_following( .await .unwrap(); + cx_d.foreground().run_until_parked(); + let active_call_d = cx_d.read(ActiveCall::global); + let project_d = client_d.build_remote_project(project_id, cx_d).await; + let workspace_d = client_d.build_workspace(&project_d, cx_d); + active_call_d + .update(cx_d, |call, cx| call.set_location(Some(&project_d), cx)) + .await + .unwrap(); + drop(project_d); + // All clients see that clients B and C are following client A. cx_c.foreground().run_until_parked(); for (name, active_call, cx) in [ ("A", &active_call_a, &cx_a), ("B", &active_call_b, &cx_b), ("C", &active_call_c, &cx_c), + ("D", &active_call_d, &cx_d), ] { active_call.read_with(*cx, |call, cx| { let room = call.room().unwrap().read(cx); @@ -5954,6 +5960,7 @@ async fn test_following( ("A", &active_call_a, &cx_a), ("B", &active_call_b, &cx_b), ("C", &active_call_c, &cx_c), + ("D", &active_call_d, &cx_d), ] { active_call.read_with(*cx, |call, cx| { let room = call.room().unwrap().read(cx); @@ -5965,6 +5972,90 @@ async fn test_following( }); } + // Client C re-follows client A. + workspace_c.update(cx_c, |workspace, cx| { + workspace.toggle_follow(&ToggleFollow(peer_id_a), cx); + }); + + // All clients see that clients B and C are following client A. + cx_c.foreground().run_until_parked(); + for (name, active_call, cx) in [ + ("A", &active_call_a, &cx_a), + ("B", &active_call_b, &cx_b), + ("C", &active_call_c, &cx_c), + ("D", &active_call_d, &cx_d), + ] { + active_call.read_with(*cx, |call, cx| { + let room = call.room().unwrap().read(cx); + assert_eq!( + room.followers_for(peer_id_a, project_id), + &[peer_id_b, peer_id_c], + "checking followers for A as {name}" + ); + }); + } + + // Client D follows client C. + workspace_d + .update(cx_d, |workspace, cx| { + workspace + .toggle_follow(&ToggleFollow(peer_id_c), cx) + .unwrap() + }) + .await + .unwrap(); + + // All clients see that D is following C + cx_d.foreground().run_until_parked(); + for (name, active_call, cx) in [ + ("A", &active_call_a, &cx_a), + ("B", &active_call_b, &cx_b), + ("C", &active_call_c, &cx_c), + ("D", &active_call_d, &cx_d), + ] { + active_call.read_with(*cx, |call, cx| { + let room = call.room().unwrap().read(cx); + assert_eq!( + room.followers_for(peer_id_c, project_id), + &[peer_id_d], + "checking followers for C as {name}" + ); + }); + } + + // Client C closes the project. + cx_c.drop_last(workspace_c); + + // Clients A and B see that client B is following A, and client C is not present in the followers. + cx_c.foreground().run_until_parked(); + for (name, active_call, cx) in [("A", &active_call_a, &cx_a), ("B", &active_call_b, &cx_b)] { + active_call.read_with(*cx, |call, cx| { + let room = call.room().unwrap().read(cx); + assert_eq!( + room.followers_for(peer_id_a, project_id), + &[peer_id_b], + "checking followers for A as {name}" + ); + }); + } + + // All clients see that no-one is following C + for (name, active_call, cx) in [ + ("A", &active_call_a, &cx_a), + ("B", &active_call_b, &cx_b), + ("C", &active_call_c, &cx_c), + ("D", &active_call_d, &cx_d), + ] { + active_call.read_with(*cx, |call, cx| { + let room = call.room().unwrap().read(cx); + assert_eq!( + room.followers_for(peer_id_c, project_id), + &[], + "checking followers for C as {name}" + ); + }); + } + let editor_b2 = workspace_b.read_with(cx_b, |workspace, cx| { workspace .active_item(cx) diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 1366e7e6ed..ad78346991 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -18,9 +18,10 @@ use smol::stream::StreamExt; use crate::{ executor, geometry::vector::Vector2F, keymap_matcher::Keystroke, platform, Action, - AnyViewHandle, AppContext, Appearance, Entity, Event, FontCache, InputHandler, KeyDownEvent, - ModelContext, ModelHandle, MutableAppContext, Platform, ReadModelWith, ReadViewWith, - RenderContext, Task, UpdateModel, UpdateView, View, ViewContext, ViewHandle, WeakHandle, + AnyViewHandle, AppContext, Appearance, Entity, Event, FontCache, Handle, InputHandler, + KeyDownEvent, ModelContext, ModelHandle, MutableAppContext, Platform, ReadModelWith, + ReadViewWith, RenderContext, Task, UpdateModel, UpdateView, View, ViewContext, ViewHandle, + WeakHandle, }; use collections::BTreeMap; @@ -329,6 +330,14 @@ impl TestAppContext { .assert_dropped(handle.id()) } + /// Drop a handle, assuming it is the last. If it is not the last, panic with debug information about + /// where the stray handles were created. + pub fn drop_last>(&mut self, handle: H) { + let weak = handle.downgrade(); + self.update(|_| drop(handle)); + self.assert_dropped(weak); + } + fn window_mut(&self, window_id: usize) -> std::cell::RefMut { std::cell::RefMut::map(self.cx.borrow_mut(), |state| { let (_, window) = state diff --git a/crates/workspace/src/dock/toggle_dock_button.rs b/crates/workspace/src/dock/toggle_dock_button.rs index cafbea7db3..0b96c3e67b 100644 --- a/crates/workspace/src/dock/toggle_dock_button.rs +++ b/crates/workspace/src/dock/toggle_dock_button.rs @@ -42,6 +42,7 @@ impl View for ToggleDockButton { let workspace = workspace.unwrap(); let dock_position = workspace.read(cx).dock.position; + let dock_pane = workspace.read(cx.app).dock_pane().clone(); let theme = cx.global::().theme.clone(); @@ -67,7 +68,6 @@ impl View for ToggleDockButton { }) .with_cursor_style(CursorStyle::PointingHand) .on_up(MouseButton::Left, move |event, cx| { - let dock_pane = workspace.read(cx.app).dock_pane(); let drop_index = dock_pane.read(cx.app).items_len() + 1; handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx); }); From 904993dfc99ef2146357872d0bd9123952e9ca51 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 7 Mar 2023 16:08:01 -0800 Subject: [PATCH 31/93] Change open paths to replace the existing window if dispatched from a window co-authored-by: Max --- crates/journal/src/journal.rs | 2 +- crates/workspace/src/workspace.rs | 208 ++++++++++++++++++------------ crates/zed/src/main.rs | 6 +- crates/zed/src/zed.rs | 37 +++++- 4 files changed, 163 insertions(+), 90 deletions(-) diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 76a56af93d..c02d61c3e2 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -48,7 +48,7 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { async move { let (journal_dir, entry_path) = create_entry.await?; let (workspace, _) = cx - .update(|cx| workspace::open_paths(&[journal_dir], &app_state, cx)) + .update(|cx| workspace::open_paths(&[journal_dir], &app_state, None, cx)) .await; let opened = workspace diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index f987d00ef1..65072637ac 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -44,7 +44,8 @@ use gpui::{ platform::{CursorStyle, WindowOptions}, AnyModelHandle, AnyViewHandle, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MouseButton, MutableAppContext, PathPromptOptions, Platform, PromptLevel, RenderContext, - SizeConstraint, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowBounds, + SizeConstraint, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, + WindowBounds, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ProjectItem}; use language::LanguageRegistry; @@ -188,12 +189,54 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { dock::init(cx); notifications::init(cx); - cx.add_global_action(open); + cx.add_global_action(|_: &Open, cx: &mut MutableAppContext| { + let mut paths = cx.prompt_for_paths(PathPromptOptions { + files: true, + directories: true, + multiple: true, + }); + + cx.spawn(|mut cx| async move { + if let Some(paths) = paths.recv().await.flatten() { + cx.update(|cx| cx.dispatch_global_action(OpenPaths { paths })); + } + }) + .detach(); + }); + cx.add_action(|_, _: &Open, cx: &mut ViewContext| { + let mut paths = cx.prompt_for_paths(PathPromptOptions { + files: true, + directories: true, + multiple: true, + }); + + let handle = cx.handle().downgrade(); + cx.spawn(|_, mut cx| async move { + if let Some(paths) = paths.recv().await.flatten() { + cx.update(|cx| { + cx.dispatch_action_at(handle.window_id(), handle.id(), OpenPaths { paths }) + }) + } + }) + .detach(); + }); cx.add_global_action({ let app_state = Arc::downgrade(&app_state); move |action: &OpenPaths, cx: &mut MutableAppContext| { if let Some(app_state) = app_state.upgrade() { - open_paths(&action.paths, &app_state, cx).detach(); + open_paths(&action.paths, &app_state, None, cx).detach(); + } + } + }); + cx.add_action({ + let app_state = Arc::downgrade(&app_state); + move |_, action: &OpenPaths, cx: &mut ViewContext| { + if let Some(app_state) = app_state.upgrade() { + let window_id = cx.window_id(); + let action = action.clone(); + cx.as_mut().defer(move |cx| { + open_paths(&action.paths, &app_state, Some(window_id), cx).detach(); + }) } } }); @@ -488,6 +531,7 @@ pub struct Workspace { active_call: Option<(ModelHandle, Vec)>, leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>, database_id: WorkspaceId, + _window_subscriptions: [Subscription; 3], _apply_leader_updates: Task>, _observe_current_user: Task<()>, } @@ -519,10 +563,6 @@ impl Workspace { dock_default_factory: DockDefaultItemFactory, cx: &mut ViewContext, ) -> Self { - cx.observe_fullscreen(|_, _, cx| cx.notify()).detach(); - - cx.observe_window_activation(Self::on_window_activation_changed) - .detach(); cx.observe(&project, |_, _, cx| cx.notify()).detach(); cx.subscribe(&project, move |this, _, event, cx| { match event { @@ -629,6 +669,28 @@ impl Workspace { active_call = Some((call, subscriptions)); } + let subscriptions = [ + cx.observe_fullscreen(|_, _, cx| cx.notify()), + cx.observe_window_activation(Self::on_window_activation_changed), + cx.observe_window_bounds(move |_, mut bounds, display, cx| { + // Transform fixed bounds to be stored in terms of the containing display + if let WindowBounds::Fixed(mut window_bounds) = bounds { + if let Some(screen) = cx.platform().screen_by_id(display) { + let screen_bounds = screen.bounds(); + window_bounds + .set_origin_x(window_bounds.origin_x() - screen_bounds.origin_x()); + window_bounds + .set_origin_y(window_bounds.origin_y() - screen_bounds.origin_y()); + bounds = WindowBounds::Fixed(window_bounds); + } + } + + cx.background() + .spawn(DB.set_window_bounds(workspace_id, bounds, display)) + .detach_and_log_err(cx); + }), + ]; + let mut this = Workspace { modal: None, weak_self: weak_handle.clone(), @@ -660,6 +722,7 @@ impl Workspace { _observe_current_user, _apply_leader_updates, leader_updates_tx, + _window_subscriptions: subscriptions, }; this.project_remote_id_changed(project.read(cx).remote_id(), cx); cx.defer(|this, cx| this.update_window_title(cx)); @@ -676,6 +739,7 @@ impl Workspace { fn new_local( abs_paths: Vec, app_state: Arc, + requesting_window_id: Option, cx: &mut MutableAppContext, ) -> Task<( ViewHandle, @@ -731,42 +795,9 @@ impl Workspace { )) }); - let (bounds, display) = if let Some(bounds) = window_bounds_override { - (Some(bounds), None) - } else { - serialized_workspace - .as_ref() - .and_then(|serialized_workspace| { - let display = serialized_workspace.display?; - let mut bounds = serialized_workspace.bounds?; - - // Stored bounds are relative to the containing display. - // So convert back to global coordinates if that screen still exists - if let WindowBounds::Fixed(mut window_bounds) = bounds { - if let Some(screen) = cx.platform().screen_by_id(display) { - let screen_bounds = screen.bounds(); - window_bounds.set_origin_x( - window_bounds.origin_x() + screen_bounds.origin_x(), - ); - window_bounds.set_origin_y( - window_bounds.origin_y() + screen_bounds.origin_y(), - ); - bounds = WindowBounds::Fixed(window_bounds); - } else { - // Screen no longer exists. Return none here. - return None; - } - } - - Some((bounds, display)) - }) - .unzip() - }; - - // Use the serialized workspace to construct the new window - let (_, workspace) = cx.add_window( - (app_state.build_window_options)(bounds, display, cx.platform().as_ref()), - |cx| { + let build_workspace = + |cx: &mut ViewContext, + serialized_workspace: Option| { let mut workspace = Workspace::new( serialized_workspace, workspace_id, @@ -775,29 +806,53 @@ impl Workspace { cx, ); (app_state.initialize_workspace)(&mut workspace, &app_state, cx); - cx.observe_window_bounds(move |_, mut bounds, display, cx| { - // Transform fixed bounds to be stored in terms of the containing display - if let WindowBounds::Fixed(mut window_bounds) = bounds { - if let Some(screen) = cx.platform().screen_by_id(display) { - let screen_bounds = screen.bounds(); - window_bounds.set_origin_x( - window_bounds.origin_x() - screen_bounds.origin_x(), - ); - window_bounds.set_origin_y( - window_bounds.origin_y() - screen_bounds.origin_y(), - ); - bounds = WindowBounds::Fixed(window_bounds); - } - } - - cx.background() - .spawn(DB.set_window_bounds(workspace_id, bounds, display)) - .detach_and_log_err(cx); - }) - .detach(); workspace - }, - ); + }; + + let workspace = if let Some(window_id) = requesting_window_id { + cx.update(|cx| { + cx.replace_root_view(window_id, |cx| build_workspace(cx, serialized_workspace)) + }) + } else { + let (bounds, display) = if let Some(bounds) = window_bounds_override { + (Some(bounds), None) + } else { + serialized_workspace + .as_ref() + .and_then(|serialized_workspace| { + let display = serialized_workspace.display?; + let mut bounds = serialized_workspace.bounds?; + + // Stored bounds are relative to the containing display. + // So convert back to global coordinates if that screen still exists + if let WindowBounds::Fixed(mut window_bounds) = bounds { + if let Some(screen) = cx.platform().screen_by_id(display) { + let screen_bounds = screen.bounds(); + window_bounds.set_origin_x( + window_bounds.origin_x() + screen_bounds.origin_x(), + ); + window_bounds.set_origin_y( + window_bounds.origin_y() + screen_bounds.origin_y(), + ); + bounds = WindowBounds::Fixed(window_bounds); + } else { + // Screen no longer exists. Return none here. + return None; + } + } + + Some((bounds, display)) + }) + .unzip() + }; + + // Use the serialized workspace to construct the new window + cx.add_window( + (app_state.build_window_options)(bounds, display, cx.platform().as_ref()), + |cx| build_workspace(cx, serialized_workspace), + ) + .1 + }; notify_if_database_failed(&workspace, &mut cx); @@ -893,7 +948,7 @@ impl Workspace { if self.project.read(cx).is_local() { Task::Ready(Some(callback(self, cx))) } else { - let task = Self::new_local(Vec::new(), app_state.clone(), cx); + let task = Self::new_local(Vec::new(), app_state.clone(), None, cx); cx.spawn(|_vh, mut cx| async move { let (workspace, _) = task.await; workspace.update(&mut cx, callback) @@ -2809,21 +2864,6 @@ impl std::fmt::Debug for OpenPaths { } } -fn open(_: &Open, cx: &mut MutableAppContext) { - let mut paths = cx.prompt_for_paths(PathPromptOptions { - files: true, - directories: true, - multiple: true, - }); - - cx.spawn(|mut cx| async move { - if let Some(paths) = paths.recv().await.flatten() { - cx.update(|cx| cx.dispatch_global_action(OpenPaths { paths })); - } - }) - .detach(); -} - pub struct WorkspaceCreated(WeakViewHandle); pub fn activate_workspace_for_project( @@ -2850,6 +2890,7 @@ pub async fn last_opened_workspace_paths() -> Option { pub fn open_paths( abs_paths: &[PathBuf], app_state: &Arc, + requesting_window_id: Option, cx: &mut MutableAppContext, ) -> Task<( ViewHandle, @@ -2880,7 +2921,8 @@ pub fn open_paths( .contains(&false); cx.update(|cx| { - let task = Workspace::new_local(abs_paths, app_state.clone(), cx); + let task = + Workspace::new_local(abs_paths, app_state.clone(), requesting_window_id, cx); cx.spawn(|mut cx| async move { let (workspace, items) = task.await; @@ -2904,7 +2946,7 @@ pub fn open_new( cx: &mut MutableAppContext, init: impl FnOnce(&mut Workspace, &mut ViewContext) + 'static, ) -> Task<()> { - let task = Workspace::new_local(Vec::new(), app_state.clone(), cx); + let task = Workspace::new_local(Vec::new(), app_state.clone(), None, cx); cx.spawn(|mut cx| async move { let (workspace, opened_paths) = task.await; diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 1b794b3857..13a44fef10 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -216,7 +216,7 @@ fn main() { cx.spawn(|cx| handle_cli_connection(connection, app_state.clone(), cx)) .detach(); } else if let Ok(Some(paths)) = open_paths_rx.try_next() { - cx.update(|cx| workspace::open_paths(&paths, &app_state, cx)) + cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx)) .detach(); } else { cx.spawn(|cx| async move { restore_or_create_workspace(cx).await }) @@ -237,7 +237,7 @@ fn main() { let app_state = app_state.clone(); async move { while let Some(paths) = open_paths_rx.next().await { - cx.update(|cx| workspace::open_paths(&paths, &app_state, cx)) + cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx)) .detach(); } } @@ -603,7 +603,7 @@ async fn handle_cli_connection( paths }; let (workspace, items) = cx - .update(|cx| workspace::open_paths(&paths, &app_state, cx)) + .update(|cx| workspace::open_paths(&paths, &app_state, None, cx)) .await; let mut errored = false; diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 2fb956f5b6..63e026b9ab 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -728,6 +728,10 @@ mod tests { "ca": null, "cb": null, }, + "d": { + "da": null, + "db": null, + }, }), ) .await; @@ -736,13 +740,14 @@ mod tests { open_paths( &[PathBuf::from("/root/a"), PathBuf::from("/root/b")], &app_state, + None, cx, ) }) .await; assert_eq!(cx.window_ids().len(), 1); - cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx)) + cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx)) .await; assert_eq!(cx.window_ids().len(), 1); let workspace_1 = cx.root_view::(cx.window_ids()[0]).unwrap(); @@ -756,11 +761,37 @@ mod tests { open_paths( &[PathBuf::from("/root/b"), PathBuf::from("/root/c")], &app_state, + None, cx, ) }) .await; assert_eq!(cx.window_ids().len(), 2); + + // Replace existing windows + let window_id = cx.window_ids()[0]; + cx.update(|cx| { + open_paths( + &[PathBuf::from("/root/c"), PathBuf::from("/root/d")], + &app_state, + Some(window_id), + cx, + ) + }) + .await; + assert_eq!(cx.window_ids().len(), 2); + let workspace_1 = cx.root_view::(window_id).unwrap(); + workspace_1.read_with(cx, |workspace, cx| { + assert_eq!( + workspace + .worktrees(cx) + .map(|w| w.read(cx).abs_path()) + .collect::>(), + &[Path::new("/root/c").into(), Path::new("/root/d").into()] + ); + assert!(workspace.left_sidebar().read(cx).is_open()); + assert!(workspace.active_pane().is_focused(cx)); + }); } #[gpui::test] @@ -772,7 +803,7 @@ mod tests { .insert_tree("/root", json!({"a": "hey"})) .await; - cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx)) + cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx)) .await; assert_eq!(cx.window_ids().len(), 1); @@ -810,7 +841,7 @@ mod tests { assert!(!cx.is_window_edited(workspace.window_id())); // Opening the buffer again doesn't impact the window's edited state. - cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, cx)) + cx.update(|cx| open_paths(&[PathBuf::from("/root/a")], &app_state, None, cx)) .await; let editor = workspace.read_with(cx, |workspace, cx| { workspace From 3594243644c27085c18e8725e1d9402f6bb58357 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 7 Mar 2023 16:22:13 -0800 Subject: [PATCH 32/93] Fix bug where open would offer to hang up a remote call Co-authored-by: max --- crates/workspace/src/workspace.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 65072637ac..1c1afea4aa 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -228,16 +228,27 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { } } }); - cx.add_action({ + cx.add_async_action({ let app_state = Arc::downgrade(&app_state); - move |_, action: &OpenPaths, cx: &mut ViewContext| { - if let Some(app_state) = app_state.upgrade() { - let window_id = cx.window_id(); - let action = action.clone(); - cx.as_mut().defer(move |cx| { - open_paths(&action.paths, &app_state, Some(window_id), cx).detach(); - }) + move |workspace, action: &OpenPaths, cx: &mut ViewContext| { + if !workspace.project().read(cx).is_local() { + cx.propagate_action(); + return None; } + + let app_state = app_state.upgrade()?; + let window_id = cx.window_id(); + let action = action.clone(); + let close = workspace.prepare_to_close(false, cx); + + Some(cx.spawn_weak(|_, mut cx| async move { + let can_close = close.await?; + if can_close { + cx.update(|cx| open_paths(&action.paths, &app_state, Some(window_id), cx)) + .await; + } + Ok(()) + })) } }); From 350ddf20257e29bb9e4a55dbf2e380b703557927 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Tue, 7 Mar 2023 17:13:01 -0800 Subject: [PATCH 33/93] Add keymap picker UI Co-authored-by: Max --- Cargo.lock | 2 + crates/project_panel/src/project_panel.rs | 2 +- crates/settings/src/keymap_file.rs | 4 +- crates/settings/src/settings.rs | 46 ++++-- crates/welcome/Cargo.toml | 2 + crates/welcome/src/base_keymap_picker.rs | 175 ++++++++++++++++++++++ crates/welcome/src/welcome.rs | 17 ++- crates/zed/src/zed.rs | 3 +- 8 files changed, 225 insertions(+), 26 deletions(-) create mode 100644 crates/welcome/src/base_keymap_picker.rs diff --git a/Cargo.lock b/Cargo.lock index 2b894eff35..9d950712a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8030,9 +8030,11 @@ version = "0.1.0" dependencies = [ "anyhow", "editor", + "fuzzy", "gpui", "install_cli", "log", + "picker", "project", "settings", "theme", diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 5de68b12fd..a95a8b2deb 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1329,7 +1329,7 @@ impl View for ProjectPanel { keystroke_label( parent_view_id, - "Open a new project", + "Open project", &button_style, context_menu_item.keystroke, workspace::Open, diff --git a/crates/settings/src/keymap_file.rs b/crates/settings/src/keymap_file.rs index 9048719d3f..2235bc351d 100644 --- a/crates/settings/src/keymap_file.rs +++ b/crates/settings/src/keymap_file.rs @@ -46,8 +46,8 @@ impl KeymapFileContent { Self::load(path, cx).unwrap(); } - if let Some(base_keymap) = cx.global::().base_keymap { - Self::load(base_keymap.asset_path(), cx).log_err(); + if let Some(asset_path) = cx.global::().base_keymap.asset_path() { + Self::load(asset_path, cx).log_err(); } } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index ae67428948..49f43e7a2d 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -55,24 +55,46 @@ pub struct Settings { pub telemetry_defaults: TelemetrySettings, pub telemetry_overrides: TelemetrySettings, pub auto_update: bool, - pub base_keymap: Option, + pub base_keymap: BaseKeymap, } -#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)] pub enum BaseKeymap { + #[default] + VSCode, JetBrains, Sublime, Atom, } impl BaseKeymap { - pub fn asset_path(&self) -> &str { + pub const OPTIONS: [(&'static str, Self); 4] = [ + ("VSCode (Default)", Self::VSCode), + ("Atom", Self::Atom), + ("JetBrains", Self::JetBrains), + ("Sublime", Self::Sublime), + ]; + + pub fn asset_path(&self) -> Option<&'static str> { match self { - BaseKeymap::JetBrains => "keymaps/jetbrains.json", - BaseKeymap::Sublime => "keymaps/sublime_text.json", - BaseKeymap::Atom => "keymaps/atom.json", + BaseKeymap::JetBrains => Some("keymaps/jetbrains.json"), + BaseKeymap::Sublime => Some("keymaps/sublime_text.json"), + BaseKeymap::Atom => Some("keymaps/atom.json"), + BaseKeymap::VSCode => None, } } + + pub fn names() -> impl Iterator { + Self::OPTIONS.iter().map(|(name, _)| *name) + } + + pub fn from_names(option: &str) -> BaseKeymap { + Self::OPTIONS + .iter() + .copied() + .find_map(|(name, value)| (name == option).then(|| value)) + .unwrap_or_default() + } } #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] @@ -455,7 +477,7 @@ impl Settings { merge(&mut self.vim_mode, data.vim_mode); merge(&mut self.autosave, data.autosave); merge(&mut self.default_dock_anchor, data.default_dock_anchor); - merge(&mut self.base_keymap, Some(data.base_keymap)); + merge(&mut self.base_keymap, data.base_keymap); // Ensure terminal font is loaded, so we can request it in terminal_element layout if let Some(terminal_font) = &data.terminal.font_family { @@ -633,7 +655,7 @@ impl Settings { }, telemetry_overrides: Default::default(), auto_update: true, - base_keymap: None, + base_keymap: Default::default(), } } @@ -722,13 +744,7 @@ pub fn parse_json_with_comments(content: &str) -> Result )?) } -/// Expects the key to be unquoted, and the value to be valid JSON -/// (e.g. values should be unquoted for numbers and bools, quoted for strings) -pub fn write_settings_key( - settings_content: &mut String, - key_path: &[&str], - new_value: &T, -) { +fn write_settings_key(settings_content: &mut String, key_path: &[&str], new_value: &Value) { let mut parser = tree_sitter::Parser::new(); parser.set_language(tree_sitter_json::language()).unwrap(); let tree = parser.parse(&settings_content, None).unwrap(); diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index e76636411b..d3b0e09697 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -14,6 +14,7 @@ test-support = [] anyhow = "1.0.38" log = "0.4" editor = { path = "../editor" } +fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } install_cli = { path = "../install_cli" } project = { path = "../project" } @@ -21,4 +22,5 @@ settings = { path = "../settings" } theme = { path = "../theme" } theme_selector = { path = "../theme_selector" } util = { path = "../util" } +picker = { path = "../picker" } workspace = { path = "../workspace" } \ No newline at end of file diff --git a/crates/welcome/src/base_keymap_picker.rs b/crates/welcome/src/base_keymap_picker.rs new file mode 100644 index 0000000000..a37bcb1837 --- /dev/null +++ b/crates/welcome/src/base_keymap_picker.rs @@ -0,0 +1,175 @@ +use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; +use gpui::{ + actions, + elements::{ChildView, Element as _, Label}, + AnyViewHandle, Entity, MutableAppContext, View, ViewContext, ViewHandle, +}; +use picker::{Picker, PickerDelegate}; +use settings::{settings_file::SettingsFile, BaseKeymap, Settings}; +use workspace::Workspace; + +pub struct BaseKeymapSelector { + matches: Vec, + picker: ViewHandle>, + selected_index: usize, +} + +actions!(welcome, [ToggleBaseKeymapSelector]); + +pub fn init(cx: &mut MutableAppContext) { + Picker::::init(cx); + cx.add_action({ + move |workspace, _: &ToggleBaseKeymapSelector, cx| BaseKeymapSelector::toggle(workspace, cx) + }); +} + +pub enum Event { + Dismissed, +} + +impl BaseKeymapSelector { + fn toggle(workspace: &mut Workspace, cx: &mut ViewContext) { + workspace.toggle_modal(cx, |_, cx| { + let this = cx.add_view(|cx| Self::new(cx)); + cx.subscribe(&this, Self::on_event).detach(); + this + }); + } + + fn new(cx: &mut ViewContext) -> Self { + let base = cx.global::().base_keymap; + let selected_index = BaseKeymap::OPTIONS + .iter() + .position(|(_, value)| *value == base) + .unwrap_or(0); + + let this = cx.weak_handle(); + Self { + picker: cx.add_view(|cx| Picker::new("Select a base keymap", this, cx)), + matches: Vec::new(), + selected_index, + } + } + + fn on_event( + workspace: &mut Workspace, + _: ViewHandle, + event: &Event, + cx: &mut ViewContext, + ) { + match event { + Event::Dismissed => { + workspace.dismiss_modal(cx); + } + } + } +} + +impl Entity for BaseKeymapSelector { + type Event = Event; +} + +impl View for BaseKeymapSelector { + fn ui_name() -> &'static str { + "BaseKeymapSelector" + } + + fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox { + ChildView::new(self.picker.clone(), cx).boxed() + } + + fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { + if cx.is_self_focused() { + cx.focus(&self.picker); + } + } +} + +impl PickerDelegate for BaseKeymapSelector { + fn match_count(&self) -> usize { + self.matches.len() + } + + fn selected_index(&self) -> usize { + self.selected_index + } + + fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext) { + self.selected_index = ix; + } + + fn update_matches(&mut self, query: String, cx: &mut ViewContext) -> gpui::Task<()> { + let background = cx.background().clone(); + let candidates = BaseKeymap::names() + .enumerate() + .map(|(id, name)| StringMatchCandidate { + id, + char_bag: name.into(), + string: name.into(), + }) + .collect::>(); + + cx.spawn(|this, mut cx| async move { + let matches = if query.is_empty() { + candidates + .into_iter() + .enumerate() + .map(|(index, candidate)| StringMatch { + candidate_id: index, + string: candidate.string, + positions: Vec::new(), + score: 0.0, + }) + .collect() + } else { + match_strings( + &candidates, + &query, + false, + 100, + &Default::default(), + background, + ) + .await + }; + + this.update(&mut cx, |this, cx| { + this.matches = matches; + this.selected_index = this + .selected_index + .min(this.matches.len().saturating_sub(1)); + cx.notify(); + }); + }) + } + + fn confirm(&mut self, cx: &mut ViewContext) { + if let Some(selection) = self.matches.get(self.selected_index) { + let base_keymap = BaseKeymap::from_names(&selection.string); + SettingsFile::update(cx, move |settings| settings.base_keymap = Some(base_keymap)); + } + cx.emit(Event::Dismissed); + } + + fn dismiss(&mut self, cx: &mut ViewContext) { + cx.emit(Event::Dismissed) + } + + fn render_match( + &self, + ix: usize, + mouse_state: &mut gpui::MouseState, + selected: bool, + cx: &gpui::AppContext, + ) -> gpui::ElementBox { + let theme = &cx.global::().theme; + let keymap_match = &self.matches[ix]; + let style = theme.picker.item.style_for(mouse_state, selected); + + Label::new(keymap_match.string.clone(), style.label.clone()) + .with_highlights(keymap_match.positions.clone()) + .contained() + .with_style(style.container) + .boxed() + } +} diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 8bb055dad0..c780a06ee1 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,3 +1,5 @@ +mod base_keymap_picker; + use std::borrow::Cow; use gpui::{ @@ -9,11 +11,15 @@ use settings::{settings_file::SettingsFile, Settings, SettingsFileContent}; use theme::CheckboxStyle; use workspace::{item::Item, PaneBackdrop, Welcome, Workspace, WorkspaceId}; +use crate::base_keymap_picker::ToggleBaseKeymapSelector; + pub fn init(cx: &mut MutableAppContext) { cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { let welcome_page = cx.add_view(WelcomePage::new); workspace.add_item(Box::new(welcome_page), cx) - }) + }); + + base_keymap_picker::init(cx); } pub struct WelcomePage { @@ -64,9 +70,9 @@ impl View for WelcomePage { .contained() .with_style(theme.welcome.logo_subheading.container) .boxed(), - self.render_cta_button(2, "Choose a theme", theme_selector::Toggle, width, cx), - self.render_cta_button(3, "Choose a keymap", theme_selector::Toggle, width, cx), - self.render_cta_button(4, "Install the CLI", install_cli::Install, width, cx), + self.render_cta_button("Choose a theme", theme_selector::Toggle, width, cx), + self.render_cta_button("Choose a keymap", ToggleBaseKeymapSelector, width, cx), + self.render_cta_button("Install the CLI", install_cli::Install, width, cx), self.render_settings_checkbox::( "Do you want to send telemetry?", &theme.welcome.checkbox, @@ -110,7 +116,6 @@ impl WelcomePage { fn render_cta_button( &self, - region_id: usize, label: L, action: A, width: f32, @@ -121,7 +126,7 @@ impl WelcomePage { A: 'static + Action + Clone, { let theme = cx.global::().theme.clone(); - MouseEventHandler::::new(region_id, cx, |state, _| { + MouseEventHandler::::new(0, cx, |state, _| { let style = theme.welcome.button.style_for(state, false); Label::new(label, style.text.clone()) .aligned() diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 63e026b9ab..3b48632265 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -35,7 +35,7 @@ use std::{borrow::Cow, env, path::Path, str, sync::Arc}; use util::{channel::ReleaseChannel, paths, ResultExt, StaffMode}; use uuid::Uuid; pub use workspace; -use workspace::{dock::Dock, open_new, sidebar::SidebarSide, AppState, Restart, Workspace}; +use workspace::{open_new, sidebar::SidebarSide, AppState, Restart, Workspace}; pub const FIRST_OPEN: &str = "first_open"; @@ -270,7 +270,6 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { workspace.toggle_sidebar(SidebarSide::Left, cx); let welcome_page = cx.add_view(|cx| welcome::WelcomePage::new(cx)); workspace.add_item_to_center(Box::new(welcome_page.clone()), cx); - Dock::move_dock(workspace, settings::DockAnchor::Bottom, false, cx); cx.focus(welcome_page); cx.notify(); }) From b4561b848d05af4908b5f493767f7cad3005610d Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Mar 2023 10:56:40 +0100 Subject: [PATCH 34/93] Limit the number of parallel messages handled for any given connection --- crates/collab/src/rpc.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index d13487440f..1deaefec1a 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -53,7 +53,7 @@ use std::{ }, time::Duration, }; -use tokio::sync::watch; +use tokio::sync::{watch, Semaphore}; use tower::ServiceBuilder; use tracing::{info_span, instrument, Instrument}; @@ -542,8 +542,13 @@ impl Server { // This arrangement ensures we will attempt to process earlier messages first, but fall // back to processing messages arrived later in the spirit of making progress. let mut foreground_message_handlers = FuturesUnordered::new(); + let concurrent_handlers = Arc::new(Semaphore::new(256)); loop { - let next_message = incoming_rx.next().fuse(); + let next_message = async { + let permit = concurrent_handlers.clone().acquire_owned().await.unwrap(); + let message = incoming_rx.next().await; + (permit, message) + }.fuse(); futures::pin_mut!(next_message); futures::select_biased! { _ = teardown.changed().fuse() => return Ok(()), @@ -554,7 +559,8 @@ impl Server { break; } _ = foreground_message_handlers.next() => {} - message = next_message => { + next_message = next_message => { + let (permit, message) = next_message; if let Some(message) = message { let type_name = message.payload_type_name(); let span = tracing::info_span!("receive message", %user_id, %login, %connection_id, %address, type_name); @@ -564,7 +570,10 @@ impl Server { let handle_message = (handler)(message, session.clone()); drop(span_enter); - let handle_message = handle_message.instrument(span); + let handle_message = async move { + handle_message.await; + drop(permit); + }.instrument(span); if is_background { executor.spawn_detached(handle_message); } else { From a435dc1339bd10e2b43f5bbd89f74950332abcf5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Mar 2023 16:11:51 +0100 Subject: [PATCH 35/93] Clear selections on buffer only if they hadn't been cleared already --- crates/language/src/buffer.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index acccb553d3..09a9634227 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -1353,7 +1353,13 @@ impl Buffer { } pub fn remove_active_selections(&mut self, cx: &mut ModelContext) { - self.set_active_selections(Arc::from([]), false, Default::default(), cx); + if self + .remote_selections + .get(&self.text.replica_id()) + .map_or(true, |set| !set.selections.is_empty()) + { + self.set_active_selections(Arc::from([]), false, Default::default(), cx); + } } pub fn set_text(&mut self, text: T, cx: &mut ModelContext) -> Option From b687aec9d9f79ae7428fc2c7cc0a48d53bb9652e Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 8 Mar 2023 17:02:12 +0100 Subject: [PATCH 36/93] Avoid saving buffer if it's neither dirty nor in conflict However, keep emitting `Saved` events so that the language server is notified and can perform tasks related to saving (e.g., running `cargo check` in the case of rust-analyzer). --- crates/project/src/project.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 2ebea8d07d..f93de8e1d8 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1434,7 +1434,19 @@ impl Project { let worktree = file.worktree.clone(); let path = file.path.clone(); worktree.update(cx, |worktree, cx| match worktree { - Worktree::Local(worktree) => worktree.save_buffer(buffer, path, false, cx), + Worktree::Local(worktree) => { + if buffer.read(cx).is_dirty() || buffer.read(cx).has_conflict() { + worktree.save_buffer(buffer, path, false, cx) + } else { + buffer.update(cx, |buffer, cx| { + let version = buffer.saved_version().clone(); + let fingerprint = buffer.saved_version_fingerprint(); + let mtime = buffer.saved_mtime(); + buffer.did_save(version.clone(), fingerprint, mtime, cx); + Task::ready(Ok((version, fingerprint, mtime))) + }) + } + } Worktree::Remote(worktree) => worktree.save_buffer(buffer, cx), }) } From ae510c80dbc71ad8864ff96b023412444c630346 Mon Sep 17 00:00:00 2001 From: Joseph Lyons Date: Wed, 8 Mar 2023 13:25:32 -0500 Subject: [PATCH 37/93] v0.77.x dev --- Cargo.lock | 2 +- crates/zed/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e7997458e..f89666c86d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8358,7 +8358,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zed" -version = "0.76.0" +version = "0.77.0" dependencies = [ "activity_indicator", "anyhow", diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 19c9a3d727..8060c2af11 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] description = "The fast, collaborative code editor." edition = "2021" name = "zed" -version = "0.76.0" +version = "0.77.0" publish = false [lib] From 14497027d4ef0f86c7d251e2abf5ac4e24dc8476 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 8 Mar 2023 12:22:16 -0800 Subject: [PATCH 38/93] collab 0.6.2 --- Cargo.lock | 2 +- crates/collab/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f89666c86d..82a045ed3a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1188,7 +1188,7 @@ dependencies = [ [[package]] name = "collab" -version = "0.6.1" +version = "0.6.2" dependencies = [ "anyhow", "async-tungstenite", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index 86fe9174bf..fee7089ce6 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] default-run = "collab" edition = "2021" name = "collab" -version = "0.6.1" +version = "0.6.2" publish = false [[bin]] From 9842b7ad1a7f0c6008c9b030e2c7d8db9b63c0a2 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 8 Mar 2023 16:34:27 -0500 Subject: [PATCH 39/93] WIP Co-Authored-By: Mikayla Maki --- crates/theme/src/theme.rs | 3 + crates/welcome/src/welcome.rs | 95 ++++++++++------ styles/src/styleTree/welcome.ts | 189 ++++++++++++++++++-------------- 3 files changed, 171 insertions(+), 116 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index e7252fbf61..8fc6db3e5b 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -858,6 +858,9 @@ pub struct WelcomeStyle { pub logo_subheading: ContainedText, pub checkbox: CheckboxStyle, pub button: Interactive, + pub button_group: ContainerStyle, + pub heading_group: ContainerStyle, + pub checkbox_group: ContainerStyle, } #[derive(Clone, Deserialize, Default)] diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index c780a06ee1..d5431e5108 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -54,39 +54,72 @@ impl View for WelcomePage { self_handle.id(), Flex::column() .with_children([ - Image::new("images/zed-logo-90x90.png") - .constrained() - .with_width(90.) - .with_height(90.) - .aligned() + Flex::column() + .with_children([ + Image::new("images/zed-logo-90x90.png") + .constrained() + .with_width(90.) + .with_height(90.) + .aligned() + .contained() + .aligned() + .boxed(), + Label::new( + "Code at the speed of thought", + theme.welcome.logo_subheading.text.clone(), + ) + .aligned() + .contained() + .with_style(theme.welcome.logo_subheading.container) + .boxed(), + ]) .contained() - .aligned() + .with_style(theme.welcome.heading_group) + .boxed(), + Flex::row() + .with_children([ + self.render_cta_button( + "Choose a theme", + theme_selector::Toggle, + width, + cx, + ), + self.render_cta_button( + "Choose a keymap", + ToggleBaseKeymapSelector, + width, + cx, + ), + self.render_cta_button( + "Install the CLI", + install_cli::Install, + width, + cx, + ), + ]) + .contained() + .with_style(theme.welcome.button_group) + .boxed(), + Flex::column() + .with_children([ + self.render_settings_checkbox::( + "Do you want to send telemetry?", + &theme.welcome.checkbox, + metrics, + cx, + |content, checked| content.telemetry.set_metrics(checked), + ), + self.render_settings_checkbox::( + "Send crash reports", + &theme.welcome.checkbox, + diagnostics, + cx, + |content, checked| content.telemetry.set_diagnostics(checked), + ), + ]) + .contained() + .with_style(theme.welcome.checkbox_group) .boxed(), - Label::new( - "Code at the speed of thought", - theme.welcome.logo_subheading.text.clone(), - ) - .aligned() - .contained() - .with_style(theme.welcome.logo_subheading.container) - .boxed(), - self.render_cta_button("Choose a theme", theme_selector::Toggle, width, cx), - self.render_cta_button("Choose a keymap", ToggleBaseKeymapSelector, width, cx), - self.render_cta_button("Install the CLI", install_cli::Install, width, cx), - self.render_settings_checkbox::( - "Do you want to send telemetry?", - &theme.welcome.checkbox, - metrics, - cx, - |content, checked| content.telemetry.set_metrics(checked), - ), - self.render_settings_checkbox::( - "Send crash reports", - &theme.welcome.checkbox, - diagnostics, - cx, - |content, checked| content.telemetry.set_diagnostics(checked), - ), ]) .constrained() .with_max_width(width) diff --git a/styles/src/styleTree/welcome.ts b/styles/src/styleTree/welcome.ts index bfd67cec8d..2dc0f59b2e 100644 --- a/styles/src/styleTree/welcome.ts +++ b/styles/src/styleTree/welcome.ts @@ -4,91 +4,110 @@ import { border, background, foreground, text, TextProperties } from "./componen export default function welcome(colorScheme: ColorScheme) { - let layer = colorScheme.highest; + let layer = colorScheme.highest; - let checkboxBase = { - cornerRadius: 4, - padding: { - left: 3, - right: 3, - top: 3, - bottom: 3, - }, - shadow: colorScheme.popoverShadow, - border: border(layer), - margin: { - right: 8, - top: 5, - bottom: 5 - }, - }; - - let interactive_text_size: TextProperties = { size: "md" } - - return { - pageWidth: 450, - logoSubheading: { - ...text(layer, "sans", { size: "lg" }), - margin: { - top: 10, - bottom: 7, - }, - }, - button: { - background: background(layer), - border: border(layer, "active"), - cornerRadius: 4, - margin: { - top: 8, - bottom: 7 - }, - padding: { - top: 1, - bottom: 1, - left: 7, - right: 7, - }, - ...text(layer, "sans", "hovered", interactive_text_size), - hover: { - ...text(layer, "sans", "hovered", interactive_text_size), - background: background(layer, "hovered"), - border: border(layer, "hovered"), - }, - }, - checkbox: { - label: { - ...text(layer, "sans", interactive_text_size), - // Also supports margin, container, border, etc. - }, - container: { - margin: { - top: 5, + let checkboxBase = { + cornerRadius: 4, + padding: { + left: 3, + right: 3, + top: 3, + bottom: 3, }, - }, - width: 12, - height: 12, - checkIcon: "icons/check_12.svg", - checkIconColor: foreground(layer, "on"), - default: { - ...checkboxBase, - background: background(layer, "default"), - border: border(layer, "active") - }, - checked: { - ...checkboxBase, - background: background(layer, "hovered"), - border: border(layer, "active") - }, - hovered: { - ...checkboxBase, - background: background(layer, "hovered"), - border: border(layer, "hovered") - }, - hoveredAndChecked: { - ...checkboxBase, - background: background(layer, "hovered"), - border: border(layer, "active") - } + // shadow: colorScheme.popoverShadow, + border: border(layer), + margin: { + right: 8, + top: 5, + bottom: 5 + }, + }; + + let interactive_text_size: TextProperties = { size: "sm" } + + return { + pageWidth: 320, + logoSubheading: { + ...text(layer, "sans", { size: "lg" }), + margin: { + top: 10, + bottom: 7, + }, + }, + buttonGroup: { + border: border(layer, "active"), + margin: { + top: 8, + bottom: 7 + }, + }, + headingGroup: { + margin: { + top: 8, + bottom: 7 + }, + }, + checkboxGroup: { + margin: { + top: 8, + bottom: 7 + }, + }, + button: { + background: background(layer), + border: border(layer, "default"), + cornerRadius: 4, + margin: { + top: 8, + bottom: 7 + }, + padding: { + top: 1, + bottom: 1, + left: 7, + right: 7, + }, + ...text(layer, "sans", "default", interactive_text_size), + hover: { + ...text(layer, "sans", "default", interactive_text_size), + background: background(layer, "default"), + border: border(layer, "active"), + }, + }, + checkbox: { + label: { + ...text(layer, "sans", interactive_text_size), + // Also supports margin, container, border, etc. + }, + container: { + margin: { + top: 5, + }, + }, + width: 12, + height: 12, + checkIcon: "icons/check_12.svg", + checkIconColor: foreground(layer, "on"), + default: { + ...checkboxBase, + background: background(layer, "default"), + border: border(layer, "active") + }, + checked: { + ...checkboxBase, + background: background(layer, "hovered"), + border: border(layer, "active") + }, + hovered: { + ...checkboxBase, + background: background(layer, "hovered"), + border: border(layer, "hovered") + }, + hoveredAndChecked: { + ...checkboxBase, + background: background(layer, "hovered"), + border: border(layer, "active") + } + } } - } -} \ No newline at end of file +} From cc33f83e4e311fa454f8543ef14a57276f794162 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 8 Mar 2023 16:45:35 -0500 Subject: [PATCH 40/93] Add Zed logo icon Co-Authored-By: Mikayla Maki --- assets/icons/logo_96.svg | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 assets/icons/logo_96.svg diff --git a/assets/icons/logo_96.svg b/assets/icons/logo_96.svg new file mode 100644 index 0000000000..dc98bb8bc2 --- /dev/null +++ b/assets/icons/logo_96.svg @@ -0,0 +1,3 @@ + + + From 344f59adf7af52b3f41aa2f37b1c0de798b2d8ef Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Wed, 8 Mar 2023 17:14:15 -0500 Subject: [PATCH 41/93] Tweak welcome design Co-Authored-By: Mikayla Maki --- crates/theme/src/theme.rs | 14 +++++++++++ crates/welcome/src/welcome.rs | 17 +++++++++---- styles/src/styleTree/welcome.ts | 42 +++++++++++++++++++++------------ 3 files changed, 53 insertions(+), 20 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 8fc6db3e5b..13546b40cd 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -855,6 +855,7 @@ pub struct FeedbackStyle { #[derive(Clone, Deserialize, Default)] pub struct WelcomeStyle { pub page_width: f32, + pub logo: IconStyle, pub logo_subheading: ContainedText, pub checkbox: CheckboxStyle, pub button: Interactive, @@ -863,6 +864,19 @@ pub struct WelcomeStyle { pub checkbox_group: ContainerStyle, } +#[derive(Clone, Deserialize, Default)] +pub struct IconStyle { + pub color: Color, + pub icon: String, + pub dimensions: Dimensions, +} + +#[derive(Clone, Deserialize, Default)] +pub struct Dimensions { + pub width: f32, + pub height: f32, +} + #[derive(Clone, Deserialize, Default)] pub struct CheckboxStyle { pub check_icon: String, diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index d5431e5108..246ff26717 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -3,7 +3,7 @@ mod base_keymap_picker; use std::borrow::Cow; use gpui::{ - elements::{Empty, Flex, Image, Label, MouseEventHandler, ParentElement, Svg}, + elements::{Empty, Flex, Label, MouseEventHandler, ParentElement, Svg}, Action, Element, ElementBox, Entity, MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, }; @@ -56,10 +56,11 @@ impl View for WelcomePage { .with_children([ Flex::column() .with_children([ - Image::new("images/zed-logo-90x90.png") + Svg::new(theme.welcome.logo.icon.clone()) + .with_color(theme.welcome.logo.color) .constrained() - .with_width(90.) - .with_height(90.) + .with_width(theme.welcome.logo.dimensions.width) + .with_height(theme.welcome.logo.dimensions.height) .aligned() .contained() .aligned() @@ -75,8 +76,10 @@ impl View for WelcomePage { ]) .contained() .with_style(theme.welcome.heading_group) + .constrained() + .with_width(width) .boxed(), - Flex::row() + Flex::column() .with_children([ self.render_cta_button( "Choose a theme", @@ -99,6 +102,8 @@ impl View for WelcomePage { ]) .contained() .with_style(theme.welcome.button_group) + .constrained() + .with_width(width) .boxed(), Flex::column() .with_children([ @@ -119,6 +124,8 @@ impl View for WelcomePage { ]) .contained() .with_style(theme.welcome.checkbox_group) + .constrained() + .with_width(width) .boxed(), ]) .constrained() diff --git a/styles/src/styleTree/welcome.ts b/styles/src/styleTree/welcome.ts index 2dc0f59b2e..137c6df111 100644 --- a/styles/src/styleTree/welcome.ts +++ b/styles/src/styleTree/welcome.ts @@ -1,5 +1,6 @@ import { ColorScheme } from "../themes/common/colorScheme"; +import { withOpacity } from "../utils/color"; import { border, background, foreground, text, TextProperties } from "./components"; @@ -27,50 +28,61 @@ export default function welcome(colorScheme: ColorScheme) { return { pageWidth: 320, + logo: { + color: foreground(layer, "default"), + icon: "icons/logo_96.svg", + dimensions: { + width: 64, + height: 64, + } + }, logoSubheading: { - ...text(layer, "sans", { size: "lg" }), + ...text(layer, "sans", "variant", { size: "md" }), margin: { top: 10, bottom: 7, }, }, buttonGroup: { - border: border(layer, "active"), margin: { top: 8, - bottom: 7 + bottom: 16 }, }, headingGroup: { margin: { top: 8, - bottom: 7 + bottom: 12 }, }, checkboxGroup: { - margin: { - top: 8, - bottom: 7 + border: border(layer, "variant"), + background: withOpacity(background(layer, "hovered"), 0.25), + cornerRadius: 4, + padding: { + left: 12, + top: 2, + bottom: 2 }, }, button: { background: background(layer), - border: border(layer, "default"), + border: border(layer, "active"), cornerRadius: 4, margin: { - top: 8, - bottom: 7 + top: 4, + bottom: 4 }, padding: { - top: 1, - bottom: 1, + top: 3, + bottom: 3, left: 7, right: 7, }, ...text(layer, "sans", "default", interactive_text_size), hover: { ...text(layer, "sans", "default", interactive_text_size), - background: background(layer, "default"), + background: background(layer, "hovered"), border: border(layer, "active"), }, }, @@ -81,7 +93,7 @@ export default function welcome(colorScheme: ColorScheme) { }, container: { margin: { - top: 5, + top: 4, }, }, width: 12, @@ -101,7 +113,7 @@ export default function welcome(colorScheme: ColorScheme) { hovered: { ...checkboxBase, background: background(layer, "hovered"), - border: border(layer, "hovered") + border: border(layer, "active") }, hoveredAndChecked: { ...checkboxBase, From f62e0b502ab20c77a5e8296e0853fcfcbf2e9faa Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 8 Mar 2023 12:12:27 -0800 Subject: [PATCH 42/93] Remove welcome experience action Make logo switch between light and dark co-authored-by: Nathan --- Cargo.lock | 1 + assets/images/zed-logo-90x90.png | Bin 12413 -> 0 bytes crates/project_panel/src/project_panel.rs | 2 +- crates/welcome/Cargo.toml | 1 + crates/welcome/src/welcome.rs | 25 ++++++++++++-- crates/zed/src/main.rs | 21 ++++++------ crates/zed/src/zed.rs | 29 +--------------- styles/src/styleTree/projectPanel.ts | 39 ++++++++-------------- 8 files changed, 51 insertions(+), 67 deletions(-) delete mode 100644 assets/images/zed-logo-90x90.png diff --git a/Cargo.lock b/Cargo.lock index 9d950712a8..ac4cbb3761 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8029,6 +8029,7 @@ name = "welcome" version = "0.1.0" dependencies = [ "anyhow", + "db", "editor", "fuzzy", "gpui", diff --git a/assets/images/zed-logo-90x90.png b/assets/images/zed-logo-90x90.png deleted file mode 100644 index 17f92d2c1afbb8459fd31bfdc7ace50a412ce0a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12413 zcmZ{K2UJthvUcbK0tyP!QKU%;5G2%qq9P(iKza!!v;dLNds7hU9i${k@6x4%C^hsF ziZp=$0i^f#)R72F8#@>P@G>+ZiA+m#jn;o!IEK4qcc*AV4ML7baKs)<>J$9SJV?=iDbt)KYzEuONsoZlqIlq7V-#d zsY1oPtmVj|mZI1n#t3_(D!6 z8X|uyi1-MKDU@s6dxmE;>C zc0pX`J@smE(jXZ-6!7F0?Nh~jABD``Cv(<(r2GD&F$VDPX6RGe7>kEgF&q>Y_bYDl zJ$V&FVIi#%!SXEffmO8fOKBwg+B46X+AfAWA;aq2w*LBPx;NrIPZWmkIg@@4?tij3 z_|*BXex!Eh*&yluO;3`h2v;<4$d_ea>)n)uc^YeE#NzMoj!NIO(r(t?(+THqf4gAo z*!NBQmx>|TpSyoR86U0Y_1vW!c_8=L!x-jQ<{1`se)Dg8ZBrdP9j6|vdb3|j2dED6 zOwh{QkaV(pZSv)|F89`f9c5sx{HQH^nku2({JJ+k1epp%SFdzKhY*#%o_T8dj@ zUSit?JWn@M-9f+Cd>?-jKNg=JZ`t$s6&U7U_@p}9^No2v<7mz((pK<5=HT{0`lnBy zLO(UU%KoIwWs+mK?BDY|K{a7OOyx6GgmS8?=AibF>xk;epOM+2HD}6tplqm6UfCC& zk%3{*SSeoAIjU~_CLq-x9BkI_mv z5~M2T@blZ0Be=t7l(B5Fh;x{4ykMlRWbb)W4l_Enph3I#%~3g<@xTk92BLIUyJ^p} ztOL~P70D0|Wb?B=C-Tvndm;DAFW{2;qUiL3G??@WsYZ|rsTGwmqb>6(ljXfqu#C+F z{D<(aI4yGtSPHfunDg`Z*9zU`Vy9w=xz8fh{KJmxqWB^KopPLR9ATVPTxYJ9W{pkZ zn&$}p2n57g$!AH~XNzG=*2GNW)^N;y27Pk+y81s11Zu6T+#L$cyDKr3#pT`>&ue$f zKbyaWx%7Uv?yKpqnlyv#Pbjy2y7Rz?1<{|dI_0olunt%!1zrL#OHDDTn6|8Ku;Z2vY>M?6 zN}vC;(){D%+nE)O4ozL>{U|xvO&O5vktYOYC#fgqv9crtqysi%UCXEuiEb4~1GB7< z4v07E5h|}axB2OuQL`)yT$R*{eWe-|gu;}~ZgFCR8a{eWv9GW%nQY9_qZw~8tKZ&! zIQURl@GJLMU>BG5lfgCU(|6X}Q}YcnL>c}%ee0BtlzMlNLhBnVA z(rHCuGx3U=eR;2P#e)?jfZj&Oi#K{dO5W~#5E^qkCLw0v3riOLi|QAkFILaDA}((& zGE_?8OpD(ZKd9kS6?E`4botfkesw&29Qi(!JdC#VA)f0KU25!0B}b)(*wZ|c*VDo! zUiH2EU)J0#b^DXSi^H#*QuIJ-7>(4QC>M#8Xckl>_=I0B&E)5n+Cq+<>KffU5@r(M zhvx4G-;HCE^0h|e@Co{#Pi6Y}?Tz#i^=h7ar~D#1hWCBrZl{$CE}C1q!#AP14>gevH47UamD4#j!w+maS)wGFar@m*VGYRWG)74kB)2$=(OX?UUv+1k z=ZZGY_cJs2Xj>-2Bi2J_Wg255(!v82-bx}lCJ7`Fq>Nw?GMcFSSZRG`X5rZLJCQ3_ znIg$~g=~3C@0HQ#x_vBRNZ@ebQSa}h#|%t&a15>&xAqlOVr*lISN|XfmZyDxT=8C( z+ke>mq{83KKp6&1l8BE_@HXPp^*G__MD_SF879;h+E=gROW+^j58;1`5A+)Hw6es? zs^By6E>!D!>tWcIIbnEvPK6R|T3iGCNZd6(BmrzMI$dQsg8j7rX*$6-!6o$oTsCp; z81}9=qNLdXUdw35KY6%pwKH&3LuQZ2ugHHi{p43n!{TKBxAc;huYLuyIkiO=*xH%c z8CI_^hiOHwMWwSvEp9GU`dRsFGhm;&gNw_vRNGD4TC5E=fyJNb6A_S_o4wJAcVE5N z^l)K{6Q?MS1>wV(qk~VVd?MB&SXDw*HWd{jYs9?7vFw#{HD}*;c6#Xad9+d483Hnr z?vJ*#u58xUbgYK^ovSgn9`51`8zoLLD^vX;87=Pjx4%zntF9M>88bwg?0$3J7d=BR z-o^4$=@^xcU*UJ*KB;hn_Q$nw73@qIxK_C%i6xRx!)~bAV*VcP}z`I&oO-vZ(X5vz4*t_sxI6 z72qI*PVJJc3pI#^-@RkqbETt^jDd}T5|iI8`!_KcuTJ$Z`%Vq^js~}) zNGr+6a=aP+-0!rSe#*@yY@DHzS0jc(LBuXoU9V*Yy1M*Yh1>jo$JBZ08WPxwfW(*_ z)reSHM<1}u_c1`N1wQSNj85tmWbJKj7egKfPqcg&le{JVV%ESgn&v7xM)DHqb2Snu zx<0qj0^ovBQnNNbeRy5N8p3pyELBthkFMz(0Ad1C0MRu?03f_R(f*4jxTbmk!w<0m z5dT950DxRy0RTcY;lDXmXrlkne}(TGBnPimYHYN0opn`SOPM3=g-k6FW-uXl`*(j8 z05a}U*Q7nn*_6ZG-p;{E%3b!sKQyGS>A!5?1CD>FIKyQh=&Go5C?Jq94sju2A>juQ z3Jwkq8Ki}!l*Y3c|AJrN$v&`lc77)X1iHDo3AurU5J)Sah@_+>P*@ZwDk^xbA?W1c z;B4wH=-|Zp&q4lcoM$j6bEM5XXB&hA$KP>H%@8imvJW2oHT3V}pME;qSpLV7gVVpl zx(*QdR{|6f5(fTnFqpf|{{j0e`6uijasAVr%-_MJ6cG0BkT55wYquaGGXF5}|MC9g z-aqv4DjE35<>Br0*eL!9vCS-d!pq|u$itBC7|bt%?nwuU#|HP z`xDkqUDu?DOaTg4noV!2wI0)17&rTuHwpKelJVaZy+^Jr`%;(w#t#93`>n(KA4!-O zQ$Cnjw2q{iSX}O2t}lgleZtR#$%8sd+WXPcA5rMla2kgY&^7V9ILP~Xu~#9- ze^y{;;ktF;qIBGyptL@rpAmQXIT_;x_r}%#&k9`#=G zpuBgxbv{Iv>M8Kb`#xfHg(=j!B+HkDjPnl|_zFp8`4AkOmA9)B)&ou|X^;6~auHna zi>EA0%yRgRMZuJA+PO)pJDqRTqX_H$PuSyI{i?Ei-fxwVrG7$Hs8m&TRaJFp_j;Oa={w;^|*eYMR;9^b*TdtkpmDiY&S0sEf ztzC^`ua+V|Gw+~}&dow9C?(gc1jjH()t%?rP@T9ltevV^vyZ}qE{wyDSd`Di{!BH? z2?jJimoXRs%rIgGC}wyCSjW?lB>|;xtW<%TkP=AVxz{wV{|98{bl@OWYCzMzDim=? zxP`xieVqFKNNcy;ep%lv+yskR7EE9Mya$ZrD`OcDP66*HF;vJ>HqM{L;;t@KHc8D6ioFQNAKbl+?UNt_#US}CvC=`UyW zh@PLME{KK9w0evGR4I2RB(ZLggr;tve9@~UK2HioPwm4%-MPQGzd{K~v>KRnLCYw% zi^HttPwi1w-0k)a>54pcjJVfms5M!%gYH?k6eXRU3iovQG%+`~`q!G&*3{aq_r2J& z(0#HIE$8+_XORWmR~58KKwyp$ky?@#`K}M=W*uIm!)zd1ZG}<1Pds3a{LanINC{;D z*0+^i4)srUt#Z!h_wQsfH>k}2CZ=$9WuJGtTic@hkd;2sy=Nj~j$8O~Kw!ztQ}3}q zX9edf2I;0xaZzD;ma!^zD(KJhZ9NWz%1N)G_9i)LJjOkSlPDZ)rV)Z8zk}I$^h@CG zkR{Cb(xwB@u;IWDt8fxW6AeWryJ6zu4ArE4D znUt4G=|DcaL`?D0mEg`3eyR~(^pO#1T!Q7cn$af*rP)zp3I|uvSI~0BmUwi*BdTf5 z+V2Kmz}JDt7!mO(Yn_z_>1xy+LtF%#xx7}%@5pS>|ITxrb7Pk`S_nKJZ3+-vP*6qB zTeuPMCt=PWQ(u-mjxCL;e7!QmGXU)OhaKDmC_ecY$)D@=F2jI$x zCMk69W+Q)JV`b*2-RNAlJAZ7{nqRpbnQhI|{b+KEq5{Ex6D1#+_rq6B?hbb9M9h+v zhTwnYve~j~_TqG3c`c_DN^6_r8%w;+J@lKa`*q@Nz@X%q2N&+F@GD z>WZvP!BV-2%=as`%somavAS*LOy-y7o_mHPspYuK5TFmp;GVn}prA%ydoIow5{n^F zQv8)suvTj|AnP=lFKMtcoLlDnx~vIoe^8U%QS=KVegiG*uVo-uw58-%swv7Qb#b_H zGHxiyp}Ndc8L_q!UOLvE_6_c1d>12jF;&JQ=e4=?;7?+RtFm3ypap`!476|)=97`M zu`t$#@iyK>P(;n$>UV#^lVl?BONYnC+*1{566@ou@Vx=BH|R$&K4E)-B~=5noa$Ng zojMe6f4rQjBu*WK5yHkBVmt=kZZ(Jv^L3B%cPd{xj;%=;eCDD9V1D|si=6btdViBK z-&(PR)Nr9!#I80T^4~C*4PVo-URYF`1b{*Ylm$-4LEof>od9#0l^*+L>lB+c>@GU>2`zdOeVOG%18RwJ^wGWLZC%(S!d0rTobnr)#sFM-*s_B$ zh!6vP@hfe$2~gkZG>sJF{tCc6qrBMH7OC0$X2K7aMyH zK`KmY;zf*u<;v`N=nE0g zX1XRU^Hfmrt0?GY#hsH4-L5uF!8F~uGE#7-WMIb{47%8R?JWdI-J{(8o?%CH^@gba zHB_etYRRFTsF3l=CZH@QHLh1m**hYD5a;uV?IS~L+D!VUbX>Pim<2aJTz?6BDSaOx;MIi+XN&N1n95phD)EQ&J2>SosX|xQqwrw;F2~j8uZ?LGo)4np;K1y}bb`as%i6E6LiCk7F7|jbwIOQ9RT<$(zN;H}d45+s*$G#$-r zxXSlQuTxle=_t=vVu!$<)YD$=H`{V>%?UiXTr5~Wk3f(La%$DGI}F#b zw3{&yEF-Y=RBatrgRhfqRu?Za#CSMy^Gs&}rW6|Q^qC$M>JcGqj2G8uHH=9xomeV) z&Ug9U+BgAx1^d1%y$imP&)}@oMAGx-7oA8GfzW1rg5AS`($$uG<(d6t6G($=(9=-| z0(}l81D}IIn6G>Dw{>7{X1}X~TGX_`PSLLF=~&hJ!8nr0m~by=2_85(e9-}(IyRBMv zWU$U8hvDWh`q#Myvk!)WGKz@z->cNU5J90z9)5rk7f zZhojD_&9uK&?UN!Ib9?GOlqlm(|UJhFz`lCU=PiwyATXYE3J^ZDN~J_U+gefv-yee zS>rCNzR~Z89-B?pvx7@jiThes8^I7sJIpz`SnCFrXkvEFUVkIIlkaoc)}4f)43c}N zr#InMHci@ptk8d_Zs=}3X>#|j-9(J`t?Y}x39>OWA+BSnz7k_iF{rm~dWn}#ZI<>W z#%TI*NAEOP3428n-@U+E4VHncBgOWz_K^4XX9ox38OwJ@=)MtRT)ul9OFGSH3KFF? zW>)ib^|$6|lIi=2z`wf&>Zzf=?^7qOD4=v&t0W>4rhM$#&8QRu<439?5ZXZ|yPtsN2sR#J0-R^? zvnuB(XVBTNPC&_w(_shINkxrIG38II{8axxGcW1ALFdHDlIHN>>OX}DfU;4>*xWrv zi}m|grXJ}hew>%eV}KqV>V5@T?Oj2SclA3#OoX57xAm^{PKozH?c8Z<^MuI*MAS@e zq)p3y;PWKq^Etj$8OY#wYJS+s9Dky*f9?%=+SWjdI^*%OgW9g~f&$3`$q8|J=~&AB zSHf{)A@3-Y`}VLDdn4wmyvlfmw>5v-)@#&|9tu|6aPE&@F{a2pKcy4MVcDH8PW%eLN`i+9bEMeG{ic4cHJA{2GQQwj-Fu z-)?~?`O~Xw?0H=3mtD~J=uM0Z>-4mH_=Kni^&iQ5$;Ingeg2k`_q!513(XZeA#e_q z^vKAB23yKKZ?uVlZzN`yQbI09=bl!&`f$UG<}UFK8T$#)elfUz%DIdfpfBn-vFQ9x zV&ZPJ&)|p6ns1qXg8rhC2VOxb=cA$spVAGY>6sSfJ2ZoV+f7T$*7ilKA%LzB(m(2@!(Bw)RRbNmR zdn+YPs}ITTLAZZmb3lPlJ zO%4^xc1IJpw2&_qjMrN$@%p2zcGPy;^;tB`COJ8j&|F;F>CG6jUxcEb8x!~tI|my_ zMm)Lg`Nblu!ETp;*-lW!rh@VOUCpHD#-~A^Z27Ve$BZ#E^e&SEdsaZ6RR5Yxg{})9 z&yT6nsRTI5$E#$6hEurjWiF)b@uR6X{5*Kk4uZ|lj|aKIomW7Jzv>fNe!UeZ=eWA=g9;L(Binr8!3Wc*Va4^At;97pI+KB)m~ zc!;~NJcu$W0&ZZ1;BgEYun2ufL#B%`@ACZ-P9JAt5Rt{1a00x?eXzWWHM>2pW>jP` z4b<35|KrCR*?}ugjR?z5k7G}oK8ezD&OLl)zXDauD00CWOn)UEmBMM&)Pm#}p&a|B zI#$AGfE2Y|K2XtAaDS%qP`S5yH;V1`#EvEn?2Y0KivDsg{sKXnynxl6BwE>4yK&kY z94TlrQ5x1*EY)%vRPBx9520doM@orKu$!s)OAbi5ByQbZ)KMfe!$**(170MS`HZMR zeBxT}q#nxgJ#Kk)rpQlNmn|pQ;=e&4T)t+4HZ?HUoBl;8xlSef5bYxR^3|;xkp^G4 zCp@jM{X9qq-X2{v11wDV<&Y_LhD9)c zo+8wJRk9R=^3jywW!qeQAq+atXaO${b%1M4{Nn)hF9WgD`W_CmlD>KdSH&~~Y~u=Z zgLjh|acj`dJ);NA$v~CWAX$RG;=MfP>P-bL_WqSe2b4U^3ny{{XntXkIL*opAc416 z-#d*xUbM|fvPitTEcOQjP45Ba-Ir{?{tcYlG)d;Y>+yYa^jra#Nv37 zX1A+x@iM^S?}SLg(?3Vg&4t!Up;%RXS$(F%0uMBCU~VYM51wv$*#=ZPo>6!tk?PM^Ul<83+@1`^Vt_~ zD|OdkT68YC?_BUK=XgrBv_-E5Eeh6(W)3Q5H7euE@1{af@t$cV=}@y6b{83>tNN!* zC3y~CrvEmH(@zx zGM9{;z%hmtF8}D|gMdRr855Mq=()NDzJdNCz8ziaOpFn4$WCnUC#wBjo)?AeC<3wR zNlR@i9VD1Jov3%kcy11Q>z*!B4v4DiU1gIE&~RjBxGsXMPTRd+$P)r}$Kt|fV#lsL z%8!f>O=o4|WNH?G+Rb^`o1S}HNxe+hdD!l=bAe{?xD@QVOq-9HnbLf*Li7f8r-9N( zh^%l{!Y96Zy^K{#F>iqDv&TY75oB>}VNsmmYW)i!@iK-@ccG|mqwfogZj6I^aH&de*iy|3X1v_#dhPN%CYD75 zvbLnnSCSqZr#Z&yhx+0Od$8hZUfCsnqv=qmqrbE*bH*F@Cin~8w=Mx^#-d^q93$cH zAc+kJuO40J(*p2M_Ack_LrVMfFpUY{S0G?L6EwG(Dr(2$hIjOgCUR$3nR_+&)jHNp*1MA&UBW9I)pc;r$5R`0k{4BZ zswM^EFmrT4kWONacQuzTCPB(w{K_F~JT7R?^K|+{X;ZOa&Y?}q1b-(Lw4dNa7+x6V zKUNCit4WkSn7YV%ya>-Q)z?Yg=@}gDrt8%RW*=G8oKENq3P<3}$0*t5O^x{+S7qZM zJ-Cjjh)chuiy~NAbulbzG{(=;>$d!L4IYAJiYLO!AA87~kB*65j95}R3-HN^jB4`y zO!QV}8_9H=jJqQ%6Lwy}9^V-?YQ>I^voz%AX%?{WA=`9{&Z1lOKhf#gylDgv+k~BeJUc*xOpL!N*K_Zv1?3466Dy z8I{Hu#nvJxbfyT~^Sg08lMrC|DouNa5}9$f^o2zQCHw;9+<$BXs=gh zsHA7Ih+n=RwM@CO?$>i0dRS!M|7*m}t1lT;-WNi?v>ARg^1M3)`oiy!y_>p6`ETze z($}TD;GGXxSWU&Ia+$a_9DP@AqKPF(0rOiY@>@y>F(pKxF~Fm3P#k;xtxGkhlAfJl zL+175s0_A}TC#V(SW)%Yp)>)86DT(-D4853dbU`1&mQYS7 z9H1lYWk9F7xXUAvUP6cienPSbzox~%aXlk_=PZ_L`iGc;`pS6cVtN5co#}wthMsg< zft{6hOYgbDOd7<6O{D7t|A+-}CSUo!ai?4{aY3C2wq2fC`3v>rRm>&G)(8w%tdv!a zMP7~8TYz0Lz=LjP9Zes!H;jK>rx%|ic*bR9U$$KSORy!e-+eXNC)C))2B+rR1F>w# zUP(V$>BU)&GxSs<3)EQ8*|W{w37IgO(Bo`wLA+g9e(Jq96Nj4ekuT+0PsP>}IR7b6 zV+7-he0y}5T^hMWRnNH6eSRxuC_5exl!TNWrAHZHiuY5l-#9xOe>NTA>mr8?-SW>fk;zc!n&1?I7 zKCHM;=5!Q^uVyTwDztx%t(cY;+2+gQ-K%ghX66_-tm8F2rDjk> zE&eqxs=TY1eoSt28-JBWSk+nK=(Ic-i}j^iMlZ#F#+GUPZTPT44pfZ#q+jl@p-#15 zPsL#wj1INVtTlnpP4;@0N>Ad;SH342aEwCu+UbH2O%-6f@x;hFk=dglWc95=dz-PH}X2FL5v zso+P&*_%r}ys$Ty^VL7^c5H*)P05p~QkBA{=iMe}_ZuRH6h9fyV$=8gn&YsFg?)yr z%blJe@d6JG9t31*$-vb`V;855(rS>hort<)U?gq7ZBqvOJ;XRt7*n%P7WcTT{Q4E+ z7`yBWxnQa=AV?4Sl3y{(e3zNB$w2RwQ^#7ImRvz|b(*K$K_OY_Ome+3YZ1y534;uv zRSW%CQKurzkd(ZiMU{T$qY6$2%ITNGBC@Uwkh*XsS(nj8ev}qEmvuG=A((Y_{Q&so z5PaI5d?dGgT*d3FEADJJnIF`qc1g>+_gJ-|LaXVHY%1vZLl!kVz2~M&#{O7tDOc_8 zs7pkiD}RXRg+2to&2^QGz}fCp3we!^!eCVEMbhpvM{lh9F>Pt0ic{gBS}pdyu~Q7s z6_7I7ulN0MC3C&R&efaA^>^9a-B0VYRgL|8^qnzz9}$~hOLU7Exe*? zIj@f`KJ&PCe;nJ{SkQ?ZF1`7@vrhDd@h5hEnEMg*W`plkLS?hK+44cr-q>}+8scp` zDZ^OSKRsnQtbaA259L-`zpiNBr1@3<8kIBHXfWrZ;mYY|dg-#*qb^>qS#JgryikAW zhqPWj2zZ>wIvwjFB^hBgiGKDV@Tj4>b_S;l*?P!8fz%IEHFg(^ zm$JyI;>lfw6{fHu)d)-uWN~y2ZgN#}@6x7iVbWPQMKD(4bXq=aY+@pTk+}0D8(%RI*J~{vZ3keWzBIJX13$QJd7g$ehp{AC~i3 z&N*F|ekkFa4?;}E@+4ii&U3EI(Yi{W#q1wkD34JEy%aLZgR7>lt41iTyqZwZi*$hRI~_R>ox+>?(i5&%|Hm>`3j(&)_rv WubU>a9)CZGQhKiXtn?`~@c#k#=|!0U diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index a95a8b2deb..08a83fbc43 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1329,7 +1329,7 @@ impl View for ProjectPanel { keystroke_label( parent_view_id, - "Open project", + "Open a project", &button_style, context_menu_item.keystroke, workspace::Open, diff --git a/crates/welcome/Cargo.toml b/crates/welcome/Cargo.toml index d3b0e09697..3da90deb2d 100644 --- a/crates/welcome/Cargo.toml +++ b/crates/welcome/Cargo.toml @@ -16,6 +16,7 @@ log = "0.4" editor = { path = "../editor" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } +db = { path = "../db" } install_cli = { path = "../install_cli" } project = { path = "../project" } settings = { path = "../settings" } diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 246ff26717..a2386b8d28 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -1,7 +1,8 @@ mod base_keymap_picker; -use std::borrow::Cow; +use std::{borrow::Cow, sync::Arc}; +use db::kvp::KEY_VALUE_STORE; use gpui::{ elements::{Empty, Flex, Label, MouseEventHandler, ParentElement, Svg}, Action, Element, ElementBox, Entity, MouseButton, MutableAppContext, RenderContext, @@ -9,10 +10,15 @@ use gpui::{ }; use settings::{settings_file::SettingsFile, Settings, SettingsFileContent}; use theme::CheckboxStyle; -use workspace::{item::Item, PaneBackdrop, Welcome, Workspace, WorkspaceId}; +use workspace::{ + item::Item, open_new, sidebar::SidebarSide, AppState, PaneBackdrop, Welcome, Workspace, + WorkspaceId, +}; use crate::base_keymap_picker::ToggleBaseKeymapSelector; +pub const FIRST_OPEN: &str = "first_open"; + pub fn init(cx: &mut MutableAppContext) { cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { let welcome_page = cx.add_view(WelcomePage::new); @@ -22,6 +28,21 @@ pub fn init(cx: &mut MutableAppContext) { base_keymap_picker::init(cx); } +pub fn show_welcome_experience(app_state: &Arc, cx: &mut MutableAppContext) { + open_new(&app_state, cx, |workspace, cx| { + workspace.toggle_sidebar(SidebarSide::Left, cx); + let welcome_page = cx.add_view(|cx| WelcomePage::new(cx)); + workspace.add_item_to_center(Box::new(welcome_page.clone()), cx); + cx.focus(welcome_page); + cx.notify(); + }) + .detach(); + + db::write_and_log(cx, || { + KEY_VALUE_STORE.write_kvp(FIRST_OPEN.to_string(), "false".to_string()) + }); +} + pub struct WelcomePage { _settings_subscription: Subscription, } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 13a44fef10..9982b4114a 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -36,6 +36,7 @@ use std::{ path::PathBuf, sync::Arc, thread, time::Duration, }; use terminal_view::{get_working_directory, TerminalView}; +use welcome::{show_welcome_experience, FIRST_OPEN}; use fs::RealFs; use settings::watched_json::WatchedJsonFile; @@ -46,10 +47,7 @@ use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt}; use workspace::{ self, item::ItemHandle, notifications::NotifyResultExt, AppState, NewFile, OpenPaths, Workspace, }; -use zed::{ - self, build_window_options, initialize_workspace, languages, menus, WelcomeExperience, - FIRST_OPEN, -}; +use zed::{self, build_window_options, initialize_workspace, languages, menus}; fn main() { let http = http::client(); @@ -206,7 +204,7 @@ fn main() { cx.platform().activate(true); let paths = collect_path_args(); if paths.is_empty() { - cx.spawn(|cx| async move { restore_or_create_workspace(cx).await }) + cx.spawn(|cx| async move { restore_or_create_workspace(&app_state, cx).await }) .detach() } else { cx.dispatch_global_action(OpenPaths { paths }); @@ -219,8 +217,11 @@ fn main() { cx.update(|cx| workspace::open_paths(&paths, &app_state, None, cx)) .detach(); } else { - cx.spawn(|cx| async move { restore_or_create_workspace(cx).await }) - .detach() + cx.spawn({ + let app_state = app_state.clone(); + |cx| async move { restore_or_create_workspace(&app_state, cx).await } + }) + .detach() } cx.spawn(|cx| { @@ -259,7 +260,7 @@ fn main() { }); } -async fn restore_or_create_workspace(mut cx: AsyncAppContext) { +async fn restore_or_create_workspace(app_state: &Arc, mut cx: AsyncAppContext) { if let Some(location) = workspace::last_opened_workspace_paths().await { cx.update(|cx| { cx.dispatch_global_action(OpenPaths { @@ -267,9 +268,7 @@ async fn restore_or_create_workspace(mut cx: AsyncAppContext) { }) }); } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { - cx.update(|cx| { - cx.dispatch_global_action(WelcomeExperience); - }); + cx.update(|cx| show_welcome_experience(app_state, cx)); } else { cx.update(|cx| { cx.dispatch_global_action(NewFile); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 3b48632265..3c093836f2 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -8,7 +8,6 @@ use breadcrumbs::Breadcrumbs; pub use client; use collab_ui::{CollabTitlebarItem, ToggleContactsMenu}; use collections::VecDeque; -use db::kvp::KEY_VALUE_STORE; pub use editor; use editor::{Editor, MultiBuffer}; @@ -35,9 +34,7 @@ use std::{borrow::Cow, env, path::Path, str, sync::Arc}; use util::{channel::ReleaseChannel, paths, ResultExt, StaffMode}; use uuid::Uuid; pub use workspace; -use workspace::{open_new, sidebar::SidebarSide, AppState, Restart, Workspace}; - -pub const FIRST_OPEN: &str = "first_open"; +use workspace::{sidebar::SidebarSide, AppState, Restart, Workspace}; #[derive(Deserialize, Clone, PartialEq)] pub struct OpenBrowser { @@ -69,7 +66,6 @@ actions!( DecreaseBufferFontSize, ResetBufferFontSize, ResetDatabase, - WelcomeExperience ] ); @@ -258,29 +254,6 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { workspace.toggle_sidebar_item_focus(SidebarSide::Left, 0, cx); }, ); - - cx.add_global_action({ - let app_state = app_state.clone(); - move |_: &WelcomeExperience, cx| { - if !matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { - return; //noop, in case someone fires this from the command palette - } - - open_new(&app_state, cx, |workspace, cx| { - workspace.toggle_sidebar(SidebarSide::Left, cx); - let welcome_page = cx.add_view(|cx| welcome::WelcomePage::new(cx)); - workspace.add_item_to_center(Box::new(welcome_page.clone()), cx); - cx.focus(welcome_page); - cx.notify(); - }) - .detach(); - - db::write_and_log(cx, || { - KEY_VALUE_STORE.write_kvp(FIRST_OPEN.to_string(), "false".to_string()) - }); - } - }); - activity_indicator::init(cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); settings::KeymapFileContent::load_defaults(cx); diff --git a/styles/src/styleTree/projectPanel.ts b/styles/src/styleTree/projectPanel.ts index 2601a12691..80cb884c48 100644 --- a/styles/src/styleTree/projectPanel.ts +++ b/styles/src/styleTree/projectPanel.ts @@ -30,37 +30,26 @@ export default function projectPanel(colorScheme: ColorScheme) { return { openProjectButton: { - ...text(layer, "mono", "active", { size: "sm" }), - background: background(layer, "on"), - cornerRadius: 6, - border: border(layer, "on"), + background: background(layer), + border: border(layer, "active"), + cornerRadius: 4, margin: { - top: 20, - left: 10, - right: 10 + top: 16, + left: 16, + right: 16, }, padding: { - bottom: 2, - left: 10, - right: 10, - top: 2, - }, - active: { - ...text(layer, "mono", "on", "inverted"), - background: background(layer, "on", "inverted"), - border: border(layer, "on", "inverted"), - }, - clicked: { - ...text(layer, "mono", "on", "pressed"), - background: background(layer, "on", "pressed"), - border: border(layer, "on", "pressed"), + top: 3, + bottom: 3, + left: 7, + right: 7, }, + ...text(layer, "sans", "default", { size: "sm" }), hover: { - ...text(layer, "mono", "on", "hovered"), - background: background(layer, "on", "hovered"), - border: border(layer, "on", "hovered"), + ...text(layer, "sans", "default", { size: "sm" }), + background: background(layer, "hovered"), + border: border(layer, "active"), }, - }, background: background(layer), padding: { left: 12, right: 12, top: 6, bottom: 6 }, From dad66eb3fbc7b981c7e433a3140571ced6cb42f6 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 8 Mar 2023 14:38:33 -0800 Subject: [PATCH 43/93] Make the workspace always open the dock --- crates/workspace/src/workspace.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 1c1afea4aa..7c439522e8 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -742,6 +742,10 @@ impl Workspace { cx.defer(move |_, cx| { Self::load_from_serialized_workspace(weak_handle, serialized_workspace, cx) }); + } else { + if cx.global::().default_dock_anchor != DockAnchor::Expanded { + Dock::show(&mut this, false, cx); + } } this From 152755b04356cbb5d2990315fe81e5d57be08d23 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 8 Mar 2023 17:56:39 -0800 Subject: [PATCH 44/93] Add blank pane experience --- crates/collab/src/tests.rs | 11 +- crates/collab/src/tests/integration_tests.rs | 30 +--- crates/collab_ui/src/collab_ui.rs | 1 + crates/command_palette/src/command_palette.rs | 4 +- crates/diagnostics/src/diagnostics.rs | 10 +- crates/editor/src/editor_tests.rs | 20 +-- .../src/test/editor_lsp_test_context.rs | 12 +- crates/file_finder/src/file_finder.rs | 28 +--- crates/project_panel/src/project_panel.rs | 69 ++------- crates/terminal_view/src/terminal_view.rs | 10 +- crates/theme/src/theme.rs | 42 ++---- crates/theme/src/ui.rs | 119 +++++++++++++++ crates/welcome/src/welcome.rs | 142 ++++++++++-------- crates/workspace/src/dock.rs | 15 +- crates/workspace/src/pane.rs | 90 ++++++++--- crates/workspace/src/workspace.rs | 71 ++++++--- crates/zed/src/main.rs | 18 ++- crates/zed/src/zed.rs | 44 +----- styles/src/styleTree/contextMenu.ts | 9 +- styles/src/styleTree/welcome.ts | 20 ++- styles/src/styleTree/workspace.ts | 28 ++++ 21 files changed, 454 insertions(+), 339 deletions(-) create mode 100644 crates/theme/src/ui.rs diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index e9ffecf246..8949b60993 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -198,6 +198,7 @@ impl TestServer { build_window_options: |_, _, _| Default::default(), initialize_workspace: |_, _, _| unimplemented!(), dock_default_item_factory: |_, _| unimplemented!(), + background_actions: || unimplemented!(), }); Project::init(&client); @@ -434,15 +435,7 @@ impl TestClient { cx: &mut TestAppContext, ) -> ViewHandle { let (_, root_view) = cx.add_window(|_| EmptyView); - cx.add_view(&root_view, |cx| { - Workspace::new( - Default::default(), - 0, - project.clone(), - |_, _| unimplemented!(), - cx, - ) - }) + cx.add_view(&root_view, |cx| Workspace::test_new(project.clone(), cx)) } fn create_new_root_dir(&mut self) -> PathBuf { diff --git a/crates/collab/src/tests/integration_tests.rs b/crates/collab/src/tests/integration_tests.rs index 1ab78ac310..902242df01 100644 --- a/crates/collab/src/tests/integration_tests.rs +++ b/crates/collab/src/tests/integration_tests.rs @@ -1449,15 +1449,7 @@ async fn test_host_disconnect( deterministic.run_until_parked(); assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared())); - let (_, workspace_b) = cx_b.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project_b.clone(), - |_, _| unimplemented!(), - cx, - ) - }); + let (_, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); let editor_b = workspace_b .update(cx_b, |workspace, cx| { workspace.open_path((worktree_id, "b.txt"), None, true, cx) @@ -4706,15 +4698,7 @@ async fn test_collaborating_with_code_actions( // Join the project as client B. let project_b = client_b.build_remote_project(project_id, cx_b).await; - let (_window_b, workspace_b) = cx_b.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project_b.clone(), - |_, _| unimplemented!(), - cx, - ) - }); + let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); let editor_b = workspace_b .update(cx_b, |workspace, cx| { workspace.open_path((worktree_id, "main.rs"), None, true, cx) @@ -4937,15 +4921,7 @@ async fn test_collaborating_with_renames( .unwrap(); let project_b = client_b.build_remote_project(project_id, cx_b).await; - let (_window_b, workspace_b) = cx_b.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project_b.clone(), - |_, _| unimplemented!(), - cx, - ) - }); + let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::test_new(project_b.clone(), cx)); let editor_b = workspace_b .update(cx_b, |workspace, cx| { workspace.open_path((worktree_id, "one.rs"), None, true, cx) diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs index 6abfec21f7..2dd2b0e6b4 100644 --- a/crates/collab_ui/src/collab_ui.rs +++ b/crates/collab_ui/src/collab_ui.rs @@ -86,6 +86,7 @@ fn join_project(action: &JoinProject, app_state: Arc, cx: &mut Mutable 0, project, app_state.dock_default_item_factory, + app_state.background_actions, cx, ); (app_state.initialize_workspace)(&mut workspace, &app_state, cx); diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 55522966fa..52a0e1cdc0 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -352,9 +352,7 @@ mod tests { }); let project = Project::test(app_state.fs.clone(), [], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let editor = cx.add_view(&workspace, |cx| { let mut editor = Editor::single_line(None, cx); editor.set_text("abc", cx); diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index e447a26c6b..2232555442 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -805,15 +805,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project.clone(), - |_, _| unimplemented!(), - cx, - ) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); // Create some diagnostics project.update(cx, |project, cx| { diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 269390f72f..21cc9f8895 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -484,7 +484,9 @@ fn test_navigation_history(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); cx.set_global(DragAndDrop::::default()); use workspace::item::Item; - let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(None, cx)); + let (_, pane) = cx.add_window(Default::default(), |cx| { + Pane::new(None, || unimplemented!(), cx) + }); let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); cx.add_view(&pane, |cx| { @@ -2354,10 +2356,10 @@ async fn test_clipboard(cx: &mut gpui::TestAppContext) { e.handle_input(") ", cx); }); cx.assert_editor_state(indoc! {" - ( one✅ - three - five ) ˇtwo one✅ four three six five ( one✅ - three + ( one✅ + three + five ) ˇtwo one✅ four three six five ( one✅ + three five ) ˇ"}); // Cut with three selections, one of which is full-line. @@ -5562,7 +5564,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) { Settings::test_async(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; - let (_, pane) = cx.add_window(|cx| Pane::new(None, cx)); + let (_, pane) = cx.add_window(|cx| Pane::new(None, || unimplemented!(), cx)); let leader = pane.update(cx, |_, cx| { let multibuffer = cx.add_model(|_| MultiBuffer::new(0)); @@ -5831,11 +5833,11 @@ async fn go_to_hunk(deterministic: Arc, cx: &mut gpui::TestAppCon cx.assert_editor_state( &r#" ˇuse some::modified; - - + + fn main() { println!("hello there"); - + println!("around the"); println!("world"); } diff --git a/crates/editor/src/test/editor_lsp_test_context.rs b/crates/editor/src/test/editor_lsp_test_context.rs index 1b6d846e71..fe9a7909b8 100644 --- a/crates/editor/src/test/editor_lsp_test_context.rs +++ b/crates/editor/src/test/editor_lsp_test_context.rs @@ -65,15 +65,7 @@ impl<'a> EditorLspTestContext<'a> { .insert_tree("/root", json!({ "dir": { file_name.clone(): "" }})) .await; - let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project.clone(), - |_, _| unimplemented!(), - cx, - ) - }); + let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); project .update(cx, |project, cx| { project.find_or_create_local_worktree("/root", true, cx) @@ -134,7 +126,7 @@ impl<'a> EditorLspTestContext<'a> { (let_chain) (await_expression) ] @indent - + (_ "[" "]" @end) @indent (_ "<" ">" @end) @indent (_ "{" "}" @end) @indent diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index 273440dce2..51d4df45f5 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -329,9 +329,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); cx.dispatch_action(window_id, Toggle); let finder = cx.read(|cx| workspace.read(cx).modal::().unwrap()); @@ -385,9 +383,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/dir".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let (_, finder) = cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), None, cx)); @@ -461,9 +457,7 @@ mod tests { cx, ) .await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let (_, finder) = cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), None, cx)); finder @@ -487,9 +481,7 @@ mod tests { cx, ) .await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let (_, finder) = cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), None, cx)); @@ -541,9 +533,7 @@ mod tests { cx, ) .await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let (_, finder) = cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), None, cx)); @@ -585,9 +575,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); // When workspace has an active item, sort items which are closer to that item // first when they have the same name. In this case, b.txt is closer to dir2's a.txt @@ -624,9 +612,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let (_, finder) = cx.add_window(|cx| FileFinder::new(workspace.read(cx).project().clone(), None, cx)); finder diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 08a83fbc43..079de79b11 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -6,15 +6,14 @@ use gpui::{ actions, anyhow::{anyhow, Result}, elements::{ - AnchorCorner, ChildView, ConstrainedBox, Container, ContainerStyle, Empty, Flex, - KeystrokeLabel, Label, MouseEventHandler, ParentElement, ScrollTarget, Stack, Svg, - UniformList, UniformListState, + AnchorCorner, ChildView, ConstrainedBox, ContainerStyle, Empty, Flex, Label, + MouseEventHandler, ParentElement, ScrollTarget, Stack, Svg, UniformList, UniformListState, }, geometry::vector::Vector2F, impl_internal_actions, keymap_matcher::KeymapContext, platform::CursorStyle, - Action, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, MouseButton, + AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, MouseButton, MutableAppContext, PromptLevel, RenderContext, Task, View, ViewContext, ViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; @@ -28,7 +27,7 @@ use std::{ path::{Path, PathBuf}, sync::Arc, }; -use theme::{ContainedText, ProjectPanelEntry}; +use theme::ProjectPanelEntry; use unicase::UniCase; use workspace::Workspace; @@ -1315,7 +1314,6 @@ impl View for ProjectPanel { .with_child(ChildView::new(&self.context_menu, cx).boxed()) .boxed() } else { - let parent_view_id = cx.handle().id(); Flex::column() .with_child( MouseEventHandler::::new(2, cx, { @@ -1327,12 +1325,11 @@ impl View for ProjectPanel { let context_menu_item = context_menu_item_style.style_for(state, true).clone(); - keystroke_label( - parent_view_id, + theme::ui::keystroke_label( "Open a project", &button_style, - context_menu_item.keystroke, - workspace::Open, + &context_menu_item.keystroke, + Box::new(workspace::Open), cx, ) .boxed() @@ -1357,38 +1354,6 @@ impl View for ProjectPanel { } } -fn keystroke_label( - view_id: usize, - label_text: &'static str, - label_style: &ContainedText, - keystroke_style: ContainedText, - action: A, - cx: &mut RenderContext, -) -> Container -where - A: Action, -{ - Flex::row() - .with_child( - Label::new(label_text, label_style.text.clone()) - .contained() - .boxed(), - ) - .with_child({ - KeystrokeLabel::new( - cx.window_id(), - view_id, - Box::new(action), - keystroke_style.container, - keystroke_style.text.clone(), - ) - .flex_float() - .boxed() - }) - .contained() - .with_style(label_style.container) -} - impl Entity for ProjectPanel { type Event = Event; } @@ -1474,15 +1439,7 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project.clone(), - |_, _| unimplemented!(), - cx, - ) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx)); assert_eq!( visible_entries_as_strings(&panel, 0..50, cx), @@ -1574,15 +1531,7 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/root1".as_ref(), "/root2".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project.clone(), - |_, _| unimplemented!(), - cx, - ) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let panel = workspace.update(cx, |_, cx| ProjectPanel::new(project, cx)); select_path(&panel, "root1", cx); diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 3821185ec0..110815e870 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -970,15 +970,7 @@ mod tests { let params = cx.update(AppState::test); let project = Project::test(params.fs.clone(), [], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project.clone(), - |_, _| unimplemented!(), - cx, - ) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); (project, workspace) } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 13546b40cd..524b765626 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -9,6 +9,9 @@ use gpui::{ use serde::{de::DeserializeOwned, Deserialize}; use serde_json::Value; use std::{collections::HashMap, sync::Arc}; +use ui::{CheckboxStyle, IconStyle}; + +pub mod ui; pub use theme_registry::*; @@ -50,6 +53,7 @@ pub struct ThemeMeta { #[derive(Deserialize, Default)] pub struct Workspace { pub background: Color, + pub blank_pane: BlankPaneStyle, pub titlebar: Titlebar, pub tab_bar: TabBar, pub pane_divider: Border, @@ -69,6 +73,14 @@ pub struct Workspace { pub drop_target_overlay_color: Color, } +#[derive(Clone, Deserialize, Default)] +pub struct BlankPaneStyle { + pub logo: IconStyle, + pub keyboard_hints: ContainerStyle, + pub keyboard_hint: Interactive, + pub keyboard_hint_width: f32, +} + #[derive(Clone, Deserialize, Default)] pub struct Titlebar { #[serde(flatten)] @@ -858,46 +870,18 @@ pub struct WelcomeStyle { pub logo: IconStyle, pub logo_subheading: ContainedText, pub checkbox: CheckboxStyle, + pub checkbox_container: ContainerStyle, pub button: Interactive, pub button_group: ContainerStyle, pub heading_group: ContainerStyle, pub checkbox_group: ContainerStyle, } -#[derive(Clone, Deserialize, Default)] -pub struct IconStyle { - pub color: Color, - pub icon: String, - pub dimensions: Dimensions, -} - -#[derive(Clone, Deserialize, Default)] -pub struct Dimensions { - pub width: f32, - pub height: f32, -} - -#[derive(Clone, Deserialize, Default)] -pub struct CheckboxStyle { - pub check_icon: String, - pub check_icon_color: Color, - pub label: ContainedText, - pub container: ContainerStyle, - pub width: f32, - pub height: f32, - pub default: ContainerStyle, - pub checked: ContainerStyle, - pub hovered: ContainerStyle, - pub hovered_and_checked: ContainerStyle, -} - #[derive(Clone, Deserialize, Default)] pub struct ColorScheme { pub name: String, pub is_light: bool, - pub ramps: RampSet, - pub lowest: Layer, pub middle: Layer, pub highest: Layer, diff --git a/crates/theme/src/ui.rs b/crates/theme/src/ui.rs new file mode 100644 index 0000000000..ca71db3723 --- /dev/null +++ b/crates/theme/src/ui.rs @@ -0,0 +1,119 @@ +use gpui::{ + color::Color, + elements::{ + ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label, + MouseEventHandler, ParentElement, Svg, + }, + Action, Element, EventContext, RenderContext, View, +}; +use serde::Deserialize; + +use crate::ContainedText; + +#[derive(Clone, Deserialize, Default)] +pub struct CheckboxStyle { + pub icon: IconStyle, + pub label: ContainedText, + pub default: ContainerStyle, + pub checked: ContainerStyle, + pub hovered: ContainerStyle, + pub hovered_and_checked: ContainerStyle, +} + +pub fn checkbox( + label: &'static str, + style: &CheckboxStyle, + checked: bool, + cx: &mut RenderContext, + change: fn(checked: bool, cx: &mut EventContext) -> (), +) -> MouseEventHandler { + MouseEventHandler::::new(0, cx, |state, _| { + let indicator = if checked { + icon(&style.icon) + } else { + Empty::new() + .constrained() + .with_width(style.icon.dimensions.width) + .with_height(style.icon.dimensions.height) + }; + + Flex::row() + .with_children([ + indicator + .contained() + .with_style(if checked { + if state.hovered() { + style.hovered_and_checked + } else { + style.checked + } + } else { + if state.hovered() { + style.hovered + } else { + style.default + } + }) + .boxed(), + Label::new(label, style.label.text.clone()) + .contained() + .with_style(style.label.container) + .boxed(), + ]) + .align_children_center() + .boxed() + }) + .on_click(gpui::MouseButton::Left, move |_, cx| change(!checked, cx)) + .with_cursor_style(gpui::CursorStyle::PointingHand) +} + +#[derive(Clone, Deserialize, Default)] +pub struct IconStyle { + pub color: Color, + pub icon: String, + pub dimensions: Dimensions, +} + +#[derive(Clone, Deserialize, Default)] +pub struct Dimensions { + pub width: f32, + pub height: f32, +} + +pub fn icon(style: &IconStyle) -> ConstrainedBox { + Svg::new(style.icon.clone()) + .with_color(style.color) + .constrained() + .with_width(style.dimensions.width) + .with_height(style.dimensions.height) +} + +pub fn keystroke_label( + label_text: &'static str, + label_style: &ContainedText, + keystroke_style: &ContainedText, + action: Box, + cx: &mut RenderContext, +) -> Container { + // FIXME: Put the theme in it's own global so we can + // query the keystroke style on our own + Flex::row() + .with_child( + Label::new(label_text, label_style.text.clone()) + .contained() + .boxed(), + ) + .with_child({ + KeystrokeLabel::new( + cx.window_id(), + cx.handle().id(), + action, + keystroke_style.container, + keystroke_style.text.clone(), + ) + .flex_float() + .boxed() + }) + .contained() + .with_style(label_style.container) +} diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index a2386b8d28..89f161a283 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -4,12 +4,12 @@ use std::{borrow::Cow, sync::Arc}; use db::kvp::KEY_VALUE_STORE; use gpui::{ - elements::{Empty, Flex, Label, MouseEventHandler, ParentElement, Svg}, + elements::{Flex, Label, MouseEventHandler, ParentElement}, Action, Element, ElementBox, Entity, MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, }; -use settings::{settings_file::SettingsFile, Settings, SettingsFileContent}; -use theme::CheckboxStyle; +use settings::{settings_file::SettingsFile, Settings}; + use workspace::{ item::Item, open_new, sidebar::SidebarSide, AppState, PaneBackdrop, Welcome, Workspace, WorkspaceId, @@ -77,11 +77,7 @@ impl View for WelcomePage { .with_children([ Flex::column() .with_children([ - Svg::new(theme.welcome.logo.icon.clone()) - .with_color(theme.welcome.logo.color) - .constrained() - .with_width(theme.welcome.logo.dimensions.width) - .with_height(theme.welcome.logo.dimensions.height) + theme::ui::icon(&theme.welcome.logo) .aligned() .contained() .aligned() @@ -128,20 +124,34 @@ impl View for WelcomePage { .boxed(), Flex::column() .with_children([ - self.render_settings_checkbox::( + theme::ui::checkbox::( "Do you want to send telemetry?", &theme.welcome.checkbox, metrics, cx, - |content, checked| content.telemetry.set_metrics(checked), - ), - self.render_settings_checkbox::( + |checked, cx| { + SettingsFile::update(cx, move |file| { + file.telemetry.set_metrics(checked) + }) + }, + ) + .contained() + .with_style(theme.welcome.checkbox_container) + .boxed(), + theme::ui::checkbox::( "Send crash reports", &theme.welcome.checkbox, diagnostics, cx, - |content, checked| content.telemetry.set_diagnostics(checked), - ), + |checked, cx| { + SettingsFile::update(cx, move |file| { + file.telemetry.set_diagnostics(checked) + }) + }, + ) + .contained() + .with_style(theme.welcome.checkbox_container) + .boxed(), ]) .contained() .with_style(theme.welcome.checkbox_group) @@ -204,59 +214,59 @@ impl WelcomePage { .boxed() } - fn render_settings_checkbox( - &self, - label: &'static str, - style: &CheckboxStyle, - checked: bool, - cx: &mut RenderContext, - set_value: fn(&mut SettingsFileContent, checked: bool) -> (), - ) -> ElementBox { - MouseEventHandler::::new(0, cx, |state, _| { - let indicator = if checked { - Svg::new(style.check_icon.clone()) - .with_color(style.check_icon_color) - .constrained() - } else { - Empty::new().constrained() - }; + // fn render_settings_checkbox( + // &self, + // label: &'static str, + // style: &CheckboxStyle, + // checked: bool, + // cx: &mut RenderContext, + // set_value: fn(&mut SettingsFileContent, checked: bool) -> (), + // ) -> ElementBox { + // MouseEventHandler::::new(0, cx, |state, _| { + // let indicator = if checked { + // Svg::new(style.check_icon.clone()) + // .with_color(style.check_icon_color) + // .constrained() + // } else { + // Empty::new().constrained() + // }; - Flex::row() - .with_children([ - indicator - .with_width(style.width) - .with_height(style.height) - .contained() - .with_style(if checked { - if state.hovered() { - style.hovered_and_checked - } else { - style.checked - } - } else { - if state.hovered() { - style.hovered - } else { - style.default - } - }) - .boxed(), - Label::new(label, style.label.text.clone()) - .contained() - .with_style(style.label.container) - .boxed(), - ]) - .align_children_center() - .boxed() - }) - .on_click(gpui::MouseButton::Left, move |_, cx| { - SettingsFile::update(cx, move |content| set_value(content, !checked)) - }) - .with_cursor_style(gpui::CursorStyle::PointingHand) - .contained() - .with_style(style.container) - .boxed() - } + // Flex::row() + // .with_children([ + // indicator + // .with_width(style.width) + // .with_height(style.height) + // .contained() + // .with_style(if checked { + // if state.hovered() { + // style.hovered_and_checked + // } else { + // style.checked + // } + // } else { + // if state.hovered() { + // style.hovered + // } else { + // style.default + // } + // }) + // .boxed(), + // Label::new(label, style.label.text.clone()) + // .contained() + // .with_style(style.label.container) + // .boxed(), + // ]) + // .align_children_center() + // .boxed() + // }) + // .on_click(gpui::MouseButton::Left, move |_, cx| { + // SettingsFile::update(cx, move |content| set_value(content, !checked)) + // }) + // .with_cursor_style(gpui::CursorStyle::PointingHand) + // .contained() + // .with_style(style.container) + // .boxed() + // } } impl Item for WelcomePage { diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 4281c04649..f86a9db71a 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -13,7 +13,7 @@ use gpui::{ use settings::{DockAnchor, Settings}; use theme::Theme; -use crate::{sidebar::SidebarSide, ItemHandle, Pane, Workspace}; +use crate::{sidebar::SidebarSide, BackgroundActions, ItemHandle, Pane, Workspace}; pub use toggle_dock_button::ToggleDockButton; #[derive(PartialEq, Clone, Deserialize)] @@ -182,11 +182,12 @@ pub struct Dock { impl Dock { pub fn new( default_item_factory: DockDefaultItemFactory, + background_actions: BackgroundActions, cx: &mut ViewContext, ) -> Self { let position = DockPosition::Hidden(cx.global::().default_dock_anchor); - let pane = cx.add_view(|cx| Pane::new(Some(position.anchor()), cx)); + let pane = cx.add_view(|cx| Pane::new(Some(position.anchor()), background_actions, cx)); pane.update(cx, |pane, cx| { pane.set_active(false, cx); }); @@ -492,6 +493,7 @@ mod tests { 0, project.clone(), default_item_factory, + || unimplemented!(), cx, ) }); @@ -620,7 +622,14 @@ mod tests { cx.update(|cx| init(cx)); let project = Project::test(fs, [], cx).await; let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, default_item_factory, cx) + Workspace::new( + Default::default(), + 0, + project, + default_item_factory, + || unimplemented!(), + cx, + ) }); workspace.update(cx, |workspace, cx| { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index fe33996961..7e0e6bbe01 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -110,6 +110,8 @@ impl_internal_actions!( const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; +pub type BackgroundActions = fn() -> &'static [(&'static str, &'static dyn Action)]; + pub fn init(cx: &mut MutableAppContext) { cx.add_action(|pane: &mut Pane, action: &ActivateItem, cx| { pane.activate_item(action.0, true, true, cx); @@ -215,6 +217,7 @@ pub struct Pane { toolbar: ViewHandle, tab_bar_context_menu: ViewHandle, docked: Option, + background_actions: BackgroundActions, } pub struct ItemNavHistory { @@ -271,7 +274,11 @@ enum ItemType { } impl Pane { - pub fn new(docked: Option, cx: &mut ViewContext) -> Self { + pub fn new( + docked: Option, + background_actions: BackgroundActions, + cx: &mut ViewContext, + ) -> Self { let handle = cx.weak_handle(); let context_menu = cx.add_view(ContextMenu::new); Self { @@ -292,6 +299,7 @@ impl Pane { toolbar: cx.add_view(|_| Toolbar::new(handle)), tab_bar_context_menu: context_menu, docked, + background_actions, } } @@ -1415,6 +1423,64 @@ impl Pane { .flex(1., false) .boxed() } + + fn render_blank_pane(&mut self, theme: &Theme, cx: &mut RenderContext) -> ElementBox { + let background = theme.workspace.background; + let keystroke_style = &theme.context_menu.item; + let theme = &theme.workspace.blank_pane; + Stack::new() + .with_children([ + Empty::new() + .contained() + .with_background_color(background) + .boxed(), + Flex::column() + .align_children_center() + .with_children([ + theme::ui::icon(&theme.logo).aligned().boxed(), + Flex::column() + .with_children({ + enum KeyboardHint {} + let keyboard_hint = &theme.keyboard_hint; + (self.background_actions)().into_iter().enumerate().map( + move |(idx, (text, action))| { + let hint_action = action.boxed_clone(); + MouseEventHandler::::new( + idx, + cx, + move |state, cx| { + theme::ui::keystroke_label( + text, + &keyboard_hint.style_for(state, false), + &keystroke_style + .style_for(state, false) + .keystroke, + hint_action, + cx, + ) + .boxed() + }, + ) + .on_click(MouseButton::Left, move |_, cx| { + cx.dispatch_any_action(action.boxed_clone()) + }) + .with_cursor_style(CursorStyle::PointingHand) + .boxed() + }, + ) + }) + .contained() + .with_style(theme.keyboard_hints) + .constrained() + .with_max_width(theme.keyboard_hint_width) + .aligned() + .boxed(), + ]) + .aligned() + .boxed(), + ]) + .boxed() + } } impl Entity for Pane { @@ -1508,11 +1574,8 @@ impl View for Pane { enum EmptyPane {} let theme = cx.global::().theme.clone(); - dragged_item_receiver::(0, 0, false, None, cx, |_, _| { - Empty::new() - .contained() - .with_background_color(theme.workspace.background) - .boxed() + dragged_item_receiver::(0, 0, false, None, cx, |_, cx| { + self.render_blank_pane(&theme, cx) }) .on_down(MouseButton::Left, |_, cx| { cx.focus_parent_view(); @@ -1809,9 +1872,7 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); // 1. Add with a destination index @@ -1899,9 +1960,7 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); // 1. Add with a destination index @@ -1977,9 +2036,7 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); // singleton view @@ -2088,8 +2145,7 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, None, cx).await; - let (_, workspace) = - cx.add_window(|cx| Workspace::new(None, 0, project, |_, _| unimplemented!(), cx)); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); add_labled_item(&workspace, &pane, "A", cx); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 7c439522e8..65335d8671 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -432,6 +432,7 @@ pub struct AppState { fn(Option, Option, &dyn Platform) -> WindowOptions<'static>, pub initialize_workspace: fn(&mut Workspace, &Arc, &mut ViewContext), pub dock_default_item_factory: DockDefaultItemFactory, + pub background_actions: BackgroundActions, } impl AppState { @@ -455,6 +456,7 @@ impl AppState { initialize_workspace: |_, _, _| {}, build_window_options: |_, _, _| Default::default(), dock_default_item_factory: |_, _| unimplemented!(), + background_actions: || unimplemented!(), }) } } @@ -542,6 +544,7 @@ pub struct Workspace { active_call: Option<(ModelHandle, Vec)>, leader_updates_tx: mpsc::UnboundedSender<(PeerId, proto::UpdateFollowers)>, database_id: WorkspaceId, + background_actions: BackgroundActions, _window_subscriptions: [Subscription; 3], _apply_leader_updates: Task>, _observe_current_user: Task<()>, @@ -572,6 +575,7 @@ impl Workspace { workspace_id: WorkspaceId, project: ModelHandle, dock_default_factory: DockDefaultItemFactory, + background_actions: BackgroundActions, cx: &mut ViewContext, ) -> Self { cx.observe(&project, |_, _, cx| cx.notify()).detach(); @@ -602,7 +606,7 @@ impl Workspace { }) .detach(); - let center_pane = cx.add_view(|cx| Pane::new(None, cx)); + let center_pane = cx.add_view(|cx| Pane::new(None, background_actions, cx)); let pane_id = center_pane.id(); cx.subscribe(¢er_pane, move |this, _, event, cx| { this.handle_pane_event(pane_id, event, cx) @@ -610,7 +614,7 @@ impl Workspace { .detach(); cx.focus(¢er_pane); cx.emit(Event::PaneAdded(center_pane.clone())); - let dock = Dock::new(dock_default_factory, cx); + let dock = Dock::new(dock_default_factory, background_actions, cx); let dock_pane = dock.pane().clone(); let fs = project.read(cx).fs().clone(); @@ -730,6 +734,7 @@ impl Workspace { window_edited: false, active_call, database_id: workspace_id, + background_actions, _observe_current_user, _apply_leader_updates, leader_updates_tx, @@ -818,6 +823,7 @@ impl Workspace { workspace_id, project_handle, app_state.dock_default_item_factory, + app_state.background_actions, cx, ); (app_state.initialize_workspace)(&mut workspace, &app_state, cx); @@ -1432,7 +1438,7 @@ impl Workspace { } fn add_pane(&mut self, cx: &mut ViewContext) -> ViewHandle { - let pane = cx.add_view(|cx| Pane::new(None, cx)); + let pane = cx.add_view(|cx| Pane::new(None, self.background_actions, cx)); let pane_id = pane.id(); cx.subscribe(&pane, move |this, _, event, cx| { this.handle_pane_event(pane_id, event, cx) @@ -2648,6 +2654,11 @@ impl Workspace { }) .detach(); } + + #[cfg(any(test, feature = "test-support"))] + pub fn test_new(project: ModelHandle, cx: &mut ViewContext) -> Self { + Self::new(None, 0, project, |_, _| None, || &[], cx) + } } fn notify_if_database_failed(workspace: &ViewHandle, cx: &mut AsyncAppContext) { @@ -2988,17 +2999,10 @@ mod tests { use super::*; use fs::FakeFs; - use gpui::{executor::Deterministic, TestAppContext, ViewContext}; + use gpui::{executor::Deterministic, TestAppContext}; use project::{Project, ProjectEntryId}; use serde_json::json; - pub fn default_item_factory( - _workspace: &mut Workspace, - _cx: &mut ViewContext, - ) -> Option> { - unimplemented!() - } - #[gpui::test] async fn test_tab_disambiguation(cx: &mut TestAppContext) { cx.foreground().forbid_parking(); @@ -3011,7 +3015,8 @@ mod tests { Default::default(), 0, project.clone(), - default_item_factory, + |_, _| unimplemented!(), + || unimplemented!(), cx, ) }); @@ -3083,7 +3088,8 @@ mod tests { Default::default(), 0, project.clone(), - default_item_factory, + |_, _| unimplemented!(), + || unimplemented!(), cx, ) }); @@ -3183,7 +3189,8 @@ mod tests { Default::default(), 0, project.clone(), - default_item_factory, + |_, _| unimplemented!(), + || unimplemented!(), cx, ) }); @@ -3222,7 +3229,14 @@ mod tests { let project = Project::test(fs, None, cx).await; let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, default_item_factory, cx) + Workspace::new( + Default::default(), + 0, + project, + |_, _| unimplemented!(), + || unimplemented!(), + cx, + ) }); let item1 = cx.add_view(&workspace, |cx| { @@ -3331,7 +3345,14 @@ mod tests { let project = Project::test(fs, [], cx).await; let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, default_item_factory, cx) + Workspace::new( + Default::default(), + 0, + project, + |_, _| unimplemented!(), + || unimplemented!(), + cx, + ) }); // Create several workspace items with single project entries, and two @@ -3440,7 +3461,14 @@ mod tests { let project = Project::test(fs, [], cx).await; let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, default_item_factory, cx) + Workspace::new( + Default::default(), + 0, + project, + |_, _| unimplemented!(), + || unimplemented!(), + cx, + ) }); let item = cx.add_view(&workspace, |cx| { @@ -3559,7 +3587,14 @@ mod tests { let project = Project::test(fs, [], cx).await; let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, default_item_factory, cx) + Workspace::new( + Default::default(), + 0, + project, + |_, _| unimplemented!(), + || unimplemented!(), + cx, + ) }); let item = cx.add_view(&workspace, |cx| { diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 9982b4114a..2e2026309f 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -18,7 +18,7 @@ use futures::{ channel::{mpsc, oneshot}, FutureExt, SinkExt, StreamExt, }; -use gpui::{App, AssetSource, AsyncAppContext, MutableAppContext, Task, ViewContext}; +use gpui::{Action, App, AssetSource, AsyncAppContext, MutableAppContext, Task, ViewContext}; use isahc::{config::Configurable, Request}; use language::LanguageRegistry; use log::LevelFilter; @@ -45,9 +45,10 @@ use theme::ThemeRegistry; use util::StaffMode; use util::{channel::RELEASE_CHANNEL, paths, ResultExt, TryFutureExt}; use workspace::{ - self, item::ItemHandle, notifications::NotifyResultExt, AppState, NewFile, OpenPaths, Workspace, + self, dock::FocusDock, item::ItemHandle, notifications::NotifyResultExt, AppState, NewFile, + OpenPaths, Workspace, }; -use zed::{self, build_window_options, initialize_workspace, languages, menus}; +use zed::{self, build_window_options, initialize_workspace, languages, menus, OpenSettings}; fn main() { let http = http::client(); @@ -186,6 +187,7 @@ fn main() { build_window_options, initialize_workspace, dock_default_item_factory, + background_actions, }); auto_update::init(http, client::ZED_SERVER_URL.clone(), cx); @@ -703,3 +705,13 @@ pub fn dock_default_item_factory( Some(Box::new(terminal_view)) } + +pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] { + &[ + ("Go to file", &file_finder::Toggle), + ("Open the command palette", &command_palette::Toggle), + ("Focus the dock", &FocusDock), + ("Open recent projects", &recent_projects::OpenRecent), + ("Change your settings", &OpenSettings), + ] +} diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 3c093836f2..ae9fcc9b31 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -889,9 +889,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx)); let file1 = entries[0].clone(); @@ -1010,9 +1008,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/dir1".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); // Open a file within an existing worktree. cx.update(|cx| { @@ -1171,9 +1167,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); // Open a file within an existing worktree. cx.update(|cx| { @@ -1215,9 +1209,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; project.update(cx, |project, _| project.languages().add(rust_lang())); - let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let worktree = cx.read(|cx| workspace.read(cx).worktrees(cx).next().unwrap()); // Create a new untitled buffer @@ -1306,9 +1298,7 @@ mod tests { let project = Project::test(app_state.fs.clone(), [], cx).await; project.update(cx, |project, _| project.languages().add(rust_lang())); - let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); // Create a new untitled buffer cx.dispatch_action(window_id, NewFile); @@ -1361,9 +1351,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new(Default::default(), 0, project, |_, _| unimplemented!(), cx) - }); + let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx)); let file1 = entries[0].clone(); @@ -1437,15 +1425,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project.clone(), - |_, _| unimplemented!(), - cx, - ) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx)); let entries = cx.read(|cx| workspace.file_project_paths(cx)); let file1 = entries[0].clone(); @@ -1709,15 +1689,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/root".as_ref()], cx).await; - let (_, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project.clone(), - |_, _| unimplemented!(), - cx, - ) - }); + let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx)); let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone()); let entries = cx.read(|cx| workspace.file_project_paths(cx)); diff --git a/styles/src/styleTree/contextMenu.ts b/styles/src/styleTree/contextMenu.ts index 30f44a6314..b4a21deba4 100644 --- a/styles/src/styleTree/contextMenu.ts +++ b/styles/src/styleTree/contextMenu.ts @@ -26,14 +26,19 @@ export default function contextMenu(colorScheme: ColorScheme) { hover: { background: background(layer, "hovered"), label: text(layer, "sans", "hovered", { size: "sm" }), + keystroke: { + ...text(layer, "sans", "hovered", { + size: "sm", + weight: "bold", + }), + padding: { left: 3, right: 3 }, + }, }, active: { background: background(layer, "active"), - label: text(layer, "sans", "active", { size: "sm" }), }, activeHover: { background: background(layer, "active"), - label: text(layer, "sans", "active", { size: "sm" }), }, }, separator: { diff --git a/styles/src/styleTree/welcome.ts b/styles/src/styleTree/welcome.ts index 137c6df111..7c437f110e 100644 --- a/styles/src/styleTree/welcome.ts +++ b/styles/src/styleTree/welcome.ts @@ -86,20 +86,24 @@ export default function welcome(colorScheme: ColorScheme) { border: border(layer, "active"), }, }, + checkboxContainer: { + margin: { + top: 4, + }, + }, checkbox: { label: { ...text(layer, "sans", interactive_text_size), // Also supports margin, container, border, etc. }, - container: { - margin: { - top: 4, - }, + icon: { + color: foreground(layer, "on"), + icon: "icons/check_12.svg", + dimensions: { + width: 12, + height: 12, + } }, - width: 12, - height: 12, - checkIcon: "icons/check_12.svg", - checkIconColor: foreground(layer, "on"), default: { ...checkboxBase, background: background(layer, "default"), diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index c758e0227e..55b5c61341 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -41,6 +41,34 @@ export default function workspace(colorScheme: ColorScheme) { return { background: background(layer), + blankPane: { + logo: { + color: background(layer, "on"), + icon: "icons/logo_96.svg", + dimensions: { + width: 240, + height: 240, + } + }, + keyboardHints: { + margin: { + top: 32 + }, + padding: { + bottom: -8. + } + }, + keyboardHint: { + ...text(colorScheme.lowest, "sans", "variant", { size: "sm" }), + margin: { + bottom: 8 + }, + hover: { + ...text(colorScheme.lowest, "sans", "hovered", { size: "sm" }), + } + }, + keyboardHintWidth: 240, + }, joiningProjectAvatar: { cornerRadius: 40, width: 80, From cf6ea6d698e7bc3047e6a667f6e71c0889eb855c Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 8 Mar 2023 18:11:11 -0800 Subject: [PATCH 45/93] Fix bug with action keybindings not being resolved --- crates/editor/src/editor_tests.rs | 4 ++-- crates/theme/src/ui.rs | 22 ++++++++++++++++++++-- crates/workspace/src/dock.rs | 9 ++++++++- crates/workspace/src/pane.rs | 9 +++++++-- crates/workspace/src/workspace.rs | 6 ++++-- styles/src/styleTree/workspace.ts | 6 +++--- 6 files changed, 44 insertions(+), 12 deletions(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 21cc9f8895..0176a1e01b 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -485,7 +485,7 @@ fn test_navigation_history(cx: &mut gpui::MutableAppContext) { cx.set_global(DragAndDrop::::default()); use workspace::item::Item; let (_, pane) = cx.add_window(Default::default(), |cx| { - Pane::new(None, || unimplemented!(), cx) + Pane::new(0, None, || unimplemented!(), cx) }); let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); @@ -5564,7 +5564,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) { Settings::test_async(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; - let (_, pane) = cx.add_window(|cx| Pane::new(None, || unimplemented!(), cx)); + let (_, pane) = cx.add_window(|cx| Pane::new(0, None, || unimplemented!(), cx)); let leader = pane.update(cx, |_, cx| { let multibuffer = cx.add_model(|_| MultiBuffer::new(0)); diff --git a/crates/theme/src/ui.rs b/crates/theme/src/ui.rs index ca71db3723..5396265ad8 100644 --- a/crates/theme/src/ui.rs +++ b/crates/theme/src/ui.rs @@ -97,6 +97,24 @@ pub fn keystroke_label( ) -> Container { // FIXME: Put the theme in it's own global so we can // query the keystroke style on our own + keystroke_label_for( + cx.window_id(), + cx.handle().id(), + label_text, + label_style, + keystroke_style, + action, + ) +} + +pub fn keystroke_label_for( + window_id: usize, + view_id: usize, + label_text: &'static str, + label_style: &ContainedText, + keystroke_style: &ContainedText, + action: Box, +) -> Container { Flex::row() .with_child( Label::new(label_text, label_style.text.clone()) @@ -105,8 +123,8 @@ pub fn keystroke_label( ) .with_child({ KeystrokeLabel::new( - cx.window_id(), - cx.handle().id(), + window_id, + view_id, action, keystroke_style.container, keystroke_style.text.clone(), diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index f86a9db71a..efba568b90 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -187,7 +187,14 @@ impl Dock { ) -> Self { let position = DockPosition::Hidden(cx.global::().default_dock_anchor); - let pane = cx.add_view(|cx| Pane::new(Some(position.anchor()), background_actions, cx)); + let pane = cx.add_view(|cx| { + Pane::new( + cx.handle().id(), + Some(position.anchor()), + background_actions, + cx, + ) + }); pane.update(cx, |pane, cx| { pane.set_active(false, cx); }); diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 7e0e6bbe01..abd410d875 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -218,6 +218,7 @@ pub struct Pane { tab_bar_context_menu: ViewHandle, docked: Option, background_actions: BackgroundActions, + workspace_id: usize, } pub struct ItemNavHistory { @@ -275,6 +276,7 @@ enum ItemType { impl Pane { pub fn new( + workspace_id: usize, docked: Option, background_actions: BackgroundActions, cx: &mut ViewContext, @@ -300,6 +302,7 @@ impl Pane { tab_bar_context_menu: context_menu, docked, background_actions, + workspace_id, } } @@ -1442,6 +1445,7 @@ impl Pane { .with_children({ enum KeyboardHint {} let keyboard_hint = &theme.keyboard_hint; + let workspace_id = self.workspace_id; (self.background_actions)().into_iter().enumerate().map( move |(idx, (text, action))| { let hint_action = action.boxed_clone(); @@ -1449,14 +1453,15 @@ impl Pane { idx, cx, move |state, cx| { - theme::ui::keystroke_label( + theme::ui::keystroke_label_for( + cx.window_id(), + workspace_id, text, &keyboard_hint.style_for(state, false), &keystroke_style .style_for(state, false) .keystroke, hint_action, - cx, ) .boxed() }, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 65335d8671..40053b103a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -606,7 +606,9 @@ impl Workspace { }) .detach(); - let center_pane = cx.add_view(|cx| Pane::new(None, background_actions, cx)); + let workspace_view_id = cx.handle().id(); + let center_pane = + cx.add_view(|cx| Pane::new(workspace_view_id, None, background_actions, cx)); let pane_id = center_pane.id(); cx.subscribe(¢er_pane, move |this, _, event, cx| { this.handle_pane_event(pane_id, event, cx) @@ -1438,7 +1440,7 @@ impl Workspace { } fn add_pane(&mut self, cx: &mut ViewContext) -> ViewHandle { - let pane = cx.add_view(|cx| Pane::new(None, self.background_actions, cx)); + let pane = cx.add_view(|cx| Pane::new(cx.handle().id(), None, self.background_actions, cx)); let pane_id = pane.id(); cx.subscribe(&pane, move |this, _, event, cx| { this.handle_pane_event(pane_id, event, cx) diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 55b5c61341..cb99572853 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -46,8 +46,8 @@ export default function workspace(colorScheme: ColorScheme) { color: background(layer, "on"), icon: "icons/logo_96.svg", dimensions: { - width: 240, - height: 240, + width: 256, + height: 256, } }, keyboardHints: { @@ -67,7 +67,7 @@ export default function workspace(colorScheme: ColorScheme) { ...text(colorScheme.lowest, "sans", "hovered", { size: "sm" }), } }, - keyboardHintWidth: 240, + keyboardHintWidth: 256, }, joiningProjectAvatar: { cornerRadius: 40, From a65dd0fd98b26d3eaa349c25e6e1d35a418ee720 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 8 Mar 2023 18:15:29 -0800 Subject: [PATCH 46/93] Restore correct checkbox text --- crates/welcome/src/welcome.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index 89f161a283..a1045ea1e8 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -125,7 +125,7 @@ impl View for WelcomePage { Flex::column() .with_children([ theme::ui::checkbox::( - "Do you want to send telemetry?", + "Send anonymous usage data to improve Zed (View what we're sending via Help > View Telemetry Log)", &theme.welcome.checkbox, metrics, cx, From 981b3a459f2d204c3daf23b6883328d7c8c26ecf Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 8 Mar 2023 21:31:14 -0500 Subject: [PATCH 47/93] Sort collaborators in titlebar alphabetically instead of by replica id --- crates/collab_ui/src/collab_titlebar_item.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index f9f5738ad2..c838f6a55f 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -572,15 +572,13 @@ impl CollabTitlebarItem { room: &ModelHandle, cx: &mut RenderContext, ) -> Vec { - let project = workspace.read(cx).project().read(cx); - let mut participants = room .read(cx) .remote_participants() .values() .cloned() .collect::>(); - participants.sort_by_key(|p| Some(project.collaborators().get(&p.peer_id)?.replica_id)); + participants.sort_by_cached_key(|p| p.user.github_login.clone()); participants .into_iter() From 943ea61452a6550bec95a23db5a006bff8b38ea2 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 8 Mar 2023 18:47:52 -0800 Subject: [PATCH 48/93] Add a note on how to check the telemetry --- crates/theme/src/theme.rs | 1 + crates/theme/src/ui.rs | 22 +++++++++++++++++----- crates/welcome/src/welcome.rs | 21 +++++++++++++++++++-- crates/zed/src/menus.rs | 2 +- styles/src/styleTree/welcome.ts | 9 +++++++++ 5 files changed, 47 insertions(+), 8 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 524b765626..d331ca80a9 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -869,6 +869,7 @@ pub struct WelcomeStyle { pub page_width: f32, pub logo: IconStyle, pub logo_subheading: ContainedText, + pub usage_note: ContainedText, pub checkbox: CheckboxStyle, pub checkbox_container: ContainerStyle, pub button: Interactive, diff --git a/crates/theme/src/ui.rs b/crates/theme/src/ui.rs index 5396265ad8..5441e71168 100644 --- a/crates/theme/src/ui.rs +++ b/crates/theme/src/ui.rs @@ -4,7 +4,7 @@ use gpui::{ ConstrainedBox, Container, ContainerStyle, Empty, Flex, KeystrokeLabel, Label, MouseEventHandler, ParentElement, Svg, }, - Action, Element, EventContext, RenderContext, View, + Action, Element, ElementBox, EventContext, RenderContext, View, }; use serde::Deserialize; @@ -26,6 +26,21 @@ pub fn checkbox( checked: bool, cx: &mut RenderContext, change: fn(checked: bool, cx: &mut EventContext) -> (), +) -> MouseEventHandler { + let label = Label::new(label, style.label.text.clone()) + .contained() + .with_style(style.label.container) + .boxed(); + + checkbox_with_label(label, style, checked, cx, change) +} + +pub fn checkbox_with_label( + label: ElementBox, + style: &CheckboxStyle, + checked: bool, + cx: &mut RenderContext, + change: fn(checked: bool, cx: &mut EventContext) -> (), ) -> MouseEventHandler { MouseEventHandler::::new(0, cx, |state, _| { let indicator = if checked { @@ -55,10 +70,7 @@ pub fn checkbox( } }) .boxed(), - Label::new(label, style.label.text.clone()) - .contained() - .with_style(style.label.container) - .boxed(), + label, ]) .align_children_center() .boxed() diff --git a/crates/welcome/src/welcome.rs b/crates/welcome/src/welcome.rs index a1045ea1e8..3a35920b88 100644 --- a/crates/welcome/src/welcome.rs +++ b/crates/welcome/src/welcome.rs @@ -124,8 +124,25 @@ impl View for WelcomePage { .boxed(), Flex::column() .with_children([ - theme::ui::checkbox::( - "Send anonymous usage data to improve Zed (View what we're sending via Help > View Telemetry Log)", + theme::ui::checkbox_with_label::( + Flex::column() + .with_children([ + Label::new( + "Send anonymous usage data", + theme.welcome.checkbox.label.text.clone(), + ) + .contained() + .with_style(theme.welcome.checkbox.label.container) + .boxed(), + Label::new( + "Help > View Telemetry", + theme.welcome.usage_note.text.clone(), + ) + .contained() + .with_style(theme.welcome.usage_note.container) + .boxed(), + ]) + .boxed(), &theme.welcome.checkbox, metrics, cx, diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index 9381a90907..d5fb94b2b6 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -137,7 +137,7 @@ pub fn menus() -> Vec> { items: vec![ MenuItem::action("Command Palette", command_palette::Toggle), MenuItem::separator(), - MenuItem::action("View Telemetry Log", crate::OpenTelemetryLog), + MenuItem::action("View Telemetry", crate::OpenTelemetryLog), MenuItem::action("View Dependency Licenses", crate::OpenLicenses), MenuItem::separator(), MenuItem::action( diff --git a/styles/src/styleTree/welcome.ts b/styles/src/styleTree/welcome.ts index 7c437f110e..4ad3e51cd8 100644 --- a/styles/src/styleTree/welcome.ts +++ b/styles/src/styleTree/welcome.ts @@ -86,10 +86,19 @@ export default function welcome(colorScheme: ColorScheme) { border: border(layer, "active"), }, }, + usageNote: { + ...text(layer, "sans", "variant", { size: "2xs" }), + margin: { + top: -4 + } + }, checkboxContainer: { margin: { top: 4, }, + padding: { + bottom: 8, + } }, checkbox: { label: { From 709c101834b5429c9ea580415e5e4e736e88384f Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 8 Mar 2023 18:50:17 -0800 Subject: [PATCH 49/93] Adjust styles on usage note --- styles/src/styleTree/welcome.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/styles/src/styleTree/welcome.ts b/styles/src/styleTree/welcome.ts index 4ad3e51cd8..e1bd5c82bb 100644 --- a/styles/src/styleTree/welcome.ts +++ b/styles/src/styleTree/welcome.ts @@ -88,8 +88,9 @@ export default function welcome(colorScheme: ColorScheme) { }, usageNote: { ...text(layer, "sans", "variant", { size: "2xs" }), - margin: { - top: -4 + padding: { + top: -4, + } }, checkboxContainer: { From 325827699e0942e05e250ff38a24c6ef4c71fbb2 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 8 Mar 2023 19:02:13 -0800 Subject: [PATCH 50/93] Adjust styling for blank page experience --- styles/src/styleTree/workspace.ts | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index cb99572853..d798a22e9d 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -40,34 +40,40 @@ export default function workspace(colorScheme: ColorScheme) { const followerAvatarOuterWidth = followerAvatarWidth + 4 return { - background: background(layer), + background: background(colorScheme.lowest), blankPane: { logo: { - color: background(layer, "on"), + color: border(layer, "active").color, icon: "icons/logo_96.svg", dimensions: { - width: 256, - height: 256, + width: 272, + height: 272, } }, keyboardHints: { margin: { - top: 32 + top: 32, + // bottom: -8. }, padding: { - bottom: -8. - } + top: 8, + left: 8, + right: 8, + }, + background: background(colorScheme.lowest), + border: border(layer, "active"), + cornerRadius: 4, }, keyboardHint: { - ...text(colorScheme.lowest, "sans", "variant", { size: "sm" }), + ...text(layer, "sans", "variant", { size: "sm" }), margin: { bottom: 8 }, hover: { - ...text(colorScheme.lowest, "sans", "hovered", { size: "sm" }), + ...text(layer, "sans", "hovered", { size: "sm" }), } }, - keyboardHintWidth: 256, + keyboardHintWidth: 272, }, joiningProjectAvatar: { cornerRadius: 40, From f626920af1a7f9337900618723c7c44129e30097 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Wed, 8 Mar 2023 19:03:44 -0800 Subject: [PATCH 51/93] Remove permanent Zed stateless --- crates/db/src/db.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/db/src/db.rs b/crates/db/src/db.rs index 989dcf0af5..ae9325ea2e 100644 --- a/crates/db/src/db.rs +++ b/crates/db/src/db.rs @@ -42,7 +42,7 @@ const DB_FILE_NAME: &'static str = "db.sqlite"; lazy_static::lazy_static! { // !!!!!!! CHANGE BACK TO DEFAULT FALSE BEFORE SHIPPING - static ref ZED_STATELESS: bool = std::env::var("ZED_STATELESS").map_or(true, |v| !v.is_empty()); + static ref ZED_STATELESS: bool = std::env::var("ZED_STATELESS").map_or(false, |v| !v.is_empty()); static ref DB_FILE_OPERATIONS: Mutex<()> = Mutex::new(()); pub static ref BACKUP_DB_PATH: RwLock> = RwLock::new(None); pub static ref ALL_FILE_DB_FAILED: AtomicBool = AtomicBool::new(false); @@ -66,11 +66,11 @@ pub async fn open_db( let connection = async_iife!({ // Note: This still has a race condition where 1 set of migrations succeeds // (e.g. (Workspace, Editor)) and another fails (e.g. (Workspace, Terminal)) - // This will cause the first connection to have the database taken out + // This will cause the first connection to have the database taken out // from under it. This *should* be fine though. The second dabatase failure will // cause errors in the log and so should be observed by developers while writing // soon-to-be good migrations. If user databases are corrupted, we toss them out - // and try again from a blank. As long as running all migrations from start to end + // and try again from a blank. As long as running all migrations from start to end // on a blank database is ok, this race condition will never be triggered. // // Basically: Don't ever push invalid migrations to stable or everyone will have @@ -88,7 +88,7 @@ pub async fn open_db( }; } - // Take a lock in the failure case so that we move the db once per process instead + // Take a lock in the failure case so that we move the db once per process instead // of potentially multiple times from different threads. This shouldn't happen in the // normal path let _lock = DB_FILE_OPERATIONS.lock(); From 4ce51c813841af211fb9b36284f4df4db6078a53 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Mar 2023 07:26:22 +0100 Subject: [PATCH 52/93] Limit dirty buffer save optimization to multi-buffers --- crates/editor/src/items.rs | 31 ++++++++++++++++++++++++++++--- crates/project/src/project.rs | 14 +------------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 0d594a66ef..cda702de00 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -612,9 +612,34 @@ impl Item for Editor { let buffers = self.buffer().clone().read(cx).all_buffers(); cx.as_mut().spawn(|mut cx| async move { format.await?; - project - .update(&mut cx, |project, cx| project.save_buffers(buffers, cx)) - .await?; + + if buffers.len() == 1 { + project + .update(&mut cx, |project, cx| project.save_buffers(buffers, cx)) + .await?; + } else { + // For multi-buffers, only save those ones that contain changes. For clean buffers + // we simulate saving by calling `Buffer::did_save`, so that language servers or + // other downstream listeners of save events get notified. + let (dirty_buffers, clean_buffers) = buffers.into_iter().partition(|buffer| { + buffer.read_with(&cx, |buffer, _| buffer.is_dirty() || buffer.has_conflict()) + }); + + project + .update(&mut cx, |project, cx| { + project.save_buffers(dirty_buffers, cx) + }) + .await?; + for buffer in clean_buffers { + buffer.update(&mut cx, |buffer, cx| { + let version = buffer.saved_version().clone(); + let fingerprint = buffer.saved_version_fingerprint(); + let mtime = buffer.saved_mtime(); + buffer.did_save(version, fingerprint, mtime, cx); + }); + } + } + Ok(()) }) } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f93de8e1d8..2ebea8d07d 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1434,19 +1434,7 @@ impl Project { let worktree = file.worktree.clone(); let path = file.path.clone(); worktree.update(cx, |worktree, cx| match worktree { - Worktree::Local(worktree) => { - if buffer.read(cx).is_dirty() || buffer.read(cx).has_conflict() { - worktree.save_buffer(buffer, path, false, cx) - } else { - buffer.update(cx, |buffer, cx| { - let version = buffer.saved_version().clone(); - let fingerprint = buffer.saved_version_fingerprint(); - let mtime = buffer.saved_mtime(); - buffer.did_save(version.clone(), fingerprint, mtime, cx); - Task::ready(Ok((version, fingerprint, mtime))) - }) - } - } + Worktree::Local(worktree) => worktree.save_buffer(buffer, path, false, cx), Worktree::Remote(worktree) => worktree.save_buffer(buffer, cx), }) } From a00ce3f286db6ff34938abd4ceab6e9f08f27e28 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Mar 2023 07:42:37 +0100 Subject: [PATCH 53/93] Add randomized test to remove active selections from buffer --- crates/language/src/buffer_tests.rs | 42 ++++++++++++++++------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 0c375f0f4e..e98edbb2d3 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1804,25 +1804,31 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) { } 30..=39 if mutation_count != 0 => { buffer.update(cx, |buffer, cx| { - let mut selections = Vec::new(); - for id in 0..rng.gen_range(1..=5) { - let range = buffer.random_byte_range(0, &mut rng); - selections.push(Selection { - id, - start: buffer.anchor_before(range.start), - end: buffer.anchor_before(range.end), - reversed: false, - goal: SelectionGoal::None, - }); + if rng.gen_bool(0.2) { + log::info!("peer {} clearing active selections", replica_id); + active_selections.remove(&replica_id); + buffer.remove_active_selections(cx); + } else { + let mut selections = Vec::new(); + for id in 0..rng.gen_range(1..=5) { + let range = buffer.random_byte_range(0, &mut rng); + selections.push(Selection { + id, + start: buffer.anchor_before(range.start), + end: buffer.anchor_before(range.end), + reversed: false, + goal: SelectionGoal::None, + }); + } + let selections: Arc<[Selection]> = selections.into(); + log::info!( + "peer {} setting active selections: {:?}", + replica_id, + selections + ); + active_selections.insert(replica_id, selections.clone()); + buffer.set_active_selections(selections, false, Default::default(), cx); } - let selections: Arc<[Selection]> = selections.into(); - log::info!( - "peer {} setting active selections: {:?}", - replica_id, - selections - ); - active_selections.insert(replica_id, selections.clone()); - buffer.set_active_selections(selections, false, Default::default(), cx); }); mutation_count -= 1; } From 9328bb0153d5d8fbe58273a5f081ff68af1862da Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Mar 2023 09:17:15 +0100 Subject: [PATCH 54/93] Introduce Kubernetes liveness probe to ensure database works --- crates/collab/k8s/manifest.template.yml | 7 +++++++ crates/collab/src/main.rs | 15 +++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/collab/k8s/manifest.template.yml b/crates/collab/k8s/manifest.template.yml index 339d02892e..b73070536f 100644 --- a/crates/collab/k8s/manifest.template.yml +++ b/crates/collab/k8s/manifest.template.yml @@ -59,6 +59,13 @@ spec: ports: - containerPort: 8080 protocol: TCP + livenessProbe: + httpGet: + path: /healthz + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 readinessProbe: httpGet: path: / diff --git a/crates/collab/src/main.rs b/crates/collab/src/main.rs index 0f783c13e5..30ed35bc35 100644 --- a/crates/collab/src/main.rs +++ b/crates/collab/src/main.rs @@ -1,11 +1,12 @@ use anyhow::anyhow; -use axum::{routing::get, Router}; +use axum::{routing::get, Extension, Router}; use collab::{db, env, executor::Executor, AppState, Config, MigrateConfig, Result}; use db::Database; use std::{ env::args, net::{SocketAddr, TcpListener}, path::Path, + sync::Arc, }; use tokio::signal::unix::SignalKind; use tracing_log::LogTracer; @@ -66,7 +67,12 @@ async fn main() -> Result<()> { let app = collab::api::routes(rpc_server.clone(), state.clone()) .merge(collab::rpc::routes(rpc_server.clone())) - .merge(Router::new().route("/", get(handle_root))); + .merge( + Router::new() + .route("/", get(handle_root)) + .route("/healthz", get(handle_liveness_probe)) + .layer(Extension(state.clone())), + ); axum::Server::from_tcp(listener)? .serve(app.into_make_service_with_connect_info::()) @@ -95,6 +101,11 @@ async fn handle_root() -> String { format!("collab v{VERSION}") } +async fn handle_liveness_probe(Extension(state): Extension>) -> Result { + state.db.get_all_users(0, 1).await?; + Ok("ok".to_string()) +} + pub fn init_tracing(config: &Config) -> Option<()> { use std::str::FromStr; use tracing_subscriber::layer::SubscriberExt; From 3daeabc1d6e2b58ee9c6e1793d2e8b72b97ed468 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 9 Mar 2023 09:30:04 +0100 Subject: [PATCH 55/93] collab 0.7.0 --- Cargo.lock | 2 +- crates/collab/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 82a045ed3a..cfd27bbdd0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1188,7 +1188,7 @@ dependencies = [ [[package]] name = "collab" -version = "0.6.2" +version = "0.7.0" dependencies = [ "anyhow", "async-tungstenite", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index fee7089ce6..a2ad7d7343 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] default-run = "collab" edition = "2021" name = "collab" -version = "0.6.2" +version = "0.7.0" publish = false [[bin]] From 9187863d0ef9ecf69e7d071b180700bd8dbcd3e8 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 9 Mar 2023 00:45:05 -0800 Subject: [PATCH 56/93] re-add spaces removed by new setting --- crates/collab/src/tests.rs | 4 +-- crates/editor/src/editor_tests.rs | 14 ++++----- crates/workspace/src/dock.rs | 4 +-- crates/workspace/src/workspace.rs | 52 +++++++------------------------ 4 files changed, 22 insertions(+), 52 deletions(-) diff --git a/crates/collab/src/tests.rs b/crates/collab/src/tests.rs index 8949b60993..80df0ed6df 100644 --- a/crates/collab/src/tests.rs +++ b/crates/collab/src/tests.rs @@ -197,8 +197,8 @@ impl TestServer { fs: fs.clone(), build_window_options: |_, _, _| Default::default(), initialize_workspace: |_, _, _| unimplemented!(), - dock_default_item_factory: |_, _| unimplemented!(), - background_actions: || unimplemented!(), + dock_default_item_factory: |_, _| None, + background_actions: || &[], }); Project::init(&client); diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 0176a1e01b..d73c3c87db 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -484,9 +484,7 @@ fn test_navigation_history(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); cx.set_global(DragAndDrop::::default()); use workspace::item::Item; - let (_, pane) = cx.add_window(Default::default(), |cx| { - Pane::new(0, None, || unimplemented!(), cx) - }); + let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(0, None, || &[], cx)); let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); cx.add_view(&pane, |cx| { @@ -2356,10 +2354,10 @@ async fn test_clipboard(cx: &mut gpui::TestAppContext) { e.handle_input(") ", cx); }); cx.assert_editor_state(indoc! {" - ( one✅ - three - five ) ˇtwo one✅ four three six five ( one✅ - three + ( one✅ + three + five ) ˇtwo one✅ four three six five ( one✅ + three five ) ˇ"}); // Cut with three selections, one of which is full-line. @@ -5564,7 +5562,7 @@ async fn test_following_with_multiple_excerpts(cx: &mut gpui::TestAppContext) { Settings::test_async(cx); let fs = FakeFs::new(cx.background()); let project = Project::test(fs, ["/file.rs".as_ref()], cx).await; - let (_, pane) = cx.add_window(|cx| Pane::new(0, None, || unimplemented!(), cx)); + let (_, pane) = cx.add_window(|cx| Pane::new(0, None, || &[], cx)); let leader = pane.update(cx, |_, cx| { let multibuffer = cx.add_model(|_| MultiBuffer::new(0)); diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index efba568b90..fd7638c5fb 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -500,7 +500,7 @@ mod tests { 0, project.clone(), default_item_factory, - || unimplemented!(), + || &[], cx, ) }); @@ -634,7 +634,7 @@ mod tests { 0, project, default_item_factory, - || unimplemented!(), + || &[], cx, ) }); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 40053b103a..ef14bfa2fe 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -455,8 +455,8 @@ impl AppState { user_store, initialize_workspace: |_, _, _| {}, build_window_options: |_, _, _| Default::default(), - dock_default_item_factory: |_, _| unimplemented!(), - background_actions: || unimplemented!(), + dock_default_item_factory: |_, _| None, + background_actions: || &[], }) } } @@ -3017,8 +3017,8 @@ mod tests { Default::default(), 0, project.clone(), - |_, _| unimplemented!(), - || unimplemented!(), + |_, _| None, + || &[], cx, ) }); @@ -3090,8 +3090,8 @@ mod tests { Default::default(), 0, project.clone(), - |_, _| unimplemented!(), - || unimplemented!(), + |_, _| None, + || &[], cx, ) }); @@ -3191,8 +3191,8 @@ mod tests { Default::default(), 0, project.clone(), - |_, _| unimplemented!(), - || unimplemented!(), + |_, _| None, + || &[], cx, ) }); @@ -3231,14 +3231,7 @@ mod tests { let project = Project::test(fs, None, cx).await; let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project, - |_, _| unimplemented!(), - || unimplemented!(), - cx, - ) + Workspace::new(Default::default(), 0, project, |_, _| None, || &[], cx) }); let item1 = cx.add_view(&workspace, |cx| { @@ -3347,14 +3340,7 @@ mod tests { let project = Project::test(fs, [], cx).await; let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project, - |_, _| unimplemented!(), - || unimplemented!(), - cx, - ) + Workspace::new(Default::default(), 0, project, |_, _| None, || &[], cx) }); // Create several workspace items with single project entries, and two @@ -3463,14 +3449,7 @@ mod tests { let project = Project::test(fs, [], cx).await; let (window_id, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project, - |_, _| unimplemented!(), - || unimplemented!(), - cx, - ) + Workspace::new(Default::default(), 0, project, |_, _| None, || &[], cx) }); let item = cx.add_view(&workspace, |cx| { @@ -3589,14 +3568,7 @@ mod tests { let project = Project::test(fs, [], cx).await; let (_, workspace) = cx.add_window(|cx| { - Workspace::new( - Default::default(), - 0, - project, - |_, _| unimplemented!(), - || unimplemented!(), - cx, - ) + Workspace::new(Default::default(), 0, project, |_, _| None, || &[], cx) }); let item = cx.add_view(&workspace, |cx| { From 5bfd5e35b31ef046b7372e78cc3bca389bdf7b81 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 9 Mar 2023 10:47:01 -0500 Subject: [PATCH 57/93] Add light themes --- styles/src/themes/atelier-forest-light.ts | 55 +++++++++++++++++++++ styles/src/themes/atelier-heath-light.ts | 55 +++++++++++++++++++++ styles/src/themes/atelier-lakeside-light.ts | 55 +++++++++++++++++++++ styles/src/themes/atelier-plateau-light.ts | 55 +++++++++++++++++++++ styles/src/themes/atelier-savanna-light.ts | 55 +++++++++++++++++++++ styles/src/themes/atelier-seaside-dark.ts | 2 +- styles/src/themes/atelier-seaside-light.ts | 55 +++++++++++++++++++++ 7 files changed, 331 insertions(+), 1 deletion(-) create mode 100644 styles/src/themes/atelier-forest-light.ts create mode 100644 styles/src/themes/atelier-heath-light.ts create mode 100644 styles/src/themes/atelier-lakeside-light.ts create mode 100644 styles/src/themes/atelier-plateau-light.ts create mode 100644 styles/src/themes/atelier-savanna-light.ts create mode 100644 styles/src/themes/atelier-seaside-light.ts diff --git a/styles/src/themes/atelier-forest-light.ts b/styles/src/themes/atelier-forest-light.ts new file mode 100644 index 0000000000..ee9acd7ebb --- /dev/null +++ b/styles/src/themes/atelier-forest-light.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Forest Light`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/forest/", + }, + colors: { + base00: "#f1efee", + base01: "#e6e2e0", + base02: "#a8a19f", + base03: "#9c9491", + base04: "#766e6b", + base05: "#68615e", + base06: "#2c2421", + base07: "#1b1918", + base08: "#f22c40", + base09: "#df5320", + base0A: "#c38418", + base0B: "#7b9726", + base0C: "#3d97b8", + base0D: "#407ee7", + base0E: "#6666ea", + base0F: "#c33ff3" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, true, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ].reverse()), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-heath-light.ts b/styles/src/themes/atelier-heath-light.ts new file mode 100644 index 0000000000..3afd850fdf --- /dev/null +++ b/styles/src/themes/atelier-heath-light.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Heath Light`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/heath/", + }, + colors: { + base00: "#f7f3f7", + base01: "#d8cad8", + base02: "#ab9bab", + base03: "#9e8f9e", + base04: "#776977", + base05: "#695d69", + base06: "#292329", + base07: "#1b181b", + base08: "#ca402b", + base09: "#a65926", + base0A: "#bb8a35", + base0B: "#918b3b", + base0C: "#159393", + base0D: "#516aec", + base0E: "#7b59c0", + base0F: "#cc33cc", + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, true, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ].reverse()), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-lakeside-light.ts b/styles/src/themes/atelier-lakeside-light.ts new file mode 100644 index 0000000000..255a4dea96 --- /dev/null +++ b/styles/src/themes/atelier-lakeside-light.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Lakeside Light`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/lakeside/", + }, + colors: { + base00: "#ebf8ff", + base01: "#c1e4f6", + base02: "#7ea2b4", + base03: "#7195a8", + base04: "#5a7b8c", + base05: "#516d7b", + base06: "#1f292e", + base07: "#161b1d", + base08: "#d22d72", + base09: "#935c25", + base0A: "#8a8a0f", + base0B: "#568c3b", + base0C: "#2d8f6f", + base0D: "#257fad", + base0E: "#6b6bb8", + base0F: "#b72dd2", + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, true, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ].reverse()), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-plateau-light.ts b/styles/src/themes/atelier-plateau-light.ts new file mode 100644 index 0000000000..5f4d2400c1 --- /dev/null +++ b/styles/src/themes/atelier-plateau-light.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Plateau Light`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/plateau/", + }, + colors: { + base00: "#f4ecec", + base01: "#e7dfdf", + base02: "#8a8585", + base03: "#7e7777", + base04: "#655d5d", + base05: "#585050", + base06: "#292424", + base07: "#1b1818", + base08: "#ca4949", + base09: "#b45a3c", + base0A: "#a06e3b", + base0B: "#4b8b8b", + base0C: "#5485b6", + base0D: "#7272ca", + base0E: "#8464c4", + base0F: "#bd5187" + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, true, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ].reverse()), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-savanna-light.ts b/styles/src/themes/atelier-savanna-light.ts new file mode 100644 index 0000000000..2e1ec9857a --- /dev/null +++ b/styles/src/themes/atelier-savanna-light.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Savanna Light`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/savanna/", + }, + colors: { + base00: "#ecf4ee", + base01: "#dfe7e2", + base02: "#87928a", + base03: "#78877d", + base04: "#5f6d64", + base05: "#526057", + base06: "#232a25", + base07: "#171c19", + base08: "#b16139", + base09: "#9f713c", + base0A: "#a07e3b", + base0B: "#489963", + base0C: "#1c9aa0", + base0D: "#478c90", + base0E: "#55859b", + base0F: "#867469", + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, true, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ].reverse()), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta diff --git a/styles/src/themes/atelier-seaside-dark.ts b/styles/src/themes/atelier-seaside-dark.ts index 29bdcfb447..22136a6f13 100644 --- a/styles/src/themes/atelier-seaside-dark.ts +++ b/styles/src/themes/atelier-seaside-dark.ts @@ -7,7 +7,7 @@ const variant: Variant = { meta: { name: `${name} Seaside Dark`, ...metaCommon, - url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/savanna/", + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside/", }, colors: { base00: "#131513", diff --git a/styles/src/themes/atelier-seaside-light.ts b/styles/src/themes/atelier-seaside-light.ts new file mode 100644 index 0000000000..23db80221f --- /dev/null +++ b/styles/src/themes/atelier-seaside-light.ts @@ -0,0 +1,55 @@ +import chroma from "chroma-js" +import { Meta } from "./common/colorScheme" +import { colorRamp, createColorScheme } from "./common/ramps" +import { metaCommon, name, buildSyntax, Variant } from "./common/atelier-common" + +const variant: Variant = { + meta: { + name: `${name} Seaside Light`, + ...metaCommon, + url: "https://atelierbram.github.io/syntax-highlighting/atelier-schemes/seaside/", + }, + colors: { + base00: "#f4fbf4", + base01: "#cfe8cf", + base02: "#8ca68c", + base03: "#809980", + base04: "#687d68", + base05: "#5e6e5e", + base06: "#242924", + base07: "#131513", + base08: "#e6193c", + base09: "#87711d", + base0A: "#98981b", + base0B: "#29a329", + base0C: "#1999b3", + base0D: "#3d62f5", + base0E: "#ad2bee", + base0F: "#e619c3", + } +} + +const syntax = buildSyntax(variant) + +const theme = (variant: Variant) => { + const { meta, colors } = variant + + return createColorScheme(meta.name, true, { + neutral: chroma + .scale([ + colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + ].reverse()), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, syntax) +} + +export const dark = theme(variant) + +export const meta: Meta = variant.meta From 51be0efa1f6293d9065aeed63ca25708363e865b Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 9 Mar 2023 10:47:58 -0500 Subject: [PATCH 58/93] Format --- styles/src/buildLicenses.ts | 2 +- styles/src/styleTree/editor.ts | 2 +- styles/src/themes/andromeda.ts | 2 +- styles/src/themes/atelier-cave-dark.ts | 41 ++++++++++------- styles/src/themes/atelier-cave-light.ts | 45 ++++++++++++------- styles/src/themes/atelier-dune-dark.ts | 41 ++++++++++------- styles/src/themes/atelier-dune-light.ts | 45 ++++++++++++------- styles/src/themes/atelier-estuary-dark.ts | 41 ++++++++++------- styles/src/themes/atelier-estuary-light.ts | 45 ++++++++++++------- styles/src/themes/atelier-forest-dark.ts | 41 ++++++++++------- styles/src/themes/atelier-forest-light.ts | 45 ++++++++++++------- styles/src/themes/atelier-heath-dark.ts | 41 ++++++++++------- styles/src/themes/atelier-heath-light.ts | 43 +++++++++++------- styles/src/themes/atelier-lakeside-dark.ts | 41 ++++++++++------- styles/src/themes/atelier-lakeside-light.ts | 43 +++++++++++------- styles/src/themes/atelier-plateau-dark.ts | 41 ++++++++++------- styles/src/themes/atelier-plateau-light.ts | 45 ++++++++++++------- styles/src/themes/atelier-savanna-dark.ts | 41 ++++++++++------- styles/src/themes/atelier-savanna-light.ts | 43 +++++++++++------- styles/src/themes/atelier-seaside-dark.ts | 41 ++++++++++------- styles/src/themes/atelier-seaside-light.ts | 43 +++++++++++------- styles/src/themes/atelier-sulphurpool-dark.ts | 41 ++++++++++------- .../src/themes/atelier-sulphurpool-light.ts | 45 ++++++++++++------- styles/src/themes/common/atelier-common.ts | 2 +- styles/src/themes/gruvbox-common.ts | 3 +- styles/src/themes/gruvbox-dark-hard.ts | 6 +-- styles/src/themes/gruvbox-dark-soft.ts | 7 ++- styles/src/themes/gruvbox-dark.ts | 7 ++- styles/src/themes/gruvbox-light-hard.ts | 6 +-- styles/src/themes/gruvbox-light-soft.ts | 6 +-- styles/src/themes/gruvbox-light.ts | 6 +-- styles/src/themes/one-dark.ts | 2 +- styles/src/themes/one-light.ts | 2 +- styles/src/themes/rose-pine-dawn.ts | 2 +- styles/src/themes/rose-pine-moon.ts | 2 +- styles/src/themes/rose-pine.ts | 2 +- styles/src/themes/sandcastle.ts | 2 +- styles/src/themes/solarized.ts | 2 +- styles/src/themes/summercamp.ts | 2 +- 39 files changed, 578 insertions(+), 339 deletions(-) diff --git a/styles/src/buildLicenses.ts b/styles/src/buildLicenses.ts index 793a14a3b2..3367e50ff0 100644 --- a/styles/src/buildLicenses.ts +++ b/styles/src/buildLicenses.ts @@ -39,7 +39,7 @@ function getLicenseText( if (typeof meta.license.license_text == "string") { callback(meta, meta.license.license_text) } else { - let license_text_obj: Verification = meta.license.license_text; + let license_text_obj: Verification = meta.license.license_text // The following copied from the example code on nodejs.org: // https://nodejs.org/api/http.html#httpgetoptions-callback https diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 799363a349..64a19d714f 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -90,7 +90,7 @@ export default function editor(colorScheme: ColorScheme) { clicked: { color: colorScheme.ramps.neutral(0.5).alpha(0.7).hex(), }, - } + }, }, foldBackground: foreground(layer, "variant"), }, diff --git a/styles/src/themes/andromeda.ts b/styles/src/themes/andromeda.ts index 369f1d7f2b..7eba7b1481 100644 --- a/styles/src/themes/andromeda.ts +++ b/styles/src/themes/andromeda.ts @@ -39,7 +39,7 @@ export const meta: Meta = { "https://raw.githubusercontent.com/EliverLara/Andromeda/master/LICENSE.md", license_checksum: "2f7886f1a05cefc2c26f5e49de1a39fa4466413c1ccb06fc80960e73f5ed4b89", - } + }, }, url: "https://github.com/EliverLara/Andromeda", } diff --git a/styles/src/themes/atelier-cave-dark.ts b/styles/src/themes/atelier-cave-dark.ts index 6cb8946b7a..a56e22cd92 100644 --- a/styles/src/themes/atelier-cave-dark.ts +++ b/styles/src/themes/atelier-cave-dark.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#398bc6", base0D: "#576ddb", base0E: "#955ae7", - base0F: "#bf40bf" - } + base0F: "#bf40bf", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,31 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, false, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + return createColorScheme( + meta.name, + false, + { + neutral: chroma.scale([ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, ]), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-cave-light.ts b/styles/src/themes/atelier-cave-light.ts index b05f730f83..3b180752cf 100644 --- a/styles/src/themes/atelier-cave-light.ts +++ b/styles/src/themes/atelier-cave-light.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#398bc6", base0D: "#576ddb", base0E: "#955ae7", - base0F: "#bf40bf" - } + base0F: "#bf40bf", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,33 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, true, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 - ].reverse()), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + return createColorScheme( + meta.name, + true, + { + neutral: chroma.scale( + [ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, + ].reverse() + ), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-dune-dark.ts b/styles/src/themes/atelier-dune-dark.ts index b6f90b4c44..0ab402a99d 100644 --- a/styles/src/themes/atelier-dune-dark.ts +++ b/styles/src/themes/atelier-dune-dark.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#1fad83", base0D: "#6684e1", base0E: "#b854d4", - base0F: "#d43552" - } + base0F: "#d43552", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,31 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, false, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + return createColorScheme( + meta.name, + false, + { + neutral: chroma.scale([ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, ]), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-dune-light.ts b/styles/src/themes/atelier-dune-light.ts index 0a95dc66fc..e6a09985d5 100644 --- a/styles/src/themes/atelier-dune-light.ts +++ b/styles/src/themes/atelier-dune-light.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#1fad83", base0D: "#6684e1", base0E: "#b854d4", - base0F: "#d43552" - } + base0F: "#d43552", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,33 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, true, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 - ].reverse()), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + return createColorScheme( + meta.name, + true, + { + neutral: chroma.scale( + [ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, + ].reverse() + ), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-estuary-dark.ts b/styles/src/themes/atelier-estuary-dark.ts index 94ca7aa63b..c4ec50c1e0 100644 --- a/styles/src/themes/atelier-estuary-dark.ts +++ b/styles/src/themes/atelier-estuary-dark.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#5b9d48", base0D: "#36a166", base0E: "#5f9182", - base0F: "#9d6c7c" - } + base0F: "#9d6c7c", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,31 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, false, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + return createColorScheme( + meta.name, + false, + { + neutral: chroma.scale([ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, ]), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-estuary-light.ts b/styles/src/themes/atelier-estuary-light.ts index 4dd4b87549..6fce0e4483 100644 --- a/styles/src/themes/atelier-estuary-light.ts +++ b/styles/src/themes/atelier-estuary-light.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#5b9d48", base0D: "#36a166", base0E: "#5f9182", - base0F: "#9d6c7c" - } + base0F: "#9d6c7c", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,33 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, true, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 - ].reverse()), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + return createColorScheme( + meta.name, + true, + { + neutral: chroma.scale( + [ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, + ].reverse() + ), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-forest-dark.ts b/styles/src/themes/atelier-forest-dark.ts index 2bfe285a16..7c47c55a83 100644 --- a/styles/src/themes/atelier-forest-dark.ts +++ b/styles/src/themes/atelier-forest-dark.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#3d97b8", base0D: "#407ee7", base0E: "#6666ea", - base0F: "#c33ff3" - } + base0F: "#c33ff3", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,31 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, false, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + return createColorScheme( + meta.name, + false, + { + neutral: chroma.scale([ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, ]), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-forest-light.ts b/styles/src/themes/atelier-forest-light.ts index ee9acd7ebb..8ce0616476 100644 --- a/styles/src/themes/atelier-forest-light.ts +++ b/styles/src/themes/atelier-forest-light.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#3d97b8", base0D: "#407ee7", base0E: "#6666ea", - base0F: "#c33ff3" - } + base0F: "#c33ff3", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,33 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, true, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 - ].reverse()), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + return createColorScheme( + meta.name, + true, + { + neutral: chroma.scale( + [ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, + ].reverse() + ), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-heath-dark.ts b/styles/src/themes/atelier-heath-dark.ts index 9ae14a8903..87458ab8f5 100644 --- a/styles/src/themes/atelier-heath-dark.ts +++ b/styles/src/themes/atelier-heath-dark.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#159393", base0D: "#516aec", base0E: "#7b59c0", - base0F: "#cc33cc" - } + base0F: "#cc33cc", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,31 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, false, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + return createColorScheme( + meta.name, + false, + { + neutral: chroma.scale([ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, ]), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-heath-light.ts b/styles/src/themes/atelier-heath-light.ts index 3afd850fdf..3db3437041 100644 --- a/styles/src/themes/atelier-heath-light.ts +++ b/styles/src/themes/atelier-heath-light.ts @@ -26,7 +26,7 @@ const variant: Variant = { base0D: "#516aec", base0E: "#7b59c0", base0F: "#cc33cc", - } + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,33 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, true, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 - ].reverse()), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + return createColorScheme( + meta.name, + true, + { + neutral: chroma.scale( + [ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, + ].reverse() + ), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-lakeside-dark.ts b/styles/src/themes/atelier-lakeside-dark.ts index 45f5733cf9..a8297ef9fd 100644 --- a/styles/src/themes/atelier-lakeside-dark.ts +++ b/styles/src/themes/atelier-lakeside-dark.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#2d8f6f", base0D: "#257fad", base0E: "#6b6bb8", - base0F: "#b72dd2" - } + base0F: "#b72dd2", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,31 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, false, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + return createColorScheme( + meta.name, + false, + { + neutral: chroma.scale([ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, ]), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-lakeside-light.ts b/styles/src/themes/atelier-lakeside-light.ts index 255a4dea96..408fabcaf3 100644 --- a/styles/src/themes/atelier-lakeside-light.ts +++ b/styles/src/themes/atelier-lakeside-light.ts @@ -26,7 +26,7 @@ const variant: Variant = { base0D: "#257fad", base0E: "#6b6bb8", base0F: "#b72dd2", - } + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,33 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, true, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 - ].reverse()), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + return createColorScheme( + meta.name, + true, + { + neutral: chroma.scale( + [ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, + ].reverse() + ), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-plateau-dark.ts b/styles/src/themes/atelier-plateau-dark.ts index 22510730e7..731cb824e4 100644 --- a/styles/src/themes/atelier-plateau-dark.ts +++ b/styles/src/themes/atelier-plateau-dark.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#5485b6", base0D: "#7272ca", base0E: "#8464c4", - base0F: "#bd5187" - } + base0F: "#bd5187", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,31 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, false, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + return createColorScheme( + meta.name, + false, + { + neutral: chroma.scale([ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, ]), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-plateau-light.ts b/styles/src/themes/atelier-plateau-light.ts index 5f4d2400c1..96f295a695 100644 --- a/styles/src/themes/atelier-plateau-light.ts +++ b/styles/src/themes/atelier-plateau-light.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#5485b6", base0D: "#7272ca", base0E: "#8464c4", - base0F: "#bd5187" - } + base0F: "#bd5187", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,33 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, true, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 - ].reverse()), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + return createColorScheme( + meta.name, + true, + { + neutral: chroma.scale( + [ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, + ].reverse() + ), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-savanna-dark.ts b/styles/src/themes/atelier-savanna-dark.ts index c2fa1efa6d..dfcb4f27cb 100644 --- a/styles/src/themes/atelier-savanna-dark.ts +++ b/styles/src/themes/atelier-savanna-dark.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#1c9aa0", base0D: "#478c90", base0E: "#55859b", - base0F: "#867469" - } + base0F: "#867469", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,31 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, false, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + return createColorScheme( + meta.name, + false, + { + neutral: chroma.scale([ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, ]), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-savanna-light.ts b/styles/src/themes/atelier-savanna-light.ts index 2e1ec9857a..4bc1389fc9 100644 --- a/styles/src/themes/atelier-savanna-light.ts +++ b/styles/src/themes/atelier-savanna-light.ts @@ -26,7 +26,7 @@ const variant: Variant = { base0D: "#478c90", base0E: "#55859b", base0F: "#867469", - } + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,33 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, true, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 - ].reverse()), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + return createColorScheme( + meta.name, + true, + { + neutral: chroma.scale( + [ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, + ].reverse() + ), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-seaside-dark.ts b/styles/src/themes/atelier-seaside-dark.ts index 22136a6f13..1326a27786 100644 --- a/styles/src/themes/atelier-seaside-dark.ts +++ b/styles/src/themes/atelier-seaside-dark.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#1999b3", base0D: "#3d62f5", base0E: "#ad2bee", - base0F: "#e619c3" - } + base0F: "#e619c3", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,31 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, false, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + return createColorScheme( + meta.name, + false, + { + neutral: chroma.scale([ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, ]), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-seaside-light.ts b/styles/src/themes/atelier-seaside-light.ts index 23db80221f..6f6823718a 100644 --- a/styles/src/themes/atelier-seaside-light.ts +++ b/styles/src/themes/atelier-seaside-light.ts @@ -26,7 +26,7 @@ const variant: Variant = { base0D: "#3d62f5", base0E: "#ad2bee", base0F: "#e619c3", - } + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,33 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, true, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 - ].reverse()), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + return createColorScheme( + meta.name, + true, + { + neutral: chroma.scale( + [ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, + ].reverse() + ), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-sulphurpool-dark.ts b/styles/src/themes/atelier-sulphurpool-dark.ts index 2d282deaa2..dcfc0d932a 100644 --- a/styles/src/themes/atelier-sulphurpool-dark.ts +++ b/styles/src/themes/atelier-sulphurpool-dark.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#22a2c9", base0D: "#3d8fd1", base0E: "#6679cc", - base0F: "#9c637a" - } + base0F: "#9c637a", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,31 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, false, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 + return createColorScheme( + meta.name, + false, + { + neutral: chroma.scale([ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, ]), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/atelier-sulphurpool-light.ts b/styles/src/themes/atelier-sulphurpool-light.ts index 943ceedf2b..b2b5f7c328 100644 --- a/styles/src/themes/atelier-sulphurpool-light.ts +++ b/styles/src/themes/atelier-sulphurpool-light.ts @@ -25,8 +25,8 @@ const variant: Variant = { base0C: "#22a2c9", base0D: "#3d8fd1", base0E: "#6679cc", - base0F: "#9c637a" - } + base0F: "#9c637a", + }, } const syntax = buildSyntax(variant) @@ -34,20 +34,33 @@ const syntax = buildSyntax(variant) const theme = (variant: Variant) => { const { meta, colors } = variant - return createColorScheme(meta.name, true, { - neutral: chroma - .scale([ - colors.base00, colors.base01, colors.base02, colors.base03, colors.base04, colors.base05, colors.base06, colors.base07 - ].reverse()), - red: colorRamp(chroma(colors.base08)), - orange: colorRamp(chroma(colors.base09)), - yellow: colorRamp(chroma(colors.base0A)), - green: colorRamp(chroma(colors.base0B)), - cyan: colorRamp(chroma(colors.base0C)), - blue: colorRamp(chroma(colors.base0D)), - violet: colorRamp(chroma(colors.base0E)), - magenta: colorRamp(chroma(colors.base0F)), - }, syntax) + return createColorScheme( + meta.name, + true, + { + neutral: chroma.scale( + [ + colors.base00, + colors.base01, + colors.base02, + colors.base03, + colors.base04, + colors.base05, + colors.base06, + colors.base07, + ].reverse() + ), + red: colorRamp(chroma(colors.base08)), + orange: colorRamp(chroma(colors.base09)), + yellow: colorRamp(chroma(colors.base0A)), + green: colorRamp(chroma(colors.base0B)), + cyan: colorRamp(chroma(colors.base0C)), + blue: colorRamp(chroma(colors.base0D)), + violet: colorRamp(chroma(colors.base0E)), + magenta: colorRamp(chroma(colors.base0F)), + }, + syntax + ) } export const dark = theme(variant) diff --git a/styles/src/themes/common/atelier-common.ts b/styles/src/themes/common/atelier-common.ts index 25dda87c61..08a915d019 100644 --- a/styles/src/themes/common/atelier-common.ts +++ b/styles/src/themes/common/atelier-common.ts @@ -33,7 +33,7 @@ export const metaCommon: { https_url: "https://atelierbram.mit-license.org/license.txt", license_checksum: "f95ce526ef4e7eecf7a832bba0e3451cc1000f9ce63eb01ed6f64f8109f5d0a5", - } + }, }, } diff --git a/styles/src/themes/gruvbox-common.ts b/styles/src/themes/gruvbox-common.ts index c3e63a31cf..c42362c11c 100644 --- a/styles/src/themes/gruvbox-common.ts +++ b/styles/src/themes/gruvbox-common.ts @@ -248,7 +248,8 @@ export const meta: Meta = { name, license: { SPDX: "MIT", // "MIT/X11" - license_text: "Copyright \n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/ or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + license_text: + "Copyright \n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/ or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.", }, author: "morhetz ", url: "https://github.com/morhetz/gruvbox", diff --git a/styles/src/themes/gruvbox-dark-hard.ts b/styles/src/themes/gruvbox-dark-hard.ts index a6e20e1020..3723de4901 100644 --- a/styles/src/themes/gruvbox-dark-hard.ts +++ b/styles/src/themes/gruvbox-dark-hard.ts @@ -1,6 +1,6 @@ -import { darkHard as dark, meta as commonMeta } from "./gruvbox-common"; +import { darkHard as dark, meta as commonMeta } from "./gruvbox-common" -let meta = { ...commonMeta }; +let meta = { ...commonMeta } meta.name = `${commonMeta.name} Dark Hard` -export { dark, meta } \ No newline at end of file +export { dark, meta } diff --git a/styles/src/themes/gruvbox-dark-soft.ts b/styles/src/themes/gruvbox-dark-soft.ts index 7eaefaa99a..2887572ead 100644 --- a/styles/src/themes/gruvbox-dark-soft.ts +++ b/styles/src/themes/gruvbox-dark-soft.ts @@ -1,7 +1,6 @@ -import { darkSoft as dark, meta as commonMeta } from "./gruvbox-common"; +import { darkSoft as dark, meta as commonMeta } from "./gruvbox-common" - -let meta = { ...commonMeta }; +let meta = { ...commonMeta } meta.name = `${commonMeta.name} Dark Soft` -export { dark, meta } \ No newline at end of file +export { dark, meta } diff --git a/styles/src/themes/gruvbox-dark.ts b/styles/src/themes/gruvbox-dark.ts index ea21933714..cff7bd8bf9 100644 --- a/styles/src/themes/gruvbox-dark.ts +++ b/styles/src/themes/gruvbox-dark.ts @@ -1,7 +1,6 @@ -import { darkDefault as dark, meta as commonMeta } from "./gruvbox-common"; +import { darkDefault as dark, meta as commonMeta } from "./gruvbox-common" - -let meta = { ...commonMeta }; +let meta = { ...commonMeta } meta.name = `${commonMeta.name} Dark` -export { dark, meta } \ No newline at end of file +export { dark, meta } diff --git a/styles/src/themes/gruvbox-light-hard.ts b/styles/src/themes/gruvbox-light-hard.ts index b0f42ca4b7..cf998ce588 100644 --- a/styles/src/themes/gruvbox-light-hard.ts +++ b/styles/src/themes/gruvbox-light-hard.ts @@ -1,6 +1,6 @@ -import { lightHard as light, meta as commonMeta } from "./gruvbox-common"; +import { lightHard as light, meta as commonMeta } from "./gruvbox-common" -let meta = { ...commonMeta }; +let meta = { ...commonMeta } meta.name = `${commonMeta.name} Dark Soft` -export { light, meta } \ No newline at end of file +export { light, meta } diff --git a/styles/src/themes/gruvbox-light-soft.ts b/styles/src/themes/gruvbox-light-soft.ts index 6743d29232..90ec82e965 100644 --- a/styles/src/themes/gruvbox-light-soft.ts +++ b/styles/src/themes/gruvbox-light-soft.ts @@ -1,6 +1,6 @@ -import { lightSoft as light, meta as commonMeta } from "./gruvbox-common"; +import { lightSoft as light, meta as commonMeta } from "./gruvbox-common" -let meta = { ...commonMeta }; +let meta = { ...commonMeta } meta.name = `${commonMeta.name} Light Soft` -export { light, meta } \ No newline at end of file +export { light, meta } diff --git a/styles/src/themes/gruvbox-light.ts b/styles/src/themes/gruvbox-light.ts index f64778d856..e8f355cd11 100644 --- a/styles/src/themes/gruvbox-light.ts +++ b/styles/src/themes/gruvbox-light.ts @@ -1,6 +1,6 @@ -import { lightDefault as light, meta as commonMeta } from "./gruvbox-common"; +import { lightDefault as light, meta as commonMeta } from "./gruvbox-common" -let meta = { ...commonMeta }; +let meta = { ...commonMeta } meta.name = `${commonMeta.name} Light` -export { light, meta } \ No newline at end of file +export { light, meta } diff --git a/styles/src/themes/one-dark.ts b/styles/src/themes/one-dark.ts index f99b4114f3..85417a0e68 100644 --- a/styles/src/themes/one-dark.ts +++ b/styles/src/themes/one-dark.ts @@ -79,7 +79,7 @@ export const meta: Meta = { "https://raw.githubusercontent.com/atom/atom/master/packages/one-light-ui/LICENSE.md", license_checksum: "d5af8fc171f6f600c0ab4e7597dca398dda80dbe6821ce01cef78e859e7a00f8", - } + }, }, url: "https://github.com/atom/atom/tree/master/packages/one-dark-ui", } diff --git a/styles/src/themes/one-light.ts b/styles/src/themes/one-light.ts index 534e4c1648..7bf21aee17 100644 --- a/styles/src/themes/one-light.ts +++ b/styles/src/themes/one-light.ts @@ -78,7 +78,7 @@ export const meta: Meta = { "https://raw.githubusercontent.com/atom/atom/master/packages/one-light-ui/LICENSE.md", license_checksum: "d5af8fc171f6f600c0ab4e7597dca398dda80dbe6821ce01cef78e859e7a00f8", - } + }, }, url: "https://github.com/atom/atom/tree/master/packages/one-light-ui", } diff --git a/styles/src/themes/rose-pine-dawn.ts b/styles/src/themes/rose-pine-dawn.ts index 7f7d52079d..427b05f72b 100644 --- a/styles/src/themes/rose-pine-dawn.ts +++ b/styles/src/themes/rose-pine-dawn.ts @@ -39,7 +39,7 @@ export const meta: Meta = { "https://raw.githubusercontent.com/edunfelt/base16-rose-pine-scheme/main/LICENSE", license_checksum: "6ca1b9da8c78c8441c5aa43d024a4e4a7bf59d1ecca1480196e94fda0f91ee4a", - } + }, }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } diff --git a/styles/src/themes/rose-pine-moon.ts b/styles/src/themes/rose-pine-moon.ts index a89d44487e..be2f5a8daf 100644 --- a/styles/src/themes/rose-pine-moon.ts +++ b/styles/src/themes/rose-pine-moon.ts @@ -39,7 +39,7 @@ export const meta: Meta = { "https://raw.githubusercontent.com/edunfelt/base16-rose-pine-scheme/main/LICENSE", license_checksum: "6ca1b9da8c78c8441c5aa43d024a4e4a7bf59d1ecca1480196e94fda0f91ee4a", - } + }, }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } diff --git a/styles/src/themes/rose-pine.ts b/styles/src/themes/rose-pine.ts index 87c9d34ffe..944550f125 100644 --- a/styles/src/themes/rose-pine.ts +++ b/styles/src/themes/rose-pine.ts @@ -37,7 +37,7 @@ export const meta: Meta = { "https://raw.githubusercontent.com/edunfelt/base16-rose-pine-scheme/main/LICENSE", license_checksum: "6ca1b9da8c78c8441c5aa43d024a4e4a7bf59d1ecca1480196e94fda0f91ee4a", - } + }, }, url: "https://github.com/edunfelt/base16-rose-pine-scheme", } diff --git a/styles/src/themes/sandcastle.ts b/styles/src/themes/sandcastle.ts index 86179db3db..483f01b27a 100644 --- a/styles/src/themes/sandcastle.ts +++ b/styles/src/themes/sandcastle.ts @@ -37,7 +37,7 @@ export const meta: Meta = { "https://raw.githubusercontent.com/gessig/base16-sandcastle-scheme/master/LICENSE", license_checksum: "8399d44b4d935b60be9fee0a76d7cc9a817b4f3f11574c9d6d1e8fd57e72ffdc", - } + }, }, url: "https://github.com/gessig/base16-sandcastle-scheme", } diff --git a/styles/src/themes/solarized.ts b/styles/src/themes/solarized.ts index 42306f68c3..1210c43806 100644 --- a/styles/src/themes/solarized.ts +++ b/styles/src/themes/solarized.ts @@ -40,7 +40,7 @@ export const meta: Metadata = { "https://raw.githubusercontent.com/altercation/solarized/master/LICENSE", license_checksum: "494aefdabf86acce06bd63001ad8aedad4ee38da23509d3f917d95aa3368b9a6", - } + }, }, url: "https://github.com/altercation/solarized", } diff --git a/styles/src/themes/summercamp.ts b/styles/src/themes/summercamp.ts index e8d1bd40b2..7df125e866 100644 --- a/styles/src/themes/summercamp.ts +++ b/styles/src/themes/summercamp.ts @@ -39,6 +39,6 @@ export const meta: Meta = { "https://raw.githubusercontent.com/zoefiri/base16-sc/master/LICENSE", license_checksum: "fadcc834b7eaf2943800956600e8aeea4b495ecf6490f4c4b6c91556a90accaf", - } + }, }, } From 3952e98320799df81131a9772d2324935ee69825 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 9 Mar 2023 10:50:11 -0500 Subject: [PATCH 59/93] Remove Zed Pro theme --- styles/src/themes/staff/zed-pro.ts | 36 ------------------------------ 1 file changed, 36 deletions(-) delete mode 100644 styles/src/themes/staff/zed-pro.ts diff --git a/styles/src/themes/staff/zed-pro.ts b/styles/src/themes/staff/zed-pro.ts deleted file mode 100644 index 9b748be299..0000000000 --- a/styles/src/themes/staff/zed-pro.ts +++ /dev/null @@ -1,36 +0,0 @@ -import chroma from "chroma-js" -import { colorRamp, createColorScheme } from "../common/ramps" - -const name = "Zed Pro" -const author = "Nate Butler" -const url = "https://github.com/iamnbutler" -const license = { - type: "?", - url: "?", -} - -const ramps = { - neutral: chroma - .scale([ - "#101010", - "#1C1C1C", - "#212121", - "#2D2D2D", - "#B9B9B9", - "#DADADA", - "#E6E6E6", - "#FFFFFF", - ]) - .domain([0, 0.1, 0.2, 0.3, 0.7, 0.8, 0.9, 1]), - red: colorRamp(chroma("#DC604F")), - orange: colorRamp(chroma("#DE782F")), - yellow: colorRamp(chroma("#E0B750")), - green: colorRamp(chroma("#2A643D")), - cyan: colorRamp(chroma("#215050")), - blue: colorRamp(chroma("#2F6DB7")), - violet: colorRamp(chroma("#5874C1")), - magenta: colorRamp(chroma("#DE9AB8")), -} - -export const dark = createColorScheme(`${name} Dark`, false, ramps) -export const light = createColorScheme(`${name} Light`, true, ramps) From baff428de51407a842cd2e46400303af07aad088 Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 9 Mar 2023 10:51:28 -0500 Subject: [PATCH 60/93] Re-add Ayu --- styles/src/themes/staff/ayu-mirage.ts | 31 ++++++++++++++++ styles/src/themes/staff/ayu.ts | 52 +++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 styles/src/themes/staff/ayu-mirage.ts create mode 100644 styles/src/themes/staff/ayu.ts diff --git a/styles/src/themes/staff/ayu-mirage.ts b/styles/src/themes/staff/ayu-mirage.ts new file mode 100644 index 0000000000..5b832699b4 --- /dev/null +++ b/styles/src/themes/staff/ayu-mirage.ts @@ -0,0 +1,31 @@ +import chroma from "chroma-js" +import { colorRamp, createColorScheme } from "../common/ramps" + +const name = "Ayu" +const author = "Konstantin Pschera " +const url = "https://github.com/ayu-theme/ayu-colors" +const license = { + type: "MIT", + url: "https://github.com/ayu-theme/ayu-colors/blob/master/license", +} + +export const dark = createColorScheme(`${name} Mirage`, false, { + neutral: chroma.scale([ + "#171B24", + "#1F2430", + "#242936", + "#707A8C", + "#8A9199", + "#CCCAC2", + "#D9D7CE", + "#F3F4F5", + ]), + red: colorRamp(chroma("#F28779")), + orange: colorRamp(chroma("#FFAD66")), + yellow: colorRamp(chroma("#FFD173")), + green: colorRamp(chroma("#D5FF80")), + cyan: colorRamp(chroma("#95E6CB")), + blue: colorRamp(chroma("#5CCFE6")), + violet: colorRamp(chroma("#D4BFFF")), + magenta: colorRamp(chroma("#F29E74")), +}) diff --git a/styles/src/themes/staff/ayu.ts b/styles/src/themes/staff/ayu.ts new file mode 100644 index 0000000000..24fcdb951b --- /dev/null +++ b/styles/src/themes/staff/ayu.ts @@ -0,0 +1,52 @@ +import chroma from "chroma-js" +import { colorRamp, createColorScheme } from "../common/ramps" + +const name = "Ayu" +const author = "Konstantin Pschera " +const url = "https://github.com/ayu-theme/ayu-colors" +const license = { + type: "MIT", + url: "https://github.com/ayu-theme/ayu-colors/blob/master/license", +} + +export const dark = createColorScheme(`${name} Dark`, false, { + neutral: chroma.scale([ + "#0F1419", + "#131721", + "#272D38", + "#3E4B59", + "#BFBDB6", + "#E6E1CF", + "#E6E1CF", + "#F3F4F5", + ]), + red: colorRamp(chroma("#F07178")), + orange: colorRamp(chroma("#FF8F40")), + yellow: colorRamp(chroma("#FFB454")), + green: colorRamp(chroma("#B8CC52")), + cyan: colorRamp(chroma("#95E6CB")), + blue: colorRamp(chroma("#59C2FF")), + violet: colorRamp(chroma("#D2A6FF")), + magenta: colorRamp(chroma("#E6B673")), +}) + +export const light = createColorScheme(`${name} Light`, true, { + neutral: chroma.scale([ + "#1A1F29", + "#242936", + "#5C6773", + "#828C99", + "#ABB0B6", + "#F8F9FA", + "#F3F4F5", + "#FAFAFA", + ]), + red: colorRamp(chroma("#F07178")), + orange: colorRamp(chroma("#FA8D3E")), + yellow: colorRamp(chroma("#F2AE49")), + green: colorRamp(chroma("#86B300")), + cyan: colorRamp(chroma("#4CBF99")), + blue: colorRamp(chroma("#36A3D9")), + violet: colorRamp(chroma("#A37ACC")), + magenta: colorRamp(chroma("#E6BA7E")), +}) From 4eb75f058f44b3d0cc8fd569cacd761d259ef82e Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 9 Mar 2023 12:00:58 -0800 Subject: [PATCH 61/93] Fix bug with wrong view ids being passed --- crates/workspace/src/dock.rs | 3 ++- crates/workspace/src/workspace.rs | 22 +++++++++++++++++----- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index fd7638c5fb..f5ee8cad51 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -181,6 +181,7 @@ pub struct Dock { impl Dock { pub fn new( + workspace_id: usize, default_item_factory: DockDefaultItemFactory, background_actions: BackgroundActions, cx: &mut ViewContext, @@ -189,7 +190,7 @@ impl Dock { let pane = cx.add_view(|cx| { Pane::new( - cx.handle().id(), + workspace_id, Some(position.anchor()), background_actions, cx, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index ef14bfa2fe..b4b164ec3f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -606,9 +606,10 @@ impl Workspace { }) .detach(); - let workspace_view_id = cx.handle().id(); + let weak_handle = cx.weak_handle(); + let center_pane = - cx.add_view(|cx| Pane::new(workspace_view_id, None, background_actions, cx)); + cx.add_view(|cx| Pane::new(weak_handle.id(), None, background_actions, cx)); let pane_id = center_pane.id(); cx.subscribe(¢er_pane, move |this, _, event, cx| { this.handle_pane_event(pane_id, event, cx) @@ -616,7 +617,12 @@ impl Workspace { .detach(); cx.focus(¢er_pane); cx.emit(Event::PaneAdded(center_pane.clone())); - let dock = Dock::new(dock_default_factory, background_actions, cx); + let dock = Dock::new( + weak_handle.id(), + dock_default_factory, + background_actions, + cx, + ); let dock_pane = dock.pane().clone(); let fs = project.read(cx).fs().clone(); @@ -639,7 +645,6 @@ impl Workspace { } }); let handle = cx.handle(); - let weak_handle = cx.weak_handle(); // All leader updates are enqueued and then processed in a single task, so // that each asynchronous operation can be run in order. @@ -1440,7 +1445,14 @@ impl Workspace { } fn add_pane(&mut self, cx: &mut ViewContext) -> ViewHandle { - let pane = cx.add_view(|cx| Pane::new(cx.handle().id(), None, self.background_actions, cx)); + let pane = cx.add_view(|cx| { + Pane::new( + dbg!(self.weak_handle().id()), + None, + self.background_actions, + cx, + ) + }); let pane_id = pane.id(); cx.subscribe(&pane, move |this, _, event, cx| { this.handle_pane_event(pane_id, event, cx) From 718052bb726e9d4deb6d409e6758ddfd4c0f82e9 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 9 Mar 2023 13:02:25 -0800 Subject: [PATCH 62/93] Undo accidental indent change of hoverpopover.ts --- styles/src/styleTree/hoverPopover.ts | 78 ++++++++++++++-------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/styles/src/styleTree/hoverPopover.ts b/styles/src/styleTree/hoverPopover.ts index a2a4467d7c..032c53112b 100644 --- a/styles/src/styleTree/hoverPopover.ts +++ b/styles/src/styleTree/hoverPopover.ts @@ -2,44 +2,44 @@ import { ColorScheme } from "../themes/common/colorScheme" import { background, border, text } from "./components" export default function HoverPopover(colorScheme: ColorScheme) { - let layer = colorScheme.middle - let baseContainer = { - background: background(layer), - cornerRadius: 8, - padding: { - left: 8, - right: 8, - top: 4, - bottom: 4, - }, - shadow: colorScheme.popoverShadow, - border: border(layer), - margin: { - left: -8, - }, - } + let layer = colorScheme.middle + let baseContainer = { + background: background(layer), + cornerRadius: 8, + padding: { + left: 8, + right: 8, + top: 4, + bottom: 4, + }, + shadow: colorScheme.popoverShadow, + border: border(layer), + margin: { + left: -8, + }, + } - return { - container: baseContainer, - infoContainer: { - ...baseContainer, - background: background(layer, "accent"), - border: border(layer, "accent"), - }, - warningContainer: { - ...baseContainer, - background: background(layer, "warning"), - border: border(layer, "warning"), - }, - errorContainer: { - ...baseContainer, - background: background(layer, "negative"), - border: border(layer, "negative"), - }, - block_style: { - padding: { top: 4 }, - }, - prose: text(layer, "sans", { size: "sm" }), - highlight: colorScheme.ramps.neutral(0.5).alpha(0.2).hex(), // TODO: blend was used here. Replace with something better - } + return { + container: baseContainer, + infoContainer: { + ...baseContainer, + background: background(layer, "accent"), + border: border(layer, "accent"), + }, + warningContainer: { + ...baseContainer, + background: background(layer, "warning"), + border: border(layer, "warning"), + }, + errorContainer: { + ...baseContainer, + background: background(layer, "negative"), + border: border(layer, "negative"), + }, + block_style: { + padding: { top: 4 }, + }, + prose: text(layer, "sans", { size: "sm" }), + highlight: colorScheme.ramps.neutral(0.5).alpha(0.2).hex(), // TODO: blend was used here. Replace with something better + } } From daed75096e98a4abdb95828e684d3e2089e050d3 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 9 Mar 2023 13:13:23 -0800 Subject: [PATCH 63/93] Fix editor test to clearly show trailing whitespace Adjsut default dock size to be a multiple of 16 --- crates/editor/src/editor_tests.rs | 16 ++++++++++------ styles/src/styleTree/workspace.ts | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index d73c3c87db..4f61bde398 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -2353,12 +2353,16 @@ async fn test_clipboard(cx: &mut gpui::TestAppContext) { e.paste(&Paste, cx); e.handle_input(") ", cx); }); - cx.assert_editor_state(indoc! {" - ( one✅ - three - five ) ˇtwo one✅ four three six five ( one✅ - three - five ) ˇ"}); + cx.assert_editor_state( + &([ + "( one✅ ", + "three ", + "five ) ˇtwo one✅ four three six five ( one✅ ", + "three ", + "five ) ˇ", + ] + .join("\n")), + ); // Cut with three selections, one of which is full-line. cx.set_state(indoc! {" diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index d798a22e9d..1789b523e7 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -282,7 +282,7 @@ export default function workspace(colorScheme: ColorScheme) { }, dock: { initialSizeRight: 640, - initialSizeBottom: 300, + initialSizeBottom: 304, wash_color: withOpacity(background(colorScheme.highest), 0.5), panel: { border: border(colorScheme.middle), From 20064b5629d347621447c343fb53abc074663926 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 9 Mar 2023 15:38:28 -0800 Subject: [PATCH 64/93] Add welcome to menu remove debug --- crates/workspace/src/workspace.rs | 10 ++-------- crates/zed/src/menus.rs | 1 + 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index b4b164ec3f..609afded33 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1445,14 +1445,8 @@ impl Workspace { } fn add_pane(&mut self, cx: &mut ViewContext) -> ViewHandle { - let pane = cx.add_view(|cx| { - Pane::new( - dbg!(self.weak_handle().id()), - None, - self.background_actions, - cx, - ) - }); + let pane = + cx.add_view(|cx| Pane::new(self.weak_handle().id(), None, self.background_actions, cx)); let pane_id = pane.id(); cx.subscribe(&pane, move |this, _, event, cx| { this.handle_pane_event(pane_id, event, cx) diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index d5fb94b2b6..82422c5c19 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -139,6 +139,7 @@ pub fn menus() -> Vec> { MenuItem::separator(), MenuItem::action("View Telemetry", crate::OpenTelemetryLog), MenuItem::action("View Dependency Licenses", crate::OpenLicenses), + MenuItem::action("Show Welcome", workspace::Welcome), MenuItem::separator(), MenuItem::action( "Copy System Specs Into Clipboard", From 0384456e7d05526dc9b81124ae15a62ddaa6c9bd Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 9 Mar 2023 16:18:37 -0800 Subject: [PATCH 65/93] Change context matcher to search the entire stack --- crates/gpui/src/keymap_matcher/keymap_context.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/gpui/src/keymap_matcher/keymap_context.rs b/crates/gpui/src/keymap_matcher/keymap_context.rs index bbf6bfc14b..fbaaa5fbc5 100644 --- a/crates/gpui/src/keymap_matcher/keymap_context.rs +++ b/crates/gpui/src/keymap_matcher/keymap_context.rs @@ -64,7 +64,13 @@ impl KeymapContextPredicate { pub fn eval(&self, contexts: &[KeymapContext]) -> bool { let Some(context) = contexts.first() else { return false }; match self { - Self::Identifier(name) => (&context.set).contains(name.as_str()), + Self::Identifier(name) => { + if (&context.set).contains(name.as_str()) { + true + } else { + self.eval(&contexts[1..]) + } + } Self::Equal(left, right) => context .map .get(left.as_str()) From 8ee25be7b93bab8a2a584eee89401bf6e341cf4f Mon Sep 17 00:00:00 2001 From: Nate Butler Date: Thu, 9 Mar 2023 20:18:29 -0500 Subject: [PATCH 66/93] Update empty pane styling Co-Authored-By: Mikayla Maki --- assets/icons/logo_256.svg | 41 ++++++++++++++++++++++++++++ assets/icons/logo_shadow_256.svg | 23 ++++++++++++++++ crates/theme/src/theme.rs | 2 ++ crates/workspace/src/pane.rs | 17 +++++++----- crates/zed/src/main.rs | 2 +- styles/src/styleTree/workspace.ts | 44 ++++++++++++++++++------------- 6 files changed, 104 insertions(+), 25 deletions(-) create mode 100644 assets/icons/logo_256.svg create mode 100644 assets/icons/logo_shadow_256.svg diff --git a/assets/icons/logo_256.svg b/assets/icons/logo_256.svg new file mode 100644 index 0000000000..4629600b41 --- /dev/null +++ b/assets/icons/logo_256.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/icons/logo_shadow_256.svg b/assets/icons/logo_shadow_256.svg new file mode 100644 index 0000000000..6c331f43ab --- /dev/null +++ b/assets/icons/logo_shadow_256.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index d331ca80a9..8ee58a8af2 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -76,6 +76,8 @@ pub struct Workspace { #[derive(Clone, Deserialize, Default)] pub struct BlankPaneStyle { pub logo: IconStyle, + pub logo_shadow: IconStyle, + pub logo_container: ContainerStyle, pub keyboard_hints: ContainerStyle, pub keyboard_hint: Interactive, pub keyboard_hint_width: f32, diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index abd410d875..6d20700bcc 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1429,7 +1429,6 @@ impl Pane { fn render_blank_pane(&mut self, theme: &Theme, cx: &mut RenderContext) -> ElementBox { let background = theme.workspace.background; - let keystroke_style = &theme.context_menu.item; let theme = &theme.workspace.blank_pane; Stack::new() .with_children([ @@ -1440,7 +1439,14 @@ impl Pane { Flex::column() .align_children_center() .with_children([ - theme::ui::icon(&theme.logo).aligned().boxed(), + Stack::new() + .with_children([ + theme::ui::icon(&theme.logo_shadow).aligned().boxed(), + theme::ui::icon(&theme.logo).aligned().boxed(), + ]) + .contained() + .with_style(theme.logo_container) + .boxed(), Flex::column() .with_children({ enum KeyboardHint {} @@ -1453,14 +1459,13 @@ impl Pane { idx, cx, move |state, cx| { + let style = keyboard_hint.style_for(state, false); theme::ui::keystroke_label_for( cx.window_id(), workspace_id, text, - &keyboard_hint.style_for(state, false), - &keystroke_style - .style_for(state, false) - .keystroke, + &style, + &style, hint_action, ) .boxed() diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 2e2026309f..cbcd1aa1c6 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -709,7 +709,7 @@ pub fn dock_default_item_factory( pub fn background_actions() -> &'static [(&'static str, &'static dyn Action)] { &[ ("Go to file", &file_finder::Toggle), - ("Open the command palette", &command_palette::Toggle), + ("Open command palette", &command_palette::Toggle), ("Focus the dock", &FocusDock), ("Open recent projects", &recent_projects::OpenRecent), ("Change your settings", &OpenSettings), diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 1789b523e7..984b6b5ed3 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -42,38 +42,46 @@ export default function workspace(colorScheme: ColorScheme) { return { background: background(colorScheme.lowest), blankPane: { + logoContainer: { + width: 256, + height: 256, + }, logo: { - color: border(layer, "active").color, - icon: "icons/logo_96.svg", + color: withOpacity("#000000", colorScheme.isLight ? 0.6 : 0.8), + icon: "icons/logo_256.svg", dimensions: { - width: 272, - height: 272, - } + width: 256, + height: 256, + }, + }, + logoShadow: { + color: withOpacity(colorScheme.isLight ? "#FFFFFF" : colorScheme.lowest.base.default.background, colorScheme.isLight ? 1 : 0.6), + icon: "icons/logo_shadow_256.svg", + dimensions: { + width: 256, + height: 256, + }, }, keyboardHints: { margin: { - top: 32, - // bottom: -8. + top: 96, }, - padding: { - top: 8, - left: 8, - right: 8, - }, - background: background(colorScheme.lowest), - border: border(layer, "active"), cornerRadius: 4, }, keyboardHint: { ...text(layer, "sans", "variant", { size: "sm" }), - margin: { - bottom: 8 + padding: { + top: 3, + left: 8, + right: 8, + bottom: 3 }, + cornerRadius: 8, hover: { - ...text(layer, "sans", "hovered", { size: "sm" }), + ...text(layer, "sans", "active", { size: "sm" }), } }, - keyboardHintWidth: 272, + keyboardHintWidth: 320, }, joiningProjectAvatar: { cornerRadius: 40, From 01e3173ed0ca142f1f6bf149fefd218342b0d36b Mon Sep 17 00:00:00 2001 From: Julia Date: Thu, 9 Mar 2023 22:05:32 -0500 Subject: [PATCH 67/93] Preserve contacts popover editor contents when switching to search mode --- crates/collab_ui/src/contact_finder.rs | 14 ++++++++++++-- crates/collab_ui/src/contact_list.rs | 10 ++++++++++ crates/collab_ui/src/contacts_popover.rs | 22 ++++++++++++++-------- crates/picker/src/picker.rs | 5 +++++ 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs index 98f70e83f0..ff783c6274 100644 --- a/crates/collab_ui/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -1,7 +1,7 @@ use client::{ContactRequestStatus, User, UserStore}; use gpui::{ - elements::*, AnyViewHandle, Entity, ModelHandle, MouseState, MutableAppContext, RenderContext, - Task, View, ViewContext, ViewHandle, + elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState, MutableAppContext, + RenderContext, Task, View, ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; use settings::Settings; @@ -178,4 +178,14 @@ impl ContactFinder { selected_index: 0, } } + + pub fn editor_text(&self, cx: &AppContext) -> String { + self.picker.read(cx).query(cx) + } + + pub fn with_editor_text(self, editor_text: String, cx: &mut ViewContext) -> Self { + self.picker + .update(cx, |picker, cx| picker.set_query(editor_text, cx)); + self + } } diff --git a/crates/collab_ui/src/contact_list.rs b/crates/collab_ui/src/contact_list.rs index 3bb036d336..dd6683292e 100644 --- a/crates/collab_ui/src/contact_list.rs +++ b/crates/collab_ui/src/contact_list.rs @@ -294,6 +294,16 @@ impl ContactList { this } + pub fn editor_text(&self, cx: &AppContext) -> String { + self.filter_editor.read(cx).text(cx) + } + + pub fn with_editor_text(self, editor_text: String, cx: &mut ViewContext) -> Self { + self.filter_editor + .update(cx, |picker, cx| picker.set_text(editor_text, cx)); + self + } + fn remove_contact(&mut self, request: &RemoveContact, cx: &mut ViewContext) { let user_id = request.0; let user_store = self.user_store.clone(); diff --git a/crates/collab_ui/src/contacts_popover.rs b/crates/collab_ui/src/contacts_popover.rs index 0c67ef4c7c..017950e6cc 100644 --- a/crates/collab_ui/src/contacts_popover.rs +++ b/crates/collab_ui/src/contacts_popover.rs @@ -43,19 +43,23 @@ impl ContactsPopover { user_store, _subscription: None, }; - this.show_contact_list(cx); + this.show_contact_list(String::new(), cx); this } fn toggle_contact_finder(&mut self, _: &ToggleContactFinder, cx: &mut ViewContext) { match &self.child { - Child::ContactList(_) => self.show_contact_finder(cx), - Child::ContactFinder(_) => self.show_contact_list(cx), + Child::ContactList(list) => self.show_contact_finder(list.read(cx).editor_text(cx), cx), + Child::ContactFinder(finder) => { + self.show_contact_list(finder.read(cx).editor_text(cx), cx) + } } } - fn show_contact_finder(&mut self, cx: &mut ViewContext) { - let child = cx.add_view(|cx| ContactFinder::new(self.user_store.clone(), cx)); + fn show_contact_finder(&mut self, editor_text: String, cx: &mut ViewContext) { + let child = cx.add_view(|cx| { + ContactFinder::new(self.user_store.clone(), cx).with_editor_text(editor_text, cx) + }); cx.focus(&child); self._subscription = Some(cx.subscribe(&child, |_, _, event, cx| match event { crate::contact_finder::Event::Dismissed => cx.emit(Event::Dismissed), @@ -64,9 +68,11 @@ impl ContactsPopover { cx.notify(); } - fn show_contact_list(&mut self, cx: &mut ViewContext) { - let child = - cx.add_view(|cx| ContactList::new(self.project.clone(), self.user_store.clone(), cx)); + fn show_contact_list(&mut self, editor_text: String, cx: &mut ViewContext) { + let child = cx.add_view(|cx| { + ContactList::new(self.project.clone(), self.user_store.clone(), cx) + .with_editor_text(editor_text, cx) + }); cx.focus(&child); self._subscription = Some(cx.subscribe(&child, |_, _, event, cx| match event { crate::contact_list::Event::Dismissed => cx.emit(Event::Dismissed), diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index e4d062d575..547e016036 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -205,6 +205,11 @@ impl Picker { self.query_editor.read(cx).text(cx) } + pub fn set_query(&self, query: impl Into>, cx: &mut ViewContext) { + self.query_editor + .update(cx, |editor, cx| editor.set_text(query, cx)); + } + fn on_query_editor_event( &mut self, _: ViewHandle, From 648f0e5b7b6ec4185af1e2fe0439f727b0147305 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 9 Mar 2023 19:18:17 -0800 Subject: [PATCH 68/93] Remove new logo from style tree --- styles/src/styleTree/workspace.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 984b6b5ed3..bebe87ce55 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -48,7 +48,7 @@ export default function workspace(colorScheme: ColorScheme) { }, logo: { color: withOpacity("#000000", colorScheme.isLight ? 0.6 : 0.8), - icon: "icons/logo_256.svg", + icon: "icons/logo_96.svg", dimensions: { width: 256, height: 256, @@ -56,7 +56,7 @@ export default function workspace(colorScheme: ColorScheme) { }, logoShadow: { color: withOpacity(colorScheme.isLight ? "#FFFFFF" : colorScheme.lowest.base.default.background, colorScheme.isLight ? 1 : 0.6), - icon: "icons/logo_shadow_256.svg", + icon: "icons/logo_96.svg", dimensions: { width: 256, height: 256, From 9dc608dc4b4dedd11638d7cb472456e77d9272b6 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 9 Mar 2023 19:32:09 -0800 Subject: [PATCH 69/93] Switch from changing the meaning of the predicate to adding an additional match_dispatch_path_context API for UI elements --- crates/gpui/src/app.rs | 4 ++-- crates/gpui/src/keymap_matcher/binding.rs | 9 +++++++++ crates/gpui/src/keymap_matcher/keymap_context.rs | 8 +------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 31563010b7..ab88a39a9a 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1248,7 +1248,7 @@ impl MutableAppContext { self.keystroke_matcher .bindings_for_action_type(action.as_any().type_id()) .find_map(|b| { - if b.match_context(&contexts) { + if b.match_dispatch_path_context(&contexts) { Some(b.keystrokes().into()) } else { None @@ -1283,7 +1283,7 @@ impl MutableAppContext { deserialize("{}").ok()?, self.keystroke_matcher .bindings_for_action_type(*type_id) - .filter(|b| b.match_context(&contexts)) + .filter(|b| b.match_dispatch_path_context(&contexts)) .collect(), )) } else { diff --git a/crates/gpui/src/keymap_matcher/binding.rs b/crates/gpui/src/keymap_matcher/binding.rs index c1cfd14e82..1b0217b5ff 100644 --- a/crates/gpui/src/keymap_matcher/binding.rs +++ b/crates/gpui/src/keymap_matcher/binding.rs @@ -42,6 +42,15 @@ impl Binding { .unwrap_or(true) } + pub fn match_dispatch_path_context(&self, contexts: &[KeymapContext]) -> bool { + for i in 0..contexts.len() { + if self.match_context(&contexts[i..]) { + return true; + } + } + false + } + pub fn match_keys_and_context( &self, pending_keystrokes: &Vec, diff --git a/crates/gpui/src/keymap_matcher/keymap_context.rs b/crates/gpui/src/keymap_matcher/keymap_context.rs index fbaaa5fbc5..bbf6bfc14b 100644 --- a/crates/gpui/src/keymap_matcher/keymap_context.rs +++ b/crates/gpui/src/keymap_matcher/keymap_context.rs @@ -64,13 +64,7 @@ impl KeymapContextPredicate { pub fn eval(&self, contexts: &[KeymapContext]) -> bool { let Some(context) = contexts.first() else { return false }; match self { - Self::Identifier(name) => { - if (&context.set).contains(name.as_str()) { - true - } else { - self.eval(&contexts[1..]) - } - } + Self::Identifier(name) => (&context.set).contains(name.as_str()), Self::Equal(left, right) => context .map .get(left.as_str()) From 8b7273e46e7cdf2206491e152083b2f028af805b Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Mar 2023 09:54:57 +0100 Subject: [PATCH 70/93] Increase the amount of max connections to the database --- crates/collab/.env.toml | 1 + crates/collab/k8s/environments/preview.sh | 1 + crates/collab/k8s/environments/production.sh | 1 + crates/collab/k8s/environments/staging.sh | 1 + crates/collab/k8s/manifest.template.yml | 2 ++ crates/collab/src/lib.rs | 3 ++- 6 files changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/collab/.env.toml b/crates/collab/.env.toml index b4a6694e5e..01866012ea 100644 --- a/crates/collab/.env.toml +++ b/crates/collab/.env.toml @@ -1,4 +1,5 @@ DATABASE_URL = "postgres://postgres@localhost/zed" +DATABASE_MAX_CONNECTIONS = 5 HTTP_PORT = 8080 API_TOKEN = "secret" INVITE_LINK_PREFIX = "http://localhost:3000/invites/" diff --git a/crates/collab/k8s/environments/preview.sh b/crates/collab/k8s/environments/preview.sh index 4d9dd849e9..132a1ef53c 100644 --- a/crates/collab/k8s/environments/preview.sh +++ b/crates/collab/k8s/environments/preview.sh @@ -1,3 +1,4 @@ ZED_ENVIRONMENT=preview RUST_LOG=info INVITE_LINK_PREFIX=https://zed.dev/invites/ +DATABASE_MAX_CONNECTIONS=10 diff --git a/crates/collab/k8s/environments/production.sh b/crates/collab/k8s/environments/production.sh index 83af6630c2..cb1d4b4de7 100644 --- a/crates/collab/k8s/environments/production.sh +++ b/crates/collab/k8s/environments/production.sh @@ -1,3 +1,4 @@ ZED_ENVIRONMENT=production RUST_LOG=info INVITE_LINK_PREFIX=https://zed.dev/invites/ +DATABASE_MAX_CONNECTIONS=85 diff --git a/crates/collab/k8s/environments/staging.sh b/crates/collab/k8s/environments/staging.sh index 82d799e2bc..b9689ccb19 100644 --- a/crates/collab/k8s/environments/staging.sh +++ b/crates/collab/k8s/environments/staging.sh @@ -1,3 +1,4 @@ ZED_ENVIRONMENT=staging RUST_LOG=info INVITE_LINK_PREFIX=https://staging.zed.dev/invites/ +DATABASE_MAX_CONNECTIONS=5 diff --git a/crates/collab/k8s/manifest.template.yml b/crates/collab/k8s/manifest.template.yml index b73070536f..75c7aa989d 100644 --- a/crates/collab/k8s/manifest.template.yml +++ b/crates/collab/k8s/manifest.template.yml @@ -80,6 +80,8 @@ spec: secretKeyRef: name: database key: url + - name: DATABASE_MAX_CONNECTIONS + value: ${DATABASE_MAX_CONNECTIONS} - name: API_TOKEN valueFrom: secretKeyRef: diff --git a/crates/collab/src/lib.rs b/crates/collab/src/lib.rs index 1a83193bdf..8c99a5ea0f 100644 --- a/crates/collab/src/lib.rs +++ b/crates/collab/src/lib.rs @@ -91,6 +91,7 @@ impl std::error::Error for Error {} pub struct Config { pub http_port: u16, pub database_url: String, + pub database_max_connections: u32, pub api_token: String, pub invite_link_prefix: String, pub live_kit_server: Option, @@ -116,7 +117,7 @@ pub struct AppState { impl AppState { pub async fn new(config: Config) -> Result> { let mut db_options = db::ConnectOptions::new(config.database_url.clone()); - db_options.max_connections(5); + db_options.max_connections(config.database_max_connections); let db = Database::new(db_options).await?; let live_kit_client = if let Some(((server, key), secret)) = config .live_kit_server From 221bb54e48610ea946f5436603e3ebdcba4c9f28 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Mar 2023 11:41:13 +0100 Subject: [PATCH 71/93] Introduce a new `TryFutureExt::unwrap` method --- Cargo.lock | 1 + crates/client/src/telemetry.rs | 4 +- crates/collab_ui/src/contact_finder.rs | 2 +- crates/journal/Cargo.toml | 1 + crates/journal/src/journal.rs | 2 +- crates/lsp/src/lsp.rs | 191 ++++++++++++++----------- crates/project/src/project.rs | 2 +- crates/util/src/util.rs | 40 +++++- 8 files changed, 151 insertions(+), 92 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cfd27bbdd0..87e32ed97d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3157,6 +3157,7 @@ dependencies = [ name = "journal" version = "0.1.0" dependencies = [ + "anyhow", "chrono", "dirs 4.0.0", "editor", diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 748eb48f7e..9d486619d2 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -224,7 +224,7 @@ impl Telemetry { .header("Content-Type", "application/json") .body(json_bytes.into())?; this.http_client.send(request).await?; - Ok(()) + anyhow::Ok(()) } .log_err(), ) @@ -320,7 +320,7 @@ impl Telemetry { .header("Content-Type", "application/json") .body(json_bytes.into())?; this.http_client.send(request).await?; - Ok(()) + anyhow::Ok(()) } .log_err(), ) diff --git a/crates/collab_ui/src/contact_finder.rs b/crates/collab_ui/src/contact_finder.rs index ff783c6274..3b93414e1f 100644 --- a/crates/collab_ui/src/contact_finder.rs +++ b/crates/collab_ui/src/contact_finder.rs @@ -68,7 +68,7 @@ impl PickerDelegate for ContactFinder { this.potential_contacts = potential_contacts.into(); cx.notify(); }); - Ok(()) + anyhow::Ok(()) } .log_err() .await; diff --git a/crates/journal/Cargo.toml b/crates/journal/Cargo.toml index b532397dd1..a2656ad219 100644 --- a/crates/journal/Cargo.toml +++ b/crates/journal/Cargo.toml @@ -13,6 +13,7 @@ editor = { path = "../editor" } gpui = { path = "../gpui" } util = { path = "../util" } workspace = { path = "../workspace" } +anyhow = "1.0" chrono = "0.4" dirs = "4.0" log = { version = "0.4.16", features = ["kv_unstable_serde"] } diff --git a/crates/journal/src/journal.rs b/crates/journal/src/journal.rs index 76a56af93d..1ad97e61b1 100644 --- a/crates/journal/src/journal.rs +++ b/crates/journal/src/journal.rs @@ -73,7 +73,7 @@ pub fn new_journal_entry(app_state: Arc, cx: &mut MutableAppContext) { } } - Ok(()) + anyhow::Ok(()) } .log_err() }) diff --git a/crates/lsp/src/lsp.rs b/crates/lsp/src/lsp.rs index 6982445982..81568b9e3e 100644 --- a/crates/lsp/src/lsp.rs +++ b/crates/lsp/src/lsp.rs @@ -160,15 +160,13 @@ impl LanguageServer { server: Option, root_path: &Path, cx: AsyncAppContext, - mut on_unhandled_notification: F, + on_unhandled_notification: F, ) -> Self where Stdin: AsyncWrite + Unpin + Send + 'static, Stdout: AsyncRead + Unpin + Send + 'static, F: FnMut(AnyNotification) + 'static + Send, { - let mut stdin = BufWriter::new(stdin); - let mut stdout = BufReader::new(stdout); let (outbound_tx, outbound_rx) = channel::unbounded::>(); let notification_handlers = Arc::new(Mutex::new(HashMap::<_, NotificationHandler>::default())); @@ -177,89 +175,19 @@ impl LanguageServer { let input_task = cx.spawn(|cx| { let notification_handlers = notification_handlers.clone(); let response_handlers = response_handlers.clone(); - async move { - let _clear_response_handlers = util::defer({ - let response_handlers = response_handlers.clone(); - move || { - response_handlers.lock().take(); - } - }); - let mut buffer = Vec::new(); - loop { - buffer.clear(); - stdout.read_until(b'\n', &mut buffer).await?; - stdout.read_until(b'\n', &mut buffer).await?; - let message_len: usize = std::str::from_utf8(&buffer)? - .strip_prefix(CONTENT_LEN_HEADER) - .ok_or_else(|| anyhow!("invalid header"))? - .trim_end() - .parse()?; - - buffer.resize(message_len, 0); - stdout.read_exact(&mut buffer).await?; - log::trace!("incoming message:{}", String::from_utf8_lossy(&buffer)); - - if let Ok(msg) = serde_json::from_slice::(&buffer) { - if let Some(handler) = notification_handlers.lock().get_mut(msg.method) { - handler(msg.id, msg.params.get(), cx.clone()); - } else { - on_unhandled_notification(msg); - } - } else if let Ok(AnyResponse { - id, error, result, .. - }) = serde_json::from_slice(&buffer) - { - if let Some(handler) = response_handlers - .lock() - .as_mut() - .and_then(|handlers| handlers.remove(&id)) - { - if let Some(error) = error { - handler(Err(error)); - } else if let Some(result) = result { - handler(Ok(result.get())); - } else { - handler(Ok("null")); - } - } - } else { - warn!( - "Failed to deserialize message:\n{}", - std::str::from_utf8(&buffer)? - ); - } - - // Don't starve the main thread when receiving lots of messages at once. - smol::future::yield_now().await; - } - } + Self::handle_input( + stdout, + on_unhandled_notification, + notification_handlers, + response_handlers, + cx, + ) .log_err() }); let (output_done_tx, output_done_rx) = barrier::channel(); let output_task = cx.background().spawn({ let response_handlers = response_handlers.clone(); - async move { - let _clear_response_handlers = util::defer({ - let response_handlers = response_handlers.clone(); - move || { - response_handlers.lock().take(); - } - }); - let mut content_len_buffer = Vec::new(); - while let Ok(message) = outbound_rx.recv().await { - log::trace!("outgoing message:{}", String::from_utf8_lossy(&message)); - content_len_buffer.clear(); - write!(content_len_buffer, "{}", message.len()).unwrap(); - stdin.write_all(CONTENT_LEN_HEADER.as_bytes()).await?; - stdin.write_all(&content_len_buffer).await?; - stdin.write_all("\r\n\r\n".as_bytes()).await?; - stdin.write_all(&message).await?; - stdin.flush().await?; - } - drop(output_done_tx); - Ok(()) - } - .log_err() + Self::handle_output(stdin, outbound_rx, output_done_tx, response_handlers).log_err() }); Self { @@ -278,6 +206,105 @@ impl LanguageServer { } } + async fn handle_input( + stdout: Stdout, + mut on_unhandled_notification: F, + notification_handlers: Arc>>, + response_handlers: Arc>>>, + cx: AsyncAppContext, + ) -> anyhow::Result<()> + where + Stdout: AsyncRead + Unpin + Send + 'static, + F: FnMut(AnyNotification) + 'static + Send, + { + let mut stdout = BufReader::new(stdout); + let _clear_response_handlers = util::defer({ + let response_handlers = response_handlers.clone(); + move || { + response_handlers.lock().take(); + } + }); + let mut buffer = Vec::new(); + loop { + buffer.clear(); + stdout.read_until(b'\n', &mut buffer).await?; + stdout.read_until(b'\n', &mut buffer).await?; + let message_len: usize = std::str::from_utf8(&buffer)? + .strip_prefix(CONTENT_LEN_HEADER) + .ok_or_else(|| anyhow!("invalid header"))? + .trim_end() + .parse()?; + + buffer.resize(message_len, 0); + stdout.read_exact(&mut buffer).await?; + log::trace!("incoming message:{}", String::from_utf8_lossy(&buffer)); + + if let Ok(msg) = serde_json::from_slice::(&buffer) { + if let Some(handler) = notification_handlers.lock().get_mut(msg.method) { + handler(msg.id, msg.params.get(), cx.clone()); + } else { + on_unhandled_notification(msg); + } + } else if let Ok(AnyResponse { + id, error, result, .. + }) = serde_json::from_slice(&buffer) + { + if let Some(handler) = response_handlers + .lock() + .as_mut() + .and_then(|handlers| handlers.remove(&id)) + { + if let Some(error) = error { + handler(Err(error)); + } else if let Some(result) = result { + handler(Ok(result.get())); + } else { + handler(Ok("null")); + } + } + } else { + warn!( + "Failed to deserialize message:\n{}", + std::str::from_utf8(&buffer)? + ); + } + + // Don't starve the main thread when receiving lots of messages at once. + smol::future::yield_now().await; + } + } + + async fn handle_output( + stdin: Stdin, + outbound_rx: channel::Receiver>, + output_done_tx: barrier::Sender, + response_handlers: Arc>>>, + ) -> anyhow::Result<()> + where + Stdin: AsyncWrite + Unpin + Send + 'static, + { + let mut stdin = BufWriter::new(stdin); + let _clear_response_handlers = util::defer({ + let response_handlers = response_handlers.clone(); + move || { + response_handlers.lock().take(); + } + }); + let mut content_len_buffer = Vec::new(); + while let Ok(message) = outbound_rx.recv().await { + log::trace!("outgoing message:{}", String::from_utf8_lossy(&message)); + content_len_buffer.clear(); + write!(content_len_buffer, "{}", message.len()).unwrap(); + stdin.write_all(CONTENT_LEN_HEADER.as_bytes()).await?; + stdin.write_all(&content_len_buffer).await?; + stdin.write_all("\r\n\r\n".as_bytes()).await?; + stdin.write_all(&message).await?; + stdin.flush().await?; + } + drop(output_done_tx); + Ok(()) + } + /// Initializes a language server. /// Note that `options` is used directly to construct [`InitializeParams`], /// which is why it is owned. @@ -389,7 +416,7 @@ impl LanguageServer { output_done.recv().await; log::debug!("language server shutdown finished"); drop(tasks); - Ok(()) + anyhow::Ok(()) } .log_err(), ) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 2ebea8d07d..a970c7bb19 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -5831,7 +5831,7 @@ impl Project { })?; } - Ok(()) + anyhow::Ok(()) } .log_err(), ) diff --git a/crates/util/src/util.rs b/crates/util/src/util.rs index 5ecf889f9b..3824312a4f 100644 --- a/crates/util/src/util.rs +++ b/crates/util/src/util.rs @@ -124,11 +124,15 @@ pub trait TryFutureExt { fn warn_on_err(self) -> LogErrorFuture where Self: Sized; + fn unwrap(self) -> UnwrapFuture + where + Self: Sized; } -impl TryFutureExt for F +impl TryFutureExt for F where - F: Future>, + F: Future>, + E: std::fmt::Debug, { fn log_err(self) -> LogErrorFuture where @@ -143,17 +147,25 @@ where { LogErrorFuture(self, log::Level::Warn) } + + fn unwrap(self) -> UnwrapFuture + where + Self: Sized, + { + UnwrapFuture(self) + } } pub struct LogErrorFuture(F, log::Level); -impl Future for LogErrorFuture +impl Future for LogErrorFuture where - F: Future>, + F: Future>, + E: std::fmt::Debug, { type Output = Option; - fn poll(self: std::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { let level = self.1; let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }; match inner.poll(cx) { @@ -169,6 +181,24 @@ where } } +pub struct UnwrapFuture(F); + +impl Future for UnwrapFuture +where + F: Future>, + E: std::fmt::Debug, +{ + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll { + let inner = unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }; + match inner.poll(cx) { + Poll::Ready(result) => Poll::Ready(result.unwrap()), + Poll::Pending => Poll::Pending, + } + } +} + struct Defer(Option); impl Drop for Defer { From 7a600e7a65855148d6fd50478abd2ecbd1a08bf5 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Mar 2023 12:17:47 +0100 Subject: [PATCH 72/93] Allow waiting for language to be loaded in `LanguageRegistry` APIs --- crates/editor/src/hover_popover.rs | 11 +-- crates/feedback/src/feedback_editor.rs | 33 ++++--- crates/language/src/buffer_tests.rs | 54 +++++++---- crates/language/src/language.rs | 51 +++++++--- crates/language/src/syntax_map.rs | 30 +++--- crates/project/src/project.rs | 22 ++++- crates/zed/src/zed.rs | 126 ++++++++++++++----------- 7 files changed, 201 insertions(+), 126 deletions(-) diff --git a/crates/editor/src/hover_popover.rs b/crates/editor/src/hover_popover.rs index f92b07da1d..1a23a02efc 100644 --- a/crates/editor/src/hover_popover.rs +++ b/crates/editor/src/hover_popover.rs @@ -1,3 +1,4 @@ +use futures::FutureExt; use gpui::{ actions, elements::{Flex, MouseEventHandler, Padding, Text}, @@ -327,12 +328,10 @@ impl InfoPopover { MouseEventHandler::::new(0, cx, |_, cx| { let mut flex = Flex::new(Axis::Vertical).scrollable::(1, None, cx); flex.extend(self.contents.iter().map(|content| { - let project = self.project.read(cx); - if let Some(language) = content - .language - .clone() - .and_then(|language| project.languages().language_for_name(&language)) - { + let languages = self.project.read(cx).languages(); + if let Some(language) = content.language.clone().and_then(|language| { + languages.language_for_name(&language).now_or_never()?.ok() + }) { let runs = language .highlight_text(&content.text.as_str().into(), 0..content.text.len()); diff --git a/crates/feedback/src/feedback_editor.rs b/crates/feedback/src/feedback_editor.rs index e30b85170c..8b12e859e6 100644 --- a/crates/feedback/src/feedback_editor.rs +++ b/crates/feedback/src/feedback_editor.rs @@ -20,6 +20,7 @@ use postage::prelude::Stream; use project::Project; use serde::Serialize; +use util::ResultExt; use workspace::{ item::{Item, ItemHandle}, searchable::{SearchableItem, SearchableItemHandle}, @@ -200,24 +201,28 @@ impl FeedbackEditor { impl FeedbackEditor { pub fn deploy( system_specs: SystemSpecs, - workspace: &mut Workspace, + _: &mut Workspace, app_state: Arc, cx: &mut ViewContext, ) { - workspace - .with_local_workspace(&app_state, cx, |workspace, cx| { - let project = workspace.project().clone(); - let markdown_language = project.read(cx).languages().language_for_name("Markdown"); - let buffer = project - .update(cx, |project, cx| { - project.create_buffer("", markdown_language, cx) + let markdown = app_state.languages.language_for_name("Markdown"); + cx.spawn(|workspace, mut cx| async move { + let markdown = markdown.await.log_err(); + workspace + .update(&mut cx, |workspace, cx| { + workspace.with_local_workspace(&app_state, cx, |workspace, cx| { + let project = workspace.project().clone(); + let buffer = project + .update(cx, |project, cx| project.create_buffer("", markdown, cx)) + .expect("creating buffers on a local workspace always succeeds"); + let feedback_editor = cx + .add_view(|cx| FeedbackEditor::new(system_specs, project, buffer, cx)); + workspace.add_item(Box::new(feedback_editor), cx); }) - .expect("creating buffers on a local workspace always succeeds"); - let feedback_editor = - cx.add_view(|cx| FeedbackEditor::new(system_specs, project, buffer, cx)); - workspace.add_item(Box::new(feedback_editor), cx); - }) - .detach(); + }) + .await; + }) + .detach(); } } diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index e98edbb2d3..3e65ec3120 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -80,31 +80,49 @@ fn test_select_language() { // matching file extension assert_eq!( - registry.language_for_path("zed/lib.rs").map(|l| l.name()), + registry + .language_for_path("zed/lib.rs") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), Some("Rust".into()) ); assert_eq!( - registry.language_for_path("zed/lib.mk").map(|l| l.name()), + registry + .language_for_path("zed/lib.mk") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), Some("Make".into()) ); // matching filename assert_eq!( - registry.language_for_path("zed/Makefile").map(|l| l.name()), + registry + .language_for_path("zed/Makefile") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), Some("Make".into()) ); // matching suffix that is not the full file extension or filename assert_eq!( - registry.language_for_path("zed/cars").map(|l| l.name()), + registry + .language_for_path("zed/cars") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), None ); assert_eq!( - registry.language_for_path("zed/a.cars").map(|l| l.name()), + registry + .language_for_path("zed/a.cars") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), None ); assert_eq!( - registry.language_for_path("zed/sumk").map(|l| l.name()), + registry + .language_for_path("zed/sumk") + .now_or_never() + .and_then(|l| Some(l.ok()?.name())), None ); } @@ -666,14 +684,14 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { moˇd y { - + } } let foo = 1;"}, vec![indoc! {" mod x «{» mod y { - + } «}» let foo = 1;"}], @@ -683,7 +701,7 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { mod y ˇ{ - + } } let foo = 1;"}, @@ -691,14 +709,14 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x «{» mod y { - + } «}» let foo = 1;"}, indoc! {" mod x { mod y «{» - + «}» } let foo = 1;"}, @@ -709,7 +727,7 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { mod y { - + }ˇ } let foo = 1;"}, @@ -717,14 +735,14 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x «{» mod y { - + } «}» let foo = 1;"}, indoc! {" mod x { mod y «{» - + «}» } let foo = 1;"}, @@ -735,14 +753,14 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { mod y { - + } ˇ} let foo = 1;"}, vec![indoc! {" mod x «{» mod y { - + } «}» let foo = 1;"}], @@ -752,7 +770,7 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { mod y { - + } } let fˇoo = 1;"}, @@ -764,7 +782,7 @@ fn test_enclosing_bracket_ranges(cx: &mut MutableAppContext) { indoc! {" mod x { mod y { - + } } let foo = 1;ˇ"}, diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 983bc58f76..30fc5db563 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -13,8 +13,9 @@ use async_trait::async_trait; use client::http::HttpClient; use collections::HashMap; use futures::{ + channel::oneshot, future::{BoxFuture, Shared}, - FutureExt, TryFutureExt, + FutureExt, TryFutureExt as _, }; use gpui::{executor::Background, MutableAppContext, Task}; use highlight_map::HighlightMap; @@ -43,7 +44,7 @@ use syntax_map::SyntaxSnapshot; use theme::{SyntaxTheme, Theme}; use tree_sitter::{self, Query}; use unicase::UniCase; -use util::ResultExt; +use util::{ResultExt, TryFutureExt as _, UnwrapFuture}; #[cfg(any(test, feature = "test-support"))] use futures::channel::mpsc; @@ -484,7 +485,7 @@ impl LanguageRegistry { let (lsp_binary_statuses_tx, lsp_binary_statuses_rx) = async_broadcast::broadcast(16); Self { language_server_download_dir: None, - languages: Default::default(), + languages: RwLock::new(vec![PLAIN_TEXT.clone()]), available_languages: Default::default(), lsp_binary_statuses_tx, lsp_binary_statuses_rx, @@ -568,12 +569,18 @@ impl LanguageRegistry { self.language_server_download_dir = Some(path.into()); } - pub fn language_for_name(self: &Arc, name: &str) -> Option> { + pub fn language_for_name( + self: &Arc, + name: &str, + ) -> UnwrapFuture>>> { let name = UniCase::new(name); self.get_or_load_language(|config| UniCase::new(config.name.as_ref()) == name) } - pub fn language_for_name_or_extension(self: &Arc, string: &str) -> Option> { + pub fn language_for_name_or_extension( + self: &Arc, + string: &str, + ) -> UnwrapFuture>>> { let string = UniCase::new(string); self.get_or_load_language(|config| { UniCase::new(config.name.as_ref()) == string @@ -584,7 +591,10 @@ impl LanguageRegistry { }) } - pub fn language_for_path(self: &Arc, path: impl AsRef) -> Option> { + pub fn language_for_path( + self: &Arc, + path: impl AsRef, + ) -> UnwrapFuture>>> { let path = path.as_ref(); let filename = path.file_name().and_then(|name| name.to_str()); let extension = path.extension().and_then(|name| name.to_str()); @@ -600,17 +610,17 @@ impl LanguageRegistry { fn get_or_load_language( self: &Arc, callback: impl Fn(&LanguageConfig) -> bool, - ) -> Option> { + ) -> UnwrapFuture>>> { + let (tx, rx) = oneshot::channel(); + if let Some(language) = self .languages .read() .iter() .find(|language| callback(&language.config)) { - return Some(language.clone()); - } - - if let Some(executor) = self.executor.clone() { + let _ = tx.send(Ok(language.clone())); + } else if let Some(executor) = self.executor.clone() { let mut available_languages = self.available_languages.write(); if let Some(ix) = available_languages.iter().position(|l| callback(&l.config)) { @@ -625,18 +635,29 @@ impl LanguageRegistry { .with_lsp_adapter(language.lsp_adapter) .await; match language.with_queries(queries) { - Ok(language) => this.add(Arc::new(language)), + Ok(language) => { + let language = Arc::new(language); + this.add(language.clone()); + let _ = tx.send(Ok(language)); + } Err(err) => { - log::error!("failed to load language {}: {}", name, err); - return; + let _ = tx.send(Err(anyhow!( + "failed to load language {}: {}", + name, + err + ))); } }; }) .detach(); + } else { + let _ = tx.send(Err(anyhow!("language not found"))); } + } else { + let _ = tx.send(Err(anyhow!("executor does not exist"))); } - None + rx.unwrap() } pub fn to_vec(&self) -> Vec> { diff --git a/crates/language/src/syntax_map.rs b/crates/language/src/syntax_map.rs index 5923241955..88de8e690a 100644 --- a/crates/language/src/syntax_map.rs +++ b/crates/language/src/syntax_map.rs @@ -1,5 +1,6 @@ use crate::{Grammar, InjectionConfig, Language, LanguageRegistry}; use collections::HashMap; +use futures::FutureExt; use lazy_static::lazy_static; use parking_lot::Mutex; use std::{ @@ -382,11 +383,11 @@ impl SyntaxSnapshot { cursor.next(text); while let Some(layer) = cursor.item() { let SyntaxLayerContent::Pending { language_name } = &layer.content else { unreachable!() }; - if { - let language_registry = ®istry; - language_registry.language_for_name_or_extension(language_name) - } - .is_some() + if registry + .language_for_name_or_extension(language_name) + .now_or_never() + .and_then(|language| language.ok()) + .is_some() { resolved_injection_ranges.push(layer.range.to_offset(text)); } @@ -1116,7 +1117,10 @@ fn get_injections( combined_injection_ranges.clear(); for pattern in &config.patterns { if let (Some(language_name), true) = (pattern.language.as_ref(), pattern.combined) { - if let Some(language) = language_registry.language_for_name_or_extension(language_name) + if let Some(language) = language_registry + .language_for_name_or_extension(language_name) + .now_or_never() + .and_then(|language| language.ok()) { combined_injection_ranges.insert(language, Vec::new()); } @@ -1162,10 +1166,10 @@ fn get_injections( }; if let Some(language_name) = language_name { - let language = { - let language_name: &str = &language_name; - language_registry.language_for_name_or_extension(language_name) - }; + let language = language_registry + .language_for_name_or_extension(&language_name) + .now_or_never() + .and_then(|language| language.ok()); let range = text.anchor_before(step_range.start)..text.anchor_after(step_range.end); if let Some(language) = language { if combined { @@ -2522,7 +2526,11 @@ mod tests { registry.add(Arc::new(html_lang())); registry.add(Arc::new(erb_lang())); registry.add(Arc::new(markdown_lang())); - let language = registry.language_for_name(language_name).unwrap(); + let language = registry + .language_for_name(language_name) + .now_or_never() + .unwrap() + .unwrap(); let mut buffer = Buffer::new(0, 0, Default::default()); let mut mutated_syntax_map = SyntaxMap::new(); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index a970c7bb19..003e4dd899 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1838,7 +1838,11 @@ impl Project { ) -> Option<()> { // If the buffer has a language, set it and start the language server if we haven't already. let full_path = buffer.read(cx).file()?.full_path(cx); - let new_language = self.languages.language_for_path(&full_path)?; + let new_language = self + .languages + .language_for_path(&full_path) + .now_or_never()? + .ok()?; buffer.update(cx, |buffer, cx| { if buffer.language().map_or(true, |old_language| { !Arc::ptr_eq(old_language, &new_language) @@ -2248,8 +2252,14 @@ impl Project { }) .collect(); for (worktree_id, worktree_abs_path, full_path) in language_server_lookup_info { - let language = self.languages.language_for_path(&full_path)?; - self.restart_language_server(worktree_id, worktree_abs_path, language, cx); + if let Some(language) = self + .languages + .language_for_path(&full_path) + .now_or_never() + .and_then(|language| language.ok()) + { + self.restart_language_server(worktree_id, worktree_abs_path, language, cx); + } } None @@ -3278,12 +3288,14 @@ impl Project { path: path.into(), }; let signature = this.symbol_signature(&project_path); + let adapter_language = adapter_language.clone(); let language = this .languages .language_for_path(&project_path.path) - .unwrap_or(adapter_language.clone()); + .unwrap_or_else(move |_| adapter_language); let language_server_name = adapter.name.clone(); Some(async move { + let language = language.await; let label = language .label_for_symbol(&lsp_symbol.name, lsp_symbol.kind) .await; @@ -6060,7 +6072,7 @@ impl Project { worktree_id, path: PathBuf::from(serialized_symbol.path).into(), }; - let language = languages.language_for_path(&path.path); + let language = languages.language_for_path(&path.path).await.log_err(); Ok(Symbol { language_server_name: LanguageServerName( serialized_symbol.language_server_name.into(), diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 2fbac3613e..79a6f67f62 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -167,9 +167,8 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }); cx.add_action({ let app_state = app_state.clone(); - move |workspace: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext| { + move |_: &mut Workspace, _: &OpenLicenses, cx: &mut ViewContext| { open_bundled_file( - workspace, app_state.clone(), "licenses.md", "Open Source License Attribution", @@ -192,9 +191,8 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }); cx.add_action({ let app_state = app_state.clone(); - move |workspace: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { + move |_: &mut Workspace, _: &OpenDefaultKeymap, cx: &mut ViewContext| { open_bundled_file( - workspace, app_state.clone(), "keymaps/default.json", "Default Key Bindings", @@ -205,11 +203,8 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }); cx.add_action({ let app_state = app_state.clone(); - move |workspace: &mut Workspace, - _: &OpenDefaultSettings, - cx: &mut ViewContext| { + move |_: &mut Workspace, _: &OpenDefaultSettings, cx: &mut ViewContext| { open_bundled_file( - workspace, app_state.clone(), "settings/default.json", "Default Settings", @@ -218,32 +213,41 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { ); } }); - cx.add_action( - |workspace: &mut Workspace, _: &DebugElements, cx: &mut ViewContext| { + cx.add_action({ + let app_state = app_state.clone(); + move |_: &mut Workspace, _: &DebugElements, cx: &mut ViewContext| { + let app_state = app_state.clone(); + let markdown = app_state.languages.language_for_name("JSON"); let content = to_string_pretty(&cx.debug_elements()).unwrap(); - let project = workspace.project().clone(); - let json_language = project - .read(cx) - .languages() - .language_for_name("JSON") - .unwrap(); - if project.read(cx).is_remote() { - cx.propagate_action(); - } else if let Some(buffer) = project - .update(cx, |project, cx| { - project.create_buffer(&content, Some(json_language), cx) - }) - .log_err() - { - workspace.add_item( - Box::new( - cx.add_view(|cx| Editor::for_buffer(buffer, Some(project.clone()), cx)), - ), - cx, - ); - } - }, - ); + cx.spawn(|workspace, mut cx| async move { + let markdown = markdown.await.log_err(); + workspace + .update(&mut cx, |workspace, cx| { + workspace.with_local_workspace(&app_state, cx, move |workspace, cx| { + let project = workspace.project().clone(); + + let buffer = project + .update(cx, |project, cx| { + project.create_buffer(&content, markdown, cx) + }) + .expect("creating buffers on a local workspace always succeeds"); + let buffer = cx.add_model(|cx| { + MultiBuffer::singleton(buffer, cx) + .with_title("Debug Elements".into()) + }); + workspace.add_item( + Box::new(cx.add_view(|cx| { + Editor::for_multibuffer(buffer, Some(project.clone()), cx) + })), + cx, + ); + }) + }) + .await; + }) + .detach(); + } + }); cx.add_action( |workspace: &mut Workspace, _: &project_panel::ToggleFocus, @@ -628,6 +632,7 @@ fn open_telemetry_log_file( start_offset += newline_offset + 1; } let log_suffix = &log[start_offset..]; + let json = app_state.languages.language_for_name("JSON").await.log_err(); workspace.update(&mut cx, |workspace, cx| { let project = workspace.project().clone(); @@ -635,7 +640,7 @@ fn open_telemetry_log_file( .update(cx, |project, cx| project.create_buffer("", None, cx)) .expect("creating buffers on a local workspace always succeeds"); buffer.update(cx, |buffer, cx| { - buffer.set_language(app_state.languages.language_for_name("JSON"), cx); + buffer.set_language(json, cx); buffer.edit( [( 0..0, @@ -668,35 +673,42 @@ fn open_telemetry_log_file( } fn open_bundled_file( - workspace: &mut Workspace, app_state: Arc, asset_path: &'static str, title: &'static str, language: &'static str, cx: &mut ViewContext, ) { - workspace - .with_local_workspace(&app_state, cx, |workspace, cx| { - let project = workspace.project().clone(); - 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(); - project - .create_buffer(text, project.languages().language_for_name(language), cx) - .expect("creating buffers on a local workspace always succeeds") - }); - let buffer = - cx.add_model(|cx| MultiBuffer::singleton(buffer, cx).with_title(title.into())); - workspace.add_item( - Box::new( - cx.add_view(|cx| Editor::for_multibuffer(buffer, Some(project.clone()), cx)), - ), - cx, - ); - }) - .detach(); + let language = app_state.languages.language_for_name(language); + cx.spawn(|workspace, mut cx| async move { + let language = language.await.log_err(); + workspace + .update(&mut cx, |workspace, cx| { + workspace.with_local_workspace(&app_state, 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(); + project + .create_buffer(text, language, cx) + .expect("creating buffers on a local workspace always succeeds") + }); + let buffer = cx.add_model(|cx| { + MultiBuffer::singleton(buffer, cx).with_title(title.into()) + }); + workspace.add_item( + Box::new(cx.add_view(|cx| { + Editor::for_multibuffer(buffer, Some(project.clone()), cx) + })), + cx, + ); + }) + }) + .await; + }) + .detach(); } fn schema_file_match(path: &Path) -> &Path { From b402f27d5042308548e05de6f8626a1e285f01e1 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 24 Feb 2023 15:57:32 +0100 Subject: [PATCH 73/93] Introduce a new language picker that displays available languages Right now this panics when trying to select a language, so that's what we're going to implement next. Co-Authored-By: Julia Risley --- Cargo.lock | 16 ++ Cargo.toml | 1 + crates/language_selector/Cargo.toml | 20 ++ .../src/language_selector.rs | 196 ++++++++++++++++++ crates/zed/Cargo.toml | 1 + crates/zed/src/main.rs | 1 + 6 files changed, 235 insertions(+) create mode 100644 crates/language_selector/Cargo.toml create mode 100644 crates/language_selector/src/language_selector.rs diff --git a/Cargo.lock b/Cargo.lock index 87e32ed97d..94e7af6766 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3286,6 +3286,21 @@ dependencies = [ "util", ] +[[package]] +name = "language_selector" +version = "0.1.0" +dependencies = [ + "editor", + "fuzzy", + "gpui", + "language", + "picker", + "project", + "settings", + "theme", + "workspace", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -8399,6 +8414,7 @@ dependencies = [ "isahc", "journal", "language", + "language_selector", "lazy_static", "libc", "log", diff --git a/Cargo.toml b/Cargo.toml index c74a76ccce..63882573ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ members = [ "crates/gpui_macros", "crates/journal", "crates/language", + "crates/language_selector", "crates/live_kit_client", "crates/live_kit_server", "crates/lsp", diff --git a/crates/language_selector/Cargo.toml b/crates/language_selector/Cargo.toml new file mode 100644 index 0000000000..60ed0e6633 --- /dev/null +++ b/crates/language_selector/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "language_selector" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/language_selector.rs" +doctest = false + +[dependencies] +editor = { path = "../editor" } +fuzzy = { path = "../fuzzy" } +language = { path = "../language" } +gpui = { path = "../gpui" } +picker = { path = "../picker" } +project = { path = "../project" } +theme = { path = "../theme" } +settings = { path = "../settings" } +workspace = { path = "../workspace" } diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs new file mode 100644 index 0000000000..d48a50ce93 --- /dev/null +++ b/crates/language_selector/src/language_selector.rs @@ -0,0 +1,196 @@ +use std::sync::Arc; + +use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; +use gpui::{ + actions, elements::*, AnyViewHandle, AppContext, Entity, MouseState, MutableAppContext, + RenderContext, View, ViewContext, ViewHandle, +}; +use language::LanguageRegistry; +use picker::{Picker, PickerDelegate}; +use settings::Settings; +use workspace::{AppState, Workspace}; + +actions!(language_selector, [Toggle]); + +pub fn init(app_state: Arc, cx: &mut MutableAppContext) { + Picker::::init(cx); + cx.add_action({ + let language_registry = app_state.languages.clone(); + move |workspace, _: &Toggle, cx| { + LanguageSelector::toggle(workspace, language_registry.clone(), cx) + } + }); +} + +pub enum Event { + Dismissed, +} + +pub struct LanguageSelector { + language_registry: Arc, + matches: Vec, + picker: ViewHandle>, + selected_index: usize, +} + +impl LanguageSelector { + fn new(language_registry: Arc, cx: &mut ViewContext) -> Self { + let handle = cx.weak_handle(); + let picker = cx.add_view(|cx| Picker::new("Select Language...", handle, cx)); + + let mut matches = language_registry + .language_names() + .into_iter() + .enumerate() + .map(|(candidate_id, name)| StringMatch { + candidate_id, + score: 0.0, + positions: Default::default(), + string: name, + }) + .collect::>(); + matches.sort_unstable_by(|mat1, mat2| mat1.string.cmp(&mat2.string)); + + Self { + language_registry, + matches, + picker, + selected_index: 0, + } + } + + fn toggle( + workspace: &mut Workspace, + registry: Arc, + cx: &mut ViewContext, + ) { + workspace.toggle_modal(cx, |_, cx| { + let this = cx.add_view(|cx| Self::new(registry, cx)); + cx.subscribe(&this, Self::on_event).detach(); + this + }); + } + + fn on_event( + workspace: &mut Workspace, + _: ViewHandle, + event: &Event, + cx: &mut ViewContext, + ) { + match event { + Event::Dismissed => { + workspace.dismiss_modal(cx); + } + } + } +} + +impl Entity for LanguageSelector { + type Event = Event; +} + +impl View for LanguageSelector { + fn ui_name() -> &'static str { + "LanguageSelector" + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + ChildView::new(self.picker.clone(), cx).boxed() + } + + fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { + if cx.is_self_focused() { + cx.focus(&self.picker); + } + } +} + +impl PickerDelegate for LanguageSelector { + fn match_count(&self) -> usize { + self.matches.len() + } + + fn confirm(&mut self, cx: &mut ViewContext) { + todo!(); + cx.emit(Event::Dismissed); + } + + fn dismiss(&mut self, cx: &mut ViewContext) { + cx.emit(Event::Dismissed); + } + + fn selected_index(&self) -> usize { + self.selected_index + } + + fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext) { + self.selected_index = ix; + } + + fn update_matches(&mut self, query: String, cx: &mut ViewContext) -> gpui::Task<()> { + let background = cx.background().clone(); + let candidates = self + .language_registry + .language_names() + .into_iter() + .enumerate() + .map(|(id, name)| StringMatchCandidate { + id, + char_bag: name.as_str().into(), + string: name.clone(), + }) + .collect::>(); + + cx.spawn(|this, mut cx| async move { + let matches = if query.is_empty() { + candidates + .into_iter() + .enumerate() + .map(|(index, candidate)| StringMatch { + candidate_id: index, + string: candidate.string, + positions: Vec::new(), + score: 0.0, + }) + .collect() + } else { + match_strings( + &candidates, + &query, + false, + 100, + &Default::default(), + background, + ) + .await + }; + + this.update(&mut cx, |this, cx| { + this.matches = matches; + this.selected_index = this + .selected_index + .min(this.matches.len().saturating_sub(1)); + cx.notify(); + }); + }) + } + + fn render_match( + &self, + ix: usize, + mouse_state: &mut MouseState, + selected: bool, + cx: &AppContext, + ) -> ElementBox { + let settings = cx.global::(); + let theme = &settings.theme; + let theme_match = &self.matches[ix]; + let style = theme.picker.item.style_for(mouse_state, selected); + + Label::new(theme_match.string.clone(), style.label.clone()) + .with_highlights(theme_match.positions.clone()) + .contained() + .with_style(style.container) + .boxed() + } +} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 8060c2af11..a7c4861e02 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -40,6 +40,7 @@ go_to_line = { path = "../go_to_line" } gpui = { path = "../gpui" } journal = { path = "../journal" } language = { path = "../language" } +language_selector = { path = "../language_selector" } lsp = { path = "../lsp" } outline = { path = "../outline" } plugin_runtime = { path = "../plugin_runtime" } diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index b9e3ed550b..655e3968cf 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -185,6 +185,7 @@ fn main() { workspace::init(app_state.clone(), cx); journal::init(app_state.clone(), cx); + language_selector::init(app_state.clone(), cx); theme_selector::init(app_state.clone(), cx); zed::init(&app_state, cx); collab_ui::init(app_state.clone(), cx); From 686f5439ad033d61d72dcbf22f14a19d3ad45057 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 24 Feb 2023 16:28:56 +0100 Subject: [PATCH 74/93] Set buffer language when confirming selection in language selector Co-Authored-By: Julia Risley --- Cargo.lock | 1 + crates/editor/src/editor.rs | 9 ++ crates/language_selector/Cargo.toml | 1 + .../src/language_selector.rs | 83 ++++++++++++------- crates/project/src/project.rs | 36 +++++--- 5 files changed, 89 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 94e7af6766..0f19d4455f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3290,6 +3290,7 @@ dependencies = [ name = "language_selector" version = "0.1.0" dependencies = [ + "anyhow", "editor", "fuzzy", "gpui", diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 3e7a14d2ae..7f60309f6a 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1254,6 +1254,15 @@ impl Editor { self.buffer.read(cx).language_at(point, cx) } + pub fn active_excerpt( + &self, + cx: &AppContext, + ) -> Option<(ExcerptId, ModelHandle, Range)> { + self.buffer + .read(cx) + .excerpt_containing(self.selections.newest_anchor().head(), cx) + } + fn style(&self, cx: &AppContext) -> EditorStyle { build_style( cx.global::(), diff --git a/crates/language_selector/Cargo.toml b/crates/language_selector/Cargo.toml index 60ed0e6633..14aa41a465 100644 --- a/crates/language_selector/Cargo.toml +++ b/crates/language_selector/Cargo.toml @@ -18,3 +18,4 @@ project = { path = "../project" } theme = { path = "../theme" } settings = { path = "../settings" } workspace = { path = "../workspace" } +anyhow = "1.0" diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs index d48a50ce93..5a2918660e 100644 --- a/crates/language_selector/src/language_selector.rs +++ b/crates/language_selector/src/language_selector.rs @@ -1,12 +1,14 @@ use std::sync::Arc; +use editor::Editor; use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, elements::*, AnyViewHandle, AppContext, Entity, MouseState, MutableAppContext, - RenderContext, View, ViewContext, ViewHandle, + actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState, + MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; -use language::LanguageRegistry; +use language::{Buffer, LanguageRegistry}; use picker::{Picker, PickerDelegate}; +use project::Project; use settings::Settings; use workspace::{AppState, Workspace}; @@ -27,32 +29,47 @@ pub enum Event { } pub struct LanguageSelector { + buffer: ModelHandle, + project: ModelHandle, language_registry: Arc, + candidates: Vec, matches: Vec, picker: ViewHandle>, selected_index: usize, } impl LanguageSelector { - fn new(language_registry: Arc, cx: &mut ViewContext) -> Self { + fn new( + buffer: ModelHandle, + project: ModelHandle, + language_registry: Arc, + cx: &mut ViewContext, + ) -> Self { let handle = cx.weak_handle(); let picker = cx.add_view(|cx| Picker::new("Select Language...", handle, cx)); - let mut matches = language_registry + let candidates = language_registry .language_names() .into_iter() .enumerate() - .map(|(candidate_id, name)| StringMatch { - candidate_id, - score: 0.0, + .map(|(candidate_id, name)| StringMatchCandidate::new(candidate_id, name)) + .collect::>(); + let mut matches = candidates + .iter() + .map(|candidate| StringMatch { + candidate_id: candidate.id, + score: 0., positions: Default::default(), - string: name, + string: candidate.string.clone(), }) .collect::>(); matches.sort_unstable_by(|mat1, mat2| mat1.string.cmp(&mat2.string)); Self { + buffer, + project, language_registry, + candidates, matches, picker, selected_index: 0, @@ -64,11 +81,18 @@ impl LanguageSelector { registry: Arc, cx: &mut ViewContext, ) { - workspace.toggle_modal(cx, |_, cx| { - let this = cx.add_view(|cx| Self::new(registry, cx)); - cx.subscribe(&this, Self::on_event).detach(); - this - }); + if let Some((_, buffer, _)) = workspace + .active_item(cx) + .and_then(|active_item| active_item.act_as::(cx)) + .and_then(|editor| editor.read(cx).active_excerpt(cx)) + { + workspace.toggle_modal(cx, |workspace, cx| { + let project = workspace.project().clone(); + let this = cx.add_view(|cx| Self::new(buffer, project, registry, cx)); + cx.subscribe(&this, Self::on_event).detach(); + this + }); + } } fn on_event( @@ -111,7 +135,21 @@ impl PickerDelegate for LanguageSelector { } fn confirm(&mut self, cx: &mut ViewContext) { - todo!(); + if let Some(mat) = self.matches.get(self.selected_index) { + let language_name = &self.candidates[mat.candidate_id].string; + let language = self.language_registry.language_for_name(language_name); + cx.spawn(|this, mut cx| async move { + let language = language.await?; + this.update(&mut cx, |this, cx| { + this.project.update(cx, |project, cx| { + project.set_language_for_buffer(&this.buffer, language, cx); + }); + }); + anyhow::Ok(()) + }) + .detach_and_log_err(cx); + } + cx.emit(Event::Dismissed); } @@ -123,24 +161,13 @@ impl PickerDelegate for LanguageSelector { self.selected_index } - fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext) { + fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext) { self.selected_index = ix; } fn update_matches(&mut self, query: String, cx: &mut ViewContext) -> gpui::Task<()> { let background = cx.background().clone(); - let candidates = self - .language_registry - .language_names() - .into_iter() - .enumerate() - .map(|(id, name)| StringMatchCandidate { - id, - char_bag: name.as_str().into(), - string: name.clone(), - }) - .collect::>(); - + let candidates = self.candidates.clone(); cx.spawn(|this, mut cx| async move { let matches = if query.is_empty() { candidates diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 003e4dd899..a164b5b885 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -1464,7 +1464,7 @@ impl Project { }) .await?; this.update(&mut cx, |this, cx| { - this.assign_language_to_buffer(&buffer, cx); + this.detect_language_for_buffer(&buffer, cx); this.register_buffer_with_language_server(&buffer, cx); }); Ok(()) @@ -1531,7 +1531,7 @@ impl Project { }) .detach(); - self.assign_language_to_buffer(buffer, cx); + self.detect_language_for_buffer(buffer, cx); self.register_buffer_with_language_server(buffer, cx); cx.observe_release(buffer, |this, buffer, cx| { if let Some(file) = File::from_dyn(buffer.file()) { @@ -1818,7 +1818,7 @@ impl Project { } for buffer in plain_text_buffers { - project.assign_language_to_buffer(&buffer, cx); + project.detect_language_for_buffer(&buffer, cx); project.register_buffer_with_language_server(&buffer, cx); } @@ -1831,7 +1831,7 @@ impl Project { }) } - fn assign_language_to_buffer( + fn detect_language_for_buffer( &mut self, buffer: &ModelHandle, cx: &mut ModelContext, @@ -1843,6 +1843,16 @@ impl Project { .language_for_path(&full_path) .now_or_never()? .ok()?; + self.set_language_for_buffer(buffer, new_language, cx); + None + } + + pub fn set_language_for_buffer( + &mut self, + buffer: &ModelHandle, + new_language: Arc, + cx: &mut ModelContext, + ) { buffer.update(cx, |buffer, cx| { if buffer.language().map_or(true, |old_language| { !Arc::ptr_eq(old_language, &new_language) @@ -1851,13 +1861,13 @@ impl Project { } }); - let file = File::from_dyn(buffer.read(cx).file())?; - let worktree = file.worktree.read(cx).as_local()?; - let worktree_id = worktree.id(); - let worktree_abs_path = worktree.abs_path().clone(); - self.start_language_server(worktree_id, worktree_abs_path, new_language, cx); - - None + if let Some(file) = File::from_dyn(buffer.read(cx).file()) { + if let Some(worktree) = file.worktree.read(cx).as_local() { + let worktree_id = worktree.id(); + let worktree_abs_path = worktree.abs_path().clone(); + self.start_language_server(worktree_id, worktree_abs_path, new_language, cx); + } + } } fn merge_json_value_into(source: serde_json::Value, target: &mut serde_json::Value) { @@ -4553,7 +4563,7 @@ impl Project { for (buffer, old_path) in renamed_buffers { self.unregister_buffer_from_language_server(&buffer, old_path, cx); - self.assign_language_to_buffer(&buffer, cx); + self.detect_language_for_buffer(&buffer, cx); self.register_buffer_with_language_server(&buffer, cx); } } @@ -5222,7 +5232,7 @@ impl Project { buffer.update(cx, |buffer, cx| { buffer.file_updated(Arc::new(file), cx).detach(); }); - this.assign_language_to_buffer(&buffer, cx); + this.detect_language_for_buffer(&buffer, cx); } Ok(()) }) From f28806d09ba0f79e27c4c32c10b1cc844b157c98 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Mar 2023 15:48:39 +0100 Subject: [PATCH 75/93] Emphasize currently-selected language --- crates/language_selector/src/language_selector.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs index 5a2918660e..786dd5abe0 100644 --- a/crates/language_selector/src/language_selector.rs +++ b/crates/language_selector/src/language_selector.rs @@ -211,11 +211,16 @@ impl PickerDelegate for LanguageSelector { ) -> ElementBox { let settings = cx.global::(); let theme = &settings.theme; - let theme_match = &self.matches[ix]; + let mat = &self.matches[ix]; let style = theme.picker.item.style_for(mouse_state, selected); + let buffer_language_name = self.buffer.read(cx).language().map(|l| l.name()); + let mut label = mat.string.clone(); + if buffer_language_name.as_deref() == Some(mat.string.as_str()) { + label.push_str(" (current)"); + } - Label::new(theme_match.string.clone(), style.label.clone()) - .with_highlights(theme_match.positions.clone()) + Label::new(label, style.label.clone()) + .with_highlights(mat.positions.clone()) .contained() .with_style(style.container) .boxed() From ce828d55d5669eae01781aafa5d3a65cbd52d313 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Mar 2023 16:32:18 +0100 Subject: [PATCH 76/93] Bind `language_selector::Toggle` to `cmd-k m` Co-Authored-By: Nathan Sobo --- assets/keymaps/default.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index cce65eda8a..57f5075aca 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -353,7 +353,8 @@ "cmd-shift-p": "command_palette::Toggle", "cmd-shift-m": "diagnostics::Deploy", "cmd-shift-e": "project_panel::ToggleFocus", - "cmd-alt-s": "workspace::SaveAll" + "cmd-alt-s": "workspace::SaveAll", + "cmd-k m": "language_selector::Toggle" } }, // Bindings from Sublime Text @@ -537,4 +538,4 @@ ] } } -] \ No newline at end of file +] From 6e37ff880f616f7128c09e567af22c4ee46c3cd7 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Mar 2023 17:02:52 +0100 Subject: [PATCH 77/93] Replace "Give Feedback" with an icon and move it to the left This is so we can show the current language in the status bar on the right, and having two pieces of text sitting next to each other felt too busy. Co-Authored-By: Nathan Sobo --- assets/icons/speech_bubble_12.svg | 3 +++ crates/feedback/src/deploy_feedback_button.rs | 20 ++++++++++++------- crates/theme/src/theme.rs | 2 +- crates/zed/src/zed.rs | 2 +- styles/src/styleTree/statusBar.ts | 8 ++++++-- 5 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 assets/icons/speech_bubble_12.svg diff --git a/assets/icons/speech_bubble_12.svg b/assets/icons/speech_bubble_12.svg new file mode 100644 index 0000000000..f5f330056a --- /dev/null +++ b/assets/icons/speech_bubble_12.svg @@ -0,0 +1,3 @@ + + + diff --git a/crates/feedback/src/deploy_feedback_button.rs b/crates/feedback/src/deploy_feedback_button.rs index 8fcafdfede..7519d2d06e 100644 --- a/crates/feedback/src/deploy_feedback_button.rs +++ b/crates/feedback/src/deploy_feedback_button.rs @@ -1,7 +1,4 @@ -use gpui::{ - elements::{MouseEventHandler, ParentElement, Stack, Text}, - CursorStyle, Element, ElementBox, Entity, MouseButton, RenderContext, View, ViewContext, -}; +use gpui::{elements::*, CursorStyle, Entity, MouseButton, RenderContext, View, ViewContext}; use settings::Settings; use workspace::{item::ItemHandle, StatusItemView}; @@ -23,9 +20,18 @@ impl View for DeployFeedbackButton { .with_child( MouseEventHandler::::new(0, cx, |state, cx| { let theme = &cx.global::().theme; - let theme = &theme.workspace.status_bar.feedback; - - Text::new("Give Feedback", theme.style_for(state, true).clone()).boxed() + let style = &theme.workspace.status_bar.feedback.style_for(state, false); + Svg::new("icons/speech_bubble_12.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .with_height(style.button_width) + .contained() + .with_style(style.container) + .boxed() }) .with_cursor_style(CursorStyle::PointingHand) .on_click(MouseButton::Left, |_, cx| cx.dispatch_action(GiveFeedback)) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 484c542ede..56ef87e0af 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -280,7 +280,7 @@ pub struct StatusBar { pub auto_update_progress_message: TextStyle, pub auto_update_done_message: TextStyle, pub lsp_status: Interactive, - pub feedback: Interactive, + pub feedback: Interactive, pub sidebar_buttons: StatusBarSidebarButtons, pub diagnostic_summary: Interactive, pub diagnostic_message: Interactive, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 79a6f67f62..f853e41c04 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -342,10 +342,10 @@ pub fn initialize_workspace( let feedback_button = cx.add_view(|_| feedback::deploy_feedback_button::DeployFeedbackButton {}); workspace.status_bar().update(cx, |status_bar, cx| { + status_bar.add_left_item(feedback_button, cx); status_bar.add_left_item(diagnostic_summary, cx); status_bar.add_left_item(activity_indicator, cx); status_bar.add_right_item(cursor_position, cx); - status_bar.add_right_item(feedback_button, cx); }); auto_update::notify_of_any_new_update(cx.weak_handle(), cx); diff --git a/styles/src/styleTree/statusBar.ts b/styles/src/styleTree/statusBar.ts index a60a55df1e..eb72346788 100644 --- a/styles/src/styleTree/statusBar.ts +++ b/styles/src/styleTree/statusBar.ts @@ -45,8 +45,12 @@ export default function statusBar(colorScheme: ColorScheme) { hover: text(layer, "sans", "hovered"), }, feedback: { - ...text(layer, "sans", "variant"), - hover: text(layer, "sans", "hovered"), + color: foreground(layer, "variant"), + iconWidth: 14, + buttonWidth: 20, + hover: { + color: foreground(layer, "on"), + }, }, diagnosticSummary: { height: 20, From b3c7526fb5609e06cb3dad5771adf4de0389a929 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Mar 2023 17:21:06 +0100 Subject: [PATCH 78/93] Return last excerpt in MultiBuffer::excerpt_containing if overshooting --- crates/editor/src/multi_buffer.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index da3c6bc4bd..e1e8285931 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1082,18 +1082,21 @@ impl MultiBuffer { let mut cursor = snapshot.excerpts.cursor::(); cursor.seek(&position, Bias::Right, &()); - cursor.item().map(|excerpt| { - ( - excerpt.id.clone(), - self.buffers - .borrow() - .get(&excerpt.buffer_id) - .unwrap() - .buffer - .clone(), - excerpt.range.context.clone(), - ) - }) + cursor + .item() + .or_else(|| snapshot.excerpts.last()) + .map(|excerpt| { + ( + excerpt.id.clone(), + self.buffers + .borrow() + .get(&excerpt.buffer_id) + .unwrap() + .buffer + .clone(), + excerpt.range.context.clone(), + ) + }) } // If point is at the end of the buffer, the last excerpt is returned From 693172854c45bbb910973fb2dde941a0b3308944 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Mar 2023 17:28:27 +0100 Subject: [PATCH 79/93] Show active buffer's language on the right in the status bar --- .../src/active_buffer_language.rs | 86 +++++++++++++++++++ .../src/language_selector.rs | 4 +- crates/theme/src/theme.rs | 1 + crates/zed/src/zed.rs | 2 + styles/src/styleTree/statusBar.ts | 9 +- 5 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 crates/language_selector/src/active_buffer_language.rs diff --git a/crates/language_selector/src/active_buffer_language.rs b/crates/language_selector/src/active_buffer_language.rs new file mode 100644 index 0000000000..c4129d1448 --- /dev/null +++ b/crates/language_selector/src/active_buffer_language.rs @@ -0,0 +1,86 @@ +use editor::Editor; +use gpui::{ + elements::*, CursorStyle, Entity, MouseButton, RenderContext, Subscription, View, ViewContext, + ViewHandle, +}; +use settings::Settings; +use std::sync::Arc; +use workspace::{item::ItemHandle, StatusItemView}; + +pub struct ActiveBufferLanguage { + active_language: Option>, + _observe_active_editor: Option, +} + +impl Default for ActiveBufferLanguage { + fn default() -> Self { + Self::new() + } +} + +impl ActiveBufferLanguage { + pub fn new() -> Self { + Self { + active_language: None, + _observe_active_editor: None, + } + } + + fn update_language(&mut self, editor: ViewHandle, cx: &mut ViewContext) { + let editor = editor.read(cx); + self.active_language.take(); + if let Some((_, buffer, _)) = editor.active_excerpt(cx) { + if let Some(language) = buffer.read(cx).language() { + self.active_language = Some(language.name()); + } + } + + cx.notify(); + } +} + +impl Entity for ActiveBufferLanguage { + type Event = (); +} + +impl View for ActiveBufferLanguage { + fn ui_name() -> &'static str { + "ActiveBufferLanguage" + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + if let Some(active_language) = self.active_language.as_ref() { + MouseEventHandler::::new(0, cx, |state, cx| { + let theme = &cx.global::().theme.workspace.status_bar; + let style = theme.active_language.style_for(state, false); + Label::new(active_language.to_string(), style.text.clone()) + .contained() + .with_style(style.container) + .boxed() + }) + .with_cursor_style(CursorStyle::PointingHand) + .on_click(MouseButton::Left, |_, cx| cx.dispatch_action(crate::Toggle)) + .boxed() + } else { + Empty::new().boxed() + } + } +} + +impl StatusItemView for ActiveBufferLanguage { + fn set_active_pane_item( + &mut self, + active_pane_item: Option<&dyn ItemHandle>, + cx: &mut ViewContext, + ) { + if let Some(editor) = active_pane_item.and_then(|item| item.act_as::(cx)) { + self._observe_active_editor = Some(cx.observe(&editor, Self::update_language)); + self.update_language(editor, cx); + } else { + self.active_language = None; + self._observe_active_editor = None; + } + + cx.notify(); + } +} diff --git a/crates/language_selector/src/language_selector.rs b/crates/language_selector/src/language_selector.rs index 786dd5abe0..711e36f9c4 100644 --- a/crates/language_selector/src/language_selector.rs +++ b/crates/language_selector/src/language_selector.rs @@ -1,5 +1,6 @@ -use std::sync::Arc; +mod active_buffer_language; +pub use active_buffer_language::ActiveBufferLanguage; use editor::Editor; use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ @@ -10,6 +11,7 @@ use language::{Buffer, LanguageRegistry}; use picker::{Picker, PickerDelegate}; use project::Project; use settings::Settings; +use std::sync::Arc; use workspace::{AppState, Workspace}; actions!(language_selector, [Toggle]); diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 56ef87e0af..aa379bf838 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -277,6 +277,7 @@ pub struct StatusBar { pub height: f32, pub item_spacing: f32, pub cursor_position: TextStyle, + pub active_language: Interactive, pub auto_update_progress_message: TextStyle, pub auto_update_done_message: TextStyle, pub lsp_status: Interactive, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index f853e41c04..664833de1e 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -338,6 +338,7 @@ pub fn initialize_workspace( cx.add_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace.project(), cx)); let activity_indicator = activity_indicator::ActivityIndicator::new(workspace, app_state.languages.clone(), cx); + let active_buffer_language = cx.add_view(|_| language_selector::ActiveBufferLanguage::new()); let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new()); let feedback_button = cx.add_view(|_| feedback::deploy_feedback_button::DeployFeedbackButton {}); @@ -346,6 +347,7 @@ pub fn initialize_workspace( status_bar.add_left_item(diagnostic_summary, cx); status_bar.add_left_item(activity_indicator, cx); status_bar.add_right_item(cursor_position, cx); + status_bar.add_right_item(active_buffer_language, cx); }); auto_update::notify_of_any_new_update(cx.weak_handle(), cx); diff --git a/styles/src/styleTree/statusBar.ts b/styles/src/styleTree/statusBar.ts index eb72346788..de33f3d1d5 100644 --- a/styles/src/styleTree/statusBar.ts +++ b/styles/src/styleTree/statusBar.ts @@ -25,6 +25,12 @@ export default function statusBar(colorScheme: ColorScheme) { }, border: border(layer, { top: true, overlay: true }), cursorPosition: text(layer, "sans", "variant"), + activeLanguage: { + ...text(layer, "sans", "variant"), + hover: { + ...text(layer, "sans", "on") + } + }, autoUpdateProgressMessage: text(layer, "sans", "variant"), autoUpdateDoneMessage: text(layer, "sans", "variant"), lspStatus: { @@ -45,9 +51,10 @@ export default function statusBar(colorScheme: ColorScheme) { hover: text(layer, "sans", "hovered"), }, feedback: { + margin: { left: 2 }, color: foreground(layer, "variant"), iconWidth: 14, - buttonWidth: 20, + buttonWidth: 14, hover: { color: foreground(layer, "on"), }, From f50b51bdad4333f94a65a6e3eea45026a9b246d9 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Mar 2023 17:37:45 +0100 Subject: [PATCH 80/93] Adjust styling --- crates/theme/src/theme.rs | 2 +- crates/zed/src/zed.rs | 2 +- styles/src/styleTree/statusBar.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index aa379bf838..87504d6cd9 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -277,7 +277,7 @@ pub struct StatusBar { pub height: f32, pub item_spacing: f32, pub cursor_position: TextStyle, - pub active_language: Interactive, + pub active_language: Interactive, pub auto_update_progress_message: TextStyle, pub auto_update_done_message: TextStyle, pub lsp_status: Interactive, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 664833de1e..26989102e4 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -346,8 +346,8 @@ pub fn initialize_workspace( status_bar.add_left_item(feedback_button, cx); status_bar.add_left_item(diagnostic_summary, cx); status_bar.add_left_item(activity_indicator, cx); - status_bar.add_right_item(cursor_position, cx); status_bar.add_right_item(active_buffer_language, cx); + status_bar.add_right_item(cursor_position, cx); }); auto_update::notify_of_any_new_update(cx.weak_handle(), cx); diff --git a/styles/src/styleTree/statusBar.ts b/styles/src/styleTree/statusBar.ts index de33f3d1d5..e5c07c07c2 100644 --- a/styles/src/styleTree/statusBar.ts +++ b/styles/src/styleTree/statusBar.ts @@ -26,6 +26,7 @@ export default function statusBar(colorScheme: ColorScheme) { border: border(layer, { top: true, overlay: true }), cursorPosition: text(layer, "sans", "variant"), activeLanguage: { + padding: { left: 6, right: 6 }, ...text(layer, "sans", "variant"), hover: { ...text(layer, "sans", "on") From bb721a08f56e87391280937686e1be2ce3a95ef4 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Fri, 10 Mar 2023 17:43:48 +0100 Subject: [PATCH 81/93] :lipstick: --- crates/language_selector/src/active_buffer_language.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/language_selector/src/active_buffer_language.rs b/crates/language_selector/src/active_buffer_language.rs index c4129d1448..1da0b4323c 100644 --- a/crates/language_selector/src/active_buffer_language.rs +++ b/crates/language_selector/src/active_buffer_language.rs @@ -27,8 +27,9 @@ impl ActiveBufferLanguage { } fn update_language(&mut self, editor: ViewHandle, cx: &mut ViewContext) { - let editor = editor.read(cx); self.active_language.take(); + + let editor = editor.read(cx); if let Some((_, buffer, _)) = editor.active_excerpt(cx) { if let Some(language) = buffer.read(cx).language() { self.active_language = Some(language.name()); From 281ff92236cfa2fc73e793f9ae4666700ec5c5d7 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 10 Mar 2023 09:54:20 -0800 Subject: [PATCH 82/93] Stub out blank pane experience --- assets/icons/logo_256.svg | 41 ------------------ assets/icons/logo_shadow_256.svg | 23 ---------- crates/workspace/src/pane.rs | 73 ++++---------------------------- 3 files changed, 8 insertions(+), 129 deletions(-) delete mode 100644 assets/icons/logo_256.svg delete mode 100644 assets/icons/logo_shadow_256.svg diff --git a/assets/icons/logo_256.svg b/assets/icons/logo_256.svg deleted file mode 100644 index 4629600b41..0000000000 --- a/assets/icons/logo_256.svg +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/icons/logo_shadow_256.svg b/assets/icons/logo_shadow_256.svg deleted file mode 100644 index 6c331f43ab..0000000000 --- a/assets/icons/logo_shadow_256.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 6d20700bcc..587f180a4a 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -217,8 +217,8 @@ pub struct Pane { toolbar: ViewHandle, tab_bar_context_menu: ViewHandle, docked: Option, - background_actions: BackgroundActions, - workspace_id: usize, + _background_actions: BackgroundActions, + _workspace_id: usize, } pub struct ItemNavHistory { @@ -301,8 +301,8 @@ impl Pane { toolbar: cx.add_view(|_| Toolbar::new(handle)), tab_bar_context_menu: context_menu, docked, - background_actions, - workspace_id, + _background_actions: background_actions, + _workspace_id: workspace_id, } } @@ -1427,68 +1427,11 @@ impl Pane { .boxed() } - fn render_blank_pane(&mut self, theme: &Theme, cx: &mut RenderContext) -> ElementBox { + fn render_blank_pane(&mut self, theme: &Theme, _cx: &mut RenderContext) -> ElementBox { let background = theme.workspace.background; - let theme = &theme.workspace.blank_pane; - Stack::new() - .with_children([ - Empty::new() - .contained() - .with_background_color(background) - .boxed(), - Flex::column() - .align_children_center() - .with_children([ - Stack::new() - .with_children([ - theme::ui::icon(&theme.logo_shadow).aligned().boxed(), - theme::ui::icon(&theme.logo).aligned().boxed(), - ]) - .contained() - .with_style(theme.logo_container) - .boxed(), - Flex::column() - .with_children({ - enum KeyboardHint {} - let keyboard_hint = &theme.keyboard_hint; - let workspace_id = self.workspace_id; - (self.background_actions)().into_iter().enumerate().map( - move |(idx, (text, action))| { - let hint_action = action.boxed_clone(); - MouseEventHandler::::new( - idx, - cx, - move |state, cx| { - let style = keyboard_hint.style_for(state, false); - theme::ui::keystroke_label_for( - cx.window_id(), - workspace_id, - text, - &style, - &style, - hint_action, - ) - .boxed() - }, - ) - .on_click(MouseButton::Left, move |_, cx| { - cx.dispatch_any_action(action.boxed_clone()) - }) - .with_cursor_style(CursorStyle::PointingHand) - .boxed() - }, - ) - }) - .contained() - .with_style(theme.keyboard_hints) - .constrained() - .with_max_width(theme.keyboard_hint_width) - .aligned() - .boxed(), - ]) - .aligned() - .boxed(), - ]) + Empty::new() + .contained() + .with_background_color(background) .boxed() } } From 00a38e4c3b1149ba11f0a5dbde0b1be78681f5db Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 10 Mar 2023 12:12:32 -0800 Subject: [PATCH 83/93] Bound the search range for the keybindings by the highest handler path --- crates/gpui/src/app.rs | 58 ++++++++++++++++++----- crates/gpui/src/keymap_matcher/binding.rs | 9 ---- 2 files changed, 46 insertions(+), 21 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index ab88a39a9a..2f29815f03 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -485,7 +485,9 @@ pub struct MutableAppContext { cx: AppContext, action_deserializers: HashMap<&'static str, (TypeId, DeserializeActionCallback)>, capture_actions: HashMap>>>, + // Entity Types -> { Action Types -> Action Handlers } actions: HashMap>>>, + // Action Types -> Action Handlers global_actions: HashMap>, keystroke_matcher: KeymapMatcher, next_entity_id: usize, @@ -1239,20 +1241,34 @@ impl MutableAppContext { action: &dyn Action, ) -> Option> { let mut contexts = Vec::new(); - for view_id in self.ancestors(window_id, view_id) { + let mut highest_handler = None; + for (i, view_id) in self.ancestors(window_id, view_id).enumerate() { if let Some(view) = self.views.get(&(window_id, view_id)) { + if let Some(actions) = self.actions.get(&view.as_any().type_id()) { + if actions.contains_key(&action.as_any().type_id()) { + highest_handler = Some(i); + } + } contexts.push(view.keymap_context(self)); } } + if self.global_actions.contains_key(&action.as_any().type_id()) { + highest_handler = Some(contexts.len()) + } + self.keystroke_matcher .bindings_for_action_type(action.as_any().type_id()) .find_map(|b| { - if b.match_dispatch_path_context(&contexts) { - Some(b.keystrokes().into()) - } else { - None - } + highest_handler + .map(|highest_handler| { + if (0..=highest_handler).any(|depth| b.match_context(&contexts[depth..])) { + Some(b.keystrokes().into()) + } else { + None + } + }) + .flatten() }) } @@ -1261,29 +1277,47 @@ impl MutableAppContext { window_id: usize, view_id: usize, ) -> impl Iterator, SmallVec<[&Binding; 1]>)> { - let mut action_types: HashSet<_> = self.global_actions.keys().copied().collect(); - let mut contexts = Vec::new(); - for view_id in self.ancestors(window_id, view_id) { + let mut handler_depth_by_action_type = HashMap::>::default(); + for (i, view_id) in self.ancestors(window_id, view_id).enumerate() { if let Some(view) = self.views.get(&(window_id, view_id)) { contexts.push(view.keymap_context(self)); let view_type = view.as_any().type_id(); if let Some(actions) = self.actions.get(&view_type) { - action_types.extend(actions.keys().copied()); + handler_depth_by_action_type.extend( + actions + .keys() + .copied() + .map(|action_type| (action_type, Some(i))), + ); } } } + handler_depth_by_action_type.extend( + self.global_actions + .keys() + .copied() + .map(|action_type| (action_type, None)), + ); + self.action_deserializers .iter() .filter_map(move |(name, (type_id, deserialize))| { - if action_types.contains(type_id) { + if let Some(action_depth) = handler_depth_by_action_type.get(type_id) { Some(( *name, deserialize("{}").ok()?, self.keystroke_matcher .bindings_for_action_type(*type_id) - .filter(|b| b.match_dispatch_path_context(&contexts)) + .filter(|b| { + if let Some(action_depth) = *action_depth { + (0..=action_depth) + .any(|depth| b.match_context(&contexts[depth..])) + } else { + true + } + }) .collect(), )) } else { diff --git a/crates/gpui/src/keymap_matcher/binding.rs b/crates/gpui/src/keymap_matcher/binding.rs index 1b0217b5ff..c1cfd14e82 100644 --- a/crates/gpui/src/keymap_matcher/binding.rs +++ b/crates/gpui/src/keymap_matcher/binding.rs @@ -42,15 +42,6 @@ impl Binding { .unwrap_or(true) } - pub fn match_dispatch_path_context(&self, contexts: &[KeymapContext]) -> bool { - for i in 0..contexts.len() { - if self.match_context(&contexts[i..]) { - return true; - } - } - false - } - pub fn match_keys_and_context( &self, pending_keystrokes: &Vec, From ddbffd2c41d01a7c79284fa86ba31d5785158d70 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 10 Mar 2023 12:23:29 -0800 Subject: [PATCH 84/93] Make terminal fallback correctly when unable to deserialize your cwd --- crates/terminal_view/src/terminal_view.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/crates/terminal_view/src/terminal_view.rs b/crates/terminal_view/src/terminal_view.rs index 110815e870..330be6dcf4 100644 --- a/crates/terminal_view/src/terminal_view.rs +++ b/crates/terminal_view/src/terminal_view.rs @@ -653,7 +653,7 @@ impl Item for TerminalView { fn deserialize( project: ModelHandle, - _workspace: WeakViewHandle, + workspace: WeakViewHandle, workspace_id: workspace::WorkspaceId, item_id: workspace::ItemId, cx: &mut ViewContext, @@ -663,7 +663,18 @@ impl Item for TerminalView { let cwd = TERMINAL_DB .get_working_directory(item_id, workspace_id) .log_err() - .flatten(); + .flatten() + .or_else(|| { + cx.read(|cx| { + let strategy = cx.global::().terminal_strategy(); + workspace + .upgrade(cx) + .map(|workspace| { + get_working_directory(workspace.read(cx), cx, strategy) + }) + .flatten() + }) + }); cx.update(|cx| { let terminal = project.update(cx, |project, cx| { From e45104a1c046cd2dd441f0363cd25be933763627 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 10 Mar 2023 12:48:11 -0800 Subject: [PATCH 85/93] Move feedback to overflow menu and help menu --- Cargo.lock | 1 + crates/collab_ui/Cargo.toml | 1 + crates/collab_ui/src/collab_titlebar_item.rs | 18 ++++++++++++++---- crates/zed/src/menus.rs | 1 + crates/zed/src/zed.rs | 3 --- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f19d4455f..ade18d799e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1259,6 +1259,7 @@ dependencies = [ "collections", "context_menu", "editor", + "feedback", "futures 0.3.25", "fuzzy", "gpui", diff --git a/crates/collab_ui/Cargo.toml b/crates/collab_ui/Cargo.toml index 899f8cc8b4..2afeb8ad8a 100644 --- a/crates/collab_ui/Cargo.toml +++ b/crates/collab_ui/Cargo.toml @@ -29,6 +29,7 @@ clock = { path = "../clock" } collections = { path = "../collections" } context_menu = { path = "../context_menu" } editor = { path = "../editor" } +feedback = { path = "../feedback" } fuzzy = { path = "../fuzzy" } gpui = { path = "../gpui" } menu = { path = "../menu" } diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs index c838f6a55f..4968097ac5 100644 --- a/crates/collab_ui/src/collab_titlebar_item.rs +++ b/crates/collab_ui/src/collab_titlebar_item.rs @@ -304,12 +304,22 @@ impl CollabTitlebarItem { label: "Sign out".into(), action: Box::new(SignOut), }, + ContextMenuItem::Item { + label: "Give Feedback".into(), + action: Box::new(feedback::feedback_editor::GiveFeedback), + }, ] } else { - vec![ContextMenuItem::Item { - label: "Sign in".into(), - action: Box::new(Authenticate), - }] + vec![ + ContextMenuItem::Item { + label: "Sign in".into(), + action: Box::new(Authenticate), + }, + ContextMenuItem::Item { + label: "Give Feedback".into(), + action: Box::new(feedback::feedback_editor::GiveFeedback), + }, + ] }; user_menu.show( diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index bb519c7a95..2c16d4ba8b 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -140,6 +140,7 @@ pub fn menus() -> Vec> { MenuItem::action("View Telemetry Log", crate::OpenTelemetryLog), MenuItem::action("View Dependency Licenses", crate::OpenLicenses), MenuItem::separator(), + MenuItem::action("Give us feedback", feedback::feedback_editor::GiveFeedback), MenuItem::action( "Copy System Specs Into Clipboard", feedback::CopySystemSpecsIntoClipboard, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 26989102e4..d742c733e5 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -340,10 +340,7 @@ pub fn initialize_workspace( activity_indicator::ActivityIndicator::new(workspace, app_state.languages.clone(), cx); let active_buffer_language = cx.add_view(|_| language_selector::ActiveBufferLanguage::new()); let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new()); - let feedback_button = - cx.add_view(|_| feedback::deploy_feedback_button::DeployFeedbackButton {}); workspace.status_bar().update(cx, |status_bar, cx| { - status_bar.add_left_item(feedback_button, cx); status_bar.add_left_item(diagnostic_summary, cx); status_bar.add_left_item(activity_indicator, cx); status_bar.add_right_item(active_buffer_language, cx); From 9398de6a577f3d88e08135f50027e21f80cf8b4e Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 10 Mar 2023 13:15:36 -0800 Subject: [PATCH 86/93] Add feedback for telemetry item when there's no data --- crates/zed/src/zed.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 20dbcdb7ef..bebc195464 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -577,8 +577,13 @@ fn open_telemetry_log_file( workspace.with_local_workspace(&app_state.clone(), cx, move |_, cx| { cx.spawn_weak(|workspace, mut cx| async move { let workspace = workspace.upgrade(&cx)?; - let path = app_state.client.telemetry_log_file_path()?; - let log = app_state.fs.load(&path).await.log_err()?; + + async fn fetch_log_string(app_state: &Arc) -> Option { + let path = app_state.client.telemetry_log_file_path()?; + app_state.fs.load(&path).await.log_err() + } + + let log = fetch_log_string(&app_state).await.unwrap_or_else(|| "// No data has been collected yet".to_string()); const MAX_TELEMETRY_LOG_LEN: usize = 5 * 1024 * 1024; let mut start_offset = log.len().saturating_sub(MAX_TELEMETRY_LOG_LEN); From 0a5cf4b83174816e02bc72a456e9e545a96261ea Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 10 Mar 2023 16:54:57 -0500 Subject: [PATCH 87/93] Avoid panic scanning recursive symlink before gitignore is encountered --- crates/project/src/worktree.rs | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 8b622ab607..40b8881067 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -2361,7 +2361,7 @@ impl BackgroundScanner { job: &ScanJob, ) -> Result<()> { let mut new_entries: Vec = Vec::new(); - let mut new_jobs: Vec = Vec::new(); + let mut new_jobs: Vec> = Vec::new(); let mut ignore_stack = job.ignore_stack.clone(); let mut new_ignore = None; @@ -2374,6 +2374,7 @@ impl BackgroundScanner { continue; } }; + let child_name = child_abs_path.file_name().unwrap(); let child_path: Arc = job.path.join(child_name).into(); let child_metadata = match self.fs.metadata(&child_abs_path).await { @@ -2412,12 +2413,15 @@ impl BackgroundScanner { let entry_abs_path = self.abs_path().join(&entry.path); entry.is_ignored = ignore_stack.is_abs_path_ignored(&entry_abs_path, entry.is_dir()); + if entry.is_dir() { - new_jobs.next().unwrap().ignore_stack = if entry.is_ignored { - IgnoreStack::all() - } else { - ignore_stack.clone() - }; + if let Some(job) = new_jobs.next().expect("Missing scan job for entry") { + job.ignore_stack = if entry.is_ignored { + IgnoreStack::all() + } else { + ignore_stack.clone() + }; + } } } } @@ -2433,10 +2437,12 @@ impl BackgroundScanner { let is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, true); child_entry.is_ignored = is_ignored; + // Avoid recursing until crash in the case of a recursive symlink if !job.ancestor_inodes.contains(&child_entry.inode) { let mut ancestor_inodes = job.ancestor_inodes.clone(); ancestor_inodes.insert(child_entry.inode); - new_jobs.push(ScanJob { + + new_jobs.push(Some(ScanJob { abs_path: child_abs_path, path: child_path, ignore_stack: if is_ignored { @@ -2446,7 +2452,9 @@ impl BackgroundScanner { }, ancestor_inodes, scan_queue: job.scan_queue.clone(), - }); + })); + } else { + new_jobs.push(None); } } else { child_entry.is_ignored = ignore_stack.is_abs_path_ignored(&child_abs_path, false); @@ -2461,8 +2469,11 @@ impl BackgroundScanner { new_ignore, self.fs.as_ref(), ); + for new_job in new_jobs { - job.scan_queue.send(new_job).await.unwrap(); + if let Some(new_job) = new_job { + job.scan_queue.send(new_job).await.unwrap(); + } } Ok(()) From adf94a16812b979206ce8ad5d0593c32cf696c2d Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 10 Mar 2023 14:43:28 -0800 Subject: [PATCH 88/93] Switch from using the key window to the main window mac platform API When the help menu is open, the help menu's search field is the key window, and this was causing menu item action resolution to fail co-authored-by: Max --- crates/gpui/src/app.rs | 2 +- crates/gpui/src/app/menu.rs | 6 +++--- crates/gpui/src/platform.rs | 2 +- crates/gpui/src/platform/mac/platform.rs | 4 ++-- crates/gpui/src/platform/mac/window.rs | 8 ++++---- crates/gpui/src/platform/test.rs | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 0397032de8..cc8facafc6 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1294,7 +1294,7 @@ impl MutableAppContext { pub fn is_action_available(&self, action: &dyn Action) -> bool { let action_type = action.as_any().type_id(); - if let Some(window_id) = self.cx.platform.key_window_id() { + if let Some(window_id) = self.cx.platform.main_window_id() { if let Some(focused_view_id) = self.focused_view_id(window_id) { for view_id in self.ancestors(window_id, focused_view_id) { if let Some(view) = self.views.get(&(window_id, view_id)) { diff --git a/crates/gpui/src/app/menu.rs b/crates/gpui/src/app/menu.rs index 5fe307a7c1..b0965539a5 100644 --- a/crates/gpui/src/app/menu.rs +++ b/crates/gpui/src/app/menu.rs @@ -77,9 +77,9 @@ pub(crate) fn setup_menu_handlers(foreground_platform: &dyn ForegroundPlatform, let cx = app.0.clone(); move |action| { let mut cx = cx.borrow_mut(); - if let Some(key_window_id) = cx.cx.platform.key_window_id() { - if let Some(view_id) = cx.focused_view_id(key_window_id) { - cx.handle_dispatch_action_from_effect(key_window_id, Some(view_id), action); + if let Some(main_window_id) = cx.cx.platform.main_window_id() { + if let Some(view_id) = cx.focused_view_id(main_window_id) { + cx.handle_dispatch_action_from_effect(main_window_id, Some(view_id), action); return; } } diff --git a/crates/gpui/src/platform.rs b/crates/gpui/src/platform.rs index 76c2707d26..4c1ba49690 100644 --- a/crates/gpui/src/platform.rs +++ b/crates/gpui/src/platform.rs @@ -58,7 +58,7 @@ pub trait Platform: Send + Sync { options: WindowOptions, executor: Rc, ) -> Box; - fn key_window_id(&self) -> Option; + fn main_window_id(&self) -> Option; fn add_status_item(&self) -> Box; diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index aeec02c8ca..9bb6430c64 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -587,8 +587,8 @@ impl platform::Platform for MacPlatform { Box::new(Window::open(id, options, executor, self.fonts())) } - fn key_window_id(&self) -> Option { - Window::key_window_id() + fn main_window_id(&self) -> Option { + Window::main_window_id() } fn add_status_item(&self) -> Box { diff --git a/crates/gpui/src/platform/mac/window.rs b/crates/gpui/src/platform/mac/window.rs index 19173707fb..a0c1820368 100644 --- a/crates/gpui/src/platform/mac/window.rs +++ b/crates/gpui/src/platform/mac/window.rs @@ -604,12 +604,12 @@ impl Window { } } - pub fn key_window_id() -> Option { + pub fn main_window_id() -> Option { unsafe { let app = NSApplication::sharedApplication(nil); - let key_window: id = msg_send![app, keyWindow]; - if msg_send![key_window, isKindOfClass: WINDOW_CLASS] { - let id = get_window_state(&*key_window).borrow().id; + let main_window: id = msg_send![app, mainWindow]; + if msg_send![main_window, isKindOfClass: WINDOW_CLASS] { + let id = get_window_state(&*main_window).borrow().id; Some(id) } else { None diff --git a/crates/gpui/src/platform/test.rs b/crates/gpui/src/platform/test.rs index 194684bd12..a3532dd96e 100644 --- a/crates/gpui/src/platform/test.rs +++ b/crates/gpui/src/platform/test.rs @@ -157,7 +157,7 @@ impl super::Platform for Platform { })) } - fn key_window_id(&self) -> Option { + fn main_window_id(&self) -> Option { None } From ece2af1e223b299b329d4b2b1762daa8a47798c2 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 10 Mar 2023 15:36:20 -0800 Subject: [PATCH 89/93] Fix a corner case in available action resolution Add tests for new keybinding resolution behavior co-authored-by: max --- crates/gpui/src/app.rs | 152 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 135 insertions(+), 17 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 2f29815f03..1dbea615ed 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1241,12 +1241,12 @@ impl MutableAppContext { action: &dyn Action, ) -> Option> { let mut contexts = Vec::new(); - let mut highest_handler = None; + let mut handler_depth = None; for (i, view_id) in self.ancestors(window_id, view_id).enumerate() { if let Some(view) = self.views.get(&(window_id, view_id)) { if let Some(actions) = self.actions.get(&view.as_any().type_id()) { if actions.contains_key(&action.as_any().type_id()) { - highest_handler = Some(i); + handler_depth = Some(i); } } contexts.push(view.keymap_context(self)); @@ -1254,13 +1254,13 @@ impl MutableAppContext { } if self.global_actions.contains_key(&action.as_any().type_id()) { - highest_handler = Some(contexts.len()) + handler_depth = Some(contexts.len()) } self.keystroke_matcher .bindings_for_action_type(action.as_any().type_id()) .find_map(|b| { - highest_handler + handler_depth .map(|highest_handler| { if (0..=highest_handler).any(|depth| b.match_context(&contexts[depth..])) { Some(b.keystrokes().into()) @@ -1278,45 +1278,40 @@ impl MutableAppContext { view_id: usize, ) -> impl Iterator, SmallVec<[&Binding; 1]>)> { let mut contexts = Vec::new(); - let mut handler_depth_by_action_type = HashMap::>::default(); - for (i, view_id) in self.ancestors(window_id, view_id).enumerate() { + let mut handler_depths_by_action_type = HashMap::::default(); + for (depth, view_id) in self.ancestors(window_id, view_id).enumerate() { if let Some(view) = self.views.get(&(window_id, view_id)) { contexts.push(view.keymap_context(self)); let view_type = view.as_any().type_id(); if let Some(actions) = self.actions.get(&view_type) { - handler_depth_by_action_type.extend( + handler_depths_by_action_type.extend( actions .keys() .copied() - .map(|action_type| (action_type, Some(i))), + .map(|action_type| (action_type, depth)), ); } } } - handler_depth_by_action_type.extend( + handler_depths_by_action_type.extend( self.global_actions .keys() .copied() - .map(|action_type| (action_type, None)), + .map(|action_type| (action_type, contexts.len())), ); self.action_deserializers .iter() .filter_map(move |(name, (type_id, deserialize))| { - if let Some(action_depth) = handler_depth_by_action_type.get(type_id) { + if let Some(action_depth) = handler_depths_by_action_type.get(type_id).copied() { Some(( *name, deserialize("{}").ok()?, self.keystroke_matcher .bindings_for_action_type(*type_id) .filter(|b| { - if let Some(action_depth) = *action_depth { - (0..=action_depth) - .any(|depth| b.match_context(&contexts[depth..])) - } else { - true - } + (0..=action_depth).any(|depth| b.match_context(&contexts[depth..])) }) .collect(), )) @@ -5321,6 +5316,7 @@ impl Subscription { mod tests { use super::*; use crate::{actions, elements::*, impl_actions, MouseButton, MouseButtonEvent}; + use itertools::Itertools; use postage::{sink::Sink, stream::Stream}; use serde::Deserialize; use smol::future::poll_once; @@ -6751,6 +6747,128 @@ mod tests { actions.borrow_mut().clear(); } + #[crate::test(self)] + fn test_keystrokes_for_action(cx: &mut MutableAppContext) { + actions!(test, [Action1, Action2, GlobalAction]); + + struct View1 {} + struct View2 {} + + impl Entity for View1 { + type Event = (); + } + impl Entity for View2 { + type Event = (); + } + + impl super::View for View1 { + fn render(&mut self, _: &mut RenderContext) -> ElementBox { + Empty::new().boxed() + } + fn ui_name() -> &'static str { + "View1" + } + } + impl super::View for View2 { + fn render(&mut self, _: &mut RenderContext) -> ElementBox { + Empty::new().boxed() + } + fn ui_name() -> &'static str { + "View2" + } + } + + let (window_id, view_1) = cx.add_window(Default::default(), |_| View1 {}); + let view_2 = cx.add_view(&view_1, |cx| { + cx.focus_self(); + View2 {} + }); + + cx.add_action(|_: &mut View1, _: &Action1, _cx| {}); + cx.add_action(|_: &mut View2, _: &Action2, _cx| {}); + cx.add_global_action(|_: &GlobalAction, _| {}); + + cx.add_bindings(vec![ + Binding::new("a", Action1, Some("View1")), + Binding::new("b", Action2, Some("View1 > View2")), + Binding::new("c", GlobalAction, Some("View3")), // View 3 does not exist + ]); + + // Sanity check + assert_eq!( + cx.keystrokes_for_action(window_id, view_1.id(), &Action1) + .unwrap() + .as_slice(), + &[Keystroke::parse("a").unwrap()] + ); + assert_eq!( + cx.keystrokes_for_action(window_id, view_2.id(), &Action2) + .unwrap() + .as_slice(), + &[Keystroke::parse("b").unwrap()] + ); + + // The 'a' keystroke propagates up the view tree from view_2 + // to view_1. The action, Action1, is handled by view_1. + assert_eq!( + cx.keystrokes_for_action(window_id, view_2.id(), &Action1) + .unwrap() + .as_slice(), + &[Keystroke::parse("a").unwrap()] + ); + + // Actions that are handled below the current view don't have bindings + assert_eq!( + cx.keystrokes_for_action(window_id, view_1.id(), &Action2), + None + ); + + // Actions that are handled in other branches of the tree should not have a binding + assert_eq!( + cx.keystrokes_for_action(window_id, view_2.id(), &GlobalAction), + None + ); + + // Produces a list of actions and keybindings + fn available_actions( + window_id: usize, + view_id: usize, + cx: &mut MutableAppContext, + ) -> Vec<(&'static str, Vec)> { + cx.available_actions(window_id, view_id) + .map(|(action_name, _, bindings)| { + ( + action_name, + bindings + .iter() + .map(|binding| binding.keystrokes()[0].clone()) + .collect::>(), + ) + }) + .sorted_by(|(name1, _), (name2, _)| name1.cmp(name2)) + .collect() + } + + // Check that global actions do not have a binding, even if a binding does exist in another view + assert_eq!( + &available_actions(window_id, view_1.id(), cx), + &[ + ("test::Action1", vec![Keystroke::parse("a").unwrap()]), + ("test::GlobalAction", vec![]) + ], + ); + + // Check that view 1 actions and bindings are available even when called from view 2 + assert_eq!( + &available_actions(window_id, view_2.id(), cx), + &[ + ("test::Action1", vec![Keystroke::parse("a").unwrap()]), + ("test::Action2", vec![Keystroke::parse("b").unwrap()]), + ("test::GlobalAction", vec![]), + ], + ); + } + #[crate::test(self)] async fn test_model_condition(cx: &mut TestAppContext) { struct Counter(usize); From 11d8394af249f1a2f2641bac9454c204a0ef1b86 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 10 Mar 2023 15:54:42 -0800 Subject: [PATCH 90/93] Fix the terminal icon button to only be active when a terminal is focused --- crates/workspace/src/terminal_button.rs | 26 ++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/crates/workspace/src/terminal_button.rs b/crates/workspace/src/terminal_button.rs index 118df7cab1..b4ad662c94 100644 --- a/crates/workspace/src/terminal_button.rs +++ b/crates/workspace/src/terminal_button.rs @@ -11,7 +11,6 @@ pub struct TerminalButton { workspace: WeakViewHandle, } -// TODO: Rename this to `DeployTerminalButton` impl TerminalButton { pub fn new(workspace: ViewHandle, cx: &mut ViewContext) -> Self { // When terminal moves, redraw so that the icon and toggle status matches. @@ -39,6 +38,13 @@ impl View for TerminalButton { return Empty::new().boxed(); } + let focused_view = cx.focused_view_id(cx.window_id()); + + // FIXME: Don't hardcode "Terminal" in here + let active = focused_view + .map(|view| cx.view_ui_name(cx.window_id(), view) == Some("Terminal")) + .unwrap_or(false); + // let workspace = workspace.unwrap(); let theme = cx.global::().theme.clone(); @@ -50,7 +56,7 @@ impl View for TerminalButton { .status_bar .sidebar_buttons .item - .style_for(state, true); + .style_for(state, active); Svg::new("icons/terminal_12.svg") .with_color(style.icon_color) @@ -63,14 +69,10 @@ impl View for TerminalButton { } }) .with_cursor_style(CursorStyle::PointingHand) - .on_up(MouseButton::Left, move |_, _| { - // TODO: Do we need this stuff? - // let dock_pane = workspace.read(cx.app).dock_pane(); - // let drop_index = dock_pane.read(cx.app).items_len() + 1; - // handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx); - }) - .on_click(MouseButton::Left, |_, cx| { - cx.dispatch_action(FocusDock); + .on_click(MouseButton::Left, move |_, cx| { + if !active { + cx.dispatch_action(FocusDock); + } }) .with_tooltip::( 0, @@ -84,5 +86,7 @@ impl View for TerminalButton { } impl StatusItemView for TerminalButton { - fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, _: &mut ViewContext) {} + fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, cx: &mut ViewContext) { + cx.notify(); + } } From c8de738972142fba2fefe7ea59aff501d93d5bb6 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Fri, 10 Mar 2023 16:19:33 -0800 Subject: [PATCH 91/93] Align feedback button styles with other sidebar buttons Make feedback button reflect whether you're in a feedback buffer --- crates/feedback/src/deploy_feedback_button.rs | 47 +++++++++++++++---- crates/theme/src/theme.rs | 1 - crates/zed/src/zed.rs | 3 ++ styles/src/styleTree/statusBar.ts | 9 ---- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/crates/feedback/src/deploy_feedback_button.rs b/crates/feedback/src/deploy_feedback_button.rs index 7519d2d06e..222c542eed 100644 --- a/crates/feedback/src/deploy_feedback_button.rs +++ b/crates/feedback/src/deploy_feedback_button.rs @@ -2,39 +2,58 @@ use gpui::{elements::*, CursorStyle, Entity, MouseButton, RenderContext, View, V use settings::Settings; use workspace::{item::ItemHandle, StatusItemView}; -use crate::feedback_editor::GiveFeedback; +use crate::feedback_editor::{FeedbackEditor, GiveFeedback}; -pub struct DeployFeedbackButton; +pub struct DeployFeedbackButton { + active: bool, +} impl Entity for DeployFeedbackButton { type Event = (); } +impl DeployFeedbackButton { + pub fn new() -> Self { + DeployFeedbackButton { active: false } + } +} + impl View for DeployFeedbackButton { fn ui_name() -> &'static str { "DeployFeedbackButton" } fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox { + let active = self.active; Stack::new() .with_child( MouseEventHandler::::new(0, cx, |state, cx| { let theme = &cx.global::().theme; - let style = &theme.workspace.status_bar.feedback.style_for(state, false); + let style = &theme + .workspace + .status_bar + .sidebar_buttons + .item + .style_for(state, active); + Svg::new("icons/speech_bubble_12.svg") - .with_color(style.color) + .with_color(style.icon_color) .constrained() - .with_width(style.icon_width) + .with_width(style.icon_size) .aligned() .constrained() - .with_width(style.button_width) - .with_height(style.button_width) + .with_width(style.icon_size) + .with_height(style.icon_size) .contained() .with_style(style.container) .boxed() }) .with_cursor_style(CursorStyle::PointingHand) - .on_click(MouseButton::Left, |_, cx| cx.dispatch_action(GiveFeedback)) + .on_click(MouseButton::Left, move |_, cx| { + if !active { + cx.dispatch_action(GiveFeedback) + } + }) .boxed(), ) .boxed() @@ -42,5 +61,15 @@ impl View for DeployFeedbackButton { } impl StatusItemView for DeployFeedbackButton { - fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, _: &mut ViewContext) {} + fn set_active_pane_item(&mut self, item: Option<&dyn ItemHandle>, cx: &mut ViewContext) { + if let Some(item) = item { + if let Some(_) = item.downcast::() { + self.active = true; + cx.notify(); + return; + } + } + self.active = false; + cx.notify(); + } } diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index 87504d6cd9..70ee22d37d 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -281,7 +281,6 @@ pub struct StatusBar { pub auto_update_progress_message: TextStyle, pub auto_update_done_message: TextStyle, pub lsp_status: Interactive, - pub feedback: Interactive, pub sidebar_buttons: StatusBarSidebarButtons, pub diagnostic_summary: Interactive, pub diagnostic_message: Interactive, diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index d742c733e5..79ec9b4118 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -339,10 +339,13 @@ pub fn initialize_workspace( let activity_indicator = activity_indicator::ActivityIndicator::new(workspace, app_state.languages.clone(), cx); let active_buffer_language = cx.add_view(|_| language_selector::ActiveBufferLanguage::new()); + let feedback_button = + cx.add_view(|_| feedback::deploy_feedback_button::DeployFeedbackButton::new()); let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new()); workspace.status_bar().update(cx, |status_bar, cx| { status_bar.add_left_item(diagnostic_summary, cx); status_bar.add_left_item(activity_indicator, cx); + status_bar.add_right_item(feedback_button, cx); status_bar.add_right_item(active_buffer_language, cx); status_bar.add_right_item(cursor_position, cx); }); diff --git a/styles/src/styleTree/statusBar.ts b/styles/src/styleTree/statusBar.ts index e5c07c07c2..9fa427d302 100644 --- a/styles/src/styleTree/statusBar.ts +++ b/styles/src/styleTree/statusBar.ts @@ -51,15 +51,6 @@ export default function statusBar(colorScheme: ColorScheme) { ...text(layer, "sans"), hover: text(layer, "sans", "hovered"), }, - feedback: { - margin: { left: 2 }, - color: foreground(layer, "variant"), - iconWidth: 14, - buttonWidth: 14, - hover: { - color: foreground(layer, "on"), - }, - }, diagnosticSummary: { height: 20, iconWidth: 16, From ff1c7db38fb3c7ceaae347b8fae6f7c4c9264eb0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 10 Mar 2023 16:36:03 -0800 Subject: [PATCH 92/93] collab 0.7.1 --- Cargo.lock | 2 +- crates/collab/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 21d78cd599..384ded5b3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1188,7 +1188,7 @@ dependencies = [ [[package]] name = "collab" -version = "0.7.0" +version = "0.7.1" dependencies = [ "anyhow", "async-tungstenite", diff --git a/crates/collab/Cargo.toml b/crates/collab/Cargo.toml index a2ad7d7343..0f06a14b68 100644 --- a/crates/collab/Cargo.toml +++ b/crates/collab/Cargo.toml @@ -3,7 +3,7 @@ authors = ["Nathan Sobo "] default-run = "collab" edition = "2021" name = "collab" -version = "0.7.0" +version = "0.7.1" publish = false [[bin]] From e8b3d4e0fa8c95b1078bbda05f5fde981d5e9b2e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 10 Mar 2023 17:19:16 -0800 Subject: [PATCH 93/93] Encode db-max-connections env var as a string in k8s manifest --- crates/collab/k8s/manifest.template.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/collab/k8s/manifest.template.yml b/crates/collab/k8s/manifest.template.yml index 75c7aa989d..0662a287d4 100644 --- a/crates/collab/k8s/manifest.template.yml +++ b/crates/collab/k8s/manifest.template.yml @@ -81,7 +81,7 @@ spec: name: database key: url - name: DATABASE_MAX_CONNECTIONS - value: ${DATABASE_MAX_CONNECTIONS} + value: "${DATABASE_MAX_CONNECTIONS}" - name: API_TOKEN valueFrom: secretKeyRef: