keymap_ui: Add auto-complete for context in keybind editor (#34031)

Closes #ISSUE

Implements a very basic completion provider that is attached to the
context editor in the keybind editing modal.

The context identifiers used for completions are scraped from the
default, vim, and base keymaps on demand.

Release Notes:

- N/A *or* Added/Fixed/Improved ...
This commit is contained in:
Ben Kunkle 2025-07-07 16:54:51 -05:00 committed by GitHub
parent 66a1c356bf
commit 877ef5e1b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 163 additions and 20 deletions

View file

@ -0,0 +1,120 @@
use std::fmt::{Display, Formatter};
use crate::{Settings, SettingsSources, VsCodeSettings};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// Base key bindings scheme. Base keymaps can be overridden with user keymaps.
///
/// Default: VSCode
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Default)]
pub enum BaseKeymap {
#[default]
VSCode,
JetBrains,
SublimeText,
Atom,
TextMate,
Emacs,
Cursor,
None,
}
impl Display for BaseKeymap {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
BaseKeymap::VSCode => write!(f, "VSCode"),
BaseKeymap::JetBrains => write!(f, "JetBrains"),
BaseKeymap::SublimeText => write!(f, "Sublime Text"),
BaseKeymap::Atom => write!(f, "Atom"),
BaseKeymap::TextMate => write!(f, "TextMate"),
BaseKeymap::Emacs => write!(f, "Emacs (beta)"),
BaseKeymap::Cursor => write!(f, "Cursor (beta)"),
BaseKeymap::None => write!(f, "None"),
}
}
}
impl BaseKeymap {
#[cfg(target_os = "macos")]
pub const OPTIONS: [(&'static str, Self); 7] = [
("VSCode (Default)", Self::VSCode),
("Atom", Self::Atom),
("JetBrains", Self::JetBrains),
("Sublime Text", Self::SublimeText),
("Emacs (beta)", Self::Emacs),
("TextMate", Self::TextMate),
("Cursor (beta)", Self::Cursor),
];
#[cfg(not(target_os = "macos"))]
pub const OPTIONS: [(&'static str, Self); 6] = [
("VSCode (Default)", Self::VSCode),
("Atom", Self::Atom),
("JetBrains", Self::JetBrains),
("Sublime Text", Self::SublimeText),
("Emacs (beta)", Self::Emacs),
("Cursor (beta)", Self::Cursor),
];
pub fn asset_path(&self) -> Option<&'static str> {
#[cfg(target_os = "macos")]
match self {
BaseKeymap::JetBrains => Some("keymaps/macos/jetbrains.json"),
BaseKeymap::SublimeText => Some("keymaps/macos/sublime_text.json"),
BaseKeymap::Atom => Some("keymaps/macos/atom.json"),
BaseKeymap::TextMate => Some("keymaps/macos/textmate.json"),
BaseKeymap::Emacs => Some("keymaps/macos/emacs.json"),
BaseKeymap::Cursor => Some("keymaps/macos/cursor.json"),
BaseKeymap::VSCode => None,
BaseKeymap::None => None,
}
#[cfg(not(target_os = "macos"))]
match self {
BaseKeymap::JetBrains => Some("keymaps/linux/jetbrains.json"),
BaseKeymap::SublimeText => Some("keymaps/linux/sublime_text.json"),
BaseKeymap::Atom => Some("keymaps/linux/atom.json"),
BaseKeymap::Emacs => Some("keymaps/linux/emacs.json"),
BaseKeymap::Cursor => Some("keymaps/linux/cursor.json"),
BaseKeymap::TextMate => None,
BaseKeymap::VSCode => None,
BaseKeymap::None => None,
}
}
pub fn names() -> impl Iterator<Item = &'static str> {
Self::OPTIONS.iter().map(|(name, _)| *name)
}
pub fn from_names(option: &str) -> BaseKeymap {
Self::OPTIONS
.iter()
.copied()
.find_map(|(name, value)| (name == option).then_some(value))
.unwrap_or_default()
}
}
impl Settings for BaseKeymap {
const KEY: Option<&'static str> = Some("base_keymap");
type FileContent = Option<Self>;
fn load(
sources: SettingsSources<Self::FileContent>,
_: &mut gpui::App,
) -> anyhow::Result<Self> {
if let Some(Some(user_value)) = sources.user.copied() {
return Ok(user_value);
}
if let Some(Some(server_value)) = sources.server.copied() {
return Ok(server_value);
}
sources.default.ok_or_else(Self::missing_default)
}
fn import_from_vscode(_vscode: &VsCodeSettings, current: &mut Self::FileContent) {
*current = Some(BaseKeymap::VSCode);
}
}