diff --git a/Cargo.lock b/Cargo.lock index d1b0e62ca7..8925fa3fe7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,22 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "activity_indicator" +version = "0.1.0" +dependencies = [ + "auto_update", + "editor", + "futures", + "gpui", + "language", + "project", + "settings", + "smallvec", + "util", + "workspace", +] + [[package]] name = "addr2line" version = "0.17.0" @@ -2566,21 +2582,6 @@ dependencies = [ "url", ] -[[package]] -name = "lsp_status" -version = "0.1.0" -dependencies = [ - "editor", - "futures", - "gpui", - "language", - "project", - "settings", - "smallvec", - "util", - "workspace", -] - [[package]] name = "malloc_buf" version = "0.0.6" @@ -5971,6 +5972,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" name = "zed" version = "0.42.0" dependencies = [ + "activity_indicator", "anyhow", "assets", "async-compression", @@ -6011,7 +6013,6 @@ dependencies = [ "libc", "log", "lsp", - "lsp_status", "num_cpus", "outline", "parking_lot 0.11.2", diff --git a/crates/lsp_status/Cargo.toml b/crates/activity_indicator/Cargo.toml similarity index 78% rename from crates/lsp_status/Cargo.toml rename to crates/activity_indicator/Cargo.toml index 19d428d3b2..63998fa47b 100644 --- a/crates/lsp_status/Cargo.toml +++ b/crates/activity_indicator/Cargo.toml @@ -1,13 +1,14 @@ [package] -name = "lsp_status" +name = "activity_indicator" version = "0.1.0" edition = "2021" [lib] -path = "src/lsp_status.rs" +path = "src/activity_indicator.rs" doctest = false [dependencies] +auto_update = { path = "../auto_update" } editor = { path = "../editor" } language = { path = "../language" } gpui = { path = "../gpui" } diff --git a/crates/lsp_status/src/lsp_status.rs b/crates/activity_indicator/src/activity_indicator.rs similarity index 60% rename from crates/lsp_status/src/lsp_status.rs rename to crates/activity_indicator/src/activity_indicator.rs index fedf5299de..8bc84f911c 100644 --- a/crates/lsp_status/src/lsp_status.rs +++ b/crates/activity_indicator/src/activity_indicator.rs @@ -1,7 +1,8 @@ +use auto_update::{AutoUpdateStatus, AutoUpdater, DismissErrorMessage}; use editor::Editor; use futures::StreamExt; use gpui::{ - actions, elements::*, platform::CursorStyle, AppContext, Entity, EventContext, ModelHandle, + actions, elements::*, platform::CursorStyle, Action, AppContext, Entity, ModelHandle, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; use language::{LanguageRegistry, LanguageServerBinaryStatus}; @@ -14,13 +15,18 @@ use workspace::{ItemHandle, StatusItemView, Workspace}; actions!(lsp_status, [ShowErrorMessage]); +const DOWNLOAD_ICON: &'static str = "icons/download-solid-14.svg"; +const WARNING_ICON: &'static str = "icons/warning-solid-14.svg"; +const DONE_ICON: &'static str = "icons/accept.svg"; + pub enum Event { ShowError { lsp_name: Arc, error: String }, } -pub struct LspStatusItem { +pub struct ActivityIndicator { statuses: Vec, project: ModelHandle, + auto_updater: Option>, } struct LspStatus { @@ -29,15 +35,16 @@ struct LspStatus { } pub fn init(cx: &mut MutableAppContext) { - cx.add_action(LspStatusItem::show_error_message); + cx.add_action(ActivityIndicator::show_error_message); + cx.add_action(ActivityIndicator::dismiss_error_message); } -impl LspStatusItem { +impl ActivityIndicator { pub fn new( workspace: &mut Workspace, languages: Arc, cx: &mut ViewContext, - ) -> ViewHandle { + ) -> ViewHandle { let project = workspace.project().clone(); let this = cx.add_view(|cx: &mut ViewContext| { let mut status_events = languages.language_server_binary_statuses(); @@ -63,6 +70,7 @@ impl LspStatusItem { Self { statuses: Default::default(), project: project.clone(), + auto_updater: AutoUpdater::get(cx), } }); cx.subscribe(&this, move |workspace, _, event, cx| match event { @@ -106,6 +114,15 @@ impl LspStatusItem { cx.notify(); } + fn dismiss_error_message(&mut self, _: &DismissErrorMessage, cx: &mut ViewContext) { + if let Some(updater) = &self.auto_updater { + updater.update(cx, |updater, cx| { + updater.dismiss_error(cx); + }); + } + cx.notify(); + } + fn pending_language_server_work<'a>( &self, cx: &'a AppContext, @@ -129,25 +146,15 @@ impl LspStatusItem { }) .flatten() } -} - -impl Entity for LspStatusItem { - type Event = Event; -} - -impl View for LspStatusItem { - fn ui_name() -> &'static str { - "LspStatus" - } - - fn render(&mut self, cx: &mut RenderContext) -> ElementBox { - let mut message; - let mut icon = None; - let mut handler = None; + fn content_to_render( + &mut self, + cx: &mut RenderContext, + ) -> (Option<&'static str>, String, Option>) { + // Show any language server has pending activity. let mut pending_work = self.pending_language_server_work(cx); if let Some((lang_server_name, progress_token, progress)) = pending_work.next() { - message = lang_server_name.to_string(); + let mut message = lang_server_name.to_string(); message.push_str(": "); if let Some(progress_message) = progress.message.as_ref() { @@ -164,38 +171,43 @@ impl View for LspStatusItem { if additional_work_count > 0 { write!(&mut message, " + {} more", additional_work_count).unwrap(); } - } else { - drop(pending_work); - let mut downloading = SmallVec::<[_; 3]>::new(); - let mut checking_for_update = SmallVec::<[_; 3]>::new(); - let mut failed = SmallVec::<[_; 3]>::new(); - for status in &self.statuses { - match status.status { - LanguageServerBinaryStatus::CheckingForUpdate => { - checking_for_update.push(status.name.clone()); - } - LanguageServerBinaryStatus::Downloading => { - downloading.push(status.name.clone()); - } - LanguageServerBinaryStatus::Failed { .. } => { - failed.push(status.name.clone()); - } - LanguageServerBinaryStatus::Downloaded | LanguageServerBinaryStatus::Cached => { - } + return (None, message, None); + } + + // Show any language server installation info. + let mut downloading = SmallVec::<[_; 3]>::new(); + let mut checking_for_update = SmallVec::<[_; 3]>::new(); + let mut failed = SmallVec::<[_; 3]>::new(); + for status in &self.statuses { + match status.status { + LanguageServerBinaryStatus::CheckingForUpdate => { + checking_for_update.push(status.name.clone()); } + LanguageServerBinaryStatus::Downloading => { + downloading.push(status.name.clone()); + } + LanguageServerBinaryStatus::Failed { .. } => { + failed.push(status.name.clone()); + } + LanguageServerBinaryStatus::Downloaded | LanguageServerBinaryStatus::Cached => {} } + } - if !downloading.is_empty() { - icon = Some("icons/download-solid-14.svg"); - message = format!( + if !downloading.is_empty() { + return ( + Some(DOWNLOAD_ICON), + format!( "Downloading {} language server{}...", downloading.join(", "), if downloading.len() > 1 { "s" } else { "" } - ); - } else if !checking_for_update.is_empty() { - icon = Some("icons/download-solid-14.svg"); - message = format!( + ), + None, + ); + } else if !checking_for_update.is_empty() { + return ( + Some(DOWNLOAD_ICON), + format!( "Checking for updates to {} language server{}...", checking_for_update.join(", "), if checking_for_update.len() > 1 { @@ -203,20 +215,68 @@ impl View for LspStatusItem { } else { "" } - ); - } else if !failed.is_empty() { - icon = Some("icons/warning-solid-14.svg"); - message = format!( + ), + None, + ); + } else if !failed.is_empty() { + return ( + Some(WARNING_ICON), + format!( "Failed to download {} language server{}. Click to show error.", failed.join(", "), if failed.len() > 1 { "s" } else { "" } - ); - handler = Some(|_, _, cx: &mut EventContext| cx.dispatch_action(ShowErrorMessage)); - } else { - return Empty::new().boxed(); - } + ), + Some(Box::new(ShowErrorMessage)), + ); } + // Show any application auto-update info. + if let Some(updater) = &self.auto_updater { + // let theme = &cx.global::().theme.workspace.status_bar; + match &updater.read(cx).status() { + AutoUpdateStatus::Checking => ( + Some(DOWNLOAD_ICON), + "Checking for Zed updates…".to_string(), + None, + ), + AutoUpdateStatus::Downloading => ( + Some(DOWNLOAD_ICON), + "Downloading Zed update…".to_string(), + None, + ), + AutoUpdateStatus::Installing => ( + Some(DOWNLOAD_ICON), + "Installing Zed update…".to_string(), + None, + ), + AutoUpdateStatus::Updated => { + (Some(DONE_ICON), "Restart to update Zed".to_string(), None) + } + AutoUpdateStatus::Errored => ( + Some(WARNING_ICON), + "Auto update failed".to_string(), + Some(Box::new(DismissErrorMessage)), + ), + AutoUpdateStatus::Idle => Default::default(), + } + } else { + Default::default() + } + } +} + +impl Entity for ActivityIndicator { + type Event = Event; +} + +impl View for ActivityIndicator { + fn ui_name() -> &'static str { + "ActivityIndicator" + } + + fn render(&mut self, cx: &mut RenderContext) -> ElementBox { + let (icon, message, action) = self.content_to_render(cx); + let mut element = MouseEventHandler::new::(0, cx, |state, cx| { let theme = &cx .global::() @@ -224,7 +284,7 @@ impl View for LspStatusItem { .workspace .status_bar .lsp_status; - let style = if state.hovered && handler.is_some() { + let style = if state.hovered && action.is_some() { theme.hover.as_ref().unwrap_or(&theme.default) } else { &theme.default @@ -238,9 +298,14 @@ impl View for LspStatusItem { .contained() .with_margin_right(style.icon_spacing) .aligned() - .named("warning-icon") + .named("activity-icon") })) - .with_child(Label::new(message, style.message.clone()).aligned().boxed()) + .with_child( + Text::new(message, style.message.clone()) + .with_soft_wrap(false) + .aligned() + .boxed(), + ) .constrained() .with_height(style.height) .contained() @@ -249,16 +314,16 @@ impl View for LspStatusItem { .boxed() }); - if let Some(handler) = handler { + if let Some(action) = action { element = element .with_cursor_style(CursorStyle::PointingHand) - .on_click(handler); + .on_click(move |_, _, cx| cx.dispatch_any_action(action.boxed_clone())); } element.boxed() } } -impl StatusItemView for LspStatusItem { +impl StatusItemView for ActivityIndicator { fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, _: &mut ViewContext) {} } diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index 44eb5fe2e8..6e4f171f60 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -3,19 +3,15 @@ mod update_notification; use anyhow::{anyhow, Context, Result}; use client::{http::HttpClient, ZED_SECRET_CLIENT_TOKEN}; use gpui::{ - actions, - elements::{Empty, MouseEventHandler, Text}, - platform::AppVersion, - AppContext, AsyncAppContext, Element, Entity, ModelContext, ModelHandle, MutableAppContext, - Task, View, ViewContext, WeakViewHandle, + actions, platform::AppVersion, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, + MutableAppContext, Task, WeakViewHandle, }; use lazy_static::lazy_static; use serde::Deserialize; -use settings::Settings; use smol::{fs::File, io::AsyncReadExt, process::Command}; use std::{env, ffi::OsString, path::PathBuf, sync::Arc, time::Duration}; use update_notification::UpdateNotification; -use workspace::{ItemHandle, StatusItemView, Workspace}; +use workspace::Workspace; const SHOULD_SHOW_UPDATE_NOTIFICATION_KEY: &'static str = "auto-updater-should-show-updated-notification"; @@ -30,7 +26,7 @@ lazy_static! { actions!(auto_update, [Check, DismissErrorMessage, ViewReleaseNotes]); -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub enum AutoUpdateStatus { Idle, Checking, @@ -49,10 +45,6 @@ pub struct AutoUpdater { server_url: String, } -pub struct AutoUpdateIndicator { - updater: Option>, -} - #[derive(Deserialize)] struct JsonRelease { version: String, @@ -84,7 +76,6 @@ pub fn init( cx.add_global_action(move |_: &ViewReleaseNotes, cx| { cx.platform().open_url(&format!("{server_url}/releases")); }); - cx.add_action(AutoUpdateIndicator::dismiss_error_message); cx.add_action(UpdateNotification::dismiss); } } @@ -120,7 +111,7 @@ pub fn notify_of_any_new_update( } impl AutoUpdater { - fn get(cx: &mut MutableAppContext) -> Option> { + pub fn get(cx: &mut MutableAppContext) -> Option> { cx.default_global::>>().clone() } @@ -170,6 +161,15 @@ impl AutoUpdater { })); } + pub fn status(&self) -> AutoUpdateStatus { + self.status + } + + pub fn dismiss_error(&mut self, cx: &mut ModelContext) { + self.status = AutoUpdateStatus::Idle; + cx.notify(); + } + async fn update(this: ModelHandle, mut cx: AsyncAppContext) -> Result<()> { let (client, server_url, current_version) = this.read_with(&cx, |this, _| { ( @@ -299,79 +299,3 @@ impl AutoUpdater { }) } } - -impl Entity for AutoUpdateIndicator { - type Event = (); -} - -impl View for AutoUpdateIndicator { - fn ui_name() -> &'static str { - "AutoUpdateIndicator" - } - - fn render(&mut self, cx: &mut gpui::RenderContext<'_, Self>) -> gpui::ElementBox { - if let Some(updater) = &self.updater { - let theme = &cx.global::().theme.workspace.status_bar; - match &updater.read(cx).status { - AutoUpdateStatus::Checking => Text::new( - "Checking for updates…".to_string(), - theme.auto_update_progress_message.clone(), - ) - .boxed(), - AutoUpdateStatus::Downloading => Text::new( - "Downloading update…".to_string(), - theme.auto_update_progress_message.clone(), - ) - .boxed(), - AutoUpdateStatus::Installing => Text::new( - "Installing update…".to_string(), - theme.auto_update_progress_message.clone(), - ) - .boxed(), - AutoUpdateStatus::Updated => Text::new( - "Restart to update Zed".to_string(), - theme.auto_update_done_message.clone(), - ) - .boxed(), - AutoUpdateStatus::Errored => { - MouseEventHandler::new::(0, cx, |_, cx| { - let theme = &cx.global::().theme.workspace.status_bar; - Text::new( - "Auto update failed".to_string(), - theme.auto_update_done_message.clone(), - ) - .boxed() - }) - .on_click(|_, _, cx| cx.dispatch_action(DismissErrorMessage)) - .boxed() - } - AutoUpdateStatus::Idle => Empty::new().boxed(), - } - } else { - Empty::new().boxed() - } - } -} - -impl StatusItemView for AutoUpdateIndicator { - fn set_active_pane_item(&mut self, _: Option<&dyn ItemHandle>, _: &mut ViewContext) {} -} - -impl AutoUpdateIndicator { - pub fn new(cx: &mut ViewContext) -> Self { - let updater = AutoUpdater::get(cx); - if let Some(updater) = &updater { - cx.observe(updater, |_, _, cx| cx.notify()).detach(); - } - Self { updater } - } - - fn dismiss_error_message(&mut self, _: &DismissErrorMessage, cx: &mut ViewContext) { - if let Some(updater) = &self.updater { - updater.update(cx, |updater, cx| { - updater.status = AutoUpdateStatus::Idle; - cx.notify(); - }); - } - } -} diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 56472be040..c14dce992a 100644 --- a/crates/zed/Cargo.toml +++ b/crates/zed/Cargo.toml @@ -15,6 +15,7 @@ name = "Zed" path = "src/main.rs" [dependencies] +activity_indicator = { path = "../activity_indicator" } assets = { path = "../assets" } auto_update = { path = "../auto_update" } breadcrumbs = { path = "../breadcrumbs" } @@ -37,7 +38,6 @@ gpui = { path = "../gpui" } journal = { path = "../journal" } language = { path = "../language" } lsp = { path = "../lsp" } -lsp_status = { path = "../lsp_status" } outline = { path = "../outline" } project = { path = "../project" } project_panel = { path = "../project_panel" } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 8341f3639e..7240aaef2f 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -139,7 +139,7 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }, ); - lsp_status::init(cx); + activity_indicator::init(cx); settings::KeymapFileContent::load_defaults(cx); } @@ -222,15 +222,14 @@ pub fn initialize_workspace( let diagnostic_summary = cx.add_view(|cx| diagnostics::items::DiagnosticIndicator::new(workspace.project(), cx)); - let lsp_status = lsp_status::LspStatusItem::new(workspace, app_state.languages.clone(), cx); + let activity_indicator = + activity_indicator::ActivityIndicator::new(workspace, app_state.languages.clone(), cx); let cursor_position = cx.add_view(|_| editor::items::CursorPosition::new()); - let auto_update = cx.add_view(|cx| auto_update::AutoUpdateIndicator::new(cx)); let feedback_link = cx.add_view(|_| feedback::FeedbackLink); workspace.status_bar().update(cx, |status_bar, cx| { status_bar.add_left_item(diagnostic_summary, cx); - status_bar.add_left_item(lsp_status, cx); + status_bar.add_left_item(activity_indicator, cx); status_bar.add_right_item(cursor_position, cx); - status_bar.add_right_item(auto_update, cx); status_bar.add_right_item(feedback_link, cx); });