keymap_ui: Infer use key equivalents (#34498)
Closes #ISSUE This PR attempts to add workarounds for `use_key_equivalents` in the keymap UI. First of all it makes it so that `use_key_equivalents` is ignored when searching for a binding to replace so that replacing a keybind with `use_key_equivalents` set to true does not result in a new binding. Second, it attempts to infer the value of `use_key_equivalents` off of a base binding when adding a binding by adding an optional `from` parameter to the `KeymapUpdateOperation::Add` variant. Neither workaround will work when the `from` binding for an add or the `target` binding for a replace are not in the user keymap. cc: @Anthony-Eid Release Notes: - N/A *or* Added/Fixed/Improved ...
This commit is contained in:
parent
2a9a82d757
commit
21b4a2ecdd
3 changed files with 201 additions and 119 deletions
|
@ -1,5 +1,5 @@
|
|||
use std::{
|
||||
ops::{Not, Range},
|
||||
ops::{Not as _, Range},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
|
@ -1602,32 +1602,45 @@ impl KeybindingEditorModal {
|
|||
Ok(action_arguments)
|
||||
}
|
||||
|
||||
fn save(&mut self, cx: &mut Context<Self>) {
|
||||
let existing_keybind = self.editing_keybind.clone();
|
||||
let fs = self.fs.clone();
|
||||
fn validate_keystrokes(&self, cx: &App) -> anyhow::Result<Vec<Keystroke>> {
|
||||
let new_keystrokes = self
|
||||
.keybind_editor
|
||||
.read_with(cx, |editor, _| editor.keystrokes().to_vec());
|
||||
if new_keystrokes.is_empty() {
|
||||
self.set_error(InputError::error("Keystrokes cannot be empty"), cx);
|
||||
return;
|
||||
}
|
||||
let tab_size = cx.global::<settings::SettingsStore>().json_tab_size();
|
||||
anyhow::ensure!(!new_keystrokes.is_empty(), "Keystrokes cannot be empty");
|
||||
Ok(new_keystrokes)
|
||||
}
|
||||
|
||||
fn validate_context(&self, cx: &App) -> anyhow::Result<Option<String>> {
|
||||
let new_context = self
|
||||
.context_editor
|
||||
.read_with(cx, |input, cx| input.editor().read(cx).text(cx));
|
||||
let new_context = new_context.is_empty().not().then_some(new_context);
|
||||
let new_context_err = new_context.as_deref().and_then(|context| {
|
||||
gpui::KeyBindingContextPredicate::parse(context)
|
||||
.context("Failed to parse key context")
|
||||
.err()
|
||||
});
|
||||
if let Some(err) = new_context_err {
|
||||
// TODO: store and display as separate error
|
||||
// TODO: also, should be validating on keystroke
|
||||
self.set_error(InputError::error(err.to_string()), cx);
|
||||
return;
|
||||
}
|
||||
let Some(context) = new_context.is_empty().not().then_some(new_context) else {
|
||||
return Ok(None);
|
||||
};
|
||||
gpui::KeyBindingContextPredicate::parse(&context).context("Failed to parse key context")?;
|
||||
|
||||
Ok(Some(context))
|
||||
}
|
||||
|
||||
fn save(&mut self, cx: &mut Context<Self>) {
|
||||
let existing_keybind = self.editing_keybind.clone();
|
||||
let fs = self.fs.clone();
|
||||
let tab_size = cx.global::<settings::SettingsStore>().json_tab_size();
|
||||
let new_keystrokes = match self.validate_keystrokes(cx) {
|
||||
Err(err) => {
|
||||
self.set_error(InputError::error(err.to_string()), cx);
|
||||
return;
|
||||
}
|
||||
Ok(keystrokes) => keystrokes,
|
||||
};
|
||||
|
||||
let new_context = match self.validate_context(cx) {
|
||||
Err(err) => {
|
||||
self.set_error(InputError::error(err.to_string()), cx);
|
||||
return;
|
||||
}
|
||||
Ok(context) => context,
|
||||
};
|
||||
|
||||
let new_action_args = match self.validate_action_arguments(cx) {
|
||||
Err(input_err) => {
|
||||
|
@ -2064,46 +2077,45 @@ async fn save_keybinding_update(
|
|||
.await
|
||||
.context("Failed to load keymap file")?;
|
||||
|
||||
let operation = if !create {
|
||||
let existing_keystrokes = existing.keystrokes().unwrap_or_default();
|
||||
let existing_context = existing
|
||||
.context
|
||||
.as_ref()
|
||||
.and_then(KeybindContextString::local_str);
|
||||
let existing_args = existing
|
||||
.action_arguments
|
||||
.as_ref()
|
||||
.map(|args| args.text.as_ref());
|
||||
let existing_keystrokes = existing.keystrokes().unwrap_or_default();
|
||||
let existing_context = existing
|
||||
.context
|
||||
.as_ref()
|
||||
.and_then(KeybindContextString::local_str);
|
||||
let existing_args = existing
|
||||
.action_arguments
|
||||
.as_ref()
|
||||
.map(|args| args.text.as_ref());
|
||||
|
||||
let target = settings::KeybindUpdateTarget {
|
||||
context: existing_context,
|
||||
keystrokes: existing_keystrokes,
|
||||
action_name: &existing.action_name,
|
||||
action_arguments: existing_args,
|
||||
};
|
||||
|
||||
let source = settings::KeybindUpdateTarget {
|
||||
context: new_context,
|
||||
keystrokes: new_keystrokes,
|
||||
action_name: &existing.action_name,
|
||||
action_arguments: new_args,
|
||||
};
|
||||
|
||||
let operation = if !create {
|
||||
settings::KeybindUpdateOperation::Replace {
|
||||
target: settings::KeybindUpdateTarget {
|
||||
context: existing_context,
|
||||
keystrokes: existing_keystrokes,
|
||||
action_name: &existing.action_name,
|
||||
use_key_equivalents: false,
|
||||
action_arguments: existing_args,
|
||||
},
|
||||
target,
|
||||
target_keybind_source: existing
|
||||
.source
|
||||
.as_ref()
|
||||
.map(|(source, _name)| *source)
|
||||
.unwrap_or(KeybindSource::User),
|
||||
source: settings::KeybindUpdateTarget {
|
||||
context: new_context,
|
||||
keystrokes: new_keystrokes,
|
||||
action_name: &existing.action_name,
|
||||
use_key_equivalents: false,
|
||||
action_arguments: new_args,
|
||||
},
|
||||
source,
|
||||
}
|
||||
} else {
|
||||
settings::KeybindUpdateOperation::Add(settings::KeybindUpdateTarget {
|
||||
context: new_context,
|
||||
keystrokes: new_keystrokes,
|
||||
action_name: &existing.action_name,
|
||||
use_key_equivalents: false,
|
||||
action_arguments: new_args,
|
||||
})
|
||||
settings::KeybindUpdateOperation::Add {
|
||||
source,
|
||||
from: Some(target),
|
||||
}
|
||||
};
|
||||
let updated_keymap_contents =
|
||||
settings::KeymapFile::update_keybinding(operation, keymap_contents, tab_size)
|
||||
|
@ -2137,7 +2149,6 @@ async fn remove_keybinding(
|
|||
.and_then(KeybindContextString::local_str),
|
||||
keystrokes,
|
||||
action_name: &existing.action_name,
|
||||
use_key_equivalents: false,
|
||||
action_arguments: existing
|
||||
.action_arguments
|
||||
.as_ref()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue