keymap_ui: Add default sort to keymap table (#33781)

Closes #ISSUE

Adds a default sort when no filter query is provided in the keymap
editor.

The default sorting algorithm sorts by the source of the binding
(roughly in order of precedence)
 
- User
- Vim
- Base
- Default
- None (unbound actions) 

within each source it sorts by action name alphabetically.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Ben Kunkle 2025-07-02 10:00:18 -05:00 committed by GitHub
parent 132bba8d8b
commit 6d09f3fa40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -190,13 +190,13 @@ impl KeymapEditor {
this: WeakEntity<Self>, this: WeakEntity<Self>,
query: String, query: String,
cx: &mut AsyncApp, cx: &mut AsyncApp,
) -> Result<(), db::anyhow::Error> { ) -> anyhow::Result<()> {
let query = command_palette::normalize_action_query(&query); let query = command_palette::normalize_action_query(&query);
let (string_match_candidates, keybind_count) = this.read_with(cx, |this, _| { let (string_match_candidates, keybind_count) = this.read_with(cx, |this, _| {
(this.string_match_candidates.clone(), this.keybindings.len()) (this.string_match_candidates.clone(), this.keybindings.len())
})?; })?;
let executor = cx.background_executor().clone(); let executor = cx.background_executor().clone();
let matches = fuzzy::match_strings( let mut matches = fuzzy::match_strings(
&string_match_candidates, &string_match_candidates,
&query, &query,
true, true,
@ -207,6 +207,23 @@ impl KeymapEditor {
) )
.await; .await;
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
if query.is_empty() {
// apply default sort
// sorts by source precedence, and alphabetically by action name within each source
matches.sort_by_key(|match_item| {
let keybind = &this.keybindings[match_item.candidate_id];
let source = keybind.source.as_ref().map(|s| s.0);
use KeybindSource::*;
let source_precedence = match source {
Some(User) => 0,
Some(Vim) => 1,
Some(Base) => 2,
Some(Default) => 3,
None => 4,
};
return (source_precedence, keybind.action.as_ref());
});
}
this.selected_index.take(); this.selected_index.take();
this.scroll_to_item(0, ScrollStrategy::Top, cx); this.scroll_to_item(0, ScrollStrategy::Top, cx);
this.matches = matches; this.matches = matches;
@ -216,7 +233,7 @@ impl KeymapEditor {
fn process_bindings( fn process_bindings(
json_language: Arc<Language>, json_language: Arc<Language>,
cx: &mut Context<Self>, cx: &mut App,
) -> (Vec<ProcessedKeybinding>, Vec<StringMatchCandidate>) { ) -> (Vec<ProcessedKeybinding>, Vec<StringMatchCandidate>) {
let key_bindings_ptr = cx.key_bindings(); let key_bindings_ptr = cx.key_bindings();
let lock = key_bindings_ptr.borrow(); let lock = key_bindings_ptr.borrow();
@ -246,7 +263,7 @@ impl KeymapEditor {
unmapped_action_names.remove(&action_name); unmapped_action_names.remove(&action_name);
let action_input = key_binding let action_input = key_binding
.action_input() .action_input()
.map(|input| TextWithSyntaxHighlighting::new(input, json_language.clone())); .map(|input| SyntaxHighlightedText::new(input, json_language.clone()));
let index = processed_bindings.len(); let index = processed_bindings.len();
let string_match_candidate = StringMatchCandidate::new(index, &action_name); let string_match_candidate = StringMatchCandidate::new(index, &action_name);
@ -283,6 +300,7 @@ impl KeymapEditor {
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
cx.spawn(async move |this, cx| { cx.spawn(async move |this, cx| {
let json_language = Self::load_json_language(workspace, cx).await; let json_language = Self::load_json_language(workspace, cx).await;
let query = this.update(cx, |this, cx| { let query = this.update(cx, |this, cx| {
let (key_bindings, string_match_candidates) = let (key_bindings, string_match_candidates) =
Self::process_bindings(json_language.clone(), cx); Self::process_bindings(json_language.clone(), cx);
@ -502,7 +520,7 @@ struct ProcessedKeybinding {
keystroke_text: SharedString, keystroke_text: SharedString,
ui_key_binding: Option<ui::KeyBinding>, ui_key_binding: Option<ui::KeyBinding>,
action: SharedString, action: SharedString,
action_input: Option<TextWithSyntaxHighlighting>, action_input: Option<SyntaxHighlightedText>,
context: Option<KeybindContextString>, context: Option<KeybindContextString>,
source: Option<(KeybindSource, SharedString)>, source: Option<(KeybindSource, SharedString)>,
} }
@ -671,12 +689,12 @@ impl Render for KeymapEditor {
} }
#[derive(Debug, Clone, IntoElement)] #[derive(Debug, Clone, IntoElement)]
struct TextWithSyntaxHighlighting { struct SyntaxHighlightedText {
text: SharedString, text: SharedString,
language: Arc<Language>, language: Arc<Language>,
} }
impl TextWithSyntaxHighlighting { impl SyntaxHighlightedText {
pub fn new(text: impl Into<SharedString>, language: Arc<Language>) -> Self { pub fn new(text: impl Into<SharedString>, language: Arc<Language>) -> Self {
Self { Self {
text: text.into(), text: text.into(),
@ -685,7 +703,7 @@ impl TextWithSyntaxHighlighting {
} }
} }
impl RenderOnce for TextWithSyntaxHighlighting { impl RenderOnce for SyntaxHighlightedText {
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
let text_style = window.text_style(); let text_style = window.text_style();
let syntax_theme = cx.theme().syntax(); let syntax_theme = cx.theme().syntax();