docs: Update keybindings in webstorm.md (#49583)

- add Linux/Windows keybindings
- use consistent spacing around the `+` in key combos

Release Notes:

- I don't have access to a Mac, so I haven't verified any of the macOS
keybindings. I suspect some may be out of date. Please verify.
- All other migration guides should be updated with Linux/Windows
shortcuts, and should use consistent spacing around the `+`:
  - https://zed.dev/docs/migrate/vs-code#differences-in-keybindings
  - https://zed.dev/docs/migrate/intellij#differences-in-keybindings
  - https://zed.dev/docs/migrate/pycharm#differences-in-keybindings
  - https://zed.dev/docs/migrate/rustrover#differences-in-keybindings

---------

Co-authored-by: MrSubidubi <finn@zed.dev>
This commit is contained in:
Ted RobertsonandMrSubidubi authored and GitHub committed 2026-03-26 12:37:18 +00:00
1 parent 161c622ad0
commit 786181a6e7
3 files changed
+161 -82

No files matched your search

+92 -14
View File
@@ -22,8 +22,45 @@ static KEYMAP_WINDOWS: LazyLock<KeymapFile> = LazyLock::new(|| {
load_keymap("keymaps/default-windows.json").expect("Failed to load Windows keymap")
});
static KEYMAP_JETBRAINS_MACOS: LazyLock<KeymapFile> = LazyLock::new(|| {
load_keymap("keymaps/macos/jetbrains.json").expect("Failed to load JetBrains macOS keymap")
});
static KEYMAP_JETBRAINS_LINUX: LazyLock<KeymapFile> = LazyLock::new(|| {
load_keymap("keymaps/linux/jetbrains.json").expect("Failed to load JetBrains Linux keymap")
});
static ALL_ACTIONS: LazyLock<ActionManifest> = LazyLock::new(load_all_actions);
#[derive(Clone, Copy)]
#[allow(dead_code)]
enum Os {
MacOs,
Linux,
Windows,
}
#[derive(Clone, Copy)]
enum KeymapOverlay {
JetBrains,
}
impl KeymapOverlay {
fn parse(name: &str) -> Option<Self> {
match name {
"jetbrains" => Some(Self::JetBrains),
_ => None,
}
}
fn keymap(self, os: Os) -> &'static KeymapFile {
match (self, os) {
(Self::JetBrains, Os::MacOs) => &KEYMAP_JETBRAINS_MACOS,
(Self::JetBrains, Os::Linux | Os::Windows) => &KEYMAP_JETBRAINS_LINUX,
}
}
}
const FRONT_MATTER_COMMENT: &str = "<!-- ZED_META {} -->";
fn main() -> Result<()> {
@@ -64,6 +101,9 @@ enum PreprocessorError {
snippet: String,
error: String,
},
UnknownKeymapOverlay {
overlay_name: String,
},
}
impl PreprocessorError {
@@ -125,6 +165,13 @@ impl std::fmt::Display for PreprocessorError {
snippet
)
}
PreprocessorError::UnknownKeymapOverlay { overlay_name } => {
write!(
f,
"Unknown keymap overlay: '{}'. Supported overlays: jetbrains",
overlay_name
)
}
}
}
}
@@ -205,20 +252,39 @@ fn format_binding(binding: String) -> String {
}
fn template_and_validate_keybindings(book: &mut Book, errors: &mut HashSet<PreprocessorError>) {
let regex = Regex::new(r"\{#kb (.*?)\}").unwrap();
let regex = Regex::new(r"\{#kb(?::(\w+))?\s+(.*?)\}").unwrap();
for_each_chapter_mut(book, |chapter| {
chapter.content = regex
.replace_all(&chapter.content, |caps: &regex::Captures| {
let action = caps[1].trim();
let overlay_name = caps.get(1).map(|m| m.as_str());
let action = caps[2].trim();
if is_missing_action(action) {
errors.insert(PreprocessorError::new_for_not_found_action(
action.to_string(),
));
return String::new();
}
let macos_binding = find_binding("macos", action).unwrap_or_default();
let linux_binding = find_binding("linux", action).unwrap_or_default();
let overlay = if let Some(name) = overlay_name {
let Some(overlay) = KeymapOverlay::parse(name) else {
errors.insert(PreprocessorError::UnknownKeymapOverlay {
overlay_name: name.to_string(),
});
return String::new();
};
Some(overlay)
} else {
None
};
let macos_binding =
find_binding_with_overlay(Os::MacOs, action, overlay)
.unwrap_or_default();
let linux_binding =
find_binding_with_overlay(Os::Linux, action, overlay)
.unwrap_or_default();
if macos_binding.is_empty() && linux_binding.is_empty() {
return "<div>No default binding</div>".to_string();
@@ -227,7 +293,7 @@ fn template_and_validate_keybindings(book: &mut Book, errors: &mut HashSet<Prepr
let formatted_macos_binding = format_binding(macos_binding);
let formatted_linux_binding = format_binding(linux_binding);
format!("<kbd class=\"keybinding\">{formatted_macos_binding}|{formatted_linux_binding}</kbd>")
format!("<kbd class=\"keybinding\">{formatted_macos_binding}&#124;{formatted_linux_binding}</kbd>")
})
.into_owned()
});
@@ -270,15 +336,8 @@ fn is_missing_action(name: &str) -> bool {
actions_available() && find_action_by_name(name).is_none()
}
fn find_binding(os: &str, action: &str) -> Option<String> {
let keymap = match os {
"macos" => &KEYMAP_MACOS,
"linux" | "freebsd" => &KEYMAP_LINUX,
"windows" => &KEYMAP_WINDOWS,
_ => unreachable!("Not a valid OS: {}", os),
};
// Find the binding in reverse order, as the last binding takes precedence.
// Find the binding in reverse order, as the last binding takes precedence.
fn find_binding_in_keymap(keymap: &KeymapFile, action: &str) -> Option<String> {
keymap.sections().rev().find_map(|section| {
section.bindings().rev().find_map(|(keystroke, a)| {
if name_for_action(a.to_string()) == action {
@@ -290,6 +349,25 @@ fn find_binding(os: &str, action: &str) -> Option<String> {
})
}
fn find_binding(os: Os, action: &str) -> Option<String> {
let keymap = match os {
Os::MacOs => &KEYMAP_MACOS,
Os::Linux => &KEYMAP_LINUX,
Os::Windows => &KEYMAP_WINDOWS,
};
find_binding_in_keymap(keymap, action)
}
fn find_binding_with_overlay(
os: Os,
action: &str,
overlay: Option<KeymapOverlay>,
) -> Option<String> {
overlay
.and_then(|overlay| find_binding_in_keymap(overlay.keymap(os), action))
.or_else(|| find_binding(os, action))
}
fn template_and_validate_json_snippets(book: &mut Book, errors: &mut HashSet<PreprocessorError>) {
let settings_schema = SettingsStore::json_schema(&Default::default());
let settings_validator = jsonschema::validator_for(&settings_schema)