mirror of
https://github.com/nushell/nushell
synced 2025-01-03 16:58:58 +00:00
426 lines
14 KiB
Rust
426 lines
14 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
fn convert_keypress(keypress: KeyPress) -> rustyline::KeyPress {
|
|
match keypress {
|
|
KeyPress::UnknownEscSeq => rustyline::KeyPress::UnknownEscSeq,
|
|
KeyPress::Backspace => rustyline::KeyPress::Backspace,
|
|
KeyPress::BackTab => rustyline::KeyPress::BackTab,
|
|
KeyPress::BracketedPasteStart => rustyline::KeyPress::BracketedPasteStart,
|
|
KeyPress::BracketedPasteEnd => rustyline::KeyPress::BracketedPasteEnd,
|
|
KeyPress::Char(c) => rustyline::KeyPress::Char(c),
|
|
KeyPress::ControlDown => rustyline::KeyPress::ControlDown,
|
|
KeyPress::ControlLeft => rustyline::KeyPress::ControlLeft,
|
|
KeyPress::ControlRight => rustyline::KeyPress::ControlRight,
|
|
KeyPress::ControlUp => rustyline::KeyPress::ControlUp,
|
|
KeyPress::Ctrl(c) => rustyline::KeyPress::Ctrl(c),
|
|
KeyPress::Delete => rustyline::KeyPress::Delete,
|
|
KeyPress::Down => rustyline::KeyPress::Down,
|
|
KeyPress::End => rustyline::KeyPress::End,
|
|
KeyPress::Enter => rustyline::KeyPress::Enter,
|
|
KeyPress::Esc => rustyline::KeyPress::Esc,
|
|
KeyPress::F(u) => rustyline::KeyPress::F(u),
|
|
KeyPress::Home => rustyline::KeyPress::Home,
|
|
KeyPress::Insert => rustyline::KeyPress::Insert,
|
|
KeyPress::Left => rustyline::KeyPress::Left,
|
|
KeyPress::Meta(c) => rustyline::KeyPress::Meta(c),
|
|
KeyPress::Null => rustyline::KeyPress::Null,
|
|
KeyPress::PageDown => rustyline::KeyPress::PageDown,
|
|
KeyPress::PageUp => rustyline::KeyPress::PageUp,
|
|
KeyPress::Right => rustyline::KeyPress::Right,
|
|
KeyPress::ShiftDown => rustyline::KeyPress::ShiftDown,
|
|
KeyPress::ShiftLeft => rustyline::KeyPress::ShiftLeft,
|
|
KeyPress::ShiftRight => rustyline::KeyPress::ShiftRight,
|
|
KeyPress::ShiftUp => rustyline::KeyPress::ShiftUp,
|
|
KeyPress::Tab => rustyline::KeyPress::Tab,
|
|
KeyPress::Up => rustyline::KeyPress::Up,
|
|
}
|
|
}
|
|
|
|
fn convert_word(word: Word) -> rustyline::Word {
|
|
match word {
|
|
Word::Big => rustyline::Word::Big,
|
|
Word::Emacs => rustyline::Word::Emacs,
|
|
Word::Vi => rustyline::Word::Vi,
|
|
}
|
|
}
|
|
|
|
fn convert_at(at: At) -> rustyline::At {
|
|
match at {
|
|
At::AfterEnd => rustyline::At::AfterEnd,
|
|
At::BeforeEnd => rustyline::At::BeforeEnd,
|
|
At::Start => rustyline::At::Start,
|
|
}
|
|
}
|
|
|
|
fn convert_char_search(search: CharSearch) -> rustyline::CharSearch {
|
|
match search {
|
|
CharSearch::Backward(c) => rustyline::CharSearch::Backward(c),
|
|
CharSearch::BackwardAfter(c) => rustyline::CharSearch::BackwardAfter(c),
|
|
CharSearch::Forward(c) => rustyline::CharSearch::Forward(c),
|
|
CharSearch::ForwardBefore(c) => rustyline::CharSearch::ForwardBefore(c),
|
|
}
|
|
}
|
|
|
|
fn convert_movement(movement: Movement) -> rustyline::Movement {
|
|
match movement {
|
|
Movement::BackwardChar(u) => rustyline::Movement::BackwardChar(u),
|
|
Movement::BackwardWord { repeat, word } => {
|
|
rustyline::Movement::BackwardWord(repeat, convert_word(word))
|
|
}
|
|
Movement::BeginningOfBuffer => rustyline::Movement::BeginningOfBuffer,
|
|
Movement::BeginningOfLine => rustyline::Movement::BeginningOfLine,
|
|
Movement::EndOfBuffer => rustyline::Movement::EndOfBuffer,
|
|
Movement::EndOfLine => rustyline::Movement::EndOfLine,
|
|
Movement::ForwardChar(u) => rustyline::Movement::ForwardChar(u),
|
|
Movement::ForwardWord { repeat, at, word } => {
|
|
rustyline::Movement::ForwardWord(repeat, convert_at(at), convert_word(word))
|
|
}
|
|
Movement::LineDown(u) => rustyline::Movement::LineDown(u),
|
|
Movement::LineUp(u) => rustyline::Movement::LineUp(u),
|
|
Movement::ViCharSearch { repeat, search } => {
|
|
rustyline::Movement::ViCharSearch(repeat, convert_char_search(search))
|
|
}
|
|
Movement::ViFirstPrint => rustyline::Movement::ViFirstPrint,
|
|
Movement::WholeBuffer => rustyline::Movement::WholeBuffer,
|
|
Movement::WholeLine => rustyline::Movement::WholeLine,
|
|
}
|
|
}
|
|
|
|
fn convert_anchor(anchor: Anchor) -> rustyline::Anchor {
|
|
match anchor {
|
|
Anchor::After => rustyline::Anchor::After,
|
|
Anchor::Before => rustyline::Anchor::Before,
|
|
}
|
|
}
|
|
|
|
fn convert_cmd(cmd: Cmd) -> rustyline::Cmd {
|
|
match cmd {
|
|
Cmd::Abort => rustyline::Cmd::Abort,
|
|
Cmd::AcceptLine => rustyline::Cmd::AcceptLine,
|
|
Cmd::AcceptOrInsertLine => rustyline::Cmd::AcceptOrInsertLine,
|
|
Cmd::BeginningOfHistory => rustyline::Cmd::BeginningOfHistory,
|
|
Cmd::CapitalizeWord => rustyline::Cmd::CapitalizeWord,
|
|
Cmd::ClearScreen => rustyline::Cmd::ClearScreen,
|
|
Cmd::Complete => rustyline::Cmd::Complete,
|
|
Cmd::CompleteBackward => rustyline::Cmd::CompleteBackward,
|
|
Cmd::CompleteHint => rustyline::Cmd::CompleteHint,
|
|
Cmd::DowncaseWord => rustyline::Cmd::DowncaseWord,
|
|
Cmd::EndOfFile => rustyline::Cmd::EndOfFile,
|
|
Cmd::EndOfHistory => rustyline::Cmd::EndOfHistory,
|
|
Cmd::ForwardSearchHistory => rustyline::Cmd::ForwardSearchHistory,
|
|
Cmd::HistorySearchBackward => rustyline::Cmd::HistorySearchBackward,
|
|
Cmd::HistorySearchForward => rustyline::Cmd::HistorySearchForward,
|
|
Cmd::Insert { repeat, string } => rustyline::Cmd::Insert(repeat, string),
|
|
Cmd::Interrupt => rustyline::Cmd::Interrupt,
|
|
Cmd::Kill(movement) => rustyline::Cmd::Kill(convert_movement(movement)),
|
|
Cmd::LineDownOrNextHistory(u) => rustyline::Cmd::LineDownOrNextHistory(u),
|
|
Cmd::LineUpOrPreviousHistory(u) => rustyline::Cmd::LineUpOrPreviousHistory(u),
|
|
Cmd::Move(movement) => rustyline::Cmd::Move(convert_movement(movement)),
|
|
Cmd::NextHistory => rustyline::Cmd::NextHistory,
|
|
Cmd::Noop => rustyline::Cmd::Noop,
|
|
Cmd::Overwrite(c) => rustyline::Cmd::Overwrite(c),
|
|
Cmd::PreviousHistory => rustyline::Cmd::PreviousHistory,
|
|
Cmd::QuotedInsert => rustyline::Cmd::QuotedInsert,
|
|
Cmd::Replace {
|
|
movement,
|
|
replacement,
|
|
} => rustyline::Cmd::Replace(convert_movement(movement), replacement),
|
|
Cmd::ReplaceChar { repeat, ch } => rustyline::Cmd::ReplaceChar(repeat, ch),
|
|
Cmd::ReverseSearchHistory => rustyline::Cmd::ReverseSearchHistory,
|
|
Cmd::SelfInsert { repeat, ch } => rustyline::Cmd::SelfInsert(repeat, ch),
|
|
Cmd::Suspend => rustyline::Cmd::Suspend,
|
|
Cmd::TransposeChars => rustyline::Cmd::TransposeChars,
|
|
Cmd::TransposeWords(u) => rustyline::Cmd::TransposeWords(u),
|
|
Cmd::Undo(u) => rustyline::Cmd::Undo(u),
|
|
Cmd::Unknown => rustyline::Cmd::Unknown,
|
|
Cmd::UpcaseWord => rustyline::Cmd::UpcaseWord,
|
|
Cmd::ViYankTo(movement) => rustyline::Cmd::ViYankTo(convert_movement(movement)),
|
|
Cmd::Yank { repeat, anchor } => rustyline::Cmd::Yank(repeat, convert_anchor(anchor)),
|
|
Cmd::YankPop => rustyline::Cmd::YankPop,
|
|
}
|
|
}
|
|
|
|
fn convert_keybinding(keybinding: Keybinding) -> (rustyline::KeyPress, rustyline::Cmd) {
|
|
(
|
|
convert_keypress(keybinding.key),
|
|
convert_cmd(keybinding.binding),
|
|
)
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
pub enum KeyPress {
|
|
/// Unsupported escape sequence (on unix platform)
|
|
UnknownEscSeq,
|
|
/// ⌫ or `KeyPress::Ctrl('H')`
|
|
Backspace,
|
|
/// ⇤ (usually Shift-Tab)
|
|
BackTab,
|
|
/// Paste (on unix platform)
|
|
BracketedPasteStart,
|
|
/// Paste (on unix platform)
|
|
BracketedPasteEnd,
|
|
/// Single char
|
|
Char(char),
|
|
/// Ctrl-↓
|
|
ControlDown,
|
|
/// Ctrl-←
|
|
ControlLeft,
|
|
/// Ctrl-→
|
|
ControlRight,
|
|
/// Ctrl-↑
|
|
ControlUp,
|
|
/// Ctrl-char
|
|
Ctrl(char),
|
|
/// ⌦
|
|
Delete,
|
|
/// ↓ arrow key
|
|
Down,
|
|
/// ⇲
|
|
End,
|
|
/// ↵ or `KeyPress::Ctrl('M')`
|
|
Enter,
|
|
/// Escape or `KeyPress::Ctrl('[')`
|
|
Esc,
|
|
/// Function key
|
|
F(u8),
|
|
/// ⇱
|
|
Home,
|
|
/// Insert key
|
|
Insert,
|
|
/// ← arrow key
|
|
Left,
|
|
/// Escape-char or Alt-char
|
|
Meta(char),
|
|
/// `KeyPress::Char('\0')`
|
|
Null,
|
|
/// ⇟
|
|
PageDown,
|
|
/// ⇞
|
|
PageUp,
|
|
/// → arrow key
|
|
Right,
|
|
/// Shift-↓
|
|
ShiftDown,
|
|
/// Shift-←
|
|
ShiftLeft,
|
|
/// Shift-→
|
|
ShiftRight,
|
|
/// Shift-↑
|
|
ShiftUp,
|
|
/// ⇥ or `KeyPress::Ctrl('I')`
|
|
Tab,
|
|
/// ↑ arrow key
|
|
Up,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
pub enum Cmd {
|
|
/// abort
|
|
Abort, // Miscellaneous Command
|
|
/// accept-line
|
|
AcceptLine,
|
|
/// beginning-of-history
|
|
BeginningOfHistory,
|
|
/// capitalize-word
|
|
CapitalizeWord,
|
|
/// clear-screen
|
|
ClearScreen,
|
|
/// complete
|
|
Complete,
|
|
/// complete-backward
|
|
CompleteBackward,
|
|
/// complete-hint
|
|
CompleteHint,
|
|
/// downcase-word
|
|
DowncaseWord,
|
|
/// vi-eof-maybe
|
|
EndOfFile,
|
|
/// end-of-history
|
|
EndOfHistory,
|
|
/// forward-search-history
|
|
ForwardSearchHistory,
|
|
/// history-search-backward
|
|
HistorySearchBackward,
|
|
/// history-search-forward
|
|
HistorySearchForward,
|
|
/// Insert text
|
|
Insert { repeat: RepeatCount, string: String },
|
|
/// Interrupt signal (Ctrl-C)
|
|
Interrupt,
|
|
/// backward-delete-char, backward-kill-line, backward-kill-word
|
|
/// delete-char, kill-line, kill-word, unix-line-discard, unix-word-rubout,
|
|
/// vi-delete, vi-delete-to, vi-rubout
|
|
Kill(Movement),
|
|
/// backward-char, backward-word, beginning-of-line, end-of-line,
|
|
/// forward-char, forward-word, vi-char-search, vi-end-word, vi-next-word,
|
|
/// vi-prev-word
|
|
Move(Movement),
|
|
/// next-history
|
|
NextHistory,
|
|
/// No action
|
|
Noop,
|
|
/// vi-replace
|
|
Overwrite(char),
|
|
/// previous-history
|
|
PreviousHistory,
|
|
/// quoted-insert
|
|
QuotedInsert,
|
|
/// vi-change-char
|
|
ReplaceChar { repeat: RepeatCount, ch: char },
|
|
/// vi-change-to, vi-substitute
|
|
Replace {
|
|
movement: Movement,
|
|
replacement: Option<String>,
|
|
},
|
|
/// reverse-search-history
|
|
ReverseSearchHistory,
|
|
/// self-insert
|
|
SelfInsert { repeat: RepeatCount, ch: char },
|
|
/// Suspend signal (Ctrl-Z on unix platform)
|
|
Suspend,
|
|
/// transpose-chars
|
|
TransposeChars,
|
|
/// transpose-words
|
|
TransposeWords(RepeatCount),
|
|
/// undo
|
|
Undo(RepeatCount),
|
|
/// Unsupported / unexpected
|
|
Unknown,
|
|
/// upcase-word
|
|
UpcaseWord,
|
|
/// vi-yank-to
|
|
ViYankTo(Movement),
|
|
/// yank, vi-put
|
|
Yank { repeat: RepeatCount, anchor: Anchor },
|
|
/// yank-pop
|
|
YankPop,
|
|
/// moves cursor to the line above or switches to prev history entry if
|
|
/// the cursor is already on the first line
|
|
LineUpOrPreviousHistory(RepeatCount),
|
|
/// moves cursor to the line below or switches to next history entry if
|
|
/// the cursor is already on the last line
|
|
LineDownOrNextHistory(RepeatCount),
|
|
/// accepts the line when cursor is at the end of the text (non including
|
|
/// trailing whitespace), inserts newline character otherwise
|
|
AcceptOrInsertLine,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
pub enum Movement {
|
|
/// Whole current line (not really a movement but a range)
|
|
WholeLine,
|
|
/// beginning-of-line
|
|
BeginningOfLine,
|
|
/// end-of-line
|
|
EndOfLine,
|
|
/// backward-word, vi-prev-word
|
|
BackwardWord { repeat: RepeatCount, word: Word }, // Backward until start of word
|
|
/// forward-word, vi-end-word, vi-next-word
|
|
ForwardWord {
|
|
repeat: RepeatCount,
|
|
at: At,
|
|
word: Word,
|
|
}, // Forward until start/end of word
|
|
/// vi-char-search
|
|
ViCharSearch {
|
|
repeat: RepeatCount,
|
|
search: CharSearch,
|
|
},
|
|
/// vi-first-print
|
|
ViFirstPrint,
|
|
/// backward-char
|
|
BackwardChar(RepeatCount),
|
|
/// forward-char
|
|
ForwardChar(RepeatCount),
|
|
/// move to the same column on the previous line
|
|
LineUp(RepeatCount),
|
|
/// move to the same column on the next line
|
|
LineDown(RepeatCount),
|
|
/// Whole user input (not really a movement but a range)
|
|
WholeBuffer,
|
|
/// beginning-of-buffer
|
|
BeginningOfBuffer,
|
|
/// end-of-buffer
|
|
EndOfBuffer,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
enum InputMode {
|
|
/// Vi Command/Alternate
|
|
Command,
|
|
/// Insert/Input mode
|
|
Insert,
|
|
/// Overwrite mode
|
|
Replace,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
pub enum Word {
|
|
/// non-blanks characters
|
|
Big,
|
|
/// alphanumeric characters
|
|
Emacs,
|
|
/// alphanumeric (and '_') characters
|
|
Vi,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
pub enum At {
|
|
/// Start of word.
|
|
Start,
|
|
/// Before end of word.
|
|
BeforeEnd,
|
|
/// After end of word.
|
|
AfterEnd,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
pub enum Anchor {
|
|
/// After cursor
|
|
After,
|
|
/// Before cursor
|
|
Before,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
pub enum CharSearch {
|
|
/// Forward search
|
|
Forward(char),
|
|
/// Forward search until
|
|
ForwardBefore(char),
|
|
/// Backward search
|
|
Backward(char),
|
|
/// Backward search until
|
|
BackwardAfter(char),
|
|
}
|
|
|
|
/// The number of times one command should be repeated.
|
|
pub type RepeatCount = usize;
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
pub struct Keybinding {
|
|
key: KeyPress,
|
|
binding: Cmd,
|
|
}
|
|
|
|
type Keybindings = Vec<Keybinding>;
|
|
|
|
pub(crate) fn load_keybindings(
|
|
rl: &mut rustyline::Editor<crate::shell::Helper>,
|
|
) -> Result<(), nu_errors::ShellError> {
|
|
let filename = nu_data::keybinding::keybinding_path()?;
|
|
let contents = std::fs::read_to_string(filename);
|
|
|
|
// Silently fail if there is no file there
|
|
if let Ok(contents) = contents {
|
|
let keybindings: Keybindings = serde_yaml::from_str(&contents)?;
|
|
|
|
for keybinding in keybindings.into_iter() {
|
|
let (k, b) = convert_keybinding(keybinding);
|
|
|
|
rl.bind_sequence(k, b);
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|