Match keymap-style action names in command palette (#22149)

For example, `editor::TabPrev` matches "editor: tab prev".

Release Notes:

- Added support for searching command palette using keymap-style action
names.

---------

Co-authored-by: Peter Tripp <peter@zed.dev>
This commit is contained in:
Agus Zubiaga 2024-12-19 09:48:54 -03:00 committed by GitHub
parent d54662e683
commit 11260e6d37
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -36,21 +36,26 @@ pub struct CommandPalette {
picker: View<Picker<CommandPaletteDelegate>>,
}
fn trim_consecutive_whitespaces(input: &str) -> String {
/// Removes subsequent whitespace characters and double colons from the query.
///
/// This improves the likelihood of a match by either humanized name or keymap-style name.
fn normalize_query(input: &str) -> String {
let mut result = String::with_capacity(input.len());
let mut last_char_was_whitespace = false;
let mut last_char = None;
for char in input.trim().chars() {
if char.is_whitespace() {
if !last_char_was_whitespace {
result.push(char);
match (last_char, char) {
(Some(':'), ':') => continue,
(Some(last_char), char) if last_char.is_whitespace() && char.is_whitespace() => {
continue
}
_ => {
last_char = Some(char);
}
last_char_was_whitespace = true;
} else {
result.push(char);
last_char_was_whitespace = false;
}
result.push(char);
}
result
}
@ -258,7 +263,7 @@ impl PickerDelegate for CommandPaletteDelegate {
let mut commands = self.all_commands.clone();
let hit_counts = cx.global::<HitCounts>().clone();
let executor = cx.background_executor().clone();
let query = trim_consecutive_whitespaces(query.as_str());
let query = normalize_query(query.as_str());
async move {
commands.sort_by_key(|action| {
(
@ -463,6 +468,25 @@ mod tests {
);
}
#[test]
fn test_normalize_query() {
assert_eq!(normalize_query("editor: backspace"), "editor: backspace");
assert_eq!(normalize_query("editor: backspace"), "editor: backspace");
assert_eq!(normalize_query("editor: backspace"), "editor: backspace");
assert_eq!(
normalize_query("editor::GoToDefinition"),
"editor:GoToDefinition"
);
assert_eq!(
normalize_query("editor::::GoToDefinition"),
"editor:GoToDefinition"
);
assert_eq!(
normalize_query("editor: :GoToDefinition"),
"editor: :GoToDefinition"
);
}
#[gpui::test]
async fn test_command_palette(cx: &mut TestAppContext) {
let app_state = init_test(cx);
@ -533,6 +557,40 @@ mod tests {
assert!(palette.delegate.matches.is_empty())
});
}
#[gpui::test]
async fn test_normalized_matches(cx: &mut TestAppContext) {
let app_state = init_test(cx);
let project = Project::test(app_state.fs.clone(), [], cx).await;
let (workspace, cx) = cx.add_window_view(|cx| Workspace::test_new(project.clone(), cx));
let editor = cx.new_view(|cx| {
let mut editor = Editor::single_line(cx);
editor.set_text("abc", cx);
editor
});
workspace.update(cx, |workspace, cx| {
workspace.add_item_to_active_pane(Box::new(editor.clone()), None, true, cx);
editor.update(cx, |editor, cx| editor.focus(cx))
});
// Test normalize (trimming whitespace and double colons)
cx.simulate_keystrokes("cmd-shift-p");
let palette = workspace.update(cx, |workspace, cx| {
workspace
.active_modal::<CommandPalette>(cx)
.unwrap()
.read(cx)
.picker
.clone()
});
cx.simulate_input("Editor:: Backspace");
palette.update(cx, |palette, _| {
assert_eq!(palette.delegate.matches[0].string, "editor: backspace");
});
}
#[gpui::test]
async fn test_go_to_line(cx: &mut TestAppContext) {