From f07da9d9f2a24ee91aa14b9af15d282b26bae373 Mon Sep 17 00:00:00 2001
From: hong jihwan <124516776+flareseek@users.noreply.github.com>
Date: Tue, 9 Sep 2025 01:17:48 +0900
Subject: [PATCH] Correctly parse backslash character on replacement (#37014)
When a keybind contains a backslash character (\\), it is parsed
incorrectly, which results in an invalid keybind configuration.
This patch fixes the issue by ensuring that backslashes are properly
escaped during the parsing process. This allows them to be used as
intended in keybind definitions.
Release Notes:
- Fixed an issue where keybinds containing a backslash character (\\)
failed to be replaced correctly
## Screenshots
---
crates/settings/src/keymap_file.rs | 122 +++++++++++++++++++++++++++
crates/settings/src/settings_json.rs | 10 ++-
2 files changed, 128 insertions(+), 4 deletions(-)
diff --git a/crates/settings/src/keymap_file.rs b/crates/settings/src/keymap_file.rs
index 91fcca8d5cbddf9dd30b867b3b89848cbc86de1e..5dddff28d8d25652d366565801b249facd584344 100644
--- a/crates/settings/src/keymap_file.rs
+++ b/crates/settings/src/keymap_file.rs
@@ -1100,6 +1100,24 @@ mod tests {
.unindent(),
);
+ check_keymap_update(
+ "[]",
+ KeybindUpdateOperation::add(KeybindUpdateTarget {
+ keystrokes: &parse_keystrokes("\\ a"),
+ action_name: "zed::SomeAction",
+ context: None,
+ action_arguments: None,
+ }),
+ r#"[
+ {
+ "bindings": {
+ "\\ a": "zed::SomeAction"
+ }
+ }
+ ]"#
+ .unindent(),
+ );
+
check_keymap_update(
"[]",
KeybindUpdateOperation::add(KeybindUpdateTarget {
@@ -1302,6 +1320,79 @@ mod tests {
.unindent(),
);
+ check_keymap_update(
+ r#"[
+ {
+ "bindings": {
+ "\\ a": "zed::SomeAction"
+ }
+ }
+ ]"#
+ .unindent(),
+ KeybindUpdateOperation::Replace {
+ target: KeybindUpdateTarget {
+ keystrokes: &parse_keystrokes("\\ a"),
+ action_name: "zed::SomeAction",
+ context: None,
+ action_arguments: None,
+ },
+ source: KeybindUpdateTarget {
+ keystrokes: &parse_keystrokes("\\ b"),
+ action_name: "zed::SomeOtherAction",
+ context: None,
+ action_arguments: Some(r#"{"foo": "bar"}"#),
+ },
+ target_keybind_source: KeybindSource::User,
+ },
+ r#"[
+ {
+ "bindings": {
+ "\\ b": [
+ "zed::SomeOtherAction",
+ {
+ "foo": "bar"
+ }
+ ]
+ }
+ }
+ ]"#
+ .unindent(),
+ );
+
+ check_keymap_update(
+ r#"[
+ {
+ "bindings": {
+ "\\ a": "zed::SomeAction"
+ }
+ }
+ ]"#
+ .unindent(),
+ KeybindUpdateOperation::Replace {
+ target: KeybindUpdateTarget {
+ keystrokes: &parse_keystrokes("\\ a"),
+ action_name: "zed::SomeAction",
+ context: None,
+ action_arguments: None,
+ },
+ source: KeybindUpdateTarget {
+ keystrokes: &parse_keystrokes("\\ a"),
+ action_name: "zed::SomeAction",
+ context: None,
+ action_arguments: None,
+ },
+ target_keybind_source: KeybindSource::User,
+ },
+ r#"[
+ {
+ "bindings": {
+ "\\ a": "zed::SomeAction"
+ }
+ }
+ ]"#
+ .unindent(),
+ );
+
check_keymap_update(
r#"[
{
@@ -1494,6 +1585,37 @@ mod tests {
.unindent(),
);
+ check_keymap_update(
+ r#"[
+ {
+ "context": "SomeContext",
+ "bindings": {
+ "\\ a": "foo::bar",
+ "c": "foo::baz",
+ }
+ },
+ ]"#
+ .unindent(),
+ KeybindUpdateOperation::Remove {
+ target: KeybindUpdateTarget {
+ context: Some("SomeContext"),
+ keystrokes: &parse_keystrokes("\\ a"),
+ action_name: "foo::bar",
+ action_arguments: None,
+ },
+ target_keybind_source: KeybindSource::User,
+ },
+ r#"[
+ {
+ "context": "SomeContext",
+ "bindings": {
+ "c": "foo::baz",
+ }
+ },
+ ]"#
+ .unindent(),
+ );
+
check_keymap_update(
r#"[
{
diff --git a/crates/settings/src/settings_json.rs b/crates/settings/src/settings_json.rs
index 480fe057eacb8d96255a3bf2d7b5f96208f87ced..70c76e23c402b8debcb5e406cc86fa7125e78c5a 100644
--- a/crates/settings/src/settings_json.rs
+++ b/crates/settings/src/settings_json.rs
@@ -140,8 +140,10 @@ pub fn replace_value_in_json_text>(
let found_key = text
.get(key_range.clone())
- .map(|key_text| {
- depth < key_path.len() && key_text == format!("\"{}\"", key_path[depth].as_ref())
+ .and_then(|key_text| {
+ serde_json::to_string(key_path[depth].as_ref())
+ .ok()
+ .map(|key_path| depth < key_path.len() && key_text == key_path)
})
.unwrap_or(false);
@@ -163,8 +165,8 @@ pub fn replace_value_in_json_text>(
if depth == key_path.len() {
if let Some(new_value) = new_value {
let new_val = to_pretty_json(new_value, tab_size, tab_size * depth);
- if let Some(replace_key) = replace_key {
- let new_key = format!("\"{}\": ", replace_key);
+ if let Some(replace_key) = replace_key.and_then(|str| serde_json::to_string(str).ok()) {
+ let new_key = format!("{}: ", replace_key);
if let Some(key_start) = text[..existing_value_range.start].rfind('"') {
if let Some(prev_key_start) = text[..key_start].rfind('"') {
existing_value_range.start = prev_key_start;