Skip to content

Commit 0b696aa

Browse files
yara-blueJosephTLyonsP1n3appl3
committed
Turns the keybind in command pallet entry into button to keymap editor
Opens a keybind create modal if there was no keybind set for the command pallet entry. Co-authored-by: Joseph T. Lyons <[email protected]> Co-authored-by: Julia Ryan <[email protected]>
1 parent f213f4b commit 0b696aa

File tree

4 files changed

+118
-14
lines changed

4 files changed

+118
-14
lines changed

crates/command_palette/src/command_palette.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ use persistence::COMMAND_PALETTE_HISTORY;
2222
use picker::{Picker, PickerDelegate};
2323
use postage::{sink::Sink, stream::Stream};
2424
use settings::Settings;
25-
use ui::{HighlightedLabel, KeyBinding, ListItem, ListItemSpacing, h_flex, prelude::*, v_flex};
25+
use ui::{
26+
ButtonLike, HighlightedLabel, KeyBinding, ListItem, ListItemSpacing, h_flex, prelude::*, v_flex,
27+
};
2628
use util::ResultExt;
2729
use workspace::{ModalView, Workspace, WorkspaceSettings};
2830
use zed_actions::{OpenZedUrl, command_palette::Toggle};
@@ -443,11 +445,14 @@ impl PickerDelegate for CommandPaletteDelegate {
443445
&self,
444446
ix: usize,
445447
selected: bool,
446-
_: &mut Window,
448+
window: &mut Window,
447449
cx: &mut Context<Picker<Self>>,
448450
) -> Option<Self::ListItem> {
449451
let matching_command = self.matches.get(ix)?;
450452
let command = self.commands.get(matching_command.candidate_id)?;
453+
let name = command.action.name();
454+
let keybind = KeyBinding::for_action_in(&*command.action, &self.previous_focus_handle, cx);
455+
451456
Some(
452457
ListItem::new(ix)
453458
.inset(true)
@@ -462,10 +467,45 @@ impl PickerDelegate for CommandPaletteDelegate {
462467
command.name.clone(),
463468
matching_command.positions.clone(),
464469
))
465-
.child(KeyBinding::for_action_in(
466-
&*command.action,
467-
&self.previous_focus_handle,
468-
cx,
470+
.child(div().when_else(
471+
keybind.has_binding(window),
472+
|this| {
473+
this.child(
474+
ButtonLike::new(name)
475+
.style(ButtonStyle::Transparent)
476+
.child(keybind)
477+
.tooltip(ui::Tooltip::text("Change key binding…"))
478+
.on_click(|_, window, cx| {
479+
window.dispatch_action(
480+
zed_actions::OpenKeymapWithFilter {
481+
filter: name.to_string(),
482+
}
483+
.boxed_clone(),
484+
cx,
485+
);
486+
})
487+
.into_any_element(),
488+
)
489+
},
490+
|this| {
491+
this.child(
492+
IconButton::new(command.action.name(), IconName::Keyboard)
493+
.style(ButtonStyle::Transparent)
494+
.alpha(0.8)
495+
.icon_size(IconSize::XSmall)
496+
.tooltip(ui::Tooltip::text("Add key binding…"))
497+
.on_click(|_, window, cx| {
498+
window.dispatch_action(
499+
zed_actions::OpenKeymapWithFilter {
500+
filter: name.to_string(),
501+
}
502+
.boxed_clone(),
503+
cx,
504+
);
505+
})
506+
.into_any_element(),
507+
)
508+
},
469509
)),
470510
),
471511
)

crates/keymap_editor/src/keymap_editor.rs

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use std::{
2+
cell::RefCell,
23
cmp::{self},
34
ops::{Not as _, Range},
45
rc::Rc,
56
sync::Arc,
6-
time::Duration,
7+
time::{Duration, Instant},
78
};
89

910
mod ui_components;
@@ -41,7 +42,7 @@ use workspace::{
4142
};
4243

4344
pub use ui_components::*;
44-
use zed_actions::OpenKeymap;
45+
use zed_actions::{OpenKeymap, OpenKeymapWithFilter};
4546

4647
use crate::{
4748
persistence::KEYBINDING_EDITORS,
@@ -80,37 +81,75 @@ pub fn init(cx: &mut App) {
8081
let keymap_event_channel = KeymapEventChannel::new();
8182
cx.set_global(keymap_event_channel);
8283

83-
cx.on_action(|_: &OpenKeymap, cx| {
84+
fn common(filter: String, cx: &mut App) {
8485
workspace::with_active_or_new_workspace(cx, move |workspace, window, cx| {
8586
workspace
86-
.with_local_workspace(window, cx, |workspace, window, cx| {
87+
.with_local_workspace(window, cx, move |workspace, window, cx| {
8788
let existing = workspace
8889
.active_pane()
8990
.read(cx)
9091
.items()
9192
.find_map(|item| item.downcast::<KeymapEditor>());
9293

93-
if let Some(existing) = existing {
94+
let keymap_editor = if let Some(existing) = existing {
9495
workspace.activate_item(&existing, true, true, window, cx);
96+
existing
9597
} else {
9698
let keymap_editor =
9799
cx.new(|cx| KeymapEditor::new(workspace.weak_handle(), window, cx));
98100
workspace.add_item_to_active_pane(
99-
Box::new(keymap_editor),
101+
Box::new(keymap_editor.clone()),
100102
None,
101103
true,
102104
window,
103105
cx,
104106
);
105-
}
107+
keymap_editor
108+
};
109+
110+
keymap_editor.update(cx, |editor, cx| {
111+
editor.filter_editor.update(cx, |editor, cx| {
112+
editor.clear(window, cx);
113+
editor.insert(&filter, window, cx);
114+
});
115+
if !editor.has_binding_for(&filter) {
116+
open_binding_modal_after_loading(cx)
117+
}
118+
})
106119
})
107120
.detach();
108121
})
109-
});
122+
}
123+
124+
cx.on_action(|_: &OpenKeymap, cx| common(String::new(), cx));
125+
cx.on_action(|action: &OpenKeymapWithFilter, cx| common(action.filter.clone(), cx));
110126

111127
register_serializable_item::<KeymapEditor>(cx);
112128
}
113129

130+
fn open_binding_modal_after_loading(cx: &mut Context<KeymapEditor>) {
131+
let started_at = Instant::now();
132+
let observer = Rc::new(RefCell::new(None));
133+
let handle = {
134+
let observer = Rc::clone(&observer);
135+
cx.observe(&cx.entity(), move |editor, _, cx| {
136+
let subscription = observer.borrow_mut().take();
137+
138+
if started_at.elapsed().as_secs() > 10 {
139+
return;
140+
}
141+
if !editor.matches.is_empty() {
142+
editor.selected_index = Some(0);
143+
cx.dispatch_action(&CreateBinding);
144+
return;
145+
}
146+
147+
*observer.borrow_mut() = subscription;
148+
})
149+
};
150+
*observer.borrow_mut() = Some(handle);
151+
}
152+
114153
pub struct KeymapEventChannel {}
115154

116155
impl Global for KeymapEventChannel {}
@@ -1320,6 +1359,13 @@ impl KeymapEditor {
13201359
editor.set_keystrokes(keystrokes, cx);
13211360
});
13221361
}
1362+
1363+
fn has_binding_for(&self, action_name: &str) -> bool {
1364+
self.keybindings
1365+
.iter()
1366+
.filter(|kb| kb.keystrokes().is_some())
1367+
.any(|kb| kb.action().name == action_name)
1368+
}
13231369
}
13241370

13251371
struct HumanizedActionNameCache {

crates/ui/src/components/keybinding.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@ impl KeyBinding {
6868
pub fn for_action_in(action: &dyn Action, focus: &FocusHandle, cx: &App) -> Self {
6969
Self::new(action, Some(focus.clone()), cx)
7070
}
71+
pub fn has_binding(&self, window: &Window) -> bool {
72+
match &self.source {
73+
Source::Action {
74+
action,
75+
focus_handle: Some(focus),
76+
} => window
77+
.highest_precedence_binding_for_action_in(action.as_ref(), focus)
78+
.is_some(),
79+
_ => false,
80+
}
81+
}
7182

7283
pub fn set_vim_mode(cx: &mut App, enabled: bool) {
7384
cx.set_global(VimStyle(enabled));

crates/zed_actions/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ pub struct OpenZedUrl {
2727
pub url: String,
2828
}
2929

30+
/// Opens the keymap editor.
31+
#[derive(PartialEq, Clone, Deserialize, Default, JsonSchema, Action)]
32+
#[action(namespace = zed)]
33+
pub struct OpenKeymapWithFilter {
34+
pub filter: String,
35+
}
36+
3037
actions!(
3138
zed,
3239
[

0 commit comments

Comments
 (0)