diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index e7936b8ebc..eaf91b089b 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -406,6 +406,129 @@ pub fn init_settings(cx: &mut AppContext) { pub fn init(cx: &mut AppContext) { init_settings(cx); + // cx.register_action_type(Editor::new_file); + // cx.register_action_type(Editor::new_file_in_direction); + // cx.register_action_type(Editor::cancel); + // cx.register_action_type(Editor::newline); + // cx.register_action_type(Editor::newline_above); + // cx.register_action_type(Editor::newline_below); + // cx.register_action_type(Editor::backspace); + // cx.register_action_type(Editor::delete); + // cx.register_action_type(Editor::tab); + // cx.register_action_type(Editor::tab_prev); + // cx.register_action_type(Editor::indent); + // cx.register_action_type(Editor::outdent); + // cx.register_action_type(Editor::delete_line); + // cx.register_action_type(Editor::join_lines); + // cx.register_action_type(Editor::sort_lines_case_sensitive); + // cx.register_action_type(Editor::sort_lines_case_insensitive); + // cx.register_action_type(Editor::reverse_lines); + // cx.register_action_type(Editor::shuffle_lines); + // cx.register_action_type(Editor::convert_to_upper_case); + // cx.register_action_type(Editor::convert_to_lower_case); + // cx.register_action_type(Editor::convert_to_title_case); + // cx.register_action_type(Editor::convert_to_snake_case); + // cx.register_action_type(Editor::convert_to_kebab_case); + // cx.register_action_type(Editor::convert_to_upper_camel_case); + // cx.register_action_type(Editor::convert_to_lower_camel_case); + // cx.register_action_type(Editor::delete_to_previous_word_start); + // cx.register_action_type(Editor::delete_to_previous_subword_start); + // cx.register_action_type(Editor::delete_to_next_word_end); + // cx.register_action_type(Editor::delete_to_next_subword_end); + // cx.register_action_type(Editor::delete_to_beginning_of_line); + // cx.register_action_type(Editor::delete_to_end_of_line); + // cx.register_action_type(Editor::cut_to_end_of_line); + // cx.register_action_type(Editor::duplicate_line); + // cx.register_action_type(Editor::move_line_up); + // cx.register_action_type(Editor::move_line_down); + // cx.register_action_type(Editor::transpose); + // cx.register_action_type(Editor::cut); + // cx.register_action_type(Editor::copy); + // cx.register_action_type(Editor::paste); + // cx.register_action_type(Editor::undo); + // cx.register_action_type(Editor::redo); + // cx.register_action_type(Editor::move_page_up); + // cx.register_action_type::(); + // cx.register_action_type(Editor::move_page_down); + // cx.register_action_type(Editor::next_screen); + // cx.register_action_type::(); + // cx.register_action_type::(); + // cx.register_action_type(Editor::move_to_previous_word_start); + // cx.register_action_type(Editor::move_to_previous_subword_start); + // cx.register_action_type(Editor::move_to_next_word_end); + // cx.register_action_type(Editor::move_to_next_subword_end); + // cx.register_action_type(Editor::move_to_beginning_of_line); + // cx.register_action_type(Editor::move_to_end_of_line); + // cx.register_action_type(Editor::move_to_start_of_paragraph); + // cx.register_action_type(Editor::move_to_end_of_paragraph); + // cx.register_action_type(Editor::move_to_beginning); + // cx.register_action_type(Editor::move_to_end); + // cx.register_action_type(Editor::select_up); + // cx.register_action_type(Editor::select_down); + // cx.register_action_type(Editor::select_left); + // cx.register_action_type(Editor::select_right); + // cx.register_action_type(Editor::select_to_previous_word_start); + // cx.register_action_type(Editor::select_to_previous_subword_start); + // cx.register_action_type(Editor::select_to_next_word_end); + // cx.register_action_type(Editor::select_to_next_subword_end); + // cx.register_action_type(Editor::select_to_beginning_of_line); + // cx.register_action_type(Editor::select_to_end_of_line); + // cx.register_action_type(Editor::select_to_start_of_paragraph); + // cx.register_action_type(Editor::select_to_end_of_paragraph); + // cx.register_action_type(Editor::select_to_beginning); + // cx.register_action_type(Editor::select_to_end); + // cx.register_action_type(Editor::select_all); + // cx.register_action_type(Editor::select_all_matches); + // cx.register_action_type(Editor::select_line); + // cx.register_action_type(Editor::split_selection_into_lines); + // cx.register_action_type(Editor::add_selection_above); + // cx.register_action_type(Editor::add_selection_below); + // cx.register_action_type(Editor::select_next); + // cx.register_action_type(Editor::select_previous); + // cx.register_action_type(Editor::toggle_comments); + // cx.register_action_type(Editor::select_larger_syntax_node); + // cx.register_action_type(Editor::select_smaller_syntax_node); + // cx.register_action_type(Editor::move_to_enclosing_bracket); + // cx.register_action_type(Editor::undo_selection); + // cx.register_action_type(Editor::redo_selection); + // cx.register_action_type(Editor::go_to_diagnostic); + // cx.register_action_type(Editor::go_to_prev_diagnostic); + // cx.register_action_type(Editor::go_to_hunk); + // cx.register_action_type(Editor::go_to_prev_hunk); + // cx.register_action_type(Editor::go_to_definition); + // cx.register_action_type(Editor::go_to_definition_split); + // cx.register_action_type(Editor::go_to_type_definition); + // cx.register_action_type(Editor::go_to_type_definition_split); + // cx.register_action_type(Editor::fold); + // cx.register_action_type(Editor::fold_at); + // cx.register_action_type(Editor::unfold_lines); + // cx.register_action_type(Editor::unfold_at); + // cx.register_action_type(Editor::gutter_hover); + // cx.register_action_type(Editor::fold_selected_ranges); + // cx.register_action_type(Editor::show_completions); + // cx.register_action_type(Editor::toggle_code_actions); + // cx.register_action_type(Editor::open_excerpts); + // cx.register_action_type(Editor::toggle_soft_wrap); + // cx.register_action_type(Editor::toggle_inlay_hints); + // cx.register_action_type(Editor::reveal_in_finder); + // cx.register_action_type(Editor::copy_path); + // cx.register_action_type(Editor::copy_relative_path); + // cx.register_action_type(Editor::copy_highlight_json); + // cx.add_async_action(Editor::format); + // cx.register_action_type(Editor::restart_language_server); + // cx.register_action_type(Editor::show_character_palette); + // cx.add_async_action(Editor::confirm_completion); + // cx.add_async_action(Editor::confirm_code_action); + // cx.add_async_action(Editor::rename); + // cx.add_async_action(Editor::confirm_rename); + // cx.add_async_action(Editor::find_all_references); + // cx.register_action_type(Editor::next_copilot_suggestion); + // cx.register_action_type(Editor::previous_copilot_suggestion); + // cx.register_action_type(Editor::copilot_suggest); + // cx.register_action_type(Editor::context_menu_first); + // cx.register_action_type(Editor::context_menu_prev); + // cx.register_action_type(Editor::context_menu_next); + // cx.register_action_type(Editor::context_menu_last); hover_popover::init(cx); scroll::actions::init(cx); diff --git a/crates/gpui2/src/action.rs b/crates/gpui2/src/action.rs index ba4ed077f2..6e0bcedf4d 100644 --- a/crates/gpui2/src/action.rs +++ b/crates/gpui2/src/action.rs @@ -6,6 +6,49 @@ use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard}; use serde::Deserialize; use std::any::{type_name, Any}; +/// Actions are used to implement keyboard-driven UI. +/// When you declare an action, you can bind keys to the action in the keymap and +/// listeners for that action in the element tree. +/// +/// To declare a list of simple actions, you can use the actions! macro, which defines a simple unit struct +/// action for each listed action name. +/// ```rust +/// actions!(MoveUp, MoveDown, MoveLeft, MoveRight, Newline); +/// ``` +/// More complex data types can also be actions. If you annotate your type with the `#[action]` proc macro, +/// it will automatically +/// ``` +/// #[action] +/// pub struct SelectNext { +/// pub replace_newest: bool, +/// } +/// +/// Any type A that satisfies the following bounds is automatically an action: +/// +/// ``` +/// A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static, +/// ``` +/// +/// The `#[action]` annotation will derive these implementations for your struct automatically. If you +/// want to control them manually, you can use the lower-level `#[register_action]` macro, which only +/// generates the code needed to register your action before `main`. Then you'll need to implement all +/// the traits manually. +/// +/// ``` +/// #[gpui::register_action] +/// #[derive(gpui::serde::Deserialize, std::cmp::PartialEq, std::clone::Clone, std::fmt::Debug)] +/// pub struct Paste { +/// pub content: SharedString, +/// } +/// +/// impl std::default::Default for Paste { +/// fn default() -> Self { +/// Self { +/// content: SharedString::from("🍝"), +/// } +/// } +/// } +/// ``` pub trait Action: std::fmt::Debug + 'static { fn qualified_name() -> SharedString where @@ -19,6 +62,44 @@ pub trait Action: std::fmt::Debug + 'static { fn as_any(&self) -> &dyn Any; } +// Types become actions by satisfying a list of trait bounds. +impl Action for A +where + A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static, +{ + fn qualified_name() -> SharedString { + // todo!() remove the 2 replacement when migration is done + type_name::().replace("2::", "::").into() + } + + fn build(params: Option) -> Result> + where + Self: Sized, + { + let action = if let Some(params) = params { + serde_json::from_value(params).context("failed to deserialize action")? + } else { + Self::default() + }; + Ok(Box::new(action)) + } + + fn partial_eq(&self, action: &dyn Action) -> bool { + action + .as_any() + .downcast_ref::() + .map_or(false, |a| self == a) + } + + fn boxed_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn as_any(&self) -> &dyn Any { + self + } +} + type ActionBuilder = fn(json: Option) -> anyhow::Result>; lazy_static! { @@ -84,43 +165,6 @@ macro_rules! actions { }; } -impl Action for A -where - A: for<'a> Deserialize<'a> + PartialEq + Clone + Default + std::fmt::Debug + 'static, -{ - fn qualified_name() -> SharedString { - // todo!() remove the 2 replacement when migration is done - type_name::().replace("2::", "::").into() - } - - fn build(params: Option) -> Result> - where - Self: Sized, - { - let action = if let Some(params) = params { - serde_json::from_value(params).context("failed to deserialize action")? - } else { - Self::default() - }; - Ok(Box::new(action)) - } - - fn partial_eq(&self, action: &dyn Action) -> bool { - action - .as_any() - .downcast_ref::() - .map_or(false, |a| self == a) - } - - fn boxed_clone(&self) -> Box { - Box::new(self.clone()) - } - - fn as_any(&self) -> &dyn Any { - self - } -} - #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct DispatchContext { set: HashSet,