From 663bbb06d9e80d4f61ea5f2ae3598e6c930d04dd Mon Sep 17 00:00:00 2001 From: Mikayla Date: Tue, 21 Nov 2023 12:40:00 -0800 Subject: [PATCH] WIP --- Cargo.lock | 46 +++ Cargo.toml | 2 + crates/client2/src/client2.rs | 8 +- crates/gpui2/src/action.rs | 1 + crates/theme2/src/registry.rs | 4 + crates/theme_selector2/Cargo.toml | 28 ++ crates/theme_selector2/src/theme_selector.rs | 254 ++++++++++++++++ crates/welcome2/Cargo.toml | 36 +++ crates/welcome2/src/base_keymap_picker.rs | 152 ++++++++++ crates/welcome2/src/base_keymap_setting.rs | 65 +++++ crates/welcome2/src/welcome.rs | 287 +++++++++++++++++++ crates/zed2/Cargo.toml | 4 +- crates/zed2/src/main.rs | 83 +++--- script/crate-dep-graph | 2 +- 14 files changed, 921 insertions(+), 51 deletions(-) create mode 100644 crates/theme_selector2/Cargo.toml create mode 100644 crates/theme_selector2/src/theme_selector.rs create mode 100644 crates/welcome2/Cargo.toml create mode 100644 crates/welcome2/src/base_keymap_picker.rs create mode 100644 crates/welcome2/src/base_keymap_setting.rs create mode 100644 crates/welcome2/src/welcome.rs diff --git a/Cargo.lock b/Cargo.lock index 6aa94b08d0..2184ba5ebb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9405,6 +9405,26 @@ dependencies = [ "workspace", ] +[[package]] +name = "theme_selector2" +version = "0.1.0" +dependencies = [ + "editor2", + "feature_flags2", + "fs2", + "fuzzy2", + "gpui2", + "log", + "parking_lot 0.11.2", + "picker2", + "postage", + "settings2", + "smol", + "theme2", + "util", + "workspace2", +] + [[package]] name = "thiserror" version = "1.0.48" @@ -10978,6 +10998,30 @@ dependencies = [ "workspace", ] +[[package]] +name = "welcome2" +version = "0.1.0" +dependencies = [ + "anyhow", + "client2", + "db2", + "editor2", + "fs2", + "fuzzy2", + "gpui2", + "install_cli2", + "log", + "picker2", + "project2", + "schemars", + "serde", + "settings2", + "theme2", + "theme_selector2", + "util", + "workspace2", +] + [[package]] name = "which" version = "4.4.2" @@ -11640,6 +11684,7 @@ dependencies = [ "terminal_view2", "text2", "theme2", + "theme_selector2", "thiserror", "tiny_http", "toml 0.5.11", @@ -11676,6 +11721,7 @@ dependencies = [ "urlencoding", "util", "uuid 1.4.1", + "welcome2", "workspace2", "zed_actions2", ] diff --git a/Cargo.toml b/Cargo.toml index d7b9918f62..6c1152cf9c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -108,6 +108,7 @@ members = [ "crates/theme2", "crates/theme_importer", "crates/theme_selector", + "crates/theme_selector2", "crates/ui2", "crates/util", "crates/semantic_index", @@ -115,6 +116,7 @@ members = [ "crates/vcs_menu", "crates/workspace2", "crates/welcome", + "crates/welcome2", "crates/xtask", "crates/zed", "crates/zed2", diff --git a/crates/client2/src/client2.rs b/crates/client2/src/client2.rs index b4279b023e..028dec6803 100644 --- a/crates/client2/src/client2.rs +++ b/crates/client2/src/client2.rs @@ -694,8 +694,8 @@ impl Client { } } - pub async fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool { - read_credentials_from_keychain(cx).await.is_some() + pub fn has_keychain_credentials(&self, cx: &AsyncAppContext) -> bool { + read_credentials_from_keychain(cx).is_some() } #[async_recursion(?Send)] @@ -726,7 +726,7 @@ impl Client { let mut read_from_keychain = false; let mut credentials = self.state.read().credentials.clone(); if credentials.is_none() && try_keychain { - credentials = read_credentials_from_keychain(cx).await; + credentials = read_credentials_from_keychain(cx); read_from_keychain = credentials.is_some(); } if credentials.is_none() { @@ -1325,7 +1325,7 @@ impl Client { } } -async fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option { +fn read_credentials_from_keychain(cx: &AsyncAppContext) -> Option { if IMPERSONATE_LOGIN.is_some() { return None; } diff --git a/crates/gpui2/src/action.rs b/crates/gpui2/src/action.rs index 958eaabdb8..03ef2d2281 100644 --- a/crates/gpui2/src/action.rs +++ b/crates/gpui2/src/action.rs @@ -162,6 +162,7 @@ macro_rules! actions { ( $name:ident ) => { #[derive(::std::cmp::PartialEq, ::std::clone::Clone, ::std::default::Default, gpui::serde_derive::Deserialize, gpui::Action)] + #[serde(crate = "gpui::serde")] pub struct $name; }; diff --git a/crates/theme2/src/registry.rs b/crates/theme2/src/registry.rs index 919dd1b109..b50eb831dd 100644 --- a/crates/theme2/src/registry.rs +++ b/crates/theme2/src/registry.rs @@ -86,6 +86,10 @@ impl ThemeRegistry { })); } + pub fn clear(&mut self) { + self.themes.clear(); + } + pub fn list_names(&self, _staff: bool) -> impl Iterator + '_ { self.themes.keys().cloned() } diff --git a/crates/theme_selector2/Cargo.toml b/crates/theme_selector2/Cargo.toml new file mode 100644 index 0000000000..89b7487a7b --- /dev/null +++ b/crates/theme_selector2/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "theme_selector2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/theme_selector.rs" +doctest = false + +[dependencies] +editor = { package = "editor2", path = "../editor2" } +fuzzy = { package = "fuzzy2", path = "../fuzzy2" } +fs = { package = "fs2", path = "../fs2" } +gpui = { package = "gpui2", path = "../gpui2" } +picker = { package = "picker2", path = "../picker2" } +theme = { package = "theme2", path = "../theme2" } +settings = { package = "settings2", path = "../settings2" } +feature_flags = { package = "feature_flags2", path = "../feature_flags2" } +workspace = { package = "workspace2", path = "../workspace2" } +util = { path = "../util" } +log.workspace = true +parking_lot.workspace = true +postage.workspace = true +smol.workspace = true + +[dev-dependencies] +editor = { package = "editor2", path = "../editor2", features = ["test-support"] } diff --git a/crates/theme_selector2/src/theme_selector.rs b/crates/theme_selector2/src/theme_selector.rs new file mode 100644 index 0000000000..6e660caf51 --- /dev/null +++ b/crates/theme_selector2/src/theme_selector.rs @@ -0,0 +1,254 @@ +use feature_flags::FeatureFlagAppExt; +use fs::Fs; +use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; +use gpui::{ + actions, div, AppContext, Div, EventEmitter, FocusableView, Manager, Render, SharedString, + View, ViewContext, VisualContext, +}; +use picker::{Picker, PickerDelegate}; +use settings::{update_settings_file, SettingsStore}; +use std::sync::Arc; +use theme::{ActiveTheme, Theme, ThemeRegistry, ThemeSettings}; +use util::ResultExt; +use workspace::{ui::HighlightedLabel, Workspace}; + +actions!(Toggle, Reload); + +pub fn init(cx: &mut AppContext) { + cx.observe_new_views( + |workspace: &mut Workspace, cx: &mut ViewContext| { + workspace.register_action(toggle); + }, + ); +} + +pub fn toggle(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext) { + let fs = workspace.app_state().fs.clone(); + workspace.toggle_modal(cx, |cx| { + ThemeSelector::new(ThemeSelectorDelegate::new(fs, cx), cx) + }); +} + +#[cfg(debug_assertions)] +pub fn reload(cx: &mut AppContext) { + let current_theme_name = cx.theme().name.clone(); + let registry = cx.global::>(); + registry.clear(); + match registry.get(¤t_theme_name) { + Ok(theme) => { + ThemeSelectorDelegate::set_theme(theme, cx); + log::info!("reloaded theme {}", current_theme_name); + } + Err(error) => { + log::error!("failed to load theme {}: {:?}", current_theme_name, error) + } + } +} + +pub struct ThemeSelector { + picker: View>, +} + +impl EventEmitter for ThemeSelector {} + +impl FocusableView for ThemeSelector { + fn focus_handle(&self, cx: &AppContext) -> gpui::FocusHandle { + self.picker.focus_handle(cx) + } +} + +impl Render for ThemeSelector { + type Element = View>; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + self.picker.clone() + } +} + +impl ThemeSelector { + pub fn new(delegate: ThemeSelectorDelegate, cx: &mut ViewContext) -> Self { + let picker = cx.build_view(|cx| Picker::new(delegate, cx)); + Self { picker } + } +} + +pub struct ThemeSelectorDelegate { + fs: Arc, + theme_names: Vec, + matches: Vec, + original_theme: Arc, + selection_completed: bool, + selected_index: usize, +} + +impl ThemeSelectorDelegate { + fn new(fs: Arc, cx: &mut ViewContext) -> Self { + let original_theme = cx.theme().clone(); + + let staff_mode = cx.is_staff(); + let registry = cx.global::>(); + let mut theme_names = registry.list(staff_mode).collect::>(); + 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 { + candidate_id: 0, + score: 0.0, + positions: Default::default(), + string: meta.to_string(), + }) + .collect(); + let mut this = Self { + fs, + theme_names, + matches, + original_theme: original_theme.clone(), + selected_index: 0, + selection_completed: false, + }; + this.select_if_matching(&original_theme.meta.name); + this + } + + fn show_selected_theme(&mut self, cx: &mut ViewContext) { + if let Some(mat) = self.matches.get(self.selected_index) { + let registry = cx.global::>(); + match registry.get(&mat.string) { + Ok(theme) => { + Self::set_theme(theme, cx); + } + Err(error) => { + log::error!("error loading theme {}: {}", mat.string, error) + } + } + } + } + + fn select_if_matching(&mut self, theme_name: &str) { + self.selected_index = self + .matches + .iter() + .position(|mat| mat.string == theme_name) + .unwrap_or(self.selected_index); + } + + fn set_theme(theme: Arc, cx: &mut AppContext) { + cx.update_global::(|store, cx| { + let mut theme_settings = store.get::(None).clone(); + theme_settings.theme = theme; + store.override_global(theme_settings); + cx.refresh_windows(); + }); + } +} + +impl PickerDelegate for ThemeSelectorDelegate { + type ListItem = Div; + + fn placeholder_text(&self) -> Arc { + "Select Theme...".into() + } + + fn match_count(&self) -> usize { + self.matches.len() + } + + fn confirm(&mut self, _: bool, cx: &mut ViewContext) { + self.selection_completed = true; + + let theme_name = cx.theme().meta.name.clone(); + update_settings_file::(self.fs.clone(), cx, |settings| { + settings.theme = Some(theme_name); + }); + + cx.emit(Manager::Dismiss); + } + + fn dismissed(&mut self, cx: &mut ViewContext) { + if !self.selection_completed { + Self::set_theme(self.original_theme.clone(), cx); + self.selection_completed = true; + } + } + + fn selected_index(&self) -> usize { + self.selected_index + } + + fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext) { + self.selected_index = ix; + self.show_selected_theme(cx); + } + + fn update_matches( + &mut self, + query: String, + cx: &mut ViewContext, + ) -> gpui::Task<()> { + let background = cx.background().clone(); + let candidates = self + .theme_names + .iter() + .enumerate() + .map(|(id, meta)| StringMatchCandidate { + id, + char_bag: meta.name.as_str().into(), + string: meta.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| { + let delegate = this.delegate_mut(); + delegate.matches = matches; + delegate.selected_index = delegate + .selected_index + .min(delegate.matches.len().saturating_sub(1)); + delegate.show_selected_theme(cx); + }) + .log_err(); + }) + } + + fn render_match(&self, ix: usize, selected: bool, cx: &AppContext) -> Self::ListItem { + let theme = cx.theme(); + let colors = theme.colors(); + + let theme_match = &self.matches[ix]; + div() + .px_1() + .text_color(colors.text) + .text_ui() + .bg(colors.ghost_element_background) + .rounded_md() + .when(selected, |this| this.bg(colors.ghost_element_selected)) + .hover(|this| this.bg(colors.ghost_element_hover)) + .child(HighlightedLabel::new( + theme_match.string.clone(), + theme_match.positions.clone(), + )) + } +} diff --git a/crates/welcome2/Cargo.toml b/crates/welcome2/Cargo.toml new file mode 100644 index 0000000000..0a2d2fd781 --- /dev/null +++ b/crates/welcome2/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "welcome2" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/welcome.rs" + +[features] +test-support = [] + +[dependencies] +client = { package = "client2", path = "../client2" } +editor = { package = "editor2", path = "../editor2" } +fs = { package = "fs2", path = "../fs2" } +fuzzy = { package = "fuzzy2", path = "../fuzzy2" } +gpui = { package = "gpui2", path = "../gpui2" } +db = { package = "db2", path = "../db2" } +install_cli = { package = "install_cli2", path = "../install_cli2" } +project = { package = "project2", path = "../project2" } +settings = { package = "settings2", path = "../settings2" } +theme = { package = "theme2", path = "../theme2" } +theme_selector = { package = "theme_selector2", path = "../theme_selector2" } +util = { path = "../util" } +picker = { package = "picker2", path = "../picker2" } +workspace = { package = "workspace2", path = "../workspace2" } +# vim = { package = "vim2", path = "../vim2" } + +anyhow.workspace = true +log.workspace = true +schemars.workspace = true +serde.workspace = true + +[dev-dependencies] +editor = { package = "editor2", path = "../editor2", features = ["test-support"] } diff --git a/crates/welcome2/src/base_keymap_picker.rs b/crates/welcome2/src/base_keymap_picker.rs new file mode 100644 index 0000000000..021e3b86a0 --- /dev/null +++ b/crates/welcome2/src/base_keymap_picker.rs @@ -0,0 +1,152 @@ +use super::base_keymap_setting::BaseKeymap; +use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; +use gpui::{ + actions, + elements::{Element as _, Label}, + AppContext, Task, ViewContext, +}; +use picker::{Picker, PickerDelegate, PickerEvent}; +use project::Fs; +use settings::update_settings_file; +use std::sync::Arc; +use util::ResultExt; +use workspace::Workspace; + +actions!(welcome, [ToggleBaseKeymapSelector]); + +pub fn init(cx: &mut AppContext) { + cx.add_action(toggle); + BaseKeymapSelector::init(cx); +} + +pub fn toggle( + workspace: &mut Workspace, + _: &ToggleBaseKeymapSelector, + cx: &mut ViewContext, +) { + workspace.toggle_modal(cx, |workspace, cx| { + let fs = workspace.app_state().fs.clone(); + cx.add_view(|cx| BaseKeymapSelector::new(BaseKeymapSelectorDelegate::new(fs, cx), cx)) + }); +} + +pub type BaseKeymapSelector = Picker; + +pub struct BaseKeymapSelectorDelegate { + matches: Vec, + selected_index: usize, + fs: Arc, +} + +impl BaseKeymapSelectorDelegate { + fn new(fs: Arc, cx: &mut ViewContext) -> Self { + let base = settings::get::(cx); + let selected_index = BaseKeymap::OPTIONS + .iter() + .position(|(_, value)| value == base) + .unwrap_or(0); + Self { + matches: Vec::new(), + selected_index, + fs, + } + } +} + +impl PickerDelegate for BaseKeymapSelectorDelegate { + fn placeholder_text(&self) -> Arc { + "Select a base keymap...".into() + } + + 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, + ) -> 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, _| { + let delegate = this.delegate_mut(); + delegate.matches = matches; + delegate.selected_index = delegate + .selected_index + .min(delegate.matches.len().saturating_sub(1)); + }) + .log_err(); + }) + } + + fn confirm(&mut self, _: bool, cx: &mut ViewContext) { + if let Some(selection) = self.matches.get(self.selected_index) { + let base_keymap = BaseKeymap::from_names(&selection.string); + update_settings_file::(self.fs.clone(), cx, move |setting| { + *setting = Some(base_keymap) + }); + } + cx.emit(PickerEvent::Dismiss); + } + + fn dismissed(&mut self, _cx: &mut ViewContext) {} + + fn render_match( + &self, + ix: usize, + mouse_state: &mut gpui::MouseState, + selected: bool, + cx: &gpui::AppContext, + ) -> gpui::AnyElement> { + let theme = &theme::current(cx); + let keymap_match = &self.matches[ix]; + let style = theme.picker.item.in_state(selected).style_for(mouse_state); + + Label::new(keymap_match.string.clone(), style.label.clone()) + .with_highlights(keymap_match.positions.clone()) + .contained() + .with_style(style.container) + .into_any() + } +} diff --git a/crates/welcome2/src/base_keymap_setting.rs b/crates/welcome2/src/base_keymap_setting.rs new file mode 100644 index 0000000000..c5b6171f9b --- /dev/null +++ b/crates/welcome2/src/base_keymap_setting.rs @@ -0,0 +1,65 @@ +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; +use settings::Setting; + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)] +pub enum BaseKeymap { + #[default] + VSCode, + JetBrains, + SublimeText, + Atom, + TextMate, +} + +impl BaseKeymap { + pub const OPTIONS: [(&'static str, Self); 5] = [ + ("VSCode (Default)", Self::VSCode), + ("Atom", Self::Atom), + ("JetBrains", Self::JetBrains), + ("Sublime Text", Self::SublimeText), + ("TextMate", Self::TextMate), + ]; + + pub fn asset_path(&self) -> Option<&'static str> { + match self { + BaseKeymap::JetBrains => Some("keymaps/jetbrains.json"), + BaseKeymap::SublimeText => Some("keymaps/sublime_text.json"), + BaseKeymap::Atom => Some("keymaps/atom.json"), + BaseKeymap::TextMate => Some("keymaps/textmate.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() + } +} + +impl Setting for BaseKeymap { + const KEY: Option<&'static str> = Some("base_keymap"); + + type FileContent = Option; + + fn load( + default_value: &Self::FileContent, + user_values: &[&Self::FileContent], + _: &gpui::AppContext, + ) -> anyhow::Result + where + Self: Sized, + { + Ok(user_values + .first() + .and_then(|v| **v) + .unwrap_or(default_value.unwrap())) + } +} diff --git a/crates/welcome2/src/welcome.rs b/crates/welcome2/src/welcome.rs new file mode 100644 index 0000000000..a5d95429bd --- /dev/null +++ b/crates/welcome2/src/welcome.rs @@ -0,0 +1,287 @@ +mod base_keymap_picker; +mod base_keymap_setting; + +use crate::base_keymap_picker::ToggleBaseKeymapSelector; +use client::TelemetrySettings; +use db::kvp::KEY_VALUE_STORE; +use gpui::{ + elements::{Flex, Label, ParentElement}, + AnyElement, AppContext, Element, Entity, Subscription, View, ViewContext, WeakViewHandle, +}; +use settings::{update_settings_file, SettingsStore}; +use std::{borrow::Cow, sync::Arc}; +use vim::VimModeSetting; +use workspace::{ + dock::DockPosition, item::Item, open_new, AppState, PaneBackdrop, Welcome, Workspace, + WorkspaceId, +}; + +pub use base_keymap_setting::BaseKeymap; + +pub const FIRST_OPEN: &str = "first_open"; + +pub fn init(cx: &mut AppContext) { + settings::register::(cx); + + cx.add_action(|workspace: &mut Workspace, _: &Welcome, cx| { + let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, cx)); + workspace.add_item(Box::new(welcome_page), cx) + }); + + base_keymap_picker::init(cx); +} + +pub fn show_welcome_experience(app_state: &Arc, cx: &mut AppContext) { + open_new(&app_state, cx, |workspace, cx| { + workspace.toggle_dock(DockPosition::Left, cx); + let welcome_page = cx.add_view(|cx| WelcomePage::new(workspace, 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 { + workspace: WeakViewHandle, + _settings_subscription: Subscription, +} + +impl Entity for WelcomePage { + type Event = (); +} + +impl View for WelcomePage { + fn ui_name() -> &'static str { + "WelcomePage" + } + + fn render(&mut self, cx: &mut gpui::ViewContext) -> AnyElement { + let self_handle = cx.handle(); + let theme = theme::current(cx); + let width = theme.welcome.page_width; + + let telemetry_settings = *settings::get::(cx); + let vim_mode_setting = settings::get::(cx).0; + + enum Metrics {} + enum Diagnostics {} + + PaneBackdrop::new( + self_handle.id(), + Flex::column() + .with_child( + Flex::column() + .with_child( + theme::ui::svg(&theme.welcome.logo) + .aligned() + .contained() + .aligned(), + ) + .with_child( + Label::new( + "Code at the speed of thought", + theme.welcome.logo_subheading.text.clone(), + ) + .aligned() + .contained() + .with_style(theme.welcome.logo_subheading.container), + ) + .contained() + .with_style(theme.welcome.heading_group) + .constrained() + .with_width(width), + ) + .with_child( + Flex::column() + .with_child(theme::ui::cta_button::( + "Choose a theme", + width, + &theme.welcome.button, + cx, + |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + theme_selector::toggle(workspace, &Default::default(), cx) + }) + } + }, + )) + .with_child(theme::ui::cta_button::( + "Choose a keymap", + width, + &theme.welcome.button, + cx, + |_, this, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + workspace.update(cx, |workspace, cx| { + base_keymap_picker::toggle( + workspace, + &Default::default(), + cx, + ) + }) + } + }, + )) + .with_child(theme::ui::cta_button::( + "Install the CLI", + width, + &theme.welcome.button, + cx, + |_, _, cx| { + cx.app_context() + .spawn(|cx| async move { install_cli::install_cli(&cx).await }) + .detach_and_log_err(cx); + }, + )) + .contained() + .with_style(theme.welcome.button_group) + .constrained() + .with_width(width), + ) + .with_child( + Flex::column() + .with_child( + theme::ui::checkbox::( + "Enable vim mode", + &theme.welcome.checkbox, + vim_mode_setting, + 0, + cx, + |this, checked, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + let fs = workspace.read(cx).app_state().fs.clone(); + update_settings_file::( + fs, + cx, + move |setting| *setting = Some(checked), + ) + } + }, + ) + .contained() + .with_style(theme.welcome.checkbox_container), + ) + .with_child( + theme::ui::checkbox_with_label::( + Flex::column() + .with_child( + Label::new( + "Send anonymous usage data", + theme.welcome.checkbox.label.text.clone(), + ) + .contained() + .with_style(theme.welcome.checkbox.label.container), + ) + .with_child( + Label::new( + "Help > View Telemetry", + theme.welcome.usage_note.text.clone(), + ) + .contained() + .with_style(theme.welcome.usage_note.container), + ), + &theme.welcome.checkbox, + telemetry_settings.metrics, + 0, + cx, + |this, checked, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + let fs = workspace.read(cx).app_state().fs.clone(); + update_settings_file::( + fs, + cx, + move |setting| setting.metrics = Some(checked), + ) + } + }, + ) + .contained() + .with_style(theme.welcome.checkbox_container), + ) + .with_child( + theme::ui::checkbox::( + "Send crash reports", + &theme.welcome.checkbox, + telemetry_settings.diagnostics, + 1, + cx, + |this, checked, cx| { + if let Some(workspace) = this.workspace.upgrade(cx) { + let fs = workspace.read(cx).app_state().fs.clone(); + update_settings_file::( + fs, + cx, + move |setting| setting.diagnostics = Some(checked), + ) + } + }, + ) + .contained() + .with_style(theme.welcome.checkbox_container), + ) + .contained() + .with_style(theme.welcome.checkbox_group) + .constrained() + .with_width(width), + ) + .constrained() + .with_max_width(width) + .contained() + .with_uniform_padding(10.) + .aligned() + .into_any(), + ) + .into_any_named("welcome page") + } +} + +impl WelcomePage { + pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { + WelcomePage { + workspace: workspace.weak_handle(), + _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), + } + } +} + +impl Item for WelcomePage { + fn tab_tooltip_text(&self, _: &AppContext) -> Option> { + Some("Welcome to Zed!".into()) + } + + fn tab_content( + &self, + _detail: Option, + style: &theme::Tab, + _cx: &gpui::AppContext, + ) -> AnyElement { + Flex::row() + .with_child( + Label::new("Welcome to Zed!", style.label.clone()) + .aligned() + .contained(), + ) + .into_any() + } + + fn show_toolbar(&self) -> bool { + false + } + + fn clone_on_split( + &self, + _workspace_id: WorkspaceId, + cx: &mut ViewContext, + ) -> Option { + Some(WelcomePage { + workspace: self.workspace.clone(), + _settings_subscription: cx.observe_global::(move |_, cx| cx.notify()), + }) + } +} diff --git a/crates/zed2/Cargo.toml b/crates/zed2/Cargo.toml index 24648f87f1..5aba7faaa0 100644 --- a/crates/zed2/Cargo.toml +++ b/crates/zed2/Cargo.toml @@ -66,12 +66,12 @@ shellexpand = "2.1.0" text = { package = "text2", path = "../text2" } terminal_view = { package = "terminal_view2", path = "../terminal_view2" } theme = { package = "theme2", path = "../theme2" } -# theme_selector = { path = "../theme_selector" } +theme_selector = { package = "theme_selector2", path = "../theme_selector2" } util = { path = "../util" } # semantic_index = { path = "../semantic_index" } # vim = { path = "../vim" } workspace = { package = "workspace2", path = "../workspace2" } -# welcome = { path = "../welcome" } +welcome = { package = "welcome2", path = "../welcome2" } zed_actions = {package = "zed_actions2", path = "../zed_actions2"} anyhow.workspace = true async-compression = { version = "0.3", features = ["gzip", "futures-bufread"] } diff --git a/crates/zed2/src/main.rs b/crates/zed2/src/main.rs index 9c42badb85..648c4108d7 100644 --- a/crates/zed2/src/main.rs +++ b/crates/zed2/src/main.rs @@ -8,7 +8,7 @@ use anyhow::{anyhow, Context as _, Result}; use backtrace::Backtrace; use chrono::Utc; use cli::FORCE_CLI_MODE_ENV_VAR_NAME; -use client::UserStore; +use client::{Client, UserStore}; use db::kvp::KEY_VALUE_STORE; use editor::Editor; use fs::RealFs; @@ -36,7 +36,7 @@ use std::{ path::{Path, PathBuf}, sync::{ atomic::{AtomicU32, Ordering}, - Arc, + Arc, Weak, }, thread, }; @@ -99,16 +99,15 @@ fn main() { let listener = Arc::new(listener); let open_listener = listener.clone(); app.on_open_urls(move |urls, _| open_listener.open_urls(&urls)); - app.on_reopen(move |_cx| { - // todo!("workspace") - // if cx.has_global::>() { - // if let Some(app_state) = cx.global::>().upgrade() { - // workspace::open_new(&app_state, cx, |workspace, cx| { - // Editor::new_file(workspace, &Default::default(), cx) - // }) - // .detach(); - // } - // } + app.on_reopen(move |cx| { + if cx.has_global::>() { + if let Some(app_state) = cx.global::>().upgrade() { + workspace::open_new(&app_state, cx, |workspace, cx| { + Editor::new_file(workspace, &Default::default(), cx) + }) + .detach(); + } + } }); app.run(move |cx| { @@ -180,7 +179,6 @@ fn main() { user_store, fs, build_window_options, - // background_actions: todo!("ask Mikayla"), workspace_store, node_runtime, }); @@ -236,7 +234,7 @@ fn main() { } } - let mut _triggered_authentication = false; + let mut triggered_authentication = false; fn open_paths_and_log_errs( paths: &[PathBuf], @@ -266,17 +264,17 @@ fn main() { .detach(); } Ok(Some(OpenRequest::JoinChannel { channel_id: _ })) => { - todo!() - // triggered_authentication = true; - // let app_state = app_state.clone(); - // let client = client.clone(); - // cx.spawn(|mut cx| async move { - // // ignore errors here, we'll show a generic "not signed in" - // let _ = authenticate(client, &cx).await; - // cx.update(|cx| workspace::join_channel(channel_id, app_state, None, cx)) - // .await - // }) - // .detach_and_log_err(cx) + triggered_authentication = true; + let app_state = app_state.clone(); + let client = client.clone(); + cx.spawn(|mut cx| async move { + // ignore errors here, we'll show a generic "not signed in" + let _ = authenticate(client, &cx).await; + // cx.update(|cx| workspace::join_channel(channel_id, app_state, None, cx)) + // .await + anyhow::Ok(()) + }) + .detach_and_log_err(cx) } Ok(Some(OpenRequest::OpenChannelNotes { channel_id: _ })) => { todo!() @@ -315,23 +313,23 @@ fn main() { }) .detach(); - // if !triggered_authentication { - // cx.spawn(|cx| async move { authenticate(client, &cx).await }) - // .detach_and_log_err(cx); - // } + if !triggered_authentication { + cx.spawn(|cx| async move { authenticate(client, &cx).await }) + .detach_and_log_err(cx); + } }); } -// async fn authenticate(client: Arc, cx: &AsyncAppContext) -> Result<()> { -// if stdout_is_a_pty() { -// if client::IMPERSONATE_LOGIN.is_some() { -// client.authenticate_and_connect(false, &cx).await?; -// } -// } else if client.has_keychain_credentials(&cx) { -// client.authenticate_and_connect(true, &cx).await?; -// } -// Ok::<_, anyhow::Error>(()) -// } +async fn authenticate(client: Arc, cx: &AsyncAppContext) -> Result<()> { + if stdout_is_a_pty() { + if client::IMPERSONATE_LOGIN.is_some() { + client.authenticate_and_connect(false, &cx).await?; + } + } else if client.has_keychain_credentials(&cx) { + client.authenticate_and_connect(true, &cx).await?; + } + Ok::<_, anyhow::Error>(()) +} async fn installation_id() -> Result { let legacy_key_name = "device_id"; @@ -355,11 +353,8 @@ async fn restore_or_create_workspace(app_state: &Arc, mut cx: AsyncApp cx.update(|cx| workspace::open_paths(location.paths().as_ref(), app_state, None, cx))? .await .log_err(); - } else if matches!(KEY_VALUE_STORE.read_kvp("******* THIS IS A BAD KEY PLEASE UNCOMMENT BELOW TO FIX THIS VERY LONG LINE *******"), Ok(None)) { - // todo!(welcome) - //} else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { - //todo!() - // cx.update(|cx| show_welcome_experience(app_state, cx)); + } else if matches!(KEY_VALUE_STORE.read_kvp(FIRST_OPEN), Ok(None)) { + cx.update(|cx| show_welcome_experience(app_state, cx)); } else { cx.update(|cx| { workspace::open_new(app_state, cx, |workspace, cx| { diff --git a/script/crate-dep-graph b/script/crate-dep-graph index 25285cc097..74ea36683c 100755 --- a/script/crate-dep-graph +++ b/script/crate-dep-graph @@ -11,7 +11,7 @@ graph_file=target/crate-graph.html cargo depgraph \ --workspace-only \ --offline \ - --root=zed,cli,collab \ + --root=zed2,cli,collab2 \ --dedup-transitive-deps \ | dot -Tsvg > $graph_file