keymap_ui: Add ability to edit context (#34019)
Closes #ISSUE Adds a context input to the keybind edit modal. Also fixes some bugs in the keymap update function to handle context changes gracefully. The current keybind update strategy implemented in this PR is * when the context doesn't change, just update the binding in place * when the context changes, but the binding is the only binding in the keymap section, update the binding _and_ context in place * when the context changes, and the binding is _not_ the only binding in the keymap section, remove the existing binding and create a new section with the update context and binding so as to avoid impacting other bindings Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
parent
ddf3d99265
commit
de9053c7ca
3 changed files with 287 additions and 63 deletions
|
@ -604,7 +604,7 @@ impl KeymapFile {
|
|||
// if trying to replace a keybinding that is not user-defined, treat it as an add operation
|
||||
match operation {
|
||||
KeybindUpdateOperation::Replace {
|
||||
target_source,
|
||||
target_keybind_source: target_source,
|
||||
source,
|
||||
..
|
||||
} if target_source != KeybindSource::User => {
|
||||
|
@ -643,7 +643,12 @@ impl KeymapFile {
|
|||
else {
|
||||
continue;
|
||||
};
|
||||
if keystrokes != target.keystrokes {
|
||||
if keystrokes.len() != target.keystrokes.len()
|
||||
|| !keystrokes
|
||||
.iter()
|
||||
.zip(target.keystrokes)
|
||||
.all(|(a, b)| a.should_match(b))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if action.0 != target_action_value {
|
||||
|
@ -655,18 +660,75 @@ impl KeymapFile {
|
|||
}
|
||||
|
||||
if let Some(index) = found_index {
|
||||
let (replace_range, replace_value) = replace_top_level_array_value_in_json_text(
|
||||
&keymap_contents,
|
||||
&["bindings", &target.keystrokes_unparsed()],
|
||||
Some(&source_action_value),
|
||||
Some(&source.keystrokes_unparsed()),
|
||||
index,
|
||||
tab_size,
|
||||
)
|
||||
.context("Failed to replace keybinding")?;
|
||||
keymap_contents.replace_range(replace_range, &replace_value);
|
||||
if target.context == source.context {
|
||||
// if we are only changing the keybinding (common case)
|
||||
// not the context, etc. Then just update the binding in place
|
||||
|
||||
return Ok(keymap_contents);
|
||||
let (replace_range, replace_value) =
|
||||
replace_top_level_array_value_in_json_text(
|
||||
&keymap_contents,
|
||||
&["bindings", &target.keystrokes_unparsed()],
|
||||
Some(&source_action_value),
|
||||
Some(&source.keystrokes_unparsed()),
|
||||
index,
|
||||
tab_size,
|
||||
)
|
||||
.context("Failed to replace keybinding")?;
|
||||
keymap_contents.replace_range(replace_range, &replace_value);
|
||||
|
||||
return Ok(keymap_contents);
|
||||
} else if keymap.0[index]
|
||||
.bindings
|
||||
.as_ref()
|
||||
.map_or(true, |bindings| bindings.len() == 1)
|
||||
{
|
||||
// if we are replacing the only binding in the section,
|
||||
// just update the section in place, updating the context
|
||||
// and the binding
|
||||
|
||||
let (replace_range, replace_value) =
|
||||
replace_top_level_array_value_in_json_text(
|
||||
&keymap_contents,
|
||||
&["bindings", &target.keystrokes_unparsed()],
|
||||
Some(&source_action_value),
|
||||
Some(&source.keystrokes_unparsed()),
|
||||
index,
|
||||
tab_size,
|
||||
)
|
||||
.context("Failed to replace keybinding")?;
|
||||
keymap_contents.replace_range(replace_range, &replace_value);
|
||||
|
||||
let (replace_range, replace_value) =
|
||||
replace_top_level_array_value_in_json_text(
|
||||
&keymap_contents,
|
||||
&["context"],
|
||||
source.context.map(Into::into).as_ref(),
|
||||
None,
|
||||
index,
|
||||
tab_size,
|
||||
)
|
||||
.context("Failed to replace keybinding")?;
|
||||
keymap_contents.replace_range(replace_range, &replace_value);
|
||||
return Ok(keymap_contents);
|
||||
} else {
|
||||
// if we are replacing one of multiple bindings in a section
|
||||
// with a context change, remove the existing binding from the
|
||||
// section, then treat this operation as an add operation of the
|
||||
// new binding with the updated context.
|
||||
|
||||
let (replace_range, replace_value) =
|
||||
replace_top_level_array_value_in_json_text(
|
||||
&keymap_contents,
|
||||
&["bindings", &target.keystrokes_unparsed()],
|
||||
None,
|
||||
None,
|
||||
index,
|
||||
tab_size,
|
||||
)
|
||||
.context("Failed to replace keybinding")?;
|
||||
keymap_contents.replace_range(replace_range, &replace_value);
|
||||
operation = KeybindUpdateOperation::Add(source);
|
||||
}
|
||||
} else {
|
||||
log::warn!(
|
||||
"Failed to find keybinding to update `{:?} -> {}` creating new binding for `{:?} -> {}` instead",
|
||||
|
@ -712,7 +774,7 @@ pub enum KeybindUpdateOperation<'a> {
|
|||
source: KeybindUpdateTarget<'a>,
|
||||
/// Describes the keybind to remove
|
||||
target: KeybindUpdateTarget<'a>,
|
||||
target_source: KeybindSource,
|
||||
target_keybind_source: KeybindSource,
|
||||
},
|
||||
Add(KeybindUpdateTarget<'a>),
|
||||
}
|
||||
|
@ -1001,7 +1063,7 @@ mod tests {
|
|||
use_key_equivalents: false,
|
||||
input: Some(r#"{"foo": "bar"}"#),
|
||||
},
|
||||
target_source: KeybindSource::Base,
|
||||
target_keybind_source: KeybindSource::Base,
|
||||
},
|
||||
r#"[
|
||||
{
|
||||
|
@ -1027,14 +1089,14 @@ mod tests {
|
|||
r#"[
|
||||
{
|
||||
"bindings": {
|
||||
"ctrl-a": "zed::SomeAction"
|
||||
"a": "zed::SomeAction"
|
||||
}
|
||||
}
|
||||
]"#
|
||||
.unindent(),
|
||||
KeybindUpdateOperation::Replace {
|
||||
target: KeybindUpdateTarget {
|
||||
keystrokes: &parse_keystrokes("ctrl-a"),
|
||||
keystrokes: &parse_keystrokes("a"),
|
||||
action_name: "zed::SomeAction",
|
||||
context: None,
|
||||
use_key_equivalents: false,
|
||||
|
@ -1047,7 +1109,7 @@ mod tests {
|
|||
use_key_equivalents: false,
|
||||
input: Some(r#"{"foo": "bar"}"#),
|
||||
},
|
||||
target_source: KeybindSource::User,
|
||||
target_keybind_source: KeybindSource::User,
|
||||
},
|
||||
r#"[
|
||||
{
|
||||
|
@ -1088,7 +1150,7 @@ mod tests {
|
|||
use_key_equivalents: false,
|
||||
input: None,
|
||||
},
|
||||
target_source: KeybindSource::User,
|
||||
target_keybind_source: KeybindSource::User,
|
||||
},
|
||||
r#"[
|
||||
{
|
||||
|
@ -1131,7 +1193,7 @@ mod tests {
|
|||
use_key_equivalents: false,
|
||||
input: Some(r#"{"foo": "bar"}"#),
|
||||
},
|
||||
target_source: KeybindSource::User,
|
||||
target_keybind_source: KeybindSource::User,
|
||||
},
|
||||
r#"[
|
||||
{
|
||||
|
@ -1149,5 +1211,88 @@ mod tests {
|
|||
]"#
|
||||
.unindent(),
|
||||
);
|
||||
|
||||
check_keymap_update(
|
||||
r#"[
|
||||
{
|
||||
"context": "SomeContext",
|
||||
"bindings": {
|
||||
"a": "foo::bar",
|
||||
"b": "baz::qux",
|
||||
}
|
||||
}
|
||||
]"#
|
||||
.unindent(),
|
||||
KeybindUpdateOperation::Replace {
|
||||
target: KeybindUpdateTarget {
|
||||
keystrokes: &parse_keystrokes("a"),
|
||||
action_name: "foo::bar",
|
||||
context: Some("SomeContext"),
|
||||
use_key_equivalents: false,
|
||||
input: None,
|
||||
},
|
||||
source: KeybindUpdateTarget {
|
||||
keystrokes: &parse_keystrokes("c"),
|
||||
action_name: "foo::baz",
|
||||
context: Some("SomeOtherContext"),
|
||||
use_key_equivalents: false,
|
||||
input: None,
|
||||
},
|
||||
target_keybind_source: KeybindSource::User,
|
||||
},
|
||||
r#"[
|
||||
{
|
||||
"context": "SomeContext",
|
||||
"bindings": {
|
||||
"b": "baz::qux",
|
||||
}
|
||||
},
|
||||
{
|
||||
"context": "SomeOtherContext",
|
||||
"bindings": {
|
||||
"c": "foo::baz"
|
||||
}
|
||||
}
|
||||
]"#
|
||||
.unindent(),
|
||||
);
|
||||
|
||||
check_keymap_update(
|
||||
r#"[
|
||||
{
|
||||
"context": "SomeContext",
|
||||
"bindings": {
|
||||
"a": "foo::bar",
|
||||
}
|
||||
}
|
||||
]"#
|
||||
.unindent(),
|
||||
KeybindUpdateOperation::Replace {
|
||||
target: KeybindUpdateTarget {
|
||||
keystrokes: &parse_keystrokes("a"),
|
||||
action_name: "foo::bar",
|
||||
context: Some("SomeContext"),
|
||||
use_key_equivalents: false,
|
||||
input: None,
|
||||
},
|
||||
source: KeybindUpdateTarget {
|
||||
keystrokes: &parse_keystrokes("c"),
|
||||
action_name: "foo::baz",
|
||||
context: Some("SomeOtherContext"),
|
||||
use_key_equivalents: false,
|
||||
input: None,
|
||||
},
|
||||
target_keybind_source: KeybindSource::User,
|
||||
},
|
||||
r#"[
|
||||
{
|
||||
"context": "SomeOtherContext",
|
||||
"bindings": {
|
||||
"c": "foo::baz",
|
||||
}
|
||||
}
|
||||
]"#
|
||||
.unindent(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue