From 810b37c129bf66bbc3f452f9fdce539a5ba37cd9 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Mon, 6 Jan 2025 23:18:04 -0500 Subject: [PATCH] Rename the `OpenFile` action to `OpenSelectedFilename` to better reflect its function (#22494) Release Notes: - Renamed the `OpenFile` action to `OpenSelectedFilename` for clarity --- crates/editor/src/actions.rs | 5 +- crates/editor/src/editor.rs | 2 +- crates/editor/src/element.rs | 2 +- crates/gpui/src/action.rs | 65 +++++++++++++++++++++-- crates/gpui/src/app.rs | 5 ++ crates/gpui/src/keymap.rs | 2 - crates/gpui_macros/src/register_action.rs | 1 + crates/languages/src/json.rs | 3 +- crates/settings/src/keymap_file.rs | 61 +++++++++++++-------- 9 files changed, 113 insertions(+), 33 deletions(-) diff --git a/crates/editor/src/actions.rs b/crates/editor/src/actions.rs index 2614a440ba..a2bc64c4b5 100644 --- a/crates/editor/src/actions.rs +++ b/crates/editor/src/actions.rs @@ -1,6 +1,6 @@ //! This module contains all actions supported by [`Editor`]. use super::*; -use gpui::action_as; +use gpui::{action_aliases, action_as}; use util::serde::default_true; #[derive(PartialEq, Clone, Deserialize, Default)] @@ -311,7 +311,6 @@ gpui::actions!( OpenExcerpts, OpenExcerptsSplit, OpenProposedChangesEditor, - OpenFile, OpenDocs, OpenPermalinkToLine, OpenUrl, @@ -389,3 +388,5 @@ gpui::actions!( ); action_as!(go_to_line, ToggleGoToLine as Toggle); + +action_aliases!(editor, OpenSelectedFilename, [OpenFile]); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e65b28b9af..11d11fc457 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -9503,7 +9503,7 @@ impl Editor { url_finder.detach(); } - pub fn open_file(&mut self, _: &OpenFile, cx: &mut ViewContext) { + pub fn open_selected_filename(&mut self, _: &OpenSelectedFilename, cx: &mut ViewContext) { let Some(workspace) = self.workspace() else { return; }; diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 06fc87b908..8580acc15a 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -342,7 +342,7 @@ impl EditorElement { .detach_and_log_err(cx); }); register_action(view, cx, Editor::open_url); - register_action(view, cx, Editor::open_file); + register_action(view, cx, Editor::open_selected_filename); register_action(view, cx, Editor::fold); register_action(view, cx, Editor::fold_at_level); register_action(view, cx, Editor::fold_all); diff --git a/crates/gpui/src/action.rs b/crates/gpui/src/action.rs index 4f5256dbb0..338397a551 100644 --- a/crates/gpui/src/action.rs +++ b/crates/gpui/src/action.rs @@ -62,6 +62,14 @@ pub trait Action: 'static + Send { fn build(value: serde_json::Value) -> Result> where Self: Sized; + + /// A list of alternate, deprecated names for this action. + fn deprecated_aliases() -> &'static [&'static str] + where + Self: Sized, + { + &[] + } } impl std::fmt::Debug for dyn Action { @@ -85,6 +93,7 @@ pub(crate) struct ActionRegistry { builders_by_name: HashMap, names_by_type_id: HashMap, all_names: Vec, // So we can return a static slice. + deprecations: Vec<(SharedString, SharedString)>, } impl Default for ActionRegistry { @@ -93,6 +102,7 @@ impl Default for ActionRegistry { builders_by_name: Default::default(), names_by_type_id: Default::default(), all_names: Default::default(), + deprecations: Default::default(), }; this.load_actions(); @@ -111,6 +121,7 @@ pub type MacroActionBuilder = fn() -> ActionData; #[doc(hidden)] pub struct ActionData { pub name: &'static str, + pub aliases: &'static [&'static str], pub type_id: TypeId, pub build: ActionBuilder, } @@ -134,6 +145,7 @@ impl ActionRegistry { pub(crate) fn load_action(&mut self) { self.insert_action(ActionData { name: A::debug_name(), + aliases: A::deprecated_aliases(), type_id: TypeId::of::(), build: A::build, }); @@ -142,6 +154,10 @@ impl ActionRegistry { fn insert_action(&mut self, action: ActionData) { let name: SharedString = action.name.into(); self.builders_by_name.insert(name.clone(), action.build); + for &alias in action.aliases { + self.builders_by_name.insert(alias.into(), action.build); + self.deprecations.push((alias.into(), name.clone())); + } self.names_by_type_id.insert(action.type_id, name.clone()); self.all_names.push(name); } @@ -174,6 +190,10 @@ impl ActionRegistry { pub fn all_action_names(&self) -> &[SharedString] { self.all_names.as_slice() } + + pub fn action_deprecations(&self) -> &[(SharedString, SharedString)] { + self.deprecations.as_slice() + } } /// Defines unit structs that can be used as actions. @@ -206,7 +226,7 @@ macro_rules! actions { /// `impl_action_as!` #[macro_export] macro_rules! action_as { - ($namespace:path, $name:ident as $visual_name:tt) => { + ($namespace:path, $name:ident as $visual_name:ident) => { #[doc = "The `"] #[doc = stringify!($name)] #[doc = "` action, see [`gpui::actions!`]"] @@ -228,6 +248,43 @@ macro_rules! action_as { }; } +/// Defines a unit struct that can be used as an action, with some deprecated aliases. +#[macro_export] +macro_rules! action_aliases { + ($namespace:path, $name:ident, [$($alias:ident),* $(,)?]) => { + #[doc = "The `"] + #[doc = stringify!($name)] + #[doc = "` action, see [`gpui::actions!`]"] + #[derive( + ::std::cmp::PartialEq, + ::std::clone::Clone, + ::std::default::Default, + ::std::fmt::Debug, + gpui::private::serde_derive::Deserialize, + )] + #[serde(crate = "gpui::private::serde")] + pub struct $name; + + gpui::__impl_action!( + $namespace, + $name, + $name, + fn build( + _: gpui::private::serde_json::Value, + ) -> gpui::Result<::std::boxed::Box> { + Ok(Box::new(Self)) + }, + fn deprecated_aliases() -> &'static [&'static str] { + &[ + $(concat!(stringify!($namespace), "::", stringify!($alias))),* + ] + } + ); + + gpui::register_action!($name); + }; +} + /// Implements the Action trait for any struct that implements Clone, Default, PartialEq, and serde_deserialize::Deserialize #[macro_export] macro_rules! impl_actions { @@ -269,7 +326,7 @@ macro_rules! impl_action_as { #[doc(hidden)] #[macro_export] macro_rules! __impl_action { - ($namespace:path, $name:ident, $visual_name:tt, $build:item) => { + ($namespace:path, $name:ident, $visual_name:tt, $($items:item),*) => { impl gpui::Action for $name { fn name(&self) -> &'static str { @@ -291,8 +348,6 @@ macro_rules! __impl_action { ) } - $build - fn partial_eq(&self, action: &dyn gpui::Action) -> bool { action .as_any() @@ -307,6 +362,8 @@ macro_rules! __impl_action { fn as_any(&self) -> &dyn ::std::any::Any { self } + + $($items)* } }; } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 96e584274b..070ff29c21 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1226,6 +1226,11 @@ impl AppContext { self.actions.all_action_names() } + /// Get a list of all deprecated action aliases and their canonical names. + pub fn action_deprecations(&self) -> &[(SharedString, SharedString)] { + self.actions.action_deprecations() + } + /// Register a callback to be invoked when the application is about to quit. /// It is not possible to cancel the quit event at this point. pub fn on_app_quit( diff --git a/crates/gpui/src/keymap.rs b/crates/gpui/src/keymap.rs index ad40e284bb..c248d2a387 100644 --- a/crates/gpui/src/keymap.rs +++ b/crates/gpui/src/keymap.rs @@ -134,8 +134,6 @@ impl Keymap { /// If a user has disabled a binding with `"x": null` it will not be returned. Disabled /// bindings are evaluated with the same precedence rules so you can disable a rule in /// a given context only. - /// - /// In the case of multi-key bindings, the pub fn bindings_for_input( &self, input: &[Keystroke], diff --git a/crates/gpui_macros/src/register_action.rs b/crates/gpui_macros/src/register_action.rs index 7ec1d6dd4b..7fc8158e9b 100644 --- a/crates/gpui_macros/src/register_action.rs +++ b/crates/gpui_macros/src/register_action.rs @@ -32,6 +32,7 @@ pub(crate) fn register_action(type_name: &Ident) -> proc_macro2::TokenStream { fn #action_builder_fn_name() -> gpui::ActionData { gpui::ActionData { name: <#type_name as gpui::Action>::debug_name(), + aliases: <#type_name as gpui::Action>::deprecated_aliases(), type_id: ::std::any::TypeId::of::<#type_name>(), build: <#type_name as gpui::Action>::build, } diff --git a/crates/languages/src/json.rs b/crates/languages/src/json.rs index 41ff7e0c83..7172f96f74 100644 --- a/crates/languages/src/json.rs +++ b/crates/languages/src/json.rs @@ -76,6 +76,7 @@ impl JsonLspAdapter { fn get_workspace_config(language_names: Vec, cx: &mut AppContext) -> Value { let action_names = cx.all_action_names(); + let deprecations = cx.action_deprecations(); let font_names = &cx.text_system().all_font_names(); let settings_schema = cx.global::().json_schema( @@ -116,7 +117,7 @@ impl JsonLspAdapter { }, { "fileMatch": [schema_file_match(paths::keymap_file())], - "schema": KeymapFile::generate_json_schema(action_names), + "schema": KeymapFile::generate_json_schema(action_names, deprecations), }, { "fileMatch": [ diff --git a/crates/settings/src/keymap_file.rs b/crates/settings/src/keymap_file.rs index 82329337c6..c2b4625ffc 100644 --- a/crates/settings/src/keymap_file.rs +++ b/crates/settings/src/keymap_file.rs @@ -5,7 +5,7 @@ use gpui::{Action, AppContext, KeyBinding, SharedString}; use schemars::{ gen::{SchemaGenerator, SchemaSettings}, schema::{InstanceType, Schema, SchemaObject, SingleOrVec, SubschemaValidation}, - JsonSchema, + JsonSchema, Map, }; use serde::Deserialize; use serde_json::Value; @@ -139,34 +139,51 @@ impl KeymapFile { Ok(()) } - pub fn generate_json_schema(action_names: &[SharedString]) -> serde_json::Value { + pub fn generate_json_schema( + action_names: &[SharedString], + deprecations: &[(SharedString, SharedString)], + ) -> serde_json::Value { let mut root_schema = SchemaSettings::draft07() .with(|settings| settings.option_add_null_type = false) .into_generator() .into_root_schema_for::(); + let mut alternatives = vec![ + Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), + enum_values: Some( + action_names + .iter() + .map(|name| Value::String(name.to_string())) + .collect(), + ), + ..Default::default() + }), + Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))), + ..Default::default() + }), + Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Null))), + ..Default::default() + }), + ]; + for (old, new) in deprecations { + alternatives.push(Schema::Object(SchemaObject { + instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), + const_value: Some(Value::String(old.to_string())), + extensions: Map::from_iter([( + // deprecationMessage is not part of the JSON Schema spec, + // but json-language-server recognizes it. + "deprecationMessage".to_owned(), + format!("Deprecated, use {new}").into(), + )]), + ..Default::default() + })); + } let action_schema = Schema::Object(SchemaObject { subschemas: Some(Box::new(SubschemaValidation { - one_of: Some(vec![ - Schema::Object(SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), - enum_values: Some( - action_names - .iter() - .map(|name| Value::String(name.to_string())) - .collect(), - ), - ..Default::default() - }), - Schema::Object(SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))), - ..Default::default() - }), - Schema::Object(SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Null))), - ..Default::default() - }), - ]), + one_of: Some(alternatives), ..Default::default() })), ..Default::default()