mirror of
https://github.com/nushell/nushell.git
synced 2024-11-24 17:34:00 +01:00
Improve keybinding parsing for Unicode support (#14020)
# Description This pull request enhances the `add_parsed_keybinding` function to provide greater flexibility in specifying keycodes for keybindings in Nushell. Previously, the function only supported specifying keycodes directly through character notation (e.g., `char_e` for the character `e`). This limited users to a small set of keybindings, especially in scenarios where specific non-English characters were needed. With this new version, users can also specify characters using their Unicode codes, such as `char_u003B` for the semicolon (`;`), providing a more flexible approach to customization, for example like this: ```nushell { name: move_to_line_end_or_take_history_hint modifier: shift keycode: char_u003B # char_; mode: vi_normal event: { until: [ { send: historyhintcomplete } { edit: movetolineend } ] } } ``` # User-Facing Changes Added support for specifying keycodes using Unicode codes, e.g., char_u002C (comma - `,`): ```nushell { name: <command_name>, # name of the command modifier: none, # key modifier keycode: char_u002C, # Unicode code for the comma (',') mode: vi_normal, # mode in which this binding should work event: { send: <action> # action to be performed } } ```
This commit is contained in:
parent
2830ec008c
commit
55c3fc9141
@ -833,64 +833,76 @@ fn add_parsed_keybinding(
|
||||
}
|
||||
};
|
||||
|
||||
let keycode = match keybinding
|
||||
let keycode_str = keybinding
|
||||
.keycode
|
||||
.to_expanded_string("", config)
|
||||
.to_ascii_lowercase()
|
||||
.as_str()
|
||||
{
|
||||
"backspace" => KeyCode::Backspace,
|
||||
"enter" => KeyCode::Enter,
|
||||
c if c.starts_with("char_") => {
|
||||
let mut char_iter = c.chars().skip(5);
|
||||
let pos1 = char_iter.next();
|
||||
let pos2 = char_iter.next();
|
||||
.to_ascii_lowercase();
|
||||
|
||||
let char = if let (Some(char), None) = (pos1, pos2) {
|
||||
char
|
||||
} else {
|
||||
let keycode = if let Some(rest) = keycode_str.strip_prefix("char_") {
|
||||
let error = |exp: &str, value| ShellError::UnsupportedConfigValue {
|
||||
expected: exp.to_string(),
|
||||
value,
|
||||
span: keybinding.keycode.span(),
|
||||
};
|
||||
|
||||
let mut char_iter = rest.chars();
|
||||
let char = match (char_iter.next(), char_iter.next()) {
|
||||
(Some(char), None) => char,
|
||||
(Some('u'), Some(_)) => {
|
||||
// This will never panic as we know there are at least two symbols
|
||||
let Ok(code_point) = u32::from_str_radix(&rest[1..], 16) else {
|
||||
return Err(error("valid hex code in keycode", keycode_str));
|
||||
};
|
||||
|
||||
char::from_u32(code_point).ok_or(error("valid Unicode code point", keycode_str))?
|
||||
}
|
||||
_ => {
|
||||
return Err(error(
|
||||
"format 'char_<char>' or 'char_u<hex code>'",
|
||||
keycode_str,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
KeyCode::Char(char)
|
||||
} else {
|
||||
match keycode_str.as_str() {
|
||||
"backspace" => KeyCode::Backspace,
|
||||
"enter" => KeyCode::Enter,
|
||||
"space" => KeyCode::Char(' '),
|
||||
"down" => KeyCode::Down,
|
||||
"up" => KeyCode::Up,
|
||||
"left" => KeyCode::Left,
|
||||
"right" => KeyCode::Right,
|
||||
"home" => KeyCode::Home,
|
||||
"end" => KeyCode::End,
|
||||
"pageup" => KeyCode::PageUp,
|
||||
"pagedown" => KeyCode::PageDown,
|
||||
"tab" => KeyCode::Tab,
|
||||
"backtab" => KeyCode::BackTab,
|
||||
"delete" => KeyCode::Delete,
|
||||
"insert" => KeyCode::Insert,
|
||||
c if c.starts_with('f') => {
|
||||
let fn_num: u8 = c[1..]
|
||||
.parse()
|
||||
.ok()
|
||||
.filter(|num| matches!(num, 1..=20))
|
||||
.ok_or(ShellError::UnsupportedConfigValue {
|
||||
expected: "(f1|f2|...|f20)".to_string(),
|
||||
value: format!("unknown function key: {c}"),
|
||||
span: keybinding.keycode.span(),
|
||||
})?;
|
||||
KeyCode::F(fn_num)
|
||||
}
|
||||
"null" => KeyCode::Null,
|
||||
"esc" | "escape" => KeyCode::Esc,
|
||||
_ => {
|
||||
return Err(ShellError::UnsupportedConfigValue {
|
||||
expected: "char_<CHAR: unicode codepoint>".to_string(),
|
||||
value: c.to_string(),
|
||||
expected: "crossterm KeyCode".to_string(),
|
||||
value: keybinding.keycode.to_abbreviated_string(config),
|
||||
span: keybinding.keycode.span(),
|
||||
});
|
||||
};
|
||||
|
||||
KeyCode::Char(char)
|
||||
}
|
||||
"space" => KeyCode::Char(' '),
|
||||
"down" => KeyCode::Down,
|
||||
"up" => KeyCode::Up,
|
||||
"left" => KeyCode::Left,
|
||||
"right" => KeyCode::Right,
|
||||
"home" => KeyCode::Home,
|
||||
"end" => KeyCode::End,
|
||||
"pageup" => KeyCode::PageUp,
|
||||
"pagedown" => KeyCode::PageDown,
|
||||
"tab" => KeyCode::Tab,
|
||||
"backtab" => KeyCode::BackTab,
|
||||
"delete" => KeyCode::Delete,
|
||||
"insert" => KeyCode::Insert,
|
||||
c if c.starts_with('f') => {
|
||||
let fn_num: u8 = c[1..]
|
||||
.parse()
|
||||
.ok()
|
||||
.filter(|num| matches!(num, 1..=20))
|
||||
.ok_or(ShellError::UnsupportedConfigValue {
|
||||
expected: "(f1|f2|...|f20)".to_string(),
|
||||
value: format!("unknown function key: {c}"),
|
||||
span: keybinding.keycode.span(),
|
||||
})?;
|
||||
KeyCode::F(fn_num)
|
||||
}
|
||||
"null" => KeyCode::Null,
|
||||
"esc" | "escape" => KeyCode::Esc,
|
||||
_ => {
|
||||
return Err(ShellError::UnsupportedConfigValue {
|
||||
expected: "crossterm KeyCode".to_string(),
|
||||
value: keybinding.keycode.to_abbreviated_string(config),
|
||||
span: keybinding.keycode.span(),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(event) = parse_event(&keybinding.event, config)? {
|
||||
|
Loading…
Reference in New Issue
Block a user