Compare commits

...

1 Commits

Author SHA1 Message Date
Lukas Wirth
7f21f4e8bb gpui: Represent no action via Option 2025-09-16 11:02:57 +02:00
15 changed files with 194 additions and 130 deletions

View File

@@ -26,8 +26,8 @@ use dap::{
};
use futures::{SinkExt, channel::mpsc};
use gpui::{
Action as _, AnyView, AppContext, Axis, Entity, EntityId, EventEmitter, FocusHandle, Focusable,
NoAction, Pixels, Point, Subscription, Task, WeakEntity,
AnyView, AppContext, Axis, Entity, EntityId, EventEmitter, FocusHandle, Focusable, Pixels,
Point, Subscription, Task, WeakEntity,
};
use language::Buffer;
use loaded_source_list::LoadedSourceList;
@@ -385,7 +385,7 @@ pub(crate) fn new_debugger_pane(
project.clone(),
Default::default(),
None,
NoAction.boxed_clone(),
None,
window,
cx,
);

View File

@@ -1,7 +1,7 @@
use anyhow::{Context as _, Result};
use collections::HashMap;
pub use gpui_macros::Action;
pub use no_action::{NoAction, is_no_action};
use serde_json::json;
use std::{
any::{Any, TypeId},
@@ -419,22 +419,3 @@ pub fn generate_list_of_all_registered_actions() -> impl Iterator<Item = MacroAc
.into_iter()
.map(|builder| builder.0())
}
mod no_action {
use crate as gpui;
use std::any::Any as _;
actions!(
zed,
[
/// Action with special handling which unbinds the keybinding this is associated with,
/// if it is the highest precedence match.
NoAction
]
);
/// Returns whether or not this action represents a removed key binding.
pub fn is_no_action(action: &dyn gpui::Action) -> bool {
action.as_any().type_id() == (NoAction {}).type_id()
}
}

View File

@@ -438,7 +438,11 @@ impl DispatchTree {
) -> bool {
let (bindings, _) = keymap.bindings_for_input(&binding.keystrokes, context_stack);
if let Some(found) = bindings.iter().next() {
found.action.partial_eq(binding.action.as_ref())
match (found.action.as_deref(), binding.action.as_deref()) {
(None, None) => true,
(Some(f), Some(b)) => f.partial_eq(b),
(None, Some(_)) | (Some(_), None) => false,
}
} else {
false
}
@@ -677,7 +681,12 @@ mod tests {
let keybinding = tree.bindings_for_action(&TestAction, &contexts);
assert!(keybinding[0].action.partial_eq(&TestAction))
assert!(
keybinding[0]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&TestAction))
);
}
#[crate::test]

View File

@@ -4,7 +4,7 @@ mod context;
pub use binding::*;
pub use context::*;
use crate::{Action, AsKeystroke, Keystroke, is_no_action};
use crate::{Action, AsKeystroke, Keystroke};
use collections::{HashMap, HashSet};
use smallvec::SmallVec;
use std::any::TypeId;
@@ -43,14 +43,14 @@ impl Keymap {
/// Add more bindings to the keymap.
pub fn add_bindings<T: IntoIterator<Item = KeyBinding>>(&mut self, bindings: T) {
for binding in bindings {
let action_id = binding.action().as_any().type_id();
if is_no_action(&*binding.action) {
self.no_action_binding_indices.push(self.bindings.len());
} else {
if let Some(action) = binding.action.as_deref() {
let action_id = action.type_id();
self.binding_indices_by_action_id
.entry(action_id)
.or_default()
.push(self.bindings.len());
} else {
self.no_action_binding_indices.push(self.bindings.len());
}
self.bindings.push(binding);
}
@@ -86,7 +86,10 @@ impl Keymap {
binding_indices.filter_map(|ix| {
let binding = &self.bindings[*ix];
if !binding.action().partial_eq(action) {
if binding
.action()
.is_none_or(|action| !action.partial_eq(action))
{
return None;
}
@@ -170,7 +173,7 @@ impl Keymap {
let mut first_binding_index = None;
for (_, ix, binding) in matched_bindings {
if is_no_action(&*binding.action) {
if binding.action.is_none() {
// Only break if this is a user-defined NoAction binding
// This allows user keymaps to override base keymap NoAction bindings
if let Some(meta) = binding.meta {
@@ -195,7 +198,7 @@ impl Keymap {
{
continue;
}
if is_no_action(&*binding.action) {
if binding.action.is_none() {
pending.remove(&&binding.keystrokes);
continue;
}
@@ -204,8 +207,9 @@ impl Keymap {
(bindings, !pending.is_empty())
}
/// Check if the given binding is enabled, given a certain key context.
/// Returns the deepest depth at which the binding matches, or None if it doesn't match.
/// Returns the deepest depth at which the binding matches, or [`None`] if it doesn't match.
fn binding_enabled(&self, binding: &KeyBinding, contexts: &[KeyContext]) -> Option<usize> {
if let Some(predicate) = &binding.context_predicate {
predicate.depth_of(contexts)
@@ -219,7 +223,6 @@ impl Keymap {
mod tests {
use super::*;
use crate as gpui;
use gpui::NoAction;
actions!(
test_only,
@@ -287,8 +290,18 @@ mod tests {
assert!(!pending);
assert_eq!(result.len(), 2);
assert!(result[0].action.partial_eq(&ActionGamma {}));
assert!(result[1].action.partial_eq(&ActionBeta {}));
assert!(
result[0]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&ActionGamma {}))
);
assert!(
result[1]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&ActionBeta {}))
);
}
#[test]
@@ -296,8 +309,8 @@ mod tests {
let bindings = [
KeyBinding::new("ctrl-a", ActionAlpha {}, Some("editor")),
KeyBinding::new("ctrl-b", ActionAlpha {}, Some("editor")),
KeyBinding::new("ctrl-a", NoAction {}, Some("editor && mode==full")),
KeyBinding::new("ctrl-b", NoAction {}, None),
KeyBinding::new_no_action("ctrl-a", Some("editor && mode==full")),
KeyBinding::new_no_action("ctrl-b", None),
];
let mut keymap = Keymap::default();
@@ -351,7 +364,7 @@ mod tests {
fn test_multiple_keystroke_binding_disabled() {
let bindings = [
KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")),
KeyBinding::new("space w w", NoAction {}, Some("editor")),
KeyBinding::new_no_action("space w w", Some("editor")),
];
let mut keymap = Keymap::default();
@@ -403,7 +416,7 @@ mod tests {
// that should result in pending
let bindings = [
KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")),
KeyBinding::new("space w w", NoAction {}, Some("editor")),
KeyBinding::new_no_action("space w w", Some("editor")),
KeyBinding::new("space w x", ActionAlpha {}, Some("editor")),
];
let mut keymap = Keymap::default();
@@ -418,7 +431,7 @@ mod tests {
let bindings = [
KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")),
KeyBinding::new("space w x", ActionAlpha {}, Some("editor")),
KeyBinding::new("space w w", NoAction {}, Some("editor")),
KeyBinding::new_no_action("space w w", Some("editor")),
];
let mut keymap = Keymap::default();
keymap.add_bindings(bindings);
@@ -432,7 +445,7 @@ mod tests {
let bindings = [
KeyBinding::new("space w w", ActionAlpha {}, Some("workspace")),
KeyBinding::new("space w x", ActionAlpha {}, Some("workspace")),
KeyBinding::new("space w w", NoAction {}, Some("editor")),
KeyBinding::new_no_action("space w w", Some("editor")),
];
let mut keymap = Keymap::default();
keymap.add_bindings(bindings);
@@ -446,7 +459,7 @@ mod tests {
fn test_override_multikey() {
let bindings = [
KeyBinding::new("ctrl-w left", ActionAlpha {}, Some("editor")),
KeyBinding::new("ctrl-w", NoAction {}, Some("editor")),
KeyBinding::new_no_action("ctrl-w", Some("editor")),
];
let mut keymap = Keymap::default();
@@ -481,7 +494,7 @@ mod tests {
fn test_simple_disable() {
let bindings = [
KeyBinding::new("ctrl-x", ActionAlpha {}, Some("editor")),
KeyBinding::new("ctrl-x", NoAction {}, Some("editor")),
KeyBinding::new_no_action("ctrl-x", Some("editor")),
];
let mut keymap = Keymap::default();
@@ -501,7 +514,7 @@ mod tests {
// disabled at the wrong level
let bindings = [
KeyBinding::new("ctrl-x", ActionAlpha {}, Some("editor")),
KeyBinding::new("ctrl-x", NoAction {}, Some("workspace")),
KeyBinding::new_no_action("ctrl-x", Some("workspace")),
];
let mut keymap = Keymap::default();
@@ -523,7 +536,7 @@ mod tests {
fn test_disable_deeper() {
let bindings = [
KeyBinding::new("ctrl-x", ActionAlpha {}, Some("workspace")),
KeyBinding::new("ctrl-x", NoAction {}, Some("editor")),
KeyBinding::new_no_action("ctrl-x", Some("editor")),
];
let mut keymap = Keymap::default();
@@ -560,7 +573,12 @@ mod tests {
.map(Result::unwrap),
);
assert_eq!(matched.0.len(), 1);
assert!(matched.0[0].action.partial_eq(&ActionBeta));
assert!(
matched.0[0]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&ActionBeta))
);
assert!(matched.1);
}
@@ -568,7 +586,7 @@ mod tests {
fn test_pending_match_enabled_extended() {
let bindings = [
KeyBinding::new("ctrl-x", ActionBeta, Some("vim_mode == normal")),
KeyBinding::new("ctrl-x 0", NoAction, Some("Workspace")),
KeyBinding::new_no_action("ctrl-x 0", Some("Workspace")),
];
let mut keymap = Keymap::default();
keymap.add_bindings(bindings);
@@ -583,11 +601,16 @@ mod tests {
.map(Result::unwrap),
);
assert_eq!(matched.0.len(), 1);
assert!(matched.0[0].action.partial_eq(&ActionBeta));
assert!(
matched.0[0]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&ActionBeta))
);
assert!(!matched.1);
let bindings = [
KeyBinding::new("ctrl-x", ActionBeta, Some("Workspace")),
KeyBinding::new("ctrl-x 0", NoAction, Some("vim_mode == normal")),
KeyBinding::new_no_action("ctrl-x 0", Some("vim_mode == normal")),
];
let mut keymap = Keymap::default();
keymap.add_bindings(bindings);
@@ -602,7 +625,12 @@ mod tests {
.map(Result::unwrap),
);
assert_eq!(matched.0.len(), 1);
assert!(matched.0[0].action.partial_eq(&ActionBeta));
assert!(
matched.0[0]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&ActionBeta))
);
assert!(!matched.1);
}
@@ -625,7 +653,12 @@ mod tests {
.map(Result::unwrap),
);
assert_eq!(matched.0.len(), 1);
assert!(matched.0[0].action.partial_eq(&ActionBeta));
assert!(
matched.0[0]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&ActionBeta))
);
assert!(!matched.1);
}
@@ -652,8 +685,18 @@ mod tests {
// Both bindings should be returned, but Editor binding should be first (highest precedence)
assert_eq!(result.len(), 2);
assert!(result[0].action.partial_eq(&ActionBeta {})); // Editor binding first
assert!(result[1].action.partial_eq(&ActionAlpha {})); // Workspace binding second
assert!(
result[0]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&ActionBeta {}))
); // Editor binding first
assert!(
result[1]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&ActionAlpha {}))
); // Workspace binding second
}
#[test]
@@ -662,8 +705,8 @@ mod tests {
KeyBinding::new("ctrl-a", ActionAlpha {}, Some("pane")),
KeyBinding::new("ctrl-b", ActionBeta {}, Some("editor && mode == full")),
KeyBinding::new("ctrl-c", ActionGamma {}, Some("workspace")),
KeyBinding::new("ctrl-a", NoAction {}, Some("pane && active")),
KeyBinding::new("ctrl-b", NoAction {}, Some("editor")),
KeyBinding::new_no_action("ctrl-a", Some("pane && active")),
KeyBinding::new_no_action("ctrl-b", Some("editor")),
];
let mut keymap = Keymap::default();
@@ -707,7 +750,17 @@ mod tests {
// User binding should take precedence over default binding
assert_eq!(result.len(), 2);
assert!(result[0].action.partial_eq(&ActionBeta {}));
assert!(result[1].action.partial_eq(&ActionAlpha {}));
assert!(
result[0]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&ActionBeta {}))
);
assert!(
result[1]
.action
.as_deref()
.is_some_and(|it| it.partial_eq(&ActionAlpha {}))
);
}
}

View File

@@ -8,7 +8,7 @@ use smallvec::SmallVec;
/// A keybinding and its associated metadata, from the keymap.
pub struct KeyBinding {
pub(crate) action: Box<dyn Action>,
pub(crate) action: Option<Box<dyn Action>>,
pub(crate) keystrokes: SmallVec<[KeybindingKeystroke; 2]>,
pub(crate) context_predicate: Option<Rc<KeyBindingContextPredicate>>,
pub(crate) meta: Option<KeyBindingMetaIndex>,
@@ -19,7 +19,7 @@ pub struct KeyBinding {
impl Clone for KeyBinding {
fn clone(&self) -> Self {
KeyBinding {
action: self.action.boxed_clone(),
action: self.action.as_ref().map(|action| action.boxed_clone()),
keystrokes: self.keystrokes.clone(),
context_predicate: self.context_predicate.clone(),
meta: self.meta,
@@ -35,7 +35,22 @@ impl KeyBinding {
context.map(|context| KeyBindingContextPredicate::parse(context).unwrap().into());
Self::load(
keystrokes,
Box::new(action),
Some(Box::new(action) as Box<dyn Action>),
context_predicate,
false,
None,
&DummyKeyboardMapper,
)
.unwrap()
}
/// Construct a new keybinding from the given data with no action associated to it. Panics on parse error.
pub fn new_no_action(keystrokes: &str, context: Option<&str>) -> Self {
let context_predicate =
context.map(|context| KeyBindingContextPredicate::parse(context).unwrap().into());
Self::load(
keystrokes,
None,
context_predicate,
false,
None,
@@ -47,7 +62,7 @@ impl KeyBinding {
/// Load a keybinding from the given raw data.
pub fn load(
keystrokes: &str,
action: Box<dyn Action>,
action: impl Into<Option<Box<dyn Action>>>,
context_predicate: Option<Rc<KeyBindingContextPredicate>>,
use_key_equivalents: bool,
action_input: Option<SharedString>,
@@ -67,7 +82,7 @@ impl KeyBinding {
Ok(Self {
keystrokes,
action,
action: action.into(),
context_predicate,
meta: None,
action_input,
@@ -106,8 +121,8 @@ impl KeyBinding {
}
/// Get the action associated with this binding
pub fn action(&self) -> &dyn Action {
self.action.as_ref()
pub fn action(&self) -> Option<&dyn Action> {
self.action.as_deref()
}
/// Get the predicate used to match this binding
@@ -131,7 +146,10 @@ impl std::fmt::Debug for KeyBinding {
f.debug_struct("KeyBinding")
.field("keystrokes", &self.keystrokes)
.field("context_predicate", &self.context_predicate)
.field("action", &self.action.name())
.field(
"action",
&self.action.as_deref().map_or("null", |it| it.name()),
)
.finish()
}
}

View File

@@ -3800,11 +3800,13 @@ impl Window {
}
for binding in match_result.bindings {
self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
if let Some(action) = binding.action.as_deref() {
self.dispatch_action_on_node(node_id, action, cx);
}
if !cx.propagate_event {
self.dispatch_keystroke_observers(
event,
Some(binding.action),
binding.action,
match_result.context_stack,
cx,
);
@@ -3922,14 +3924,11 @@ impl Window {
cx.propagate_event = true;
for binding in replay.bindings {
self.dispatch_action_on_node(node_id, binding.action.as_ref(), cx);
if let Some(action) = binding.action.as_deref() {
self.dispatch_action_on_node(node_id, action, cx);
}
if !cx.propagate_event {
self.dispatch_keystroke_observers(
&event,
Some(binding.action),
Vec::default(),
cx,
);
self.dispatch_keystroke_observers(&event, binding.action, Vec::default(), cx);
continue 'replay;
}
}

View File

@@ -677,6 +677,9 @@ impl KeymapEditor {
let mut string_match_candidates = Vec::new();
for key_binding in key_bindings {
let Some(action) = key_binding.action() else {
continue;
};
let source = key_binding
.meta()
.map(KeybindSource::from_meta)
@@ -696,7 +699,7 @@ impl KeymapEditor {
})
.unwrap_or(KeybindContextString::Global);
let action_name = key_binding.action().name();
let action_name = action.name();
unmapped_action_names.remove(&action_name);
let action_arguments = key_binding
@@ -1814,7 +1817,7 @@ impl Render for KeymapEditor {
let action = div()
.id(("keymap action", index))
.child({
if action_name != gpui::NoAction.name() {
if !action_name.is_empty() {
binding
.action()
.humanized_name

View File

@@ -62,7 +62,7 @@ impl KeyContextView {
.map(|binding| {
let match_state = if let Some(predicate) = binding.predicate() {
if this.matches(&predicate) {
if this.action_matches(&e.action, binding.action()) {
if this.action_matches(e.action.as_deref(), binding.action()) {
Some(true)
} else {
Some(false)
@@ -70,7 +70,7 @@ impl KeyContextView {
} else {
None
}
} else if this.action_matches(&e.action, binding.action()) {
} else if this.action_matches(e.action.as_deref(), binding.action()) {
Some(true)
} else {
Some(false)
@@ -80,10 +80,7 @@ impl KeyContextView {
} else {
"".to_string()
};
let mut name = binding.action().name();
if name == "zed::NoAction" {
name = "(null)"
}
let name = binding.action().map_or("(null)", |action| action.name());
(
name.to_owned().into(),
@@ -130,12 +127,8 @@ impl KeyContextView {
predicate.depth_of(&self.context_stack).is_some()
}
fn action_matches(&self, a: &Option<Box<dyn Action>>, b: &dyn Action) -> bool {
if let Some(last_action) = a {
last_action.partial_eq(b)
} else {
b.name() == "zed::NoAction"
}
fn action_matches(&self, a: Option<&dyn Action>, b: Option<&dyn Action>) -> bool {
a.zip(b).is_some_and(|(a, b)| a.partial_eq(b))
}
}

View File

@@ -2,9 +2,8 @@ use anyhow::{Context as _, Result};
use collections::{BTreeMap, HashMap, IndexMap};
use fs::Fs;
use gpui::{
Action, ActionBuildError, App, InvalidKeystrokeError, KEYSTROKE_PARSE_EXPECTED_MESSAGE,
KeyBinding, KeyBindingContextPredicate, KeyBindingMetaIndex, KeybindingKeystroke, Keystroke,
NoAction, SharedString,
ActionBuildError, App, InvalidKeystrokeError, KEYSTROKE_PARSE_EXPECTED_MESSAGE, KeyBinding,
KeyBindingContextPredicate, KeyBindingMetaIndex, KeybindingKeystroke, Keystroke, SharedString,
};
use schemars::{JsonSchema, json_schema};
use serde::Deserialize;
@@ -350,12 +349,12 @@ impl KeymapFile {
let action_input = items[1].clone();
let action_input_string = action_input.to_string();
(
cx.build_action(name, Some(action_input)),
cx.build_action(name, Some(action_input)).map(Some),
Some(action_input_string),
)
}
Value::String(name) => (cx.build_action(name, None), None),
Value::Null => (Ok(NoAction.boxed_clone()), None),
Value::String(name) => (cx.build_action(name, None).map(Some), None),
Value::Null => (Ok(None), None),
_ => {
return Err(format!(
"expected two-element array of `[name, input]`. \
@@ -410,7 +409,9 @@ impl KeymapFile {
}
};
if let Some(validator) = KEY_BINDING_VALIDATORS.get(&key_binding.action().type_id()) {
if let Some(action) = key_binding.action()
&& let Some(validator) = KEY_BINDING_VALIDATORS.get(&action.type_id())
{
match validator.validate(&key_binding) {
Ok(()) => Ok(key_binding),
Err(error) => Err(error.0),
@@ -502,7 +503,7 @@ impl KeymapFile {
let mut empty_schema_action_names = vec![];
for (name, action_schema) in action_schemas.into_iter() {
let deprecation = if name == NoAction.name() {
let deprecation = if name.is_empty() {
Some("null")
} else {
deprecations.get(name).copied()
@@ -635,7 +636,7 @@ impl KeymapFile {
target_keybind_source,
} if target_keybind_source != KeybindSource::User => {
let mut source = target.clone();
source.action_name = gpui::NoAction.name();
source.action_name = "";
source.action_arguments.take();
operation = KeybindUpdateOperation::Add {
source,
@@ -930,7 +931,7 @@ pub struct KeybindUpdateTarget<'a> {
impl<'a> KeybindUpdateTarget<'a> {
fn action_value(&self) -> Result<Value> {
if self.action_name == gpui::NoAction.name() {
if self.action_name.is_empty() {
return Ok(Value::Null);
}
let action_name: Value = self.action_name.into();

View File

@@ -1040,7 +1040,7 @@ pub fn new_terminal_pane(
project.clone(),
Default::default(),
None,
NewTerminal.boxed_clone(),
Some(NewTerminal.boxed_clone()),
window,
cx,
);

View File

@@ -528,7 +528,7 @@ impl Component for KeyBinding {
single_example(
"Default",
KeyBinding::new_from_gpui(
gpui::KeyBinding::new("ctrl-s", gpui::NoAction, None),
gpui::KeyBinding::new_no_action("ctrl-s", None),
cx,
)
.into_any_element(),
@@ -536,7 +536,7 @@ impl Component for KeyBinding {
single_example(
"Mac Style",
KeyBinding::new_from_gpui(
gpui::KeyBinding::new("cmd-s", gpui::NoAction, None),
gpui::KeyBinding::new_no_action("cmd-s", None),
cx,
)
.platform_style(PlatformStyle::Mac)
@@ -545,7 +545,7 @@ impl Component for KeyBinding {
single_example(
"Windows Style",
KeyBinding::new_from_gpui(
gpui::KeyBinding::new("ctrl-s", gpui::NoAction, None),
gpui::KeyBinding::new_no_action("ctrl-s", None),
cx,
)
.platform_style(PlatformStyle::Windows)
@@ -558,7 +558,7 @@ impl Component for KeyBinding {
vec![single_example(
"Vim Mode Enabled",
KeyBinding::new_from_gpui(
gpui::KeyBinding::new("dd", gpui::NoAction, None),
gpui::KeyBinding::new_no_action("dd", None),
cx,
)
.vim_mode(true)
@@ -571,7 +571,7 @@ impl Component for KeyBinding {
single_example(
"Multiple Keys",
KeyBinding::new_from_gpui(
gpui::KeyBinding::new("ctrl-k ctrl-b", gpui::NoAction, None),
gpui::KeyBinding::new_no_action("ctrl-k ctrl-b", None),
cx,
)
.into_any_element(),
@@ -579,7 +579,7 @@ impl Component for KeyBinding {
single_example(
"With Shift",
KeyBinding::new_from_gpui(
gpui::KeyBinding::new("shift-cmd-p", gpui::NoAction, None),
gpui::KeyBinding::new_no_action("shift-cmd-p", None),
cx,
)
.into_any_element(),

View File

@@ -1,4 +1,3 @@
use gpui::NoAction;
use gpui::Render;
use itertools::Itertools;
use story::Story;
@@ -8,7 +7,7 @@ use crate::{KeyBinding, prelude::*};
pub struct KeybindingStory;
pub fn binding(key: &str) -> gpui::KeyBinding {
gpui::KeyBinding::new(key, NoAction {}, None)
gpui::KeyBinding::new_no_action(key, None)
}
impl Render for KeybindingStory {
@@ -42,7 +41,7 @@ impl Render for KeybindingStory {
.py_3()
.children(chunk.map(|permutation| {
KeyBinding::new_from_gpui(
binding(&(permutation.join("-") + "-x")),
binding(&(permutation.into_iter().join("-") + "-x")),
cx,
)
}))

View File

@@ -1244,14 +1244,14 @@ fn generate_commands(_: &App) -> Vec<VimCommand> {
.range(act_on_range),
VimCommand::str(("rev", "ert"), "git::Restore").range(act_on_range),
VimCommand::new(("d", "elete"), VisualDeleteLine).range(select_range),
VimCommand::new(("y", "ank"), gpui::NoAction).range(|_, range| {
Some(
YankCommand {
range: range.clone(),
}
.boxed_clone(),
)
}),
// VimCommand::new(("y", "ank"), gpui::NoAction).range(|_, range| {
// Some(
// YankCommand {
// range: range.clone(),
// }
// .boxed_clone(),
// )
// }),
VimCommand::new(("reg", "isters"), ToggleRegistersView).bang(ToggleRegistersView),
VimCommand::new(("di", "splay"), ToggleRegistersView).bang(ToggleRegistersView),
VimCommand::new(("marks", ""), ToggleMarksView).bang(ToggleMarksView),

View File

@@ -370,7 +370,7 @@ pub struct Pane {
/// Is None if navigation buttons are permanently turned off (and should not react to setting changes).
/// Otherwise, when `display_nav_history_buttons` is Some, it determines whether nav buttons should be displayed.
display_nav_history_buttons: Option<bool>,
double_click_dispatch_action: Box<dyn Action>,
double_click_dispatch_action: Option<Box<dyn Action>>,
save_modals_spawned: HashSet<EntityId>,
close_pane_if_empty: bool,
pub new_item_context_menu_handle: PopoverMenuHandle<ContextMenu>,
@@ -458,7 +458,7 @@ impl Pane {
project: Entity<Project>,
next_timestamp: Arc<AtomicUsize>,
can_drop_predicate: Option<Arc<dyn Fn(&dyn Any, &mut Window, &mut App) -> bool + 'static>>,
double_click_dispatch_action: Box<dyn Action>,
double_click_dispatch_action: Option<Box<dyn Action>>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
@@ -3066,10 +3066,14 @@ impl Pane {
}))
.on_click(cx.listener(move |this, event: &ClickEvent, window, cx| {
if event.click_count() == 2 {
window.dispatch_action(
this.double_click_dispatch_action.boxed_clone(),
cx,
);
if let Some(double_click_dispatch_action) =
&this.double_click_dispatch_action
{
window.dispatch_action(
double_click_dispatch_action.boxed_clone(),
cx,
);
}
}
})),
),
@@ -3744,10 +3748,14 @@ impl Render for Pane {
.on_click(cx.listener(
move |this, event: &ClickEvent, window, cx| {
if event.click_count() == 2 {
window.dispatch_action(
this.double_click_dispatch_action.boxed_clone(),
cx,
);
if let Some(double_click_dispatch_action) =
&this.double_click_dispatch_action
{
window.dispatch_action(
double_click_dispatch_action.boxed_clone(),
cx,
);
}
}
},
));

View File

@@ -1306,7 +1306,7 @@ impl Workspace {
project.clone(),
pane_history_timestamp.clone(),
None,
NewFile.boxed_clone(),
Some(NewFile.boxed_clone()),
window,
cx,
);
@@ -3187,7 +3187,7 @@ impl Workspace {
self.project.clone(),
self.pane_history_timestamp.clone(),
None,
NewFile.boxed_clone(),
Some(NewFile.boxed_clone()),
window,
cx,
);