diff --git a/Cargo.lock b/Cargo.lock index 6debc9eb8afae1967df02555e8fd9e48827f9b77..14574cc19a7f1327d6dd3c47a3578f8a715f21a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2759,6 +2759,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +[[package]] +name = "human_bytes" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39b528196c838e8b3da8b665e08c30958a6f2ede91d79f2ffcd0d4664b9c64eb" + [[package]] name = "humantime" version = "2.1.0" @@ -3757,6 +3763,15 @@ dependencies = [ "winapi 0.3.9", ] +[[package]] +name = "ntapi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc51db7b362b205941f71232e56c625156eb9a929f8cf74a428fd5bc094a4afc" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -4426,7 +4441,7 @@ source = "git+https://github.com/zed-industries/wezterm?rev=5cd757e5f2eb039ed0c6 dependencies = [ "libc", "log", - "ntapi", + "ntapi 0.3.7", "winapi 0.3.9", ] @@ -6222,6 +6237,21 @@ dependencies = [ "libc", ] +[[package]] +name = "sysinfo" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccb297c0afb439440834b4bcf02c5c9da8ec2e808e70f36b0d8e815ff403bd24" +dependencies = [ + "cfg-if 1.0.0", + "core-foundation-sys", + "libc", + "ntapi 0.4.0", + "once_cell", + "rayon", + "winapi 0.3.9", +] + [[package]] name = "system-interface" version = "0.20.0" @@ -7204,6 +7234,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8db7427f936968176eaa7cdf81b7f98b980b18495ec28f1b5791ac3bfe3eea9" + [[package]] name = "usvg" version = "0.14.1" @@ -8151,7 +8187,7 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" [[package]] name = "zed" -version = "0.68.0" +version = "0.69.0" dependencies = [ "activity_indicator", "anyhow", @@ -8183,6 +8219,7 @@ dependencies = [ "fuzzy", "go_to_line", "gpui", + "human_bytes", "ignore", "image", "indexmap", @@ -8216,6 +8253,7 @@ dependencies = [ "smallvec", "smol", "sum_tree", + "sysinfo", "tempdir", "terminal_view", "text", @@ -8244,6 +8282,7 @@ dependencies = [ "tree-sitter-typescript", "unindent", "url", + "urlencoding", "util", "vim", "workspace", diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 783d90f8e1deceb7c40d0babbafae536813e9662..fbbb517c936b194318d409bd56c965e81d99d9a7 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -20,8 +20,10 @@ "alt-cmd-left": "pane::ActivatePrevItem", "alt-cmd-right": "pane::ActivateNextItem", "cmd-w": "pane::CloseActiveItem", - "cmd-shift-w": "workspace::CloseWindow", "alt-cmd-t": "pane::CloseInactiveItems", + "cmd-k u": "pane::CloseCleanItems", + "cmd-k cmd-w": "pane::CloseAllItems", + "cmd-shift-w": "workspace::CloseWindow", "cmd-s": "workspace::Save", "cmd-shift-s": "workspace::SaveAs", "cmd-=": "zed::IncreaseBufferFontSize", @@ -67,9 +69,11 @@ "up": "editor::MoveUp", "pageup": "editor::PageUp", "shift-pageup": "editor::MovePageUp", + "home": "editor::MoveToBeginningOfLine", "down": "editor::MoveDown", "pagedown": "editor::PageDown", "shift-pagedown": "editor::MovePageDown", + "end": "editor::MoveToEndOfLine", "left": "editor::MoveLeft", "right": "editor::MoveRight", "ctrl-p": "editor::MoveUp", @@ -110,6 +114,12 @@ "stop_at_soft_wraps": true } ], + "shift-home": [ + "editor::SelectToBeginningOfLine", + { + "stop_at_soft_wraps": true + } + ], "ctrl-shift-a": [ "editor::SelectToBeginningOfLine", { @@ -122,6 +132,12 @@ "stop_at_soft_wraps": true } ], + "shift-end": [ + "editor::SelectToEndOfLine", + { + "stop_at_soft_wraps": true + } + ], "ctrl-shift-e": [ "editor::SelectToEndOfLine", { diff --git a/assets/settings/default.json b/assets/settings/default.json index 0487b11c19fdcbcd3f2ef2d7db0e66cc3a4c73c8..5b73d7643c84d344bd8f133f2b52427c0b952adf 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -221,7 +221,7 @@ // rust-analyzer // typescript-language-server // vscode-json-languageserver - // "rust_analyzer": { + // "rust-analyzer": { // //These initialization options are merged into Zed's defaults // "initialization_options": { // "checkOnSave": { diff --git a/crates/fs/src/fs.rs b/crates/fs/src/fs.rs index 184b2f2e5a009018d100b4249b7e40b26f8bf680..6a79953f41f25c3c8d5037d17b63aec5adb230cc 100644 --- a/crates/fs/src/fs.rs +++ b/crates/fs/src/fs.rs @@ -35,7 +35,7 @@ use repository::FakeGitRepositoryState; use std::sync::Weak; lazy_static! { - static ref CARRIAGE_RETURNS_REGEX: Regex = Regex::new("\r\n|\r").unwrap(); + static ref LINE_SEPERATORS_REGEX: Regex = Regex::new("\r\n|\r|\u{2028}|\u{2029}").unwrap(); } #[derive(Clone, Copy, Debug, PartialEq)] @@ -80,13 +80,13 @@ impl LineEnding { } pub fn normalize(text: &mut String) { - if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(text, "\n") { + if let Cow::Owned(replaced) = LINE_SEPERATORS_REGEX.replace_all(text, "\n") { *text = replaced; } } pub fn normalize_arc(text: Arc) -> Arc { - if let Cow::Owned(replaced) = CARRIAGE_RETURNS_REGEX.replace_all(&text, "\n") { + if let Cow::Owned(replaced) = LINE_SEPERATORS_REGEX.replace_all(&text, "\n") { replaced.into() } else { text diff --git a/crates/gpui/build.rs b/crates/gpui/build.rs index 3fb0119782cdd10aa8698362b8c6e9ce3812ff29..23f6f04992ea65e0f63603558d23d8f2a6a242a8 100644 --- a/crates/gpui/build.rs +++ b/crates/gpui/build.rs @@ -52,7 +52,7 @@ fn compile_metal_shaders() { println!("cargo:rerun-if-changed={}", shader_path); let output = Command::new("xcrun") - .args(&[ + .args([ "-sdk", "macosx", "metal", @@ -76,7 +76,7 @@ fn compile_metal_shaders() { } let output = Command::new("xcrun") - .args(&["-sdk", "macosx", "metallib"]) + .args(["-sdk", "macosx", "metallib"]) .arg(air_output_path) .arg("-o") .arg(metallib_output_path) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index f5ced700b63c370814caa4b538c99366d654395f..5568155cf7e2527bc7c1c6e8662991ed0f9c4a48 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -26,8 +26,8 @@ use smallvec::SmallVec; use smol::prelude::*; pub use action::*; -use callback_collection::{CallbackCollection, Mapping}; -use collections::{btree_map, hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque}; +use callback_collection::CallbackCollection; +use collections::{hash_map::Entry, HashMap, HashSet, VecDeque}; use keymap::MatchResult; use platform::Event; #[cfg(any(test, feature = "test-support"))] @@ -588,9 +588,9 @@ type GlobalActionCallback = dyn FnMut(&dyn Action, &mut MutableAppContext); type SubscriptionCallback = Box bool>; type GlobalSubscriptionCallback = Box; type ObservationCallback = Box bool>; -type FocusObservationCallback = Box bool>; type GlobalObservationCallback = Box; -type ReleaseObservationCallback = Box; +type FocusObservationCallback = Box bool>; +type ReleaseObservationCallback = Box; type ActionObservationCallback = Box; type WindowActivationCallback = Box bool>; type WindowFullscreenCallback = Box bool>; @@ -615,18 +615,17 @@ pub struct MutableAppContext { next_subscription_id: usize, frame_count: usize, - focus_observations: CallbackCollection, - global_subscriptions: CallbackCollection, - global_observations: CallbackCollection, subscriptions: CallbackCollection, + global_subscriptions: CallbackCollection, observations: CallbackCollection, + global_observations: CallbackCollection, + focus_observations: CallbackCollection, + release_observations: CallbackCollection, + action_dispatch_observations: CallbackCollection<(), ActionObservationCallback>, window_activation_observations: CallbackCollection, window_fullscreen_observations: CallbackCollection, keystroke_observations: CallbackCollection, - release_observations: Arc>>>, - action_dispatch_observations: Arc>>, - #[allow(clippy::type_complexity)] presenters_and_platform_windows: HashMap>, Box)>, @@ -1047,12 +1046,10 @@ impl MutableAppContext { callback(payload, cx) }), }); - - Subscription::GlobalSubscription { - id: subscription_id, - type_id, - subscriptions: Some(self.global_subscriptions.downgrade()), - } + Subscription::GlobalSubscription( + self.global_subscriptions + .subscribe(type_id, subscription_id), + ) } pub fn observe(&mut self, handle: &H, mut callback: F) -> Subscription @@ -1089,11 +1086,7 @@ impl MutableAppContext { } }), }); - Subscription::Subscription { - id: subscription_id, - entity_id: handle.id(), - subscriptions: Some(self.subscriptions.downgrade()), - } + Subscription::Subscription(self.subscriptions.subscribe(handle.id(), subscription_id)) } fn observe_internal(&mut self, handle: &H, mut callback: F) -> Subscription @@ -1117,11 +1110,7 @@ impl MutableAppContext { } }), }); - Subscription::Observation { - id: subscription_id, - entity_id, - observations: Some(self.observations.downgrade()), - } + Subscription::Observation(self.observations.subscribe(entity_id, subscription_id)) } fn observe_focus(&mut self, handle: &ViewHandle, mut callback: F) -> Subscription @@ -1144,12 +1133,7 @@ impl MutableAppContext { } }), }); - - Subscription::FocusObservation { - id: subscription_id, - view_id, - observations: Some(self.focus_observations.downgrade()), - } + Subscription::FocusObservation(self.focus_observations.subscribe(view_id, subscription_id)) } pub fn observe_global(&mut self, mut observe: F) -> Subscription @@ -1165,12 +1149,7 @@ impl MutableAppContext { id, Box::new(move |cx: &mut MutableAppContext| observe(cx)), ); - - Subscription::GlobalObservation { - id, - type_id, - observations: Some(self.global_observations.downgrade()), - } + Subscription::GlobalObservation(self.global_observations.subscribe(type_id, id)) } pub fn observe_default_global(&mut self, observe: F) -> Subscription @@ -1192,36 +1171,31 @@ impl MutableAppContext { F: 'static + FnOnce(&E, &mut Self), { let id = post_inc(&mut self.next_subscription_id); - self.release_observations - .lock() - .entry(handle.id()) - .or_default() - .insert( - id, - Box::new(move |released, cx| { - let released = released.downcast_ref().unwrap(); - callback(released, cx) - }), - ); - Subscription::ReleaseObservation { + let mut callback = Some(callback); + self.release_observations.add_callback( + handle.id(), id, - entity_id: handle.id(), - observations: Some(Arc::downgrade(&self.release_observations)), - } + Box::new(move |released, cx| { + let released = released.downcast_ref().unwrap(); + if let Some(callback) = callback.take() { + callback(released, cx) + } + }), + ); + Subscription::ReleaseObservation(self.release_observations.subscribe(handle.id(), id)) } pub fn observe_actions(&mut self, callback: F) -> Subscription where F: 'static + FnMut(TypeId, &mut MutableAppContext), { - let id = post_inc(&mut self.next_subscription_id); + let subscription_id = post_inc(&mut self.next_subscription_id); self.action_dispatch_observations - .lock() - .insert(id, Box::new(callback)); - Subscription::ActionObservation { - id, - observations: Some(Arc::downgrade(&self.action_dispatch_observations)), - } + .add_callback((), subscription_id, Box::new(callback)); + Subscription::ActionObservation( + self.action_dispatch_observations + .subscribe((), subscription_id), + ) } fn observe_window_activation(&mut self, window_id: usize, callback: F) -> Subscription @@ -1235,11 +1209,10 @@ impl MutableAppContext { subscription_id, callback: Box::new(callback), }); - Subscription::WindowActivationObservation { - id: subscription_id, - window_id, - observations: Some(self.window_activation_observations.downgrade()), - } + Subscription::WindowActivationObservation( + self.window_activation_observations + .subscribe(window_id, subscription_id), + ) } fn observe_fullscreen(&mut self, window_id: usize, callback: F) -> Subscription @@ -1253,11 +1226,10 @@ impl MutableAppContext { subscription_id, callback: Box::new(callback), }); - Subscription::WindowFullscreenObservation { - id: subscription_id, - window_id, - observations: Some(self.window_activation_observations.downgrade()), - } + Subscription::WindowActivationObservation( + self.window_activation_observations + .subscribe(window_id, subscription_id), + ) } pub fn observe_keystrokes(&mut self, window_id: usize, callback: F) -> Subscription @@ -1273,12 +1245,10 @@ impl MutableAppContext { let subscription_id = post_inc(&mut self.next_subscription_id); self.keystroke_observations .add_callback(window_id, subscription_id, Box::new(callback)); - - Subscription::KeystrokeObservation { - id: subscription_id, - window_id, - observations: Some(self.keystroke_observations.downgrade()), - } + Subscription::KeystrokeObservation( + self.keystroke_observations + .subscribe(window_id, subscription_id), + ) } pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut MutableAppContext)) { @@ -1999,15 +1969,13 @@ impl MutableAppContext { entity_id, subscription_id, callback, - } => self.subscriptions.add_or_remove_callback( - entity_id, - subscription_id, - callback, - ), + } => self + .subscriptions + .add_callback(entity_id, subscription_id, callback), Effect::Event { entity_id, payload } => { let mut subscriptions = self.subscriptions.clone(); - subscriptions.emit_and_cleanup(entity_id, self, |callback, this| { + subscriptions.emit(entity_id, self, |callback, this| { callback(payload.as_ref(), this) }) } @@ -2016,7 +1984,7 @@ impl MutableAppContext { type_id, subscription_id, callback, - } => self.global_subscriptions.add_or_remove_callback( + } => self.global_subscriptions.add_callback( type_id, subscription_id, callback, @@ -2028,16 +1996,13 @@ impl MutableAppContext { entity_id, subscription_id, callback, - } => self.observations.add_or_remove_callback( - entity_id, - subscription_id, - callback, - ), + } => self + .observations + .add_callback(entity_id, subscription_id, callback), Effect::ModelNotification { model_id } => { let mut observations = self.observations.clone(); - observations - .emit_and_cleanup(model_id, self, |callback, this| callback(this)); + observations.emit(model_id, self, |callback, this| callback(this)); } Effect::ViewNotification { window_id, view_id } => { @@ -2046,7 +2011,7 @@ impl MutableAppContext { Effect::GlobalNotification { type_id } => { let mut subscriptions = self.global_observations.clone(); - subscriptions.emit_and_cleanup(type_id, self, |callback, this| { + subscriptions.emit(type_id, self, |callback, this| { callback(this); true }); @@ -2080,7 +2045,7 @@ impl MutableAppContext { subscription_id, callback, } => { - self.focus_observations.add_or_remove_callback( + self.focus_observations.add_callback( view_id, subscription_id, callback, @@ -2099,7 +2064,7 @@ impl MutableAppContext { window_id, subscription_id, callback, - } => self.window_activation_observations.add_or_remove_callback( + } => self.window_activation_observations.add_callback( window_id, subscription_id, callback, @@ -2114,7 +2079,7 @@ impl MutableAppContext { window_id, subscription_id, callback, - } => self.window_fullscreen_observations.add_or_remove_callback( + } => self.window_fullscreen_observations.add_callback( window_id, subscription_id, callback, @@ -2159,6 +2124,7 @@ impl MutableAppContext { self.remove_dropped_entities(); } else { self.remove_dropped_entities(); + if refreshing { self.perform_window_refresh(); } else { @@ -2295,7 +2261,7 @@ impl MutableAppContext { let type_id = (&*payload).type_id(); let mut subscriptions = self.global_subscriptions.clone(); - subscriptions.emit_and_cleanup(type_id, self, |callback, this| { + subscriptions.emit(type_id, self, |callback, this| { callback(payload.as_ref(), this); true //Always alive }); @@ -2320,17 +2286,18 @@ impl MutableAppContext { } let mut observations = self.observations.clone(); - observations.emit_and_cleanup(observed_view_id, self, |callback, this| callback(this)); + observations.emit(observed_view_id, self, |callback, this| callback(this)); } } fn handle_entity_release_effect(&mut self, entity_id: usize, entity: &dyn Any) { - let callbacks = self.release_observations.lock().remove(&entity_id); - if let Some(callbacks) = callbacks { - for (_, callback) in callbacks { - callback(entity, self); - } - } + self.release_observations + .clone() + .emit(entity_id, self, |callback, this| { + callback(entity, this); + // Release observations happen one time. So clear the callback by returning false + false + }) } fn handle_fullscreen_effect(&mut self, window_id: usize, is_fullscreen: bool) { @@ -2350,7 +2317,7 @@ impl MutableAppContext { window.is_fullscreen = is_fullscreen; let mut observations = this.window_fullscreen_observations.clone(); - observations.emit_and_cleanup(window_id, this, |callback, this| { + observations.emit(window_id, this, |callback, this| { callback(is_fullscreen, this) }); @@ -2367,7 +2334,7 @@ impl MutableAppContext { ) { self.update(|this| { let mut observations = this.keystroke_observations.clone(); - observations.emit_and_cleanup(window_id, this, { + observations.emit(window_id, this, { move |callback, this| callback(&keystroke, &result, handled_by.as_ref(), this) }); }); @@ -2403,7 +2370,7 @@ impl MutableAppContext { } let mut observations = this.window_activation_observations.clone(); - observations.emit_and_cleanup(window_id, this, |callback, this| callback(active, this)); + observations.emit(window_id, this, |callback, this| callback(active, this)); Some(()) }); @@ -2443,8 +2410,7 @@ impl MutableAppContext { } let mut subscriptions = this.focus_observations.clone(); - subscriptions - .emit_and_cleanup(blurred_id, this, |callback, this| callback(false, this)); + subscriptions.emit(blurred_id, this, |callback, this| callback(false, this)); } if let Some(focused_id) = focused_id { @@ -2456,8 +2422,7 @@ impl MutableAppContext { } let mut subscriptions = this.focus_observations.clone(); - subscriptions - .emit_and_cleanup(focused_id, this, |callback, this| callback(true, this)); + subscriptions.emit(focused_id, this, |callback, this| callback(true, this)); } }) } @@ -2513,11 +2478,12 @@ impl MutableAppContext { } fn handle_action_dispatch_notification_effect(&mut self, action_id: TypeId) { - let mut callbacks = mem::take(&mut *self.action_dispatch_observations.lock()); - for callback in callbacks.values_mut() { - callback(action_id, self); - } - self.action_dispatch_observations.lock().extend(callbacks); + self.action_dispatch_observations + .clone() + .emit((), self, |callback, this| { + callback(action_id, this); + true + }); } fn handle_window_should_close_subscription_effect( @@ -4038,7 +4004,7 @@ pub struct RenderContext<'a, T: View> { pub refreshing: bool, } -#[derive(Clone, Default)] +#[derive(Debug, Clone, Default)] pub struct MouseState { hovered: bool, clicked: Option, @@ -5106,269 +5072,46 @@ impl Drop for ElementStateHandle { #[must_use] pub enum Subscription { - Subscription { - id: usize, - entity_id: usize, - subscriptions: Option>>, - }, - GlobalSubscription { - id: usize, - type_id: TypeId, - subscriptions: Option>>, - }, - Observation { - id: usize, - entity_id: usize, - observations: Option>>, - }, - GlobalObservation { - id: usize, - type_id: TypeId, - observations: Option>>, - }, - FocusObservation { - id: usize, - view_id: usize, - observations: Option>>, - }, - WindowActivationObservation { - id: usize, - window_id: usize, - observations: Option>>, - }, - WindowFullscreenObservation { - id: usize, - window_id: usize, - observations: Option>>, - }, - KeystrokeObservation { - id: usize, - window_id: usize, - observations: Option>>, - }, - - ReleaseObservation { - id: usize, - entity_id: usize, - #[allow(clippy::type_complexity)] - observations: - Option>>>>, - }, - ActionObservation { - id: usize, - observations: Option>>>, - }, + Subscription(callback_collection::Subscription), + Observation(callback_collection::Subscription), + GlobalSubscription(callback_collection::Subscription), + GlobalObservation(callback_collection::Subscription), + FocusObservation(callback_collection::Subscription), + WindowActivationObservation(callback_collection::Subscription), + WindowFullscreenObservation(callback_collection::Subscription), + KeystrokeObservation(callback_collection::Subscription), + ReleaseObservation(callback_collection::Subscription), + ActionObservation(callback_collection::Subscription<(), ActionObservationCallback>), } impl Subscription { - pub fn detach(&mut self) { + pub fn id(&self) -> usize { match self { - Subscription::Subscription { subscriptions, .. } => { - subscriptions.take(); - } - Subscription::GlobalSubscription { subscriptions, .. } => { - subscriptions.take(); - } - Subscription::Observation { observations, .. } => { - observations.take(); - } - Subscription::GlobalObservation { observations, .. } => { - observations.take(); - } - Subscription::ReleaseObservation { observations, .. } => { - observations.take(); - } - Subscription::FocusObservation { observations, .. } => { - observations.take(); - } - Subscription::ActionObservation { observations, .. } => { - observations.take(); - } - Subscription::KeystrokeObservation { observations, .. } => { - observations.take(); - } - Subscription::WindowActivationObservation { observations, .. } => { - observations.take(); - } - Subscription::WindowFullscreenObservation { observations, .. } => { - observations.take(); - } + Subscription::Subscription(subscription) => subscription.id(), + Subscription::Observation(subscription) => subscription.id(), + Subscription::GlobalSubscription(subscription) => subscription.id(), + Subscription::GlobalObservation(subscription) => subscription.id(), + Subscription::FocusObservation(subscription) => subscription.id(), + Subscription::WindowActivationObservation(subscription) => subscription.id(), + Subscription::WindowFullscreenObservation(subscription) => subscription.id(), + Subscription::KeystrokeObservation(subscription) => subscription.id(), + Subscription::ReleaseObservation(subscription) => subscription.id(), + Subscription::ActionObservation(subscription) => subscription.id(), } } -} -impl Drop for Subscription { - fn drop(&mut self) { + pub fn detach(&mut self) { match self { - Subscription::Subscription { - id, - entity_id, - subscriptions, - } => { - if let Some(subscriptions) = subscriptions.as_ref().and_then(Weak::upgrade) { - match subscriptions - .lock() - .entry(*entity_id) - .or_default() - .entry(*id) - { - btree_map::Entry::Vacant(entry) => { - entry.insert(None); - } - btree_map::Entry::Occupied(entry) => { - entry.remove(); - } - } - } - } - Subscription::GlobalSubscription { - id, - type_id, - subscriptions, - } => { - if let Some(subscriptions) = subscriptions.as_ref().and_then(Weak::upgrade) { - match subscriptions.lock().entry(*type_id).or_default().entry(*id) { - btree_map::Entry::Vacant(entry) => { - entry.insert(None); - } - btree_map::Entry::Occupied(entry) => { - entry.remove(); - } - } - } - } - Subscription::Observation { - id, - entity_id, - observations, - } => { - if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { - match observations - .lock() - .entry(*entity_id) - .or_default() - .entry(*id) - { - btree_map::Entry::Vacant(entry) => { - entry.insert(None); - } - btree_map::Entry::Occupied(entry) => { - entry.remove(); - } - } - } - } - Subscription::GlobalObservation { - id, - type_id, - observations, - } => { - if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { - match observations.lock().entry(*type_id).or_default().entry(*id) { - collections::btree_map::Entry::Vacant(entry) => { - entry.insert(None); - } - collections::btree_map::Entry::Occupied(entry) => { - entry.remove(); - } - } - } - } - Subscription::ReleaseObservation { - id, - entity_id, - observations, - } => { - if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { - if let Some(observations) = observations.lock().get_mut(entity_id) { - observations.remove(id); - } - } - } - Subscription::FocusObservation { - id, - view_id, - observations, - } => { - if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { - match observations.lock().entry(*view_id).or_default().entry(*id) { - btree_map::Entry::Vacant(entry) => { - entry.insert(None); - } - btree_map::Entry::Occupied(entry) => { - entry.remove(); - } - } - } - } - Subscription::ActionObservation { id, observations } => { - if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { - observations.lock().remove(id); - } - } - Subscription::KeystrokeObservation { - id, - window_id, - observations, - } => { - if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { - match observations - .lock() - .entry(*window_id) - .or_default() - .entry(*id) - { - btree_map::Entry::Vacant(entry) => { - entry.insert(None); - } - btree_map::Entry::Occupied(entry) => { - entry.remove(); - } - } - } - } - Subscription::WindowActivationObservation { - id, - window_id, - observations, - } => { - if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { - match observations - .lock() - .entry(*window_id) - .or_default() - .entry(*id) - { - btree_map::Entry::Vacant(entry) => { - entry.insert(None); - } - btree_map::Entry::Occupied(entry) => { - entry.remove(); - } - } - } - } - Subscription::WindowFullscreenObservation { - id, - window_id, - observations, - } => { - if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { - match observations - .lock() - .entry(*window_id) - .or_default() - .entry(*id) - { - btree_map::Entry::Vacant(entry) => { - entry.insert(None); - } - btree_map::Entry::Occupied(entry) => { - entry.remove(); - } - } - } - } + Subscription::Subscription(subscription) => subscription.detach(), + Subscription::GlobalSubscription(subscription) => subscription.detach(), + Subscription::Observation(subscription) => subscription.detach(), + Subscription::GlobalObservation(subscription) => subscription.detach(), + Subscription::FocusObservation(subscription) => subscription.detach(), + Subscription::KeystrokeObservation(subscription) => subscription.detach(), + Subscription::WindowActivationObservation(subscription) => subscription.detach(), + Subscription::WindowFullscreenObservation(subscription) => subscription.detach(), + Subscription::ReleaseObservation(subscription) => subscription.detach(), + Subscription::ActionObservation(subscription) => subscription.detach(), } } } @@ -6015,60 +5758,44 @@ mod tests { #[crate::test(self)] fn test_view_events(cx: &mut MutableAppContext) { - #[derive(Default)] - struct View { - events: Vec, - } - - impl Entity for View { - type Event = usize; - } - - impl super::View for View { - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - Empty::new().boxed() - } - - fn ui_name() -> &'static str { - "View" - } - } - struct Model; impl Entity for Model { - type Event = usize; + type Event = String; } - let (_, handle_1) = cx.add_window(Default::default(), |_| View::default()); - let handle_2 = cx.add_view(&handle_1, |_| View::default()); + let (_, handle_1) = cx.add_window(Default::default(), |_| TestView::default()); + let handle_2 = cx.add_view(&handle_1, |_| TestView::default()); let handle_3 = cx.add_model(|_| Model); handle_1.update(cx, |_, cx| { cx.subscribe(&handle_2, move |me, emitter, event, cx| { - me.events.push(*event); + me.events.push(event.clone()); cx.subscribe(&emitter, |me, _, event, _| { - me.events.push(*event * 2); + me.events.push(format!("{event} from inner")); }) .detach(); }) .detach(); cx.subscribe(&handle_3, |me, _, event, _| { - me.events.push(*event); + me.events.push(event.clone()); }) .detach(); }); - handle_2.update(cx, |_, c| c.emit(7)); - assert_eq!(handle_1.read(cx).events, vec![7]); + handle_2.update(cx, |_, c| c.emit("7".into())); + assert_eq!(handle_1.read(cx).events, vec!["7"]); - handle_2.update(cx, |_, c| c.emit(5)); - assert_eq!(handle_1.read(cx).events, vec![7, 5, 10]); + handle_2.update(cx, |_, c| c.emit("5".into())); + assert_eq!(handle_1.read(cx).events, vec!["7", "5", "5 from inner"]); - handle_3.update(cx, |_, c| c.emit(9)); - assert_eq!(handle_1.read(cx).events, vec![7, 5, 10, 9]); + handle_3.update(cx, |_, c| c.emit("9".into())); + assert_eq!( + handle_1.read(cx).events, + vec!["7", "5", "5 from inner", "9"] + ); } #[crate::test(self)] @@ -6259,31 +5986,15 @@ mod tests { #[crate::test(self)] fn test_dropping_subscribers(cx: &mut MutableAppContext) { - struct View; - - impl Entity for View { - type Event = (); - } - - impl super::View for View { - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - Empty::new().boxed() - } - - fn ui_name() -> &'static str { - "View" - } - } - struct Model; impl Entity for Model { type Event = (); } - let (_, root_view) = cx.add_window(Default::default(), |_| View); - let observing_view = cx.add_view(&root_view, |_| View); - let emitting_view = cx.add_view(&root_view, |_| View); + let (_, root_view) = cx.add_window(Default::default(), |_| TestView::default()); + let observing_view = cx.add_view(&root_view, |_| TestView::default()); + let emitting_view = cx.add_view(&root_view, |_| TestView::default()); let observing_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model); @@ -6300,165 +6011,117 @@ mod tests { drop(observing_model); }); - emitting_view.update(cx, |_, cx| cx.emit(())); + emitting_view.update(cx, |_, cx| cx.emit(Default::default())); observed_model.update(cx, |_, cx| cx.emit(())); } #[crate::test(self)] fn test_view_emit_before_subscribe_in_same_update_cycle(cx: &mut MutableAppContext) { - #[derive(Default)] - struct TestView; - - impl Entity for TestView { - type Event = (); - } - - impl View for TestView { - fn ui_name() -> &'static str { - "TestView" - } - - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - Empty::new().boxed() - } - } - - let events = Rc::new(RefCell::new(Vec::new())); - cx.add_window(Default::default(), |cx| { + let (_, view) = cx.add_window::(Default::default(), |cx| { drop(cx.subscribe(&cx.handle(), { - let events = events.clone(); - move |_, _, _, _| events.borrow_mut().push("dropped before flush") + move |this, _, _, _| this.events.push("dropped before flush".into()) })); cx.subscribe(&cx.handle(), { - let events = events.clone(); - move |_, _, _, _| events.borrow_mut().push("before emit") + move |this, _, _, _| this.events.push("before emit".into()) }) .detach(); - cx.emit(()); + cx.emit("the event".into()); cx.subscribe(&cx.handle(), { - let events = events.clone(); - move |_, _, _, _| events.borrow_mut().push("after emit") + move |this, _, _, _| this.events.push("after emit".into()) }) .detach(); - TestView + TestView { events: Vec::new() } }); - assert_eq!(*events.borrow(), ["before emit"]); + + assert_eq!(view.read(cx).events, ["before emit"]); } #[crate::test(self)] fn test_observe_and_notify_from_view(cx: &mut MutableAppContext) { - #[derive(Default)] - struct View { - events: Vec, - } - - impl Entity for View { - type Event = usize; - } - - impl super::View for View { - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - Empty::new().boxed() - } - - fn ui_name() -> &'static str { - "View" - } - } - #[derive(Default)] struct Model { - count: usize, + state: String, } impl Entity for Model { type Event = (); } - let (_, view) = cx.add_window(Default::default(), |_| View::default()); - let model = cx.add_model(|_| Model::default()); + let (_, view) = cx.add_window(Default::default(), |_| TestView::default()); + let model = cx.add_model(|_| Model { + state: "old-state".into(), + }); view.update(cx, |_, c| { c.observe(&model, |me, observed, c| { - me.events.push(observed.read(c).count) + me.events.push(observed.read(c).state.clone()) }) .detach(); }); - model.update(cx, |model, c| { - model.count = 11; - c.notify(); + model.update(cx, |model, cx| { + model.state = "new-state".into(); + cx.notify(); }); - assert_eq!(view.read(cx).events, vec![11]); + assert_eq!(view.read(cx).events, vec!["new-state"]); } #[crate::test(self)] fn test_view_notify_before_observe_in_same_update_cycle(cx: &mut MutableAppContext) { - #[derive(Default)] - struct TestView; - - impl Entity for TestView { - type Event = (); - } - - impl View for TestView { - fn ui_name() -> &'static str { - "TestView" - } - - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - Empty::new().boxed() - } - } - - let events = Rc::new(RefCell::new(Vec::new())); - cx.add_window(Default::default(), |cx| { + let (_, view) = cx.add_window::(Default::default(), |cx| { drop(cx.observe(&cx.handle(), { - let events = events.clone(); - move |_, _, _| events.borrow_mut().push("dropped before flush") + move |this, _, _| this.events.push("dropped before flush".into()) })); cx.observe(&cx.handle(), { - let events = events.clone(); - move |_, _, _| events.borrow_mut().push("before notify") + move |this, _, _| this.events.push("before notify".into()) }) .detach(); cx.notify(); cx.observe(&cx.handle(), { - let events = events.clone(); - move |_, _, _| events.borrow_mut().push("after notify") + move |this, _, _| this.events.push("after notify".into()) }) .detach(); - TestView + TestView { events: Vec::new() } }); - assert_eq!(*events.borrow(), ["before notify"]); + + assert_eq!(view.read(cx).events, ["before notify"]); } #[crate::test(self)] - fn test_dropping_observers(cx: &mut MutableAppContext) { - struct View; - - impl Entity for View { + fn test_notify_and_drop_observe_subscription_in_same_update_cycle(cx: &mut MutableAppContext) { + struct Model; + impl Entity for Model { type Event = (); } - impl super::View for View { - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - Empty::new().boxed() - } + let model = cx.add_model(|_| Model); + let (_, view) = cx.add_window(Default::default(), |_| TestView::default()); - fn ui_name() -> &'static str { - "View" - } + view.update(cx, |_, cx| { + model.update(cx, |_, cx| cx.notify()); + drop(cx.observe(&model, move |this, _, _| { + this.events.push("model notified".into()); + })); + model.update(cx, |_, cx| cx.notify()); + }); + + for _ in 0..3 { + model.update(cx, |_, cx| cx.notify()); } + assert_eq!(view.read(cx).events, Vec::::new()); + } + + #[crate::test(self)] + fn test_dropping_observers(cx: &mut MutableAppContext) { struct Model; impl Entity for Model { type Event = (); } - let (_, root_view) = cx.add_window(Default::default(), |_| View); - let observing_view = cx.add_view(root_view, |_| View); + let (_, root_view) = cx.add_window(Default::default(), |_| TestView::default()); + let observing_view = cx.add_view(root_view, |_| TestView::default()); let observing_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model); @@ -7174,47 +6837,15 @@ mod tests { #[crate::test(self)] #[should_panic] async fn test_view_condition_timeout(cx: &mut TestAppContext) { - struct View; - - impl super::Entity for View { - type Event = (); - } - - impl super::View for View { - fn ui_name() -> &'static str { - "test view" - } - - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - Empty::new().boxed() - } - } - - let (_, view) = cx.add_window(|_| View); + let (_, view) = cx.add_window(|_| TestView::default()); view.condition(cx, |_, _| false).await; } #[crate::test(self)] #[should_panic(expected = "view dropped with pending condition")] async fn test_view_condition_panic_on_drop(cx: &mut TestAppContext) { - struct View; - - impl super::Entity for View { - type Event = (); - } - - impl super::View for View { - fn ui_name() -> &'static str { - "test view" - } - - fn render(&mut self, _: &mut RenderContext) -> ElementBox { - Empty::new().boxed() - } - } - - let (_, root_view) = cx.add_window(|_| View); - let view = cx.add_view(&root_view, |_| View); + let (_, root_view) = cx.add_window(|_| TestView::default()); + let view = cx.add_view(&root_view, |_| TestView::default()); let condition = view.condition(cx, |_, _| false); cx.update(|_| drop(view)); @@ -7427,4 +7058,23 @@ mod tests { assert!(!child_rendered.take()); assert!(child_dropped.take()); } + + #[derive(Default)] + struct TestView { + events: Vec, + } + + impl Entity for TestView { + type Event = String; + } + + impl View for TestView { + fn ui_name() -> &'static str { + "TestView" + } + + fn render(&mut self, _: &mut RenderContext) -> ElementBox { + Empty::new().boxed() + } + } } diff --git a/crates/gpui/src/app/callback_collection.rs b/crates/gpui/src/app/callback_collection.rs index 4ec90fbac0b4962520945f2c3914bd3a324f2e1b..7496d715014159f7b445414519f834f0c849e0ab 100644 --- a/crates/gpui/src/app/callback_collection.rs +++ b/crates/gpui/src/app/callback_collection.rs @@ -1,19 +1,44 @@ +use crate::MutableAppContext; +use collections::{BTreeMap, HashMap, HashSet}; +use parking_lot::Mutex; use std::sync::Arc; use std::{hash::Hash, sync::Weak}; -use parking_lot::Mutex; +pub struct CallbackCollection { + internal: Arc>>, +} -use collections::{btree_map, BTreeMap, HashMap}; +pub struct Subscription { + key: K, + id: usize, + mapping: Option>>>, +} -use crate::MutableAppContext; +struct Mapping { + callbacks: HashMap>, + dropped_subscriptions: HashMap>, +} -pub type Mapping = Mutex>>>; +impl Mapping { + fn clear_dropped_state(&mut self, key: &K, subscription_id: usize) -> bool { + if let Some(subscriptions) = self.dropped_subscriptions.get_mut(&key) { + subscriptions.remove(&subscription_id) + } else { + false + } + } +} -pub struct CallbackCollection { - internal: Arc>, +impl Default for Mapping { + fn default() -> Self { + Self { + callbacks: Default::default(), + dropped_subscriptions: Default::default(), + } + } } -impl Clone for CallbackCollection { +impl Clone for CallbackCollection { fn clone(&self) -> Self { Self { internal: self.internal.clone(), @@ -21,7 +46,7 @@ impl Clone for CallbackCollection { } } -impl Default for CallbackCollection { +impl Default for CallbackCollection { fn default() -> Self { CallbackCollection { internal: Arc::new(Mutex::new(Default::default())), @@ -29,78 +54,114 @@ impl Default for CallbackCollection { } } -impl CallbackCollection { - pub fn downgrade(&self) -> Weak> { - Arc::downgrade(&self.internal) - } - +impl CallbackCollection { #[cfg(test)] pub fn is_empty(&self) -> bool { - self.internal.lock().is_empty() + self.internal.lock().callbacks.is_empty() } - pub fn add_callback(&mut self, id: K, subscription_id: usize, callback: F) { - self.internal - .lock() - .entry(id) - .or_default() - .insert(subscription_id, Some(callback)); + pub fn subscribe(&mut self, key: K, subscription_id: usize) -> Subscription { + Subscription { + key, + id: subscription_id, + mapping: Some(Arc::downgrade(&self.internal)), + } } - pub fn remove(&mut self, id: K) { - self.internal.lock().remove(&id); - } + pub fn add_callback(&mut self, key: K, subscription_id: usize, callback: F) { + let mut this = self.internal.lock(); + + // If this callback's subscription was dropped before the callback was + // added, then just drop the callback. + if this.clear_dropped_state(&key, subscription_id) { + return; + } - pub fn add_or_remove_callback(&mut self, id: K, subscription_id: usize, callback: F) { - match self - .internal - .lock() - .entry(id) + this.callbacks + .entry(key) .or_default() - .entry(subscription_id) - { - btree_map::Entry::Vacant(entry) => { - entry.insert(Some(callback)); - } + .insert(subscription_id, callback); + } - btree_map::Entry::Occupied(entry) => { - // TODO: This seems like it should never be called because no code - // should ever attempt to remove an existing callback - debug_assert!(entry.get().is_none()); - entry.remove(); - } - } + pub fn remove(&mut self, key: K) { + // Drop these callbacks after releasing the lock, in case one of them + // owns a subscription to this callback collection. + let mut this = self.internal.lock(); + let callbacks = this.callbacks.remove(&key); + this.dropped_subscriptions.remove(&key); + drop(this); + drop(callbacks); } - pub fn emit_and_cleanup bool>( + pub fn emit bool>( &mut self, - id: K, + key: K, cx: &mut MutableAppContext, mut call_callback: C, ) { - let callbacks = self.internal.lock().remove(&id); + let callbacks = self.internal.lock().callbacks.remove(&key); if let Some(callbacks) = callbacks { - for (subscription_id, callback) in callbacks { - if let Some(mut callback) = callback { - let alive = call_callback(&mut callback, cx); - if alive { - match self - .internal - .lock() - .entry(id) - .or_default() - .entry(subscription_id) - { - btree_map::Entry::Vacant(entry) => { - entry.insert(Some(callback)); - } - btree_map::Entry::Occupied(entry) => { - entry.remove(); - } - } - } + for (subscription_id, mut callback) in callbacks { + // If this callback's subscription was dropped while invoking an + // earlier callback, then just drop the callback. + let mut this = self.internal.lock(); + if this.clear_dropped_state(&key, subscription_id) { + continue; } + + drop(this); + let alive = call_callback(&mut callback, cx); + + // If this callback's subscription was dropped while invoking the callback + // itself, or if the callback returns false, then just drop the callback. + let mut this = self.internal.lock(); + if this.clear_dropped_state(&key, subscription_id) || !alive { + continue; + } + + this.callbacks + .entry(key) + .or_default() + .insert(subscription_id, callback); } } } } + +impl Subscription { + pub fn id(&self) -> usize { + self.id + } + + pub fn detach(&mut self) { + self.mapping.take(); + } +} + +impl Drop for Subscription { + fn drop(&mut self) { + if let Some(mapping) = self.mapping.as_ref().and_then(|mapping| mapping.upgrade()) { + let mut mapping = mapping.lock(); + + // If the callback is present in the mapping, then just remove it. + if let Some(callbacks) = mapping.callbacks.get_mut(&self.key) { + let callback = callbacks.remove(&self.id); + if callback.is_some() { + drop(mapping); + drop(callback); + return; + } + } + + // If this subscription's callback is not present, then either it has been + // temporarily removed during emit, or it has not yet been added. Record + // that this subscription has been dropped so that the callback can be + // removed later. + mapping + .dropped_subscriptions + .entry(self.key.clone()) + .or_default() + .insert(self.id); + } + } +} diff --git a/crates/gpui/src/platform/mac/event.rs b/crates/gpui/src/platform/mac/event.rs index 36dab7314925eda17052d37fe8fc2b7e619f0c70..0464840e86fef3001291d31b608437f2a4cc7c91 100644 --- a/crates/gpui/src/platform/mac/event.rs +++ b/crates/gpui/src/platform/mac/event.rs @@ -47,6 +47,8 @@ pub fn key_to_native(key: &str) -> Cow { "right" => NSRightArrowFunctionKey, "pageup" => NSPageUpFunctionKey, "pagedown" => NSPageDownFunctionKey, + "home" => NSHomeFunctionKey, + "end" => NSEndFunctionKey, "delete" => NSDeleteFunctionKey, "f1" => NSF1FunctionKey, "f2" => NSF2FunctionKey, @@ -258,6 +260,8 @@ unsafe fn parse_keystroke(native_event: id) -> Keystroke { Some(NSRightArrowFunctionKey) => "right".to_string(), Some(NSPageUpFunctionKey) => "pageup".to_string(), Some(NSPageDownFunctionKey) => "pagedown".to_string(), + Some(NSHomeFunctionKey) => "home".to_string(), + Some(NSEndFunctionKey) => "end".to_string(), Some(NSDeleteFunctionKey) => "delete".to_string(), Some(NSF1FunctionKey) => "f1".to_string(), Some(NSF2FunctionKey) => "f2".to_string(), diff --git a/crates/gpui/src/platform/mac/platform.rs b/crates/gpui/src/platform/mac/platform.rs index f703d863be4113d62a0b68f60c6d09aae0b53f3d..3e2ef73634cfd17d2cf1a3a8d78ca7e0d6b7d128 100644 --- a/crates/gpui/src/platform/mac/platform.rs +++ b/crates/gpui/src/platform/mac/platform.rs @@ -647,7 +647,7 @@ impl platform::Platform for MacPlatform { attrs.set(kSecReturnAttributes as *const _, cf_true); attrs.set(kSecReturnData as *const _, cf_true); - let mut result = CFTypeRef::from(ptr::null_mut()); + let mut result = CFTypeRef::from(ptr::null()); let status = SecItemCopyMatching(attrs.as_concrete_TypeRef(), &mut result); match status { security::errSecSuccess => {} diff --git a/crates/gpui_macros/src/gpui_macros.rs b/crates/gpui_macros/src/gpui_macros.rs index e28d1711d2ceda9cfcedf28310575e1cbc3cc620..cabae1ac0a14c290da73af306dc80db824273bc6 100644 --- a/crates/gpui_macros/src/gpui_macros.rs +++ b/crates/gpui_macros/src/gpui_macros.rs @@ -162,11 +162,9 @@ pub fn test(args: TokenStream, function: TokenStream) -> TokenStream { if let FnArg::Typed(arg) = arg { if let Type::Path(ty) = &*arg.ty { let last_segment = ty.path.segments.last(); - match last_segment.map(|s| s.ident.to_string()).as_deref() { - Some("StdRng") => { - inner_fn_args.extend(quote!(rand::SeedableRng::seed_from_u64(seed),)); - } - _ => {} + + if let Some("StdRng") = last_segment.map(|s| s.ident.to_string()).as_deref() { + inner_fn_args.extend(quote!(rand::SeedableRng::seed_from_u64(seed),)); } } else { inner_fn_args.extend(quote!(cx,)); diff --git a/crates/live_kit_client/build.rs b/crates/live_kit_client/build.rs index bceb4fb927da826920cab1af91b67a92325ea8a5..bcd3f76dca99d5cf22ee01ebe1e1d51cc13e103f 100644 --- a/crates/live_kit_client/build.rs +++ b/crates/live_kit_client/build.rs @@ -5,7 +5,7 @@ use std::{ process::Command, }; -const SWIFT_PACKAGE_NAME: &'static str = "LiveKitBridge"; +const SWIFT_PACKAGE_NAME: &str = "LiveKitBridge"; #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] @@ -61,8 +61,8 @@ fn build_bridge(swift_target: &SwiftTarget) { let swift_package_root = swift_package_root(); if !Command::new("swift") .arg("build") - .args(&["--configuration", &env::var("PROFILE").unwrap()]) - .args(&["--triple", &swift_target.target.triple]) + .args(["--configuration", &env::var("PROFILE").unwrap()]) + .args(["--triple", &swift_target.target.triple]) .current_dir(&swift_package_root) .status() .unwrap() @@ -116,7 +116,7 @@ fn get_swift_target() -> SwiftTarget { let target = format!("{}-apple-macosx{}", arch, MACOS_TARGET_VERSION); let swift_target_info_str = Command::new("swift") - .args(&["-target", &target, "-print-target-info"]) + .args(["-target", &target, "-print-target-info"]) .output() .unwrap() .stdout; @@ -143,7 +143,7 @@ fn copy_dir(source: &Path, destination: &Path) { assert!( Command::new("cp") .arg("-R") - .args(&[source, destination]) + .args([source, destination]) .status() .unwrap() .success(), diff --git a/crates/media/build.rs b/crates/media/build.rs index 1ee8f23e41e97c9e9f24fd1eb163242256c1407c..7e66efd39de86ebd12efc98b5e850a5a9aa2edae 100644 --- a/crates/media/build.rs +++ b/crates/media/build.rs @@ -3,7 +3,7 @@ use std::{env, path::PathBuf, process::Command}; fn main() { let sdk_path = String::from_utf8( Command::new("xcrun") - .args(&["--sdk", "macosx", "--show-sdk-path"]) + .args(["--sdk", "macosx", "--show-sdk-path"]) .output() .unwrap() .stdout, diff --git a/crates/media/src/media.rs b/crates/media/src/media.rs index fe69e684e70df437573ecd60a17e29b984291d6d..1c76e6befbf42e0069dc95c2cfbd3b7a227183cf 100644 --- a/crates/media/src/media.rs +++ b/crates/media/src/media.rs @@ -113,9 +113,9 @@ pub mod core_video { let mut this = ptr::null(); let result = CVMetalTextureCacheCreate( kCFAllocatorDefault, - ptr::null_mut(), + ptr::null(), metal_device, - ptr::null_mut(), + ptr::null(), &mut this, ); if result == kCVReturnSuccess { @@ -192,7 +192,7 @@ pub mod core_video { pub fn as_texture_ref(&self) -> &metal::TextureRef { unsafe { let texture = CVMetalTextureGetTexture(self.as_concrete_TypeRef()); - &metal::TextureRef::from_ptr(texture as *mut _) + metal::TextureRef::from_ptr(texture as *mut _) } } } diff --git a/crates/recent_projects/src/recent_projects.rs b/crates/recent_projects/src/recent_projects.rs index 42ff2b2f1c3cb0a4e34ecc02c10415accc8f749e..02e15290ab269b5f4fee7cdcc365f44e0777f31f 100644 --- a/crates/recent_projects/src/recent_projects.rs +++ b/crates/recent_projects/src/recent_projects.rs @@ -146,6 +146,7 @@ impl PickerDelegate for RecentProjectsView { .matches .iter() .enumerate() + .rev() .max_by_key(|(_, m)| OrderedFloat(m.score)) .map(|(ix, _)| ix) .unwrap_or(0); diff --git a/crates/rpc/src/auth.rs b/crates/rpc/src/auth.rs index 5254ef9ca4587ac44e591996798b9357a9305b05..39c8c39a4423de758807e67751a313e091e4f929 100644 --- a/crates/rpc/src/auth.rs +++ b/crates/rpc/src/auth.rs @@ -23,7 +23,7 @@ pub fn random_token() -> String { for byte in token_bytes.iter_mut() { *byte = rng.gen(); } - base64::encode_config(&token_bytes, base64::URL_SAFE) + base64::encode_config(token_bytes, base64::URL_SAFE) } impl PublicKey { diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 5877322feb64c96d843dffbec0f328af93e4ac97..fa95dda1af924c3c005b282afd97b34d5a149e3e 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -106,73 +106,79 @@ impl View for BufferSearchBar { .with_child( Flex::row() .with_child( - ChildView::new(&self.query_editor, cx) - .aligned() - .left() - .flex(1., true) - .boxed(), - ) - .with_children(self.active_searchable_item.as_ref().and_then( - |searchable_item| { - let matches = self - .seachable_items_with_matches - .get(&searchable_item.downgrade())?; - let message = if let Some(match_ix) = self.active_match_index { - format!("{}/{}", match_ix + 1, matches.len()) - } else { - "No matches".to_string() - }; - - Some( - Label::new(message, theme.search.match_index.text.clone()) - .contained() - .with_style(theme.search.match_index.container) + Flex::row() + .with_child( + ChildView::new(&self.query_editor, cx) .aligned() + .left() + .flex(1., true) .boxed(), ) - }, - )) - .contained() - .with_style(editor_container) - .aligned() - .constrained() - .with_min_width(theme.search.editor.min_width) - .with_max_width(theme.search.editor.max_width) - .flex(1., false) - .boxed(), - ) - .with_child( - Flex::row() - .with_child(self.render_nav_button("<", Direction::Prev, cx)) - .with_child(self.render_nav_button(">", Direction::Next, cx)) - .aligned() - .boxed(), - ) - .with_child( - Flex::row() - .with_children(self.render_search_option( - supported_options.case, - "Case", - SearchOption::CaseSensitive, - cx, - )) - .with_children(self.render_search_option( - supported_options.word, - "Word", - SearchOption::WholeWord, - cx, - )) - .with_children(self.render_search_option( - supported_options.regex, - "Regex", - SearchOption::Regex, - cx, - )) - .contained() - .with_style(theme.search.option_button_group) - .aligned() + .with_children(self.active_searchable_item.as_ref().and_then( + |searchable_item| { + let matches = self + .seachable_items_with_matches + .get(&searchable_item.downgrade())?; + let message = if let Some(match_ix) = self.active_match_index { + format!("{}/{}", match_ix + 1, matches.len()) + } else { + "No matches".to_string() + }; + + Some( + Label::new(message, theme.search.match_index.text.clone()) + .contained() + .with_style(theme.search.match_index.container) + .aligned() + .boxed(), + ) + }, + )) + .contained() + .with_style(editor_container) + .aligned() + .constrained() + .with_min_width(theme.search.editor.min_width) + .with_max_width(theme.search.editor.max_width) + .flex(1., false) + .boxed(), + ) + .with_child( + Flex::row() + .with_child(self.render_nav_button("<", Direction::Prev, cx)) + .with_child(self.render_nav_button(">", Direction::Next, cx)) + .aligned() + .boxed(), + ) + .with_child( + Flex::row() + .with_children(self.render_search_option( + supported_options.case, + "Case", + SearchOption::CaseSensitive, + cx, + )) + .with_children(self.render_search_option( + supported_options.word, + "Word", + SearchOption::WholeWord, + cx, + )) + .with_children(self.render_search_option( + supported_options.regex, + "Regex", + SearchOption::Regex, + cx, + )) + .contained() + .with_style(theme.search.option_button_group) + .aligned() + .boxed(), + ) + .flex(1., true) .boxed(), ) + .with_child(self.render_close_button(&theme.search, cx)) .contained() .with_style(theme.search.container) .named("search bar") @@ -325,7 +331,7 @@ impl BufferSearchBar { let is_active = self.is_search_option_enabled(option); Some( MouseEventHandler::::new(option as usize, cx, |state, cx| { - let style = &cx + let style = cx .global::() .theme .search @@ -373,7 +379,7 @@ impl BufferSearchBar { enum NavButton {} MouseEventHandler::::new(direction as usize, cx, |state, cx| { - let style = &cx + let style = cx .global::() .theme .search @@ -399,6 +405,38 @@ impl BufferSearchBar { .boxed() } + fn render_close_button( + &self, + theme: &theme::Search, + cx: &mut RenderContext, + ) -> ElementBox { + let action = Box::new(Dismiss); + let tooltip = "Dismiss Buffer Search"; + let tooltip_style = cx.global::().theme.tooltip.clone(); + + enum CloseButton {} + MouseEventHandler::::new(0, cx, |state, _| { + let style = theme.dismiss_button.style_for(state, false); + Svg::new("icons/x_mark_8.svg") + .with_color(style.color) + .constrained() + .with_width(style.icon_width) + .aligned() + .constrained() + .with_width(style.button_width) + .contained() + .with_style(style.container) + .boxed() + }) + .on_click(MouseButton::Left, { + let action = action.boxed_clone(); + move |_, cx| cx.dispatch_any_action(action.boxed_clone()) + }) + .with_cursor_style(CursorStyle::PointingHand) + .with_tooltip::(0, tooltip.to_string(), Some(action), tooltip_style, cx) + .boxed() + } + fn deploy(pane: &mut Pane, action: &Deploy, cx: &mut ViewContext) { if let Some(search_bar) = pane.toolbar().read(cx).item_of_type::() { if search_bar.update(cx, |search_bar, cx| search_bar.show(action.focus, true, cx)) { diff --git a/crates/snippet/src/snippet.rs b/crates/snippet/src/snippet.rs index 808701569a40376858a47012fb1b8e02b730cd74..2957f415daa80a9e06570dc977324faaed8addb2 100644 --- a/crates/snippet/src/snippet.rs +++ b/crates/snippet/src/snippet.rs @@ -62,7 +62,7 @@ fn parse_snippet<'a>( } } Some(_) => { - let chunk_end = source.find(&['}', '$', '\\']).unwrap_or(source.len()); + let chunk_end = source.find(['}', '$', '\\']).unwrap_or(source.len()); let (chunk, rest) = source.split_at(chunk_end); text.push_str(chunk); source = rest; diff --git a/crates/sqlez/src/connection.rs b/crates/sqlez/src/connection.rs index 3342845d148ca2588c4650a8579b9ebd335640cd..3c6cb31196b881054f0a503d41242039c51bd037 100644 --- a/crates/sqlez/src/connection.rs +++ b/crates/sqlez/src/connection.rs @@ -20,7 +20,7 @@ unsafe impl Send for Connection {} impl Connection { pub(crate) fn open(uri: &str, persistent: bool) -> Result { let mut connection = Self { - sqlite3: 0 as *mut _, + sqlite3: ptr::null_mut(), persistent, write: RefCell::new(true), _sqlite: PhantomData, @@ -32,7 +32,7 @@ impl Connection { CString::new(uri)?.as_ptr(), &mut connection.sqlite3, flags, - 0 as *const _, + ptr::null(), ); // Turn on extended error codes @@ -97,7 +97,7 @@ impl Connection { let remaining_sql_str = remaining_sql.to_str().unwrap().trim(); remaining_sql_str != ";" && !remaining_sql_str.is_empty() } { - let mut raw_statement = 0 as *mut sqlite3_stmt; + let mut raw_statement = ptr::null_mut::(); let mut remaining_sql_ptr = ptr::null(); sqlite3_prepare_v2( self.sqlite3, diff --git a/crates/sqlez/src/statement.rs b/crates/sqlez/src/statement.rs index 86035f5d0acd322a0edb31c534fa14b0fbc9257d..f3ec6d48541b8f2fde85a1cffdcaba99501315c2 100644 --- a/crates/sqlez/src/statement.rs +++ b/crates/sqlez/src/statement.rs @@ -48,7 +48,7 @@ impl<'a> Statement<'a> { .trim(); remaining_sql_str != ";" && !remaining_sql_str.is_empty() } { - let mut raw_statement = 0 as *mut sqlite3_stmt; + let mut raw_statement = ptr::null_mut::(); let mut remaining_sql_ptr = ptr::null(); sqlite3_prepare_v2( connection.sqlite3, @@ -101,7 +101,7 @@ impl<'a> Statement<'a> { } } - fn bind_index_with(&self, index: i32, bind: impl Fn(&*mut sqlite3_stmt) -> ()) -> Result<()> { + fn bind_index_with(&self, index: i32, bind: impl Fn(&*mut sqlite3_stmt)) -> Result<()> { let mut any_succeed = false; unsafe { for raw_statement in self.raw_statements.iter() { @@ -133,7 +133,7 @@ impl<'a> Statement<'a> { }) } - pub fn column_blob<'b>(&'b mut self, index: i32) -> Result<&'b [u8]> { + pub fn column_blob(&mut self, index: i32) -> Result<&[u8]> { let index = index as c_int; let pointer = unsafe { sqlite3_column_blob(self.current_statement(), index) }; @@ -217,7 +217,7 @@ impl<'a> Statement<'a> { }) } - pub fn column_text<'b>(&'b mut self, index: i32) -> Result<&'b str> { + pub fn column_text(&mut self, index: i32) -> Result<&str> { let index = index as c_int; let pointer = unsafe { sqlite3_column_text(self.current_statement(), index) }; diff --git a/crates/sqlez/src/thread_safe_connection.rs b/crates/sqlez/src/thread_safe_connection.rs index 2c51b776edc73ca941d75a9fcb85ce0630acbe7d..c3fe4657ee9182dbf1c229e32b19ebf3c30c0ec7 100644 --- a/crates/sqlez/src/thread_safe_connection.rs +++ b/crates/sqlez/src/thread_safe_connection.rs @@ -114,12 +114,12 @@ impl ThreadSafeConnection { let mut queues = QUEUES.write(); if !queues.contains_key(&self.uri) { let mut write_queue_constructor = - write_queue_constructor.unwrap_or(background_thread_queue()); + write_queue_constructor.unwrap_or_else(background_thread_queue); queues.insert(self.uri.clone(), write_queue_constructor()); return true; } } - return false; + false } pub fn builder(uri: &str, persistent: bool) -> ThreadSafeConnectionBuilder { @@ -187,10 +187,9 @@ impl ThreadSafeConnection { *connection.write.get_mut() = false; if let Some(initialize_query) = connection_initialize_query { - connection.exec(initialize_query).expect(&format!( - "Initialize query failed to execute: {}", - initialize_query - ))() + connection.exec(initialize_query).unwrap_or_else(|_| { + panic!("Initialize query failed to execute: {}", initialize_query) + })() .unwrap() } @@ -225,7 +224,7 @@ impl Clone for ThreadSafeConnection { Self { uri: self.uri.clone(), persistent: self.persistent, - connection_initialize_query: self.connection_initialize_query.clone(), + connection_initialize_query: self.connection_initialize_query, connections: self.connections.clone(), _migrator: PhantomData, } diff --git a/crates/sqlez/src/typed_statements.rs b/crates/sqlez/src/typed_statements.rs index c7d8b20aa556d93fd08024a32667c7337d9b1013..df4a2987b5f0198fb380d5014d963dd2f69c2aff 100644 --- a/crates/sqlez/src/typed_statements.rs +++ b/crates/sqlez/src/typed_statements.rs @@ -8,7 +8,7 @@ use crate::{ impl Connection { pub fn exec<'a>(&'a self, query: &str) -> Result Result<()>> { - let mut statement = Statement::prepare(&self, query)?; + let mut statement = Statement::prepare(self, query)?; Ok(move || statement.exec()) } @@ -16,7 +16,7 @@ impl Connection { &'a self, query: &str, ) -> Result Result<()>> { - let mut statement = Statement::prepare(&self, query)?; + let mut statement = Statement::prepare(self, query)?; Ok(move |bindings| statement.with_bindings(bindings)?.exec()) } @@ -24,7 +24,7 @@ impl Connection { &'a self, query: &str, ) -> Result Result>> { - let mut statement = Statement::prepare(&self, query)?; + let mut statement = Statement::prepare(self, query)?; Ok(move || statement.rows::()) } @@ -32,7 +32,7 @@ impl Connection { &'a self, query: &str, ) -> Result Result>> { - let mut statement = Statement::prepare(&self, query)?; + let mut statement = Statement::prepare(self, query)?; Ok(move |bindings| statement.with_bindings(bindings)?.rows::()) } @@ -40,7 +40,7 @@ impl Connection { &'a self, query: &str, ) -> Result Result>> { - let mut statement = Statement::prepare(&self, query)?; + let mut statement = Statement::prepare(self, query)?; Ok(move || statement.maybe_row::()) } @@ -48,7 +48,7 @@ impl Connection { &'a self, query: &str, ) -> Result Result>> { - let mut statement = Statement::prepare(&self, query)?; + let mut statement = Statement::prepare(self, query)?; Ok(move |bindings| { statement .with_bindings(bindings) diff --git a/crates/sqlez_macros/src/sqlez_macros.rs b/crates/sqlez_macros/src/sqlez_macros.rs index 429f45db7e55442773fd1b6f5b92bdb577c1c5da..675576c997a139a501f8b66b034643ee67165b87 100644 --- a/crates/sqlez_macros/src/sqlez_macros.rs +++ b/crates/sqlez_macros/src/sqlez_macros.rs @@ -33,14 +33,14 @@ fn create_error( .skip_while(|(offset, _)| offset <= &error_offset) .map(|(_, span)| span) .next() - .unwrap_or(Span::call_site()); + .unwrap_or_else(Span::call_site); let error_text = format!("Sql Error: {}\nFor Query: {}", error, formatted_sql); TokenStream::from(Error::new(error_span.into(), error_text).into_compile_error()) } fn make_sql(tokens: TokenStream) -> (Vec<(usize, Span)>, String) { let mut sql_tokens = vec![]; - flatten_stream(tokens.clone(), &mut sql_tokens); + flatten_stream(tokens, &mut sql_tokens); // Lookup of spans by offset at the end of the token let mut spans: Vec<(usize, Span)> = Vec::new(); let mut sql = String::new(); @@ -67,7 +67,7 @@ fn flatten_stream(tokens: TokenStream, result: &mut Vec<(String, Span)>) { result.push((close_delimiter(group.delimiter()), group.span())); } TokenTree::Ident(ident) => { - result.push((format!("{} ", ident.to_string()), ident.span())); + result.push((format!("{} ", ident), ident.span())); } leaf_tree => result.push((leaf_tree.to_string(), leaf_tree.span())), } diff --git a/crates/sum_tree/src/tree_map.rs b/crates/sum_tree/src/tree_map.rs index 8672ca398f9d13d5c9348d94f7d781adafa61dd7..112366cdf583cbd3f247a8a0cf9496c9bc8936c3 100644 --- a/crates/sum_tree/src/tree_map.rs +++ b/crates/sum_tree/src/tree_map.rs @@ -58,7 +58,7 @@ impl TreeMap { self.0.insert_or_replace(MapEntry { key, value }, &()); } - pub fn remove<'a>(&mut self, key: &'a K) -> Option { + pub fn remove(&mut self, key: &K) -> Option { let mut removed = None; let mut cursor = self.0.cursor::>(); let key = MapKeyRef(Some(key)); diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index bf6cb57adb3ce6378fed2bc5db3f4ed2d8b22962..e463310b9810eb2d6f30e924b1b66166c9b2e295 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -247,6 +247,7 @@ pub struct Search { pub results_status: TextStyle, pub tab_icon_width: f32, pub tab_icon_spacing: f32, + pub dismiss_button: Interactive, } #[derive(Clone, Deserialize, Default)] diff --git a/crates/util/src/channel.rs b/crates/util/src/channel.rs index 3edf26dc95e4386f3f89d67e4b2c9ad13fbed83c..03b0f04271b1bd017a12396e72727914018a9399 100644 --- a/crates/util/src/channel.rs +++ b/crates/util/src/channel.rs @@ -4,7 +4,7 @@ use lazy_static::lazy_static; lazy_static! { pub static ref RELEASE_CHANNEL_NAME: String = env::var("ZED_RELEASE_CHANNEL") - .unwrap_or(include_str!("../../zed/RELEASE_CHANNEL").to_string()); + .unwrap_or_else(|_| include_str!("../../zed/RELEASE_CHANNEL").to_string()); pub static ref RELEASE_CHANNEL: ReleaseChannel = match RELEASE_CHANNEL_NAME.as_str() { "dev" => ReleaseChannel::Dev, "preview" => ReleaseChannel::Preview, diff --git a/crates/util/src/lib.rs b/crates/util/src/lib.rs index d9015ca6c0b2e49829d9507aa5bc442990aee300..e79cc269c9068a85fefe920ddecaf0c937a015d7 100644 --- a/crates/util/src/lib.rs +++ b/crates/util/src/lib.rs @@ -36,7 +36,7 @@ pub fn truncate_and_trailoff(s: &str, max_chars: usize) -> String { debug_assert!(max_chars >= 5); if s.len() > max_chars { - format!("{}…", truncate(&s, max_chars.saturating_sub(3))) + format!("{}…", truncate(s, max_chars.saturating_sub(3))) } else { s.to_string() } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 428865ec3b1024b550a0698352d2221b257de554..618c650e02a538b53a70b0bd3083847f4d1e0425 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -44,6 +44,8 @@ actions!( ActivateLastItem, CloseActiveItem, CloseInactiveItems, + CloseCleanItems, + CloseAllItems, ReopenClosedItem, SplitLeft, SplitUp, @@ -122,6 +124,8 @@ pub fn init(cx: &mut MutableAppContext) { }); cx.add_async_action(Pane::close_active_item); cx.add_async_action(Pane::close_inactive_items); + cx.add_async_action(Pane::close_clean_items); + cx.add_async_action(Pane::close_all_items); cx.add_async_action(|workspace: &mut Workspace, action: &CloseItem, cx| { let pane = action.pane.upgrade(cx)?; let task = Pane::close_item(workspace, pane, action.item_id, cx); @@ -258,6 +262,13 @@ pub enum ReorderBehavior { MoveToIndex(usize), } +enum ItemType { + Active, + Inactive, + Clean, + All, +} + impl Pane { pub fn new(docked: Option, cx: &mut ViewContext) -> Self { let handle = cx.weak_handle(); @@ -696,40 +707,67 @@ impl Pane { _: &CloseActiveItem, cx: &mut ViewContext, ) -> Option>> { - let pane_handle = workspace.active_pane().clone(); - let pane = pane_handle.read(cx); - if pane.items.is_empty() { - None - } else { - let item_id_to_close = pane.items[pane.active_item_index].id(); - let task = Self::close_items(workspace, pane_handle, cx, move |item_id| { - item_id == item_id_to_close - }); - Some(cx.foreground().spawn(async move { - task.await?; - Ok(()) - })) - } + Self::close_main(workspace, ItemType::Active, cx) } pub fn close_inactive_items( workspace: &mut Workspace, _: &CloseInactiveItems, cx: &mut ViewContext, + ) -> Option>> { + Self::close_main(workspace, ItemType::Inactive, cx) + } + + pub fn close_all_items( + workspace: &mut Workspace, + _: &CloseAllItems, + cx: &mut ViewContext, + ) -> Option>> { + Self::close_main(workspace, ItemType::All, cx) + } + + pub fn close_clean_items( + workspace: &mut Workspace, + _: &CloseCleanItems, + cx: &mut ViewContext, + ) -> Option>> { + Self::close_main(workspace, ItemType::Clean, cx) + } + + fn close_main( + workspace: &mut Workspace, + close_item_type: ItemType, + cx: &mut ViewContext, ) -> Option>> { let pane_handle = workspace.active_pane().clone(); let pane = pane_handle.read(cx); if pane.items.is_empty() { - None - } else { - let active_item_id = pane.items[pane.active_item_index].id(); - let task = - Self::close_items(workspace, pane_handle, cx, move |id| id != active_item_id); - Some(cx.foreground().spawn(async move { - task.await?; - Ok(()) - })) + return None; } + + let active_item_id = pane.items[pane.active_item_index].id(); + let clean_item_ids: Vec<_> = pane + .items() + .filter(|item| !item.is_dirty(cx)) + .map(|item| item.id()) + .collect(); + let task = + Self::close_items( + workspace, + pane_handle, + cx, + move |item_id| match close_item_type { + ItemType::Active => item_id == active_item_id, + ItemType::Inactive => item_id != active_item_id, + ItemType::Clean => clean_item_ids.contains(&item_id), + ItemType::All => true, + }, + ); + + Some(cx.foreground().spawn(async move { + task.await?; + Ok(()) + })) } pub fn close_item( diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a53f20aeda1f10c2eb890205a95187ada8608cd5..06e19bbf88c1dcabb1827254209a08d8b104cd85 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -95,7 +95,7 @@ actions!( ToggleLeftSidebar, ToggleRightSidebar, NewTerminal, - NewSearch, + NewSearch ] ); diff --git a/crates/zed/Cargo.toml b/crates/zed/Cargo.toml index 574111848c38545cec10123f157bc520fe896ef5..d295deea717c3d8ac9529c8fbc50431757b22946 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.68.0" +version = "0.69.0" [lib] name = "zed" @@ -30,6 +30,7 @@ clock = { path = "../clock" } diagnostics = { path = "../diagnostics" } editor = { path = "../editor" } file_finder = { path = "../file_finder" } +human_bytes = "0.4.1" search = { path = "../search" } fs = { path = "../fs" } fsevent = { path = "../fsevent" } @@ -48,6 +49,7 @@ recent_projects = { path = "../recent_projects" } rpc = { path = "../rpc" } settings = { path = "../settings" } sum_tree = { path = "../sum_tree" } +sysinfo = "0.27.1" text = { path = "../text" } terminal_view = { path = "../terminal_view" } theme = { path = "../theme" } @@ -108,6 +110,7 @@ tree-sitter-html = "0.19.0" tree-sitter-scheme = { git = "https://github.com/6cdh/tree-sitter-scheme", rev = "af0fd1fa452cb2562dc7b5c8a8c55551c39273b9"} tree-sitter-racket = { git = "https://github.com/zed-industries/tree-sitter-racket", rev = "eb010cf2c674c6fd9a6316a84e28ef90190fe51a"} url = "2.2" +urlencoding = "2.1.2" [dev-dependencies] call = { path = "../call", features = ["test-support"] } diff --git a/crates/zed/src/languages/ruby.rs b/crates/zed/src/languages/ruby.rs index 6aad293d34a1ac674aa1b01c30a1dc218d4aa92d..cbfb5e35a70f06da84c5fa551845bb63385e3f94 100644 --- a/crates/zed/src/languages/ruby.rs +++ b/crates/zed/src/languages/ruby.rs @@ -50,14 +50,14 @@ impl LspAdapter for RubyLanguageServer { grammar.highlight_id_for_name("type")? } lsp::CompletionItemKind::KEYWORD => { - if label.starts_with(":") { + if label.starts_with(':') { grammar.highlight_id_for_name("string.special.symbol")? } else { grammar.highlight_id_for_name("keyword")? } } lsp::CompletionItemKind::VARIABLE => { - if label.starts_with("@") { + if label.starts_with('@') { grammar.highlight_id_for_name("property")? } else { return None; diff --git a/crates/zed/src/languages/typescript.rs b/crates/zed/src/languages/typescript.rs index 95f56bce5b57941e15d63d1186aa09b45984fad6..f54b09ceda55c5e94666fe16bbd7b29fc9fe0002 100644 --- a/crates/zed/src/languages/typescript.rs +++ b/crates/zed/src/languages/typescript.rs @@ -128,8 +128,14 @@ impl LspAdapter for TypeScriptLspAdapter { Kind::PROPERTY | Kind::FIELD => grammar.highlight_id_for_name("property"), _ => None, }?; + + let text = match &item.detail { + Some(detail) => format!("{} {}", item.label, detail), + None => item.label.clone(), + }; + Some(language::CodeLabel { - text: item.label.clone(), + text, runs: vec![(0..len, highlight_id)], filter_range: 0..len, }) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 5ac7cb36b6db6ce66f7b6efbbc21e716cf4f9268..f4fa919c48e5b2e2640adcde991af6a1447904c7 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -101,7 +101,7 @@ fn main() { //Setup settings global before binding actions cx.set_global(SettingsFile::new( - &*paths::SETTINGS, + &paths::SETTINGS, settings_file_content.clone(), fs.clone(), )); @@ -586,7 +586,7 @@ async fn handle_cli_connection( responses .send(CliResponse::Exit { - status: if errored { 1 } else { 0 }, + status: i32::from(errored), }) .log_err(); } diff --git a/crates/zed/src/menus.rs b/crates/zed/src/menus.rs index 18e7d6fe9b1ef8be5c634ce04d25ee5a1cd09893..b46e8ad703721f81a9b5e3cf4c925be2e28545f5 100644 --- a/crates/zed/src/menus.rs +++ b/crates/zed/src/menus.rs @@ -339,15 +339,22 @@ pub fn menus() -> Vec> { }, MenuItem::Separator, MenuItem::Action { - name: "Documentation", - action: Box::new(crate::OpenBrowser { - url: "https://zed.dev/docs".into(), - }), + name: "Copy System Specs Into Clipboard", + action: Box::new(crate::CopySystemSpecsIntoClipboard), + }, + MenuItem::Action { + name: "File Bug Report", + action: Box::new(crate::FileBugReport), }, MenuItem::Action { - name: "Give Feedback", + name: "Request Feature", + action: Box::new(crate::RequestFeature), + }, + MenuItem::Separator, + MenuItem::Action { + name: "Documentation", action: Box::new(crate::OpenBrowser { - url: super::feedback::NEW_ISSUE_URL.into(), + url: "https://zed.dev/docs".into(), }), }, MenuItem::Action { diff --git a/crates/zed/src/system_specs.rs b/crates/zed/src/system_specs.rs new file mode 100644 index 0000000000000000000000000000000000000000..b6c2c0fcba70347815c239b2adf052dea951e2b3 --- /dev/null +++ b/crates/zed/src/system_specs.rs @@ -0,0 +1,52 @@ +use std::{env, fmt::Display}; + +use gpui::AppContext; +use human_bytes::human_bytes; +use sysinfo::{System, SystemExt}; +use util::channel::ReleaseChannel; + +pub struct SystemSpecs { + app_version: &'static str, + release_channel: &'static str, + os_name: &'static str, + os_version: Option, + memory: u64, + architecture: &'static str, +} + +impl SystemSpecs { + pub fn new(cx: &AppContext) -> Self { + let platform = cx.platform(); + let system = System::new_all(); + + SystemSpecs { + app_version: env!("CARGO_PKG_VERSION"), + release_channel: cx.global::().dev_name(), + os_name: platform.os_name(), + os_version: platform + .os_version() + .ok() + .map(|os_version| os_version.to_string()), + memory: system.total_memory(), + architecture: env::consts::ARCH, + } + } +} + +impl Display for SystemSpecs { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let os_information = match &self.os_version { + Some(os_version) => format!("OS: {} {}", self.os_name, os_version), + None => format!("OS: {}", self.os_name), + }; + let system_specs = [ + format!("Zed: {} ({})", self.app_version, self.release_channel), + os_information, + format!("Memory: {}", human_bytes(self.memory as f64)), + format!("Architecture: {}", self.architecture), + ] + .join("\n"); + + write!(f, "{system_specs}") + } +} diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 099fb4eea934cf286da36959040595f1bbb0774f..c3af91306dce4b1ba79bf46f52fdce79daa34cff 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -1,6 +1,7 @@ mod feedback; pub mod languages; pub mod menus; +pub mod system_specs; #[cfg(any(test, feature = "test-support"))] pub mod test; @@ -21,7 +22,7 @@ use gpui::{ }, impl_actions, platform::{WindowBounds, WindowOptions}, - AssetSource, AsyncAppContext, TitlebarOptions, ViewContext, WindowKind, + AssetSource, AsyncAppContext, ClipboardItem, TitlebarOptions, ViewContext, WindowKind, }; use language::Rope; use lazy_static::lazy_static; @@ -33,6 +34,7 @@ use serde::Deserialize; use serde_json::to_string_pretty; use settings::{keymap_file_json_schema, settings_file_json_schema, Settings}; use std::{env, path::Path, str, sync::Arc}; +use system_specs::SystemSpecs; use util::{channel::ReleaseChannel, paths, ResultExt}; pub use workspace; use workspace::{sidebar::SidebarSide, AppState, Workspace}; @@ -67,6 +69,9 @@ actions!( ResetBufferFontSize, InstallCommandLineInterface, ResetDatabase, + CopySystemSpecsIntoClipboard, + RequestFeature, + FileBugReport ] ); @@ -245,6 +250,41 @@ pub fn init(app_state: &Arc, cx: &mut gpui::MutableAppContext) { }, ); + cx.add_action( + |_: &mut Workspace, _: &CopySystemSpecsIntoClipboard, cx: &mut ViewContext| { + let system_specs = SystemSpecs::new(cx).to_string(); + let item = ClipboardItem::new(system_specs.clone()); + cx.prompt( + gpui::PromptLevel::Info, + &format!("Copied into clipboard:\n\n{system_specs}"), + &["OK"], + ); + cx.write_to_clipboard(item); + }, + ); + + cx.add_action( + |_: &mut Workspace, _: &RequestFeature, cx: &mut ViewContext| { + let url = "https://github.com/zed-industries/feedback/issues/new?assignees=&labels=enhancement%2Ctriage&template=0_feature_request.yml"; + cx.dispatch_action(OpenBrowser { + url: url.into(), + }); + }, + ); + + cx.add_action( + |_: &mut Workspace, _: &FileBugReport, cx: &mut ViewContext| { + let system_specs_text = SystemSpecs::new(cx).to_string(); + let url = format!( + "https://github.com/zed-industries/feedback/issues/new?assignees=&labels=defect%2Ctriage&template=2_bug_report.yml&environment={}", + urlencoding::encode(&system_specs_text) + ); + cx.dispatch_action(OpenBrowser { + url: url.into(), + }); + }, + ); + activity_indicator::init(cx); call::init(app_state.client.clone(), app_state.user_store.clone(), cx); settings::KeymapFileContent::load_defaults(cx); @@ -298,11 +338,11 @@ pub fn initialize_workspace( }, "schemas": [ { - "fileMatch": [schema_file_match(&*paths::SETTINGS)], + "fileMatch": [schema_file_match(&paths::SETTINGS)], "schema": settings_file_json_schema(theme_names, language_names), }, { - "fileMatch": [schema_file_match(&*paths::KEYMAP)], + "fileMatch": [schema_file_match(&paths::KEYMAP)], "schema": keymap_file_json_schema(&action_names), } ] @@ -606,7 +646,7 @@ fn open_bundled_config_file( cx: &mut ViewContext, ) { workspace - .with_local_workspace(&app_state.clone(), cx, |workspace, cx| { + .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).unwrap().data; diff --git a/styles/src/styleTree/contactNotification.ts b/styles/src/styleTree/contactNotification.ts index e0b967203d1f72a3eb2fd40c6f88786e6376aafc..e3f15baa32285ce202a279736f1d5f9ed45850cb 100644 --- a/styles/src/styleTree/contactNotification.ts +++ b/styles/src/styleTree/contactNotification.ts @@ -32,13 +32,13 @@ export default function contactNotification(colorScheme: ColorScheme): Object { }, }, dismissButton: { - color: foreground(layer, "on"), + color: foreground(layer, "variant"), iconWidth: 8, iconHeight: 8, buttonWidth: 8, buttonHeight: 8, hover: { - color: foreground(layer, "on", "hovered"), + color: foreground(layer, "hovered"), }, }, }; diff --git a/styles/src/styleTree/editor.ts b/styles/src/styleTree/editor.ts index 63d86c8f471f3574e87cb05414c75e5a39c33c04..e96cab0316a4116d7738efeb7f2336b78cb6c532 100644 --- a/styles/src/styleTree/editor.ts +++ b/styles/src/styleTree/editor.ts @@ -257,7 +257,6 @@ export default function editor(colorScheme: ColorScheme) { right: 6, }, hover: { - color: foreground(layer, "on", "hovered"), background: background(layer, "on", "hovered"), }, }, diff --git a/styles/src/styleTree/search.ts b/styles/src/styleTree/search.ts index 4f12c42c0c267e2ec98578ae46955f915ed9f974..2edd3807a53928bf01b796e0e9d306f27c03a420 100644 --- a/styles/src/styleTree/search.ts +++ b/styles/src/styleTree/search.ts @@ -80,5 +80,17 @@ export default function search(colorScheme: ColorScheme) { ...text(layer, "mono", "on"), size: 18, }, + dismissButton: { + color: foreground(layer, "variant"), + iconWidth: 12, + buttonWidth: 14, + padding: { + left: 10, + right: 10, + }, + hover: { + color: foreground(layer, "hovered"), + }, + }, }; } diff --git a/styles/src/styleTree/tabBar.ts b/styles/src/styleTree/tabBar.ts index fcf78dc73f26854b73ef13113c2856e96f7c490c..a4875cec06a57bafde5455bff1d9d2bd9322f4f0 100644 --- a/styles/src/styleTree/tabBar.ts +++ b/styles/src/styleTree/tabBar.ts @@ -26,7 +26,7 @@ export default function tabBar(colorScheme: ColorScheme) { // Close icons iconWidth: 8, iconClose: foreground(layer, "variant"), - iconCloseActive: foreground(layer), + iconCloseActive: foreground(layer, "hovered"), // Indicators iconConflict: foreground(layer, "warning"),