use std::collections::HashMap;
#[allow(unused_imports)]
use crate::ported::zle::zle_h::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_main::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_misc::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_hist::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_move::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_word::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_params::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_vi::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_utils::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_refresh::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_tricky::*;
#[allow(unused_imports)]
use crate::ported::zle::textobjects::*;
#[allow(unused_imports)]
use crate::ported::zle::deltochar::*;
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( builtin_widgets: &[&str],
user_widgets: &HashMap<String, String>,
completion_widgets: &HashMap<String, String>,
) -> HashMap<String, String> {
let mut result = HashMap::new();
for &name in builtin_widgets {
result.insert(name.to_string(), "builtin".to_string());
}
for (name, func) in user_widgets {
result.insert(name.to_string(), format!("user:{}", func));
}
for (name, func) in completion_widgets {
result.insert(name.to_string(), format!("completion:{}", func));
}
result
}
pub fn scanpmwidgets<F>( builtin_widgets: &[&str],
user_widgets: &HashMap<String, String>,
completion_widgets: &HashMap<String, String>,
mut callback: F,
) where
F: FnMut(&str, &str),
{
for &name in builtin_widgets {
callback(name, "builtin");
}
for (name, func) in user_widgets {
callback(name, &format!("user:{}", func));
}
for (name, func) in completion_widgets {
callback(name, &format!("completion:{}", func));
}
}
pub fn keymapsgetfn(keymaps: &[&str]) -> Vec<String> { keymaps.iter().map(|s| s.to_string()).collect()
}
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::ported::zle::zle_main::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::ported::zle::zle_main::zle_test_setup();
let user = HashMap::new();
let comp = HashMap::new();
let widgets = getpmwidgets(&["accept-line", "backward-char"], &user, &comp);
assert_eq!(widgets.get("accept-line"), Some(&"builtin".to_string()));
assert_eq!(widgets.len(), 2);
}
#[test]
fn test_keymapsgetfn() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let keymaps = keymapsgetfn(DEFAULT_KEYMAPS);
assert!(keymaps.contains(&"emacs".to_string()));
assert!(keymaps.contains(&"vicmd".to_string()));
}
#[test]
fn test_builtin_widget_count() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert!(BUILTIN_WIDGETS.len() > 150);
}
#[test]
fn widgetstr_user_form_carries_function_name_after_colon() {
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 s = widgetstr("foo", true, true);
assert!(s.starts_with("completion:"),
"is_completion must dominate is_user, got: {}", s);
}
#[test]
fn getpmwidgets_user_overrides_builtin_on_name_collision() {
let mut user = HashMap::new();
user.insert("accept-line".to_string(), "my-fn".to_string());
let comp = HashMap::new();
let widgets = getpmwidgets(&["accept-line", "backward-char"], &user, &comp);
assert_eq!(widgets.get("accept-line"), Some(&"user:my-fn".to_string()),
"user widget must override builtin of same name");
assert_eq!(widgets.get("backward-char"), Some(&"builtin".to_string()));
}
#[test]
fn scanpmwidgets_callback_fires_for_every_bucket() {
let mut user = HashMap::new();
user.insert("u-widget".to_string(), "u-fn".to_string());
let mut comp = HashMap::new();
comp.insert("c-widget".to_string(), "c-fn".to_string());
let mut seen: Vec<(String, String)> = Vec::new();
scanpmwidgets(&["b-widget"], &user, &comp, |n, t| {
seen.push((n.to_string(), t.to_string()));
});
let names: std::collections::HashSet<_> = seen.iter().map(|(n, _)| n.clone()).collect();
assert!(names.contains("b-widget"));
assert!(names.contains("u-widget"));
assert!(names.contains("c-widget"));
let types: std::collections::HashSet<_> = seen.iter().map(|(_, t)| t.clone()).collect();
assert!(types.contains("builtin"));
assert!(types.iter().any(|t| t.starts_with("user:")));
assert!(types.iter().any(|t| t.starts_with("completion:")));
}
#[test]
fn keymapsgetfn_returns_independent_copies() {
let input: &[&str] = &["a", "b", "c"];
let mut out = keymapsgetfn(input);
out.push("d".to_string());
assert_eq!(input.len(), 3);
assert_eq!(out.len(), 4);
}
#[test]
fn builtin_widgets_has_no_duplicates() {
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() {
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() {
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() {
assert_eq!(setup_(), 0);
assert_eq!(boot_(), 0);
assert_eq!(cleanup_(), 0);
assert_eq!(finish_(), 0);
}
}