use super::zle_keymap::KM_IMMUTABLE;
use super::zle_thingy::Thingy;
#[allow(unused_imports)]
use crate::ported::zle::{
deltochar::*, textobjects::*, zle_hist::*, zle_main::*, zle_misc::*, zle_move::*,
zle_params::*, zle_refresh::*, zle_tricky::*, zle_utils::*, zle_vi::*, zle_word::*,
};
#[allow(unused_imports)]
#[allow(unused_imports)]
pub fn getkeystring(s: &str) -> Vec<u8> {
let mut result = Vec::new();
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
match c {
'^' => {
if let Some(&next) = chars.peek() {
chars.next();
if next == '?' {
result.push(0x7f); } else if next == '[' {
result.push(0x1b); } else {
result.push((next.to_ascii_uppercase() as u8).wrapping_sub(b'@'));
}
}
}
'\\' => {
match chars.peek() {
Some(&'a') => {
chars.next();
result.push(0x07); }
Some(&'b') => {
chars.next();
result.push(0x08); }
Some(&'e') | Some(&'E') => {
chars.next();
result.push(0x1b); }
Some(&'f') => {
chars.next();
result.push(0x0c); }
Some(&'n') => {
chars.next();
result.push(b'\n');
}
Some(&'t') => {
chars.next();
result.push(b'\t');
}
Some(&'r') => {
chars.next();
result.push(b'\r');
}
Some(&'v') => {
chars.next();
result.push(0x0b); }
Some(&'M') => {
chars.next();
if chars.peek() == Some(&'-') {
chars.next();
result.push(0x1b);
if let Some(next) = chars.next() {
result.push(next as u8);
}
}
}
Some(&'C') => {
chars.next();
if chars.peek() == Some(&'-') {
chars.next();
if let Some(next) = chars.next() {
result.push((next.to_ascii_uppercase() as u8).wrapping_sub(b'@'));
}
}
}
Some(&'x') => {
chars.next();
let mut hex = String::new();
for _ in 0..2 {
if let Some(&c) = chars.peek() {
if c.is_ascii_hexdigit() {
hex.push(c);
chars.next();
} else {
break;
}
}
}
if let Ok(n) = u8::from_str_radix(&hex, 16) {
result.push(n);
}
}
Some(&c) => {
chars.next();
result.push(c as u8);
}
None => {
result.push(b'\\');
}
}
}
_ => {
result.push(c as u8);
}
}
}
result
}
pub fn bindkey(keymap: &str, seq: &str, widget: &str) -> bool {
let seq_bytes = getkeystring(seq); let mut tab = crate::ported::zle::zle_keymap::keymapnamtab()
.lock()
.unwrap();
let node = match tab.get_mut(keymap) {
Some(n) => n,
None => return false, };
if (node.keymap.flags & KM_IMMUTABLE) != 0 {
return false; }
if seq_bytes.is_empty() {
return false; }
let inner = std::sync::Arc::make_mut(&mut node.keymap);
inner.bind_seq(&seq_bytes, Thingy::new(widget));
true }
pub fn bindlistout(keymap: &str) -> Vec<(String, String)> {
let mut bindings = Vec::new();
if let Some(map) = crate::ported::zle::zle_keymap::openkeymap(keymap) {
for (i, thingy) in map.first.iter().enumerate() {
if let Some(t) = thingy {
let seq = printbind(&[i as u8]);
bindings.push((seq, t.nam.clone()));
}
}
for (seq, binding) in &map.multi {
if let Some(t) = &binding.bind {
let seq_str = printbind(seq);
bindings.push((seq_str, t.nam.clone()));
} else if let Some(s) = &binding.str {
let seq_str = printbind(seq);
bindings.push((seq_str, format!("send-string \"{}\"", s)));
}
}
}
bindings.sort_by(|a, b| a.0.cmp(&b.0));
bindings
}
pub static EMACSBIND: [&str; 32] = [
"set-mark-command",
"beginning-of-line",
"backward-char",
"undefined-key",
"delete-char-or-list",
"end-of-line",
"forward-char",
"send-break",
"backward-delete-char",
"expand-or-complete",
"accept-line",
"kill-line",
"clear-screen",
"accept-line",
"down-line-or-history",
"accept-line-and-down-history",
"up-line-or-history",
"push-line",
"history-incremental-search-backward",
"history-incremental-search-forward",
"transpose-chars",
"kill-whole-line",
"quoted-insert",
"backward-kill-word",
"undefined-key",
"yank",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undo",
];
pub static METABIND: [&str; 128] = [
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"list-choices",
"undefined-key",
"undefined-key",
"send-break",
"backward-kill-word",
"self-insert-unmeta",
"self-insert-unmeta",
"undefined-key",
"clear-screen",
"self-insert-unmeta",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"copy-prev-word",
"expand-history",
"expand-history",
"quote-region",
"undefined-key",
"spell-word",
"undefined-key",
"undefined-key",
"quote-line",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"neg-argument",
"insert-last-word",
"undefined-key",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"undefined-key",
"undefined-key",
"beginning-of-buffer-or-history",
"undefined-key",
"end-of-buffer-or-history",
"which-command",
"undefined-key",
"accept-and-hold",
"backward-word",
"capitalize-word",
"kill-word",
"undefined-key",
"forward-word",
"get-line",
"run-help",
"undefined-key",
"undefined-key",
"undefined-key",
"down-case-word",
"undefined-key",
"history-search-forward",
"undefined-key",
"history-search-backward",
"push-line",
"undefined-key",
"spell-word",
"transpose-words",
"up-case-word",
"undefined-key",
"copy-region-as-kill",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"insert-last-word",
"undefined-key",
"accept-and-hold",
"backward-word",
"capitalize-word",
"kill-word",
"undefined-key",
"forward-word",
"get-line",
"run-help",
"undefined-key",
"undefined-key",
"undefined-key",
"down-case-word",
"undefined-key",
"history-search-forward",
"undefined-key",
"history-search-backward",
"push-line",
"undefined-key",
"spell-word",
"transpose-words",
"up-case-word",
"undefined-key",
"copy-region-as-kill",
"execute-named-cmd",
"yank-pop",
"execute-last-named-cmd",
"undefined-key",
"vi-goto-column",
"undefined-key",
"undefined-key",
"backward-kill-word",
];
pub static VIINSBIND: [&str; 32] = [
"undefined-key",
"self-insert",
"self-insert",
"self-insert",
"list-choices",
"self-insert",
"self-insert",
"list-expand",
"vi-backward-delete-char",
"expand-or-complete",
"accept-line",
"self-insert",
"clear-screen",
"accept-line",
"self-insert",
"self-insert",
"self-insert",
"vi-quoted-insert",
"redisplay",
"self-insert",
"self-insert",
"vi-kill-line",
"vi-quoted-insert",
"vi-backward-kill-word",
"undefined-key",
"self-insert",
"self-insert",
"vi-cmd-mode",
"self-insert",
"self-insert",
"self-insert",
"self-insert",
];
pub static VICMDBIND: [&str; 128] = [
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"list-choices",
"undefined-key",
"undefined-key",
"list-expand",
"vi-backward-char",
"undefined-key",
"accept-line",
"undefined-key",
"clear-screen",
"accept-line",
"down-history",
"undefined-key",
"up-history",
"undefined-key",
"redo",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"beep",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"vi-forward-char",
"undefined-key",
"vi-set-buffer",
"pound-insert",
"vi-end-of-line",
"vi-match-bracket",
"undefined-key",
"vi-goto-mark-line",
"undefined-key",
"undefined-key",
"undefined-key",
"vi-down-line-or-history",
"vi-rev-repeat-find",
"vi-up-line-or-history",
"vi-repeat-change",
"vi-history-search-backward",
"vi-digit-or-beginning-of-line",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"digit-argument",
"execute-named-cmd",
"vi-repeat-find",
"vi-unindent",
"list-choices",
"vi-indent",
"vi-history-search-forward",
"undefined-key",
"vi-add-eol",
"vi-backward-blank-word",
"vi-change-eol",
"vi-kill-eol",
"vi-forward-blank-word-end",
"vi-find-prev-char",
"vi-fetch-history",
"undefined-key",
"vi-insert-bol",
"vi-join",
"undefined-key",
"undefined-key",
"undefined-key",
"vi-rev-repeat-search",
"vi-open-line-above",
"vi-put-before",
"undefined-key",
"vi-replace",
"vi-change-whole-line",
"vi-find-prev-char-skip",
"undefined-key",
"visual-line-mode",
"vi-forward-blank-word",
"vi-backward-delete-char",
"vi-yank-whole-line",
"undefined-key",
"undefined-key",
"undefined-key",
"undefined-key",
"vi-first-non-blank",
"vi-first-non-blank",
"vi-goto-mark",
"vi-add-next",
"vi-backward-word",
"vi-change",
"vi-delete",
"vi-forward-word-end",
"vi-find-next-char",
"undefined-key",
"vi-backward-char",
"vi-insert",
"down-line-or-history",
"up-line-or-history",
"vi-forward-char",
"vi-set-mark",
"vi-repeat-search",
"vi-open-line-below",
"vi-put-after",
"undefined-key",
"vi-replace-chars",
"vi-substitute",
"vi-find-next-char-skip",
"undo",
"visual-mode",
"vi-forward-word",
"vi-delete-char",
"vi-yank",
"undefined-key",
"undefined-key",
"vi-goto-column",
"undefined-key",
"vi-swap-case",
"vi-backward-char",
];
pub const IWIDGET_NAMES: &[&str] = &[
"accept-and-hold", "accept-line", "accept-line-and-down-history",
"backward-char", "backward-delete-char", "backward-kill-word",
"backward-word", "beep", "beginning-of-buffer-or-history",
"beginning-of-line", "capitalize-word", "clear-screen",
"copy-prev-word", "copy-region-as-kill", "delete-char-or-list",
"digit-argument", "down-case-word", "down-history",
"down-line-or-history", "end-of-buffer-or-history", "end-of-line",
"execute-last-named-cmd", "execute-named-cmd", "expand-history",
"expand-or-complete", "forward-char", "forward-word", "get-line",
"history-incremental-search-backward",
"history-incremental-search-forward", "history-search-backward",
"history-search-forward", "insert-last-word", "kill-line",
"kill-whole-line", "kill-word", "list-choices", "list-expand",
"neg-argument", "pound-insert", "push-line", "quote-line",
"quote-region", "quoted-insert", "redisplay", "redo", "run-help",
"self-insert", "self-insert-unmeta", "send-break",
"set-mark-command", "spell-word", "transpose-chars",
"transpose-words", "undefined-key", "undo", "up-case-word",
"up-history", "up-line-or-history", "vi-add-eol", "vi-add-next",
"vi-backward-blank-word", "vi-backward-char",
"vi-backward-delete-char", "vi-backward-kill-word",
"vi-backward-word", "vi-change", "vi-change-eol",
"vi-change-whole-line", "vi-cmd-mode", "vi-delete",
"vi-delete-char", "vi-digit-or-beginning-of-line",
"vi-down-line-or-history", "vi-end-of-line", "vi-fetch-history",
"vi-find-next-char", "vi-find-next-char-skip",
"vi-find-prev-char", "vi-find-prev-char-skip",
"vi-first-non-blank", "vi-forward-blank-word",
"vi-forward-blank-word-end", "vi-forward-char", "vi-forward-word",
"vi-forward-word-end", "vi-goto-column", "vi-goto-mark",
"vi-goto-mark-line", "vi-history-search-backward",
"vi-history-search-forward", "vi-indent", "vi-insert",
"vi-insert-bol", "vi-join", "vi-kill-eol", "vi-kill-line",
"vi-match-bracket", "vi-open-line-above", "vi-open-line-below",
"vi-put-after", "vi-put-before", "vi-quoted-insert",
"vi-repeat-change", "vi-repeat-find", "vi-repeat-search",
"vi-replace", "vi-replace-chars", "vi-rev-repeat-find",
"vi-rev-repeat-search", "vi-set-buffer", "vi-set-mark",
"vi-substitute", "vi-swap-case", "vi-unindent",
"vi-up-line-or-history", "vi-yank", "vi-yank-whole-line",
"visual-line-mode", "visual-mode", "which-command", "yank",
"yank-pop",
];
pub fn iwidget_lookup(name: &str) -> Option<super::zle_h::ZleIntFunc> {
match name {
"beep" => Some(|_| handlefeep()),
"accept-and-hold" => Some(|_| acceptandhold()),
"accept-line-and-down-history" => Some(|_| acceptlineanddownhistory()),
"accept-line" => Some(|_| acceptline()),
"backward-char" => Some(|_| backwardchar()),
"backward-delete-char" => Some(|_| backwarddeletechar()),
"backward-kill-word" => Some(backwardkillword),
"backward-word" => Some(backwardword),
"beginning-of-buffer-or-history" => Some(|_| beginningofbufferorhistory()),
"beginning-of-line" => Some(|_| beginningofline()),
"capitalize-word" => Some(capitalizeword),
"copy-prev-word" => Some(|_| copyprevword()),
"copy-region-as-kill" => Some(copyregionaskill),
"delete-char-or-list" => Some(|_| deletecharorlist()),
"digit-argument" => Some(|_| digitargument()),
"down-case-word" => Some(downcaseword),
"down-history" => Some(|_| downhistory()),
"down-line-or-history" => Some(|_| downlineorhistory()),
"end-of-buffer-or-history" => Some(|_| endofbufferorhistory()),
"end-of-line" => Some(|_| endofline()),
"expand-history" => Some(|_| expandhistory()),
"expand-or-complete" => Some(|_| expandorcomplete()),
"forward-char" => Some(|_| forwardchar()),
"forward-word" => Some(forwardword),
"history-incremental-search-backward" => {
Some(|_| historyincrementalsearchbackward())
}
"history-incremental-search-forward" => {
Some(|_| historyincrementalsearchforward())
}
"history-search-backward" => Some(|_| historysearchbackward()),
"history-search-forward" => Some(|_| historysearchforward()),
"insert-last-word" => Some(|_| insertlastword()),
"kill-line" => Some(|_| killline()),
"kill-whole-line" => Some(|_| killwholeline()),
"kill-word" => Some(killword),
"list-choices" => Some(|_| listchoices()),
"list-expand" => Some(|_| listexpand()),
"neg-argument" => Some(|_| negargument()),
"pound-insert" => Some(|_| poundinsert()),
"push-line" => Some(|_| pushline()),
"quote-line" => Some(|_| quoteline()),
"quote-region" => Some(|_| quoteregion()),
"quoted-insert" => Some(|_| quotedinsert()),
"redo" => Some(|_| redo()),
"self-insert-unmeta" => Some(|_| selfinsertunmeta()),
"self-insert" => Some(|_| selfinsert()),
"send-break" => Some(|_| sendbreak()),
"set-mark-command" => Some(|_| setmarkcommand()),
"spell-word" => Some(|_| spellword()),
"transpose-chars" => Some(|_| transposechars()),
"transpose-words" => Some(transposewords),
"undefined-key" => Some(|_| undefinedkey()),
"undo" => Some(undo),
"up-case-word" => Some(upcaseword),
"up-history" => Some(|_| uphistory()),
"up-line-or-history" => Some(|_| uplineorhistory()),
"vi-add-eol" => Some(|_| viaddeol()),
"vi-add-next" => Some(|_| viaddnext()),
"vi-backward-blank-word" => Some(vibackwardblankword),
"vi-backward-char" => Some(|_| vibackwardchar()),
"vi-backward-delete-char" => Some(|_| vibackwarddeletechar()),
"vi-backward-kill-word" => Some(vibackwardkillword),
"vi-backward-word" => Some(vibackwardword),
"vi-change-eol" => Some(|_| vichangeeol()),
"vi-change-whole-line" => Some(|_| vichangewholeline()),
"vi-change" => Some(|_| vichange()),
"vi-cmd-mode" => Some(|_| vicmdmode()),
"vi-delete-char" => Some(|_| videletechar()),
"vi-delete" => Some(|_| videlete()),
"vi-digit-or-beginning-of-line" => Some(|_| vidigitorbeginningofline()),
"vi-down-line-or-history" => Some(|_| vidownlineorhistory()),
"vi-end-of-line" => Some(|_| viendofline()),
"vi-fetch-history" => Some(|_| vifetchhistory()),
"vi-find-next-char-skip" => Some(|_| vifindnextcharskip()),
"vi-find-next-char" => Some(|_| vifindnextchar()),
"vi-find-prev-char-skip" => Some(|_| vifindprevcharskip()),
"vi-find-prev-char" => Some(|_| vifindprevchar()),
"vi-first-non-blank" => Some(|_| vifirstnonblank()),
"vi-forward-blank-word-end" => Some(viforwardblankwordend),
"vi-forward-blank-word" => Some(viforwardblankword),
"vi-forward-char" => Some(|_| viforwardchar()),
"vi-forward-word-end" => Some(viforwardwordend),
"vi-forward-word" => Some(viforwardword),
"vi-goto-column" => Some(|_| vigotocolumn()),
"vi-goto-mark-line" => Some(|_| vigotomarkline('\0')),
"vi-goto-mark" => Some(|_| vigotomark('\0')),
"vi-history-search-backward" => Some(|_| vihistorysearchbackward()),
"vi-history-search-forward" => Some(|_| vihistorysearchforward()),
"vi-indent" => Some(|_| viindent()),
"vi-insert-bol" => Some(|_| viinsertbol()),
"vi-insert" => Some(|_| viinsert()),
"vi-join" => Some(|_| vijoin()),
"vi-kill-eol" => Some(|_| vikilleol()),
"vi-kill-line" => Some(|_| vikillline()),
"vi-match-bracket" => Some(|_| vimatchbracket()),
"vi-open-line-above" => Some(|_| viopenlineabove()),
"vi-open-line-below" => Some(|_| viopenlinebelow()),
"vi-put-after" => Some(|_| viputafter()),
"vi-put-before" => Some(|_| viputbefore()),
"vi-quoted-insert" => Some(|_| viquotedinsert()),
"vi-repeat-change" => Some(|_| virepeatchange()),
"vi-repeat-find" => Some(|_| virepeatfind()),
"vi-repeat-search" => Some(|_| virepeatsearch()),
"vi-replace-chars" => Some(|_| vireplacechars()),
"vi-replace" => Some(|_| vireplace()),
"vi-rev-repeat-find" => Some(|_| virevrepeatfind()),
"vi-rev-repeat-search" => Some(|_| virevrepeatsearch()),
"vi-set-buffer" => Some(|_| visetbuffer()),
"vi-set-mark" => Some(|_| visetmark('\0')),
"vi-substitute" => Some(|_| visubstitute()),
"vi-swap-case" => Some(|_| viswapcase()),
"vi-unindent" => Some(|_| viunindent()),
"vi-up-line-or-history" => Some(|_| viuplineorhistory()),
"vi-yank-whole-line" => Some(|_| viyankwholeline()),
"visual-line-mode" => Some(|_| visuallinemode()),
"visual-mode" => Some(|_| visualmode()),
"yank-pop" => Some(|_| yankpop()),
"clear-screen" => Some(|_| {
use std::sync::atomic::Ordering;
let fd = crate::ported::init::SHTTY.load(Ordering::Relaxed);
let out = if fd >= 0 { fd } else { 1 };
let _ = crate::ported::utils::write_loop(out, b"\x1b[H\x1b[2J");
ZLE_RESET_NEEDED.store(1, Ordering::SeqCst);
0
}),
"redisplay" => Some(|_| {
ZLE_RESET_NEEDED
.store(1, std::sync::atomic::Ordering::SeqCst);
0
}),
"yank" => Some(|_| {
let ring = KILLRING.lock().unwrap();
let text = match ring.front() {
Some(t) => t.clone(),
None => return 1,
};
drop(ring);
let cs = ZLECS.load(std::sync::atomic::Ordering::SeqCst);
let mut line = ZLELINE.lock().unwrap();
for (i, c) in text.iter().enumerate() {
line.insert(cs + i, *c);
}
let new_ll = line.len();
drop(line);
ZLELL.store(new_ll, std::sync::atomic::Ordering::SeqCst);
ZLECS
.store(cs + text.len(), std::sync::atomic::Ordering::SeqCst);
0
}),
"vi-yank" => Some(viyank),
"which-command" => Some(super::zle_misc::processcmd),
"run-help" => Some(super::zle_misc::processcmd),
"get-line" => Some(|_| super::zle_hist::zgetline()),
"execute-named-cmd" => Some(|_| 0),
"execute-last-named-cmd" => Some(|_| 0),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bindkey_returns_false_for_unknown_keymap() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
crate::ported::zle::zle_keymap::createkeymapnamtab();
crate::ported::zle::zle_keymap::default_bindings();
assert!(!bindkey("no-such-keymap", "^A", "self-insert"));
}
#[test]
fn bindkey_then_unbind_round_trips_through_emacs_keymap() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
crate::ported::zle::zle_keymap::createkeymapnamtab();
crate::ported::zle::zle_keymap::default_bindings();
assert!(bindkey("emacs", "\\ez", "self-insert"));
let listed = bindlistout("emacs");
let seq = printbind(&[0x1b, 0x7a]);
assert!(
listed.iter().any(|(k, v)| k == &seq && v == "self-insert"),
"bound sequence missing from list: {:?}",
listed
);
let seq_bytes = getkeystring("\\ez");
let mut tab = crate::ported::zle::zle_keymap::keymapnamtab()
.lock()
.unwrap();
let node = tab.get_mut("emacs").unwrap();
let inner = std::sync::Arc::make_mut(&mut node.keymap);
inner.unbind_seq(&seq_bytes);
drop(tab);
let listed = bindlistout("emacs");
assert!(
!listed.iter().any(|(k, _)| k == &seq),
"unbound sequence still present: {:?}",
listed
);
}
#[test]
fn getkeystring_decodes_canonical_escapes() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring("\\e"), vec![0x1b]); assert_eq!(getkeystring("\\t"), vec![0x09]); assert_eq!(getkeystring("\\n"), vec![0x0a]); assert_eq!(getkeystring("\\r"), vec![0x0d]); }
#[test]
fn getkeystring_decodes_control_prefix() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring("\\C-a"), vec![0x01]); assert_eq!(getkeystring("\\C-c"), vec![0x03]); }
#[test]
fn getkeystring_decodes_meta_prefix() {
let _g = crate::test_util::global_state_lock();
let b = getkeystring("\\M-a");
assert!(!b.is_empty(), "\\M-a must decode to at least 1 byte");
}
#[test]
fn getkeystring_passes_plain_ascii_through() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring("abc"), b"abc".to_vec());
}
#[test]
fn iwidget_lookup_resolves_self_insert() {
let _g = crate::test_util::global_state_lock();
assert!(iwidget_lookup("self-insert").is_some());
}
#[test]
fn iwidget_lookup_returns_none_for_unknown_name() {
let _g = crate::test_util::global_state_lock();
assert!(iwidget_lookup("definitely-not-a-real-widget-zshrs").is_none());
}
#[test]
fn bindlistout_empty_for_unknown_keymap() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert!(bindlistout("does-not-exist").is_empty());
}
#[test]
fn getkeystring_empty_string_returns_empty() {
let _g = crate::test_util::global_state_lock();
assert!(getkeystring("").is_empty());
}
#[test]
fn getkeystring_decodes_backspace() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring("\\b"), vec![0x08]);
}
#[test]
fn getkeystring_control_prefix_is_case_insensitive() {
let _g = crate::test_util::global_state_lock();
let lower = getkeystring("\\C-a");
let upper = getkeystring("\\C-A");
assert_eq!(lower, upper, r#"\\C-a and \\C-A must decode identically"#);
assert_eq!(lower, vec![0x01]);
}
#[test]
fn iwidget_lookup_resolves_accept_line() {
let _g = crate::test_util::global_state_lock();
assert!(
iwidget_lookup("accept-line").is_some(),
"accept-line is the canonical Enter-key widget; must resolve"
);
}
#[test]
fn iwidget_lookup_empty_name_returns_none() {
let _g = crate::test_util::global_state_lock();
assert!(iwidget_lookup("").is_none());
}
#[test]
fn bindkey_unknown_widget_binds_anyway_matching_c() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let ok = bindkey("main", "\\C-x", "user-widget-not-yet-defined");
assert!(
ok,
"C source resolves widgets at trigger time, so bind-time \
unknowns must SUCCEED"
);
}
#[test]
fn zle_bindings_corpus_getkeystring_caret_A_is_ctrl_a() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring("^A"), vec![0x01]);
}
#[test]
fn zle_bindings_corpus_getkeystring_caret_question_is_del() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring("^?"), vec![0x7f]);
}
#[test]
fn zle_bindings_corpus_getkeystring_caret_bracket_is_esc() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring("^["), vec![0x1b]);
}
#[test]
fn zle_bindings_corpus_getkeystring_backslash_e_is_esc() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring(r"\e"), vec![0x1b]);
}
#[test]
fn zle_bindings_corpus_getkeystring_backslash_t_is_tab() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring(r"\t"), vec![0x09]);
}
#[test]
fn zle_bindings_corpus_getkeystring_backslash_n_is_lf() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring(r"\n"), vec![0x0a]);
}
#[test]
fn zle_bindings_corpus_getkeystring_backslash_r_is_cr() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring(r"\r"), vec![0x0d]);
}
#[test]
fn zle_bindings_corpus_getkeystring_empty_is_empty() {
let _g = crate::test_util::global_state_lock();
assert!(getkeystring("").is_empty());
}
#[test]
fn zle_bindings_corpus_getkeystring_plain_ascii_passthrough() {
let _g = crate::test_util::global_state_lock();
assert_eq!(getkeystring("abc"), vec![b'a', b'b', b'c']);
}
}