use std::collections::HashMap;
#[allow(unused_imports)]
use crate::ported::zle::{
deltochar::*, textobjects::*, zle_h::*, zle_hist::*, zle_main::*, zle_misc::*, zle_move::*,
zle_params::*, zle_refresh::*, zle_tricky::*, zle_utils::*, zle_vi::*, zle_word::*,
};
#[allow(unused_imports)]
pub fn widgetstr(name: &str, is_user: bool, is_completion: bool) -> String {
if is_completion {
format!("completion:{}", name)
} else if is_user {
format!("user:{}", name)
} else {
"builtin".to_string()
}
}
pub fn getpmwidgets(
_ht: *mut crate::ported::zsh_h::HashTable,
name: &str,
) -> Option<crate::ported::zsh_h::Param> {
use crate::ported::zsh_h::{hashnode, param, Param, PM_READONLY, PM_SCALAR, PM_UNSET};
let mk = |u_str: String, extra: i32| -> Param {
Box::new(param {
node: hashnode {
next: None,
nam: name.to_string(),
flags: PM_SCALAR as i32 | PM_READONLY as i32 | extra,
},
u_data: 0,
u_arr: None,
u_str: Some(u_str),
u_val: 0,
u_dval: 0.0,
u_hash: None,
gsu_s: None,
gsu_i: None,
gsu_f: None,
gsu_a: None,
gsu_h: None,
base: 0,
width: 0,
env: None,
ename: None,
old: None,
level: 0,
})
};
match crate::ported::zle::zle_thingy::getwidgettarget(name) {
Some(target) if target == name => Some(mk("builtin".to_string(), 0)),
Some(target) => Some(mk(format!("user:{}", target), 0)),
None => Some(mk(String::new(), PM_UNSET as i32)),
}
}
pub fn scanpmwidgets(
_ht: *mut crate::ported::zsh_h::HashTable,
func: Option<crate::ported::zsh_h::ScanFunc>,
flags: i32,
) {
use crate::ported::zsh_h::{hashnode, param, PM_READONLY, PM_SCALAR};
let f = match func {
Some(f) => f,
None => return,
};
let names = crate::ported::zle::zle_thingy::listwidgets();
for name in &names {
let label = match crate::ported::zle::zle_thingy::getwidgettarget(name) {
Some(t) if t == *name => "builtin".to_string(),
Some(t) => format!("user:{}", t),
None => continue,
};
let pm = param {
node: hashnode {
next: None,
nam: name.clone(),
flags: PM_SCALAR as i32 | PM_READONLY as i32,
},
u_data: 0,
u_arr: None,
u_str: Some(label),
u_val: 0,
u_dval: 0.0,
u_hash: None,
gsu_s: None,
gsu_i: None,
gsu_f: None,
gsu_a: None,
gsu_h: None,
base: 0,
width: 0,
env: None,
ename: None,
old: None,
level: 0,
};
let node_box = Box::new(pm.node.clone());
f(&node_box, flags); }
}
pub fn keymapsgetfn(_pm: *mut crate::ported::zsh_h::param) -> Vec<String> {
let mut names: Vec<String> = crate::ported::zle::zle_keymap::keymapnamtab()
.lock()
.map(|t| t.keys().cloned().collect())
.unwrap_or_default();
names.sort();
names
}
pub fn setup_() -> i32 {
0 }
pub fn features_() -> i32 {
0 }
pub fn enables_() -> i32 {
0 }
pub fn boot_() -> i32 {
0 }
pub fn cleanup_() -> i32 {
0 }
pub fn finish_() -> i32 {
0 }
pub const BUILTIN_WIDGETS: &[&str] = &[
"accept-and-hold",
"accept-and-infer-next-history",
"accept-line",
"accept-line-and-down-history",
"backward-char",
"backward-delete-char",
"backward-kill-line",
"backward-kill-word",
"backward-word",
"beep",
"beginning-of-buffer-or-history",
"beginning-of-history",
"beginning-of-line",
"beginning-of-line-hist",
"capitalize-word",
"clear-screen",
"complete-word",
"copy-prev-word",
"copy-region-as-kill",
"delete-char",
"delete-char-or-list",
"delete-word",
"describe-key-briefly",
"digit-argument",
"down-case-word",
"down-history",
"down-line",
"down-line-or-history",
"down-line-or-search",
"emacs-backward-word",
"emacs-forward-word",
"end-of-buffer-or-history",
"end-of-history",
"end-of-line",
"end-of-line-hist",
"exchange-point-and-mark",
"execute-last-named-cmd",
"execute-named-cmd",
"expand-history",
"expand-or-complete",
"expand-or-complete-prefix",
"expand-word",
"forward-char",
"forward-word",
"get-line",
"gosmacs-transpose-chars",
"history-beginning-search-backward",
"history-beginning-search-forward",
"history-incremental-search-backward",
"history-incremental-search-forward",
"history-search-backward",
"history-search-forward",
"insert-last-word",
"kill-buffer",
"kill-line",
"kill-region",
"kill-whole-line",
"kill-word",
"list-choices",
"list-expand",
"magic-space",
"menu-complete",
"menu-expand-or-complete",
"neg-argument",
"overwrite-mode",
"pound-insert",
"push-input",
"push-line",
"push-line-or-edit",
"quoted-insert",
"bslashquote-line",
"bslashquote-region",
"read-command",
"recursive-edit",
"redisplay",
"redo",
"reset-prompt",
"reverse-menu-complete",
"run-help",
"self-insert",
"self-insert-unmeta",
"send-break",
"set-mark-command",
"spell-word",
"split-undo",
"transpose-chars",
"transpose-words",
"undefined-key",
"undo",
"universal-argument",
"up-case-word",
"up-history",
"up-line",
"up-line-or-history",
"up-line-or-search",
"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-beginning-of-line",
"vi-caps-lock-panic",
"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-oper-swap-case",
"vi-pound-insert",
"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-undo-change",
"vi-unindent",
"vi-up-line-or-history",
"vi-yank",
"vi-yank-eol",
"vi-yank-whole-line",
"what-cursor-position",
"where-is",
"which-command",
"yank",
"yank-pop",
"zap-to-char",
];
pub const DEFAULT_KEYMAPS: &[&str] = &[
"emacs", "viins", "vicmd", "viopp", "visual", "isearch", "command", "main", ".safe",
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_widgetstr() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert_eq!(widgetstr("self-insert", false, false), "builtin");
assert_eq!(widgetstr("my-widget", true, false), "user:my-widget");
assert_eq!(widgetstr("my-comp", false, true), "completion:my-comp");
}
#[test]
fn test_getpmwidgets() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
use crate::ported::zsh_h::PM_UNSET;
let pm = getpmwidgets(std::ptr::null_mut(), "definitely-not-a-widget")
.expect("getpmwidgets always returns Some(Param)");
assert!(pm.node.flags & PM_UNSET as i32 != 0, "PM_UNSET set");
assert_eq!(pm.u_str.as_deref(), Some(""));
}
#[test]
fn test_keymapsgetfn() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let keymaps = keymapsgetfn(std::ptr::null_mut());
assert!(keymaps.contains(&"emacs".to_string()));
assert!(keymaps.contains(&"vicmd".to_string()));
}
#[test]
fn test_builtin_widget_count() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert!(BUILTIN_WIDGETS.len() > 150);
}
#[test]
fn widgetstr_user_form_carries_function_name_after_colon() {
let _g = crate::test_util::global_state_lock();
let s = widgetstr("a-fn", true, false);
let (kind, rest) = s.split_once(':').expect("missing colon");
assert_eq!(kind, "user");
assert_eq!(rest, "a-fn", "function-name suffix must round-trip");
}
#[test]
fn widgetstr_completion_wins_over_user_when_both_true() {
let _g = crate::test_util::global_state_lock();
let s = widgetstr("foo", true, true);
assert!(
s.starts_with("completion:"),
"is_completion must dominate is_user, got: {}",
s
);
}
#[test]
fn keymapsgetfn_returns_independent_copies() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let mut out = keymapsgetfn(std::ptr::null_mut());
let original_len = out.len();
out.push("d".to_string());
let again = keymapsgetfn(std::ptr::null_mut());
assert_eq!(again.len(), original_len);
assert_eq!(out.len(), original_len + 1);
}
#[test]
fn builtin_widgets_has_no_duplicates() {
let _g = crate::test_util::global_state_lock();
let unique: std::collections::HashSet<_> = BUILTIN_WIDGETS.iter().copied().collect();
assert_eq!(
unique.len(),
BUILTIN_WIDGETS.len(),
"duplicate widget name in BUILTIN_WIDGETS — would corrupt $widgets"
);
}
#[test]
fn builtin_widgets_entries_are_kebab_case() {
let _g = crate::test_util::global_state_lock();
for w in BUILTIN_WIDGETS {
assert!(!w.is_empty(), "empty widget name");
for c in w.chars() {
assert!(
c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-',
"widget {:?} has non-kebab-case char {:?}",
w,
c
);
}
assert!(
!w.starts_with('-'),
"widget {:?} starts with '-' — would parse as a flag",
w
);
assert!(!w.ends_with('-'), "widget {:?} ends with '-'", w);
}
}
#[test]
fn default_keymaps_includes_required_names() {
let _g = crate::test_util::global_state_lock();
for required in ["emacs", "viins", "vicmd", "main"] {
assert!(
DEFAULT_KEYMAPS.contains(&required),
"DEFAULT_KEYMAPS missing required name: {}",
required
);
}
}
#[test]
fn module_lifecycle_shims_all_return_zero() {
let _g = crate::test_util::global_state_lock();
assert_eq!(setup_(), 0);
assert_eq!(boot_(), 0);
assert_eq!(cleanup_(), 0);
assert_eq!(finish_(), 0);
}
}