use std::sync::atomic::Ordering;
use std::sync::Mutex;
use crate::ported::params::{setiparam, setsparam};
use crate::ported::zle::zle_h::{WidgetImpl, MOD_MULT, MOD_NEG, MOD_TMULT};
use crate::ported::zle::zle_hist::{ISEARCH_ACTIVE, ISEARCH_ENDPOS, ISEARCH_STARTPOS};
use crate::ported::zle::zle_keymap::{addkeybuf, freekeynode, KeyBinding};
use crate::ported::zle::zle_main::{zleaftertrap, zlebeforetrap, ZLECONTEXT};
use crate::ported::zle::zle_misc::{
POSTDISPLAY, PREDISPLAY, PREVIOUS_ABORTED_SEARCH, PREVIOUS_SEARCH, SUFFIXLEN,
};
use crate::ported::zle::zle_thingy::Thingy;
#[allow(unused_imports)]
use crate::ported::zle::{
deltochar::*, textobjects::*, zle_h::*, zle_hist::*, zle_main::*, zle_misc::*, zle_move::*,
zle_refresh::*, zle_tricky::*, zle_utils::*, zle_vi::*, zle_word::*,
};
use crate::ported::zsh_h::{
hashnode, param, ParamScanFunc, PM_ARRAY, PM_HASHED, PM_INTEGER, PM_LOCAL, PM_READONLY,
PM_REMOVABLE, PM_SCALAR, PM_SPECIAL, PM_UNSET, ZLCON_LINE_CONT, ZLCON_LINE_START, ZLCON_SELECT,
ZLCON_VARED,
};
macro_rules! zleparams_table {
($($name:literal => $ty:expr),* $(,)?) => {
pub const ZLEPARAM_NAMES: &[&str] = &[$($name),*];
pub const ZLEPARAM_TABLE: &[(&str, u32)] = &[$(($name, $ty)),*];
};
}
zleparams_table! {
"BUFFER" => PM_SCALAR, "BUFFERLINES" => PM_INTEGER | PM_READONLY, "CONTEXT" => PM_SCALAR | PM_READONLY, "CURSOR" => PM_INTEGER, "CUTBUFFER" => PM_SCALAR, "HISTNO" => PM_INTEGER, "KEYMAP" => PM_SCALAR | PM_READONLY, "KEYS" => PM_SCALAR | PM_READONLY, "KEYS_QUEUED_COUNT" => PM_INTEGER | PM_READONLY, "killring" => PM_ARRAY, "LASTABORTEDSEARCH" => PM_SCALAR | PM_READONLY, "LASTSEARCH" => PM_SCALAR | PM_READONLY, "LASTWIDGET" => PM_SCALAR | PM_READONLY, "LBUFFER" => PM_SCALAR, "MARK" => PM_INTEGER, "NUMERIC" => PM_INTEGER | PM_UNSET, "PENDING" => PM_INTEGER | PM_READONLY, "POSTDISPLAY" => PM_SCALAR, "PREBUFFER" => PM_SCALAR | PM_READONLY, "PREDISPLAY" => PM_SCALAR, "RBUFFER" => PM_SCALAR, "REGION_ACTIVE" => PM_INTEGER, "region_highlight" => PM_ARRAY, "UNDO_CHANGE_NO" => PM_INTEGER | PM_READONLY, "UNDO_LIMIT_NO" => PM_INTEGER, "WIDGET" => PM_SCALAR | PM_READONLY, "WIDGETFUNC" => PM_SCALAR | PM_READONLY, "WIDGETSTYLE" => PM_SCALAR | PM_READONLY, "YANK_START" => PM_INTEGER, "YANK_END" => PM_INTEGER, "YANK_ACTIVE" => PM_INTEGER | PM_READONLY, "ISEARCHMATCH_START" => PM_INTEGER, "ISEARCHMATCH_END" => PM_INTEGER, "ISEARCHMATCH_ACTIVE" => PM_INTEGER | PM_READONLY, "SUFFIX_START" => PM_INTEGER, "SUFFIX_END" => PM_INTEGER, "SUFFIX_ACTIVE" => PM_INTEGER | PM_READONLY, "ZLE_RECURSIVE" => PM_INTEGER | PM_READONLY, "ZLE_STATE" => PM_SCALAR | PM_READONLY, "registers" => PM_HASHED,
}
const ZLEPARAM_READONLY_EXEMPT: &[&str] = &["registers"];
#[allow(unused_imports)]
pub fn makezleparams(ro: i32) {
let level = zleparams_level(ro);
zleparams_unlock();
zleparams_shadow(level);
let line = get_buffer(); let lbuf = get_lbuffer(); let rbuf = get_rbuffer(); let cs = get_cursor();
let _ = setsparam("BUFFER", &line); let _ = setsparam("LBUFFER", &lbuf); let _ = setsparam("RBUFFER", &rbuf); let _ = setiparam("CURSOR", cs as i64); let _ = setiparam("NUMERIC", get_numeric() as i64); let _ = setsparam("KEYMAP", &super::zle_params::get_keymap()); let lines = line.chars().filter(|c| *c == '\n').count() as i64 + 1;
let _ = setiparam("BUFFERLINES", lines); let keys_bytes = get_keys(); let mut keys_unmeta = keys_bytes.clone();
crate::ported::utils::unmetafy(&mut keys_unmeta);
let _ = setsparam("KEYS", &String::from_utf8_lossy(&keys_unmeta)); let _ = setsparam("WIDGET", &get_widget()); let _ = setsparam("LASTWIDGET", &get_lwidget()); let _ = setsparam("WIDGETFUNC", &get_widgetfunc()); let _ = setsparam("WIDGETSTYLE", &get_widgetstyle()); let _ = setiparam("HISTNO", get_histno()); let _ = setsparam("CONTEXT", get_context()); let _ = setiparam("PENDING", get_pending() as i64);
let predisp = get_predisplay(); let postdisp = get_postdisplay(); let rh = crate::ported::zle::zle_refresh::get_region_highlight(
&crate::ported::zsh_h::param::default(),
);
let _ = setsparam("PREDISPLAY", &predisp);
let _ = setsparam("POSTDISPLAY", &postdisp);
let _ = crate::ported::params::setaparam("region_highlight", rh.clone());
let mark = get_mark() as i64; let region_active = get_region_active(); let cutbuffer = get_cutbuffer(); let killring_v = get_killring(); let _ = setiparam("MARK", mark);
let _ = setiparam("REGION_ACTIVE", region_active);
let _ = setsparam("CUTBUFFER", &cutbuffer);
let _ = crate::ported::params::setaparam("killring", killring_v.clone());
let _ = setsparam("PREBUFFER", &get_prebuffer()); let _ = setiparam("KEYS_QUEUED_COUNT", get_keys_queued_count()); let _ = setiparam("YANK_START", get_yankstart()); let _ = setiparam("YANK_END", get_yankend()); let _ = setiparam("YANK_ACTIVE", get_yankactive()); let _ = setiparam("ISEARCHMATCH_START", get_isearchmatchstart()); let _ = setiparam("ISEARCHMATCH_END", get_isearchmatchend()); let _ = setiparam("ISEARCHMATCH_ACTIVE", get_isearchmatchactive()); let _ = setiparam("SUFFIX_START", get_suffixstart()); let _ = setiparam("SUFFIX_END", get_suffixend()); let _ = setiparam("SUFFIX_ACTIVE", get_suffixactive()); let _ = setiparam("ZLE_RECURSIVE", get_recursive()); let _ = setsparam("ZLE_STATE", &get_zle_state()); let _ = setiparam(
"UNDO_CHANGE_NO",
crate::ported::zle::zle_main::UNDO_CHANGENO.load(Ordering::SeqCst) as i64,
); let _ = setiparam(
"UNDO_LIMIT_NO",
crate::ported::zle::zle_main::UNDO_LIMITNO.load(Ordering::SeqCst) as i64,
); let _ = setsparam("LASTABORTEDSEARCH", &get_lasearch()); let _ = setsparam("LASTSEARCH", &get_lsearch());
let mut registers_v: Vec<String> = Vec::with_capacity(72);
let mut reg_ch: u8 = b'a'; for _ in 0..36usize {
let nam = (reg_ch as char).to_string(); let val = get_registers(&nam).unwrap_or_default(); registers_v.push(nam);
registers_v.push(val);
reg_ch = if reg_ch == b'z' { b'0' } else { reg_ch + 1 }; }
let _ = crate::ported::params::sethparam("registers", registers_v.clone());
zleparams_stamp(ro, level);
if ro != 0 {
return;
}
crate::zle_param_sync::arm_snapshot(
line,
lbuf,
rbuf,
cs as i64,
predisp,
postdisp,
rh,
mark,
region_active,
cutbuffer,
killring_v,
registers_v,
);
fn zleparams_level(ro: i32) -> i32 {
let ll = crate::ported::params::locallevel.load(Ordering::Relaxed);
if ro != 0 {
return ll + 1; }
let installed = crate::ported::params::paramtab()
.read()
.ok()
.and_then(|t| t.get("BUFFER").map(|p| (p.level, p.node.flags as u32)));
let stamp = (PM_SPECIAL | PM_REMOVABLE) as u32;
if let Some((lvl, flags)) = installed {
if lvl > 0 && lvl <= ll && flags & stamp == stamp {
return lvl;
}
}
if crate::zle_param_sync::active() {
let remembered = WIDGET_PARAM_LEVEL.load(Ordering::Relaxed);
if remembered > 0 && remembered <= ll {
return remembered;
}
}
WIDGET_PARAM_LEVEL.store(ll, Ordering::Relaxed);
ll
}
fn zleparams_unlock() {
let stamp = (PM_SPECIAL | PM_REMOVABLE) as u32;
let Ok(mut tab) = crate::ported::params::paramtab().write() else {
return;
};
for (name, _) in ZLEPARAM_TABLE {
if let Some(pm) = tab.get_mut(*name) {
if pm.level > 0 && pm.node.flags as u32 & stamp == stamp {
pm.node.flags &= !((PM_READONLY | PM_UNSET) as i32);
}
}
}
}
fn zleparams_shadow(level: i32) {
let stamp = (PM_SPECIAL | PM_REMOVABLE) as u32;
let foreign: Vec<(&str, u32)> = {
let Ok(tab) = crate::ported::params::paramtab().read() else {
return;
};
ZLEPARAM_TABLE
.iter()
.filter(|(name, _)| {
tab.get(*name)
.is_some_and(|pm| pm.level < level && pm.node.flags as u32 & stamp != stamp)
})
.map(|(name, ty)| (*name, *ty))
.collect()
};
for (name, ty) in foreign {
let ty = crate::ported::zsh_h::PM_TYPE(ty);
crate::ported::params::createparam(name, (ty | PM_LOCAL) as i32);
}
}
fn zleparams_stamp(ro: i32, level: i32) {
let numeric_given = {
let zmod = crate::ported::zle::zle_main::ZMOD.lock().unwrap();
zmod.flags & (MOD_MULT | MOD_TMULT) != 0
};
let Ok(mut tab) = crate::ported::params::paramtab().write() else {
return;
};
for (name, ty) in ZLEPARAM_TABLE {
let Some(pm) = tab.get_mut(*name) else {
continue; };
pm.level = level; pm.node.flags |= (PM_SPECIAL | PM_REMOVABLE) as i32; if (ty & PM_READONLY != 0 || ro != 0) && !ZLEPARAM_READONLY_EXEMPT.contains(name) {
pm.node.flags |= PM_READONLY as i32;
}
if ty & PM_UNSET != 0 && !numeric_given {
pm.node.flags |= PM_UNSET as i32;
}
}
}
}
static WIDGET_PARAM_LEVEL: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub fn zleunsetfn(pm: &mut param, exp: i32) {
crate::ported::params::stdunsetfn(pm, exp); }
pub fn set_buffer(s: &str) {
*ZLELINE.lock().unwrap() = s.chars().collect();
ZLELL.store(ZLELINE.lock().unwrap().len(), Ordering::SeqCst);
ZLECS.store(
ZLECS
.load(Ordering::SeqCst)
.min(ZLELL.load(Ordering::SeqCst)),
Ordering::SeqCst,
);
ZLE_RESET_NEEDED.store(1, Ordering::SeqCst);
}
pub fn get_buffer() -> String {
ZLELINE.lock().unwrap().iter().collect()
}
pub fn set_cursor(pos: usize) {
ZLECS.store(pos.min(ZLELL.load(Ordering::SeqCst)), Ordering::SeqCst);
ZLE_RESET_NEEDED.store(1, Ordering::SeqCst);
}
pub fn get_cursor() -> usize {
ZLECS.load(Ordering::SeqCst)
}
pub fn set_mark(pos: usize) {
MARK.store(pos.min(ZLELL.load(Ordering::SeqCst)), Ordering::SeqCst);
}
pub fn get_mark() -> usize {
MARK.load(Ordering::SeqCst)
}
pub fn set_region_active(
x: i64,
) {
REGION_ACTIVE.store(if x != 0 { 1 } else { 0 }, Ordering::SeqCst);
}
pub fn get_region_active() -> i64 {
REGION_ACTIVE.load(Ordering::SeqCst) as i64
}
pub fn set_lbuffer(s: &str) {
let rbuf: String = ZLELINE.lock().unwrap()[ZLECS.load(Ordering::SeqCst)..]
.iter()
.collect();
*ZLELINE.lock().unwrap() = s.chars().chain(rbuf.chars()).collect();
ZLELL.store(ZLELINE.lock().unwrap().len(), Ordering::SeqCst);
ZLECS.store(s.chars().count(), Ordering::SeqCst);
ZLE_RESET_NEEDED.store(1, Ordering::SeqCst);
}
pub fn get_lbuffer() -> String {
ZLELINE.lock().unwrap()[..ZLECS.load(Ordering::SeqCst)]
.iter()
.collect()
}
pub fn set_rbuffer(s: &str) {
let lbuf: String = ZLELINE.lock().unwrap()[..ZLECS.load(Ordering::SeqCst)]
.iter()
.collect();
*ZLELINE.lock().unwrap() = lbuf.chars().chain(s.chars()).collect();
ZLELL.store(ZLELINE.lock().unwrap().len(), Ordering::SeqCst);
ZLE_RESET_NEEDED.store(1, Ordering::SeqCst);
}
pub fn get_rbuffer() -> String {
ZLELINE.lock().unwrap()[ZLECS.load(Ordering::SeqCst)..]
.iter()
.collect()
}
pub fn get_prebuffer() -> String {
String::new()
}
pub fn get_widget() -> String {
BINDK
.lock()
.unwrap()
.as_ref()
.map(|t| t.nam.clone())
.unwrap_or_default()
}
pub fn get_widgetfunc() -> String {
let bindk_guard = BINDK.lock().unwrap();
let Some(t) = bindk_guard.as_ref() else {
return String::new();
};
let Some(w) = t.widget.as_ref() else {
return String::new();
};
if (w.flags & WIDGET_INT) != 0 {
return ".internal".to_string();
}
match &w.u {
WidgetImpl::UserFunc(name) => name.clone(),
WidgetImpl::Internal(_) => ".internal".to_string(),
_ => ".internal".to_string(),
}
}
pub fn get_widgetstyle() -> String {
let bindk_guard = BINDK.lock().unwrap();
let Some(t) = bindk_guard.as_ref() else {
return String::new();
};
let Some(w) = t.widget.as_ref() else {
return String::new();
};
if (w.flags & WIDGET_INT) != 0 {
return ".internal".to_string();
}
String::new() }
pub fn get_lwidget() -> String {
LBINDK
.lock()
.unwrap()
.as_ref()
.map(|t| t.nam.clone())
.unwrap_or_default()
}
pub fn get_keymap() -> String {
crate::ported::zle::zle_keymap::curkeymapname().clone()
}
pub fn get_keys() -> Vec<u8> {
crate::ported::zle::zle_keymap::keybuf
.lock()
.unwrap()
.clone()
}
pub fn get_keys_queued_count() -> i64 {
KUNGETBUF.lock().unwrap().len() as i64
}
pub fn set_numeric(x: i64) {
ZMOD.lock().unwrap().mult = x as i32;
ZMOD.lock().unwrap().flags = MOD_MULT;
}
pub fn get_numeric() -> i32 {
ZMOD.lock().unwrap().mult }
pub fn unset_numeric(exp: i32) {
if exp != 0 {
ZMOD.lock().unwrap().flags = 0; ZMOD.lock().unwrap().mult = 1; }
}
pub fn set_histno(x: i64) {
let idx = (x - 1).max(0) as usize;
if idx <= history().lock().unwrap().entries.len() {
history().lock().unwrap().cursor = idx;
}
}
pub fn get_histno() -> i64 {
history().lock().unwrap().cursor as i64 + 1
}
pub fn get_bufferlines() -> usize {
ZLELINE
.lock()
.unwrap()
.iter()
.filter(|&&c| c == '\n')
.count()
+ 1
}
pub fn get_pending() -> usize {
0 }
pub fn get_recursive() -> i64 {
ZLE_RECURSIVE.load(Ordering::SeqCst) as i64
}
pub fn get_yankstart() -> i64 {
YANKB.load(Ordering::SeqCst) as i64
}
pub fn get_yankend() -> i64 {
YANKE.load(Ordering::SeqCst) as i64
}
pub fn get_yankactive() -> i64 {
let last = LASTCMD.load(Ordering::SeqCst) as i32;
let yank = ((last & ZLE_YANK) != 0) as i64;
let yankafter = ((last & ZLE_YANKAFTER) != 0) as i64;
yank + yankafter
}
pub fn set_yankstart(i: i64) {
YANKB.store(i.max(0) as usize, Ordering::SeqCst);
}
pub fn set_yankend(i: i64) {
YANKE.store(i.max(0) as usize, Ordering::SeqCst);
}
pub fn get_isearchmatchstart() -> i64 {
ISEARCH_STARTPOS.load(Ordering::Relaxed) as i64
}
pub fn get_isearchmatchend() -> i64 {
ISEARCH_ENDPOS.load(Ordering::Relaxed) as i64 }
pub fn get_isearchmatchactive() -> i64 {
ISEARCH_ACTIVE.load(Ordering::Relaxed) as i64 }
pub fn get_suffixstart() -> i64 {
let sfx_len = SUFFIXLEN.load(Ordering::Relaxed);
(ZLECS.load(Ordering::SeqCst) as i64) - (sfx_len as i64) }
pub fn get_suffixend() -> i64 {
ZLECS.load(Ordering::SeqCst) as i64
}
pub fn get_suffixactive() -> i64 {
SUFFIXLEN.load(Ordering::Relaxed) as i64 }
pub fn get_cutbuffer() -> String {
KILLRING
.lock()
.unwrap()
.front()
.map(|v| v.iter().collect())
.unwrap_or_default()
}
pub fn set_cutbuffer(s: &str) {
let chars: Vec<char> = s.chars().collect();
if KILLRING.lock().unwrap().is_empty() {
KILLRING.lock().unwrap().push_front(chars);
} else {
KILLRING.lock().unwrap()[0] = chars;
}
}
pub fn unset_cutbuffer(exp: i32) {
if exp != 0 {
KILLRING.lock().unwrap().pop_front();
}
}
pub fn set_killring(x: Option<&[String]>) {
KILLRING.lock().unwrap().clear();
if let Some(arr) = x {
for entry in arr {
KILLRING.lock().unwrap().push_back(entry.chars().collect());
}
}
}
pub fn get_killring() -> Vec<String> {
KILLRING
.lock()
.unwrap()
.iter()
.map(|entry| entry.iter().collect::<String>())
.collect()
}
pub fn unset_killring(exp: i32) {
if exp != 0 {
set_killring(None);
}
}
pub fn set_register(name: char, value: &str) -> i32 {
let idx: i32 = if ('0'..='9').contains(&name) {
name as i32 - b'0' as i32 + 26
} else if ('a'..='z').contains(&name) {
name as i32 - b'a' as i32
} else {
return 1;
};
if (idx as usize) < vibuf().lock().unwrap().len() {
let mut vb = vibuf().lock().unwrap();
let slot = &mut vb[idx as usize];
slot.buf = value.to_string();
slot.len = value.chars().count(); }
0
}
pub fn unset_register(name: char, _exp: i32) {
let _ = set_register(name, "");
}
pub fn scan_registers(_ht: i32, func: Option<ParamScanFunc>, flags: i32) {
let func = match func {
Some(f) => f,
None => return,
};
let buf = vibuf().lock().unwrap().clone(); let mut ch: u8 = b'a'; for i in 0..36usize {
let val: String = buf.get(i).map(|v| v.buf.clone()).unwrap_or_default(); let pm = param {
node: hashnode {
next: None,
nam: format!("{}", ch as char), flags: (PM_SCALAR | PM_READONLY) as i32, },
u_data: 0,
u_tied: None,
u_arr: None,
u_str: Some(val), 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,
};
func(&pm, flags); ch = if ch == b'z' { b'0' } else { ch + 1 }; }
}
pub fn get_registers(name: &str) -> Option<String> {
let bytes = name.as_bytes();
if bytes.len() != 1 {
return None;
}
let c = bytes[0];
let idx: i32 = if c.is_ascii_digit() {
(c - b'0') as i32 + 26
} else if c.is_ascii_lowercase() {
(c - b'a') as i32
} else {
return None; };
if (idx as usize) < vibuf().lock().unwrap().len() {
Some(vibuf().lock().unwrap()[idx as usize].buf.clone())
} else {
None
}
}
pub fn set_registers(
map: &std::collections::HashMap<String, String>,
) {
for (name, value) in map {
if let Some(ch) = name.chars().next() {
let _ = set_register(ch, value);
}
}
}
pub fn unset_registers(exp: i32) {
if exp != 0 {
for buf in vibuf().lock().unwrap().iter_mut() {
*buf = crate::ported::zle::zle_h::cutbuffer::default();
}
}
}
pub fn set_prepost(textvar: &mut String, lenvar: &mut usize, x: Option<&str>) {
if *lenvar != 0 {
textvar.clear();
*lenvar = 0;
}
if let Some(s) = x {
textvar.push_str(s);
*lenvar = s.chars().count();
}
}
pub fn get_prepost(text: &str, len: usize) -> String {
text.chars().take(len).collect()
}
pub fn set_predisplay(x: Option<&str>) {
let mut buf = PREDISPLAY
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap(); let mut len = buf.chars().count();
set_prepost(&mut buf, &mut len, x); }
pub fn get_predisplay() -> String {
PREDISPLAY
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.clone()
}
pub fn set_postdisplay(x: Option<&str>) {
let mut buf = POSTDISPLAY
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap(); let mut len = buf.chars().count();
set_prepost(&mut buf, &mut len, x); }
pub fn get_postdisplay() -> String {
POSTDISPLAY
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.clone()
}
pub fn free_prepostdisplay() {
PREDISPLAY
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.clear();
POSTDISPLAY
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.clear();
}
pub fn get_lasearch() -> String {
PREVIOUS_ABORTED_SEARCH
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.clone()
}
pub fn get_lsearch() -> String {
PREVIOUS_SEARCH
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.clone()
}
pub fn get_context() -> &'static str {
match ZLECONTEXT.load(Ordering::SeqCst) {
x if x == ZLCON_LINE_CONT => "cont", x if x == ZLCON_SELECT => "select", x if x == ZLCON_VARED => "vared", _ => "line", }
}
pub fn get_zle_state() -> String {
use crate::ported::zsh_h::HIST_FOREIGN;
let insmode_str = if INSMODE.load(Ordering::SeqCst) != 0 {
"insert" } else {
"overwrite" };
let hist_str =
if (crate::ported::hist::hist_skip_flags.load(Ordering::SeqCst) & HIST_FOREIGN as i32) != 0
{
"localhistory" } else {
"globalhistory" };
let mut parts = vec![insmode_str.to_string(), hist_str.to_string()];
parts.sort();
parts.join(" ")
}
pub fn is_insert_mode() -> bool {
(INSMODE.load(Ordering::SeqCst) != 0)
}
pub fn is_region_active() -> bool {
REGION_ACTIVE.load(Ordering::SeqCst) != 0
}
#[cfg(test)]
mod region_active_tests {
use super::*;
#[test]
fn get_region_active_reads_field() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
REGION_ACTIVE.store(0, Ordering::SeqCst);
assert_eq!(get_region_active(), 0);
REGION_ACTIVE.store(1, Ordering::SeqCst);
assert_eq!(get_region_active(), 1);
REGION_ACTIVE.store(2, Ordering::SeqCst);
assert_eq!(get_region_active(), 2);
}
#[test]
fn set_region_active_double_bang_idiom() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
set_region_active(0);
assert_eq!(REGION_ACTIVE.load(Ordering::SeqCst), 0);
set_region_active(1);
assert_eq!(REGION_ACTIVE.load(Ordering::SeqCst), 1);
set_region_active(99);
assert_eq!(REGION_ACTIVE.load(Ordering::SeqCst), 1);
set_region_active(-1);
assert_eq!(REGION_ACTIVE.load(Ordering::SeqCst), 1);
set_region_active(0);
assert_eq!(REGION_ACTIVE.load(Ordering::SeqCst), 0);
}
}
#[cfg(test)]
mod trap_tests {
use crate::zle::zle_main::{zle_test_setup, zleaftertrap, zlebeforetrap};
#[test]
fn zlebeforetrap_returns_zero() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert_eq!(zlebeforetrap(std::ptr::null_mut(), std::ptr::null_mut()), 0);
}
#[test]
fn zleaftertrap_returns_zero() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert_eq!(zleaftertrap(std::ptr::null_mut(), std::ptr::null_mut()), 0);
}
}
#[cfg(test)]
mod numeric_tests {
use super::*;
#[test]
fn set_numeric_sets_mult_and_replaces_flags() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
ZMOD.lock().unwrap().flags |= MOD_TMULT | MOD_NEG;
ZMOD.lock().unwrap().mult = 99;
set_numeric(7);
assert_eq!(ZMOD.lock().unwrap().mult, 7);
assert_ne!(ZMOD.lock().unwrap().flags & MOD_MULT, 0);
assert_eq!(ZMOD.lock().unwrap().flags & MOD_TMULT, 0);
assert_eq!(ZMOD.lock().unwrap().flags & MOD_NEG, 0);
}
#[test]
fn unset_numeric_resets_when_exp_nonzero() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
ZMOD.lock().unwrap().flags |= MOD_MULT;
ZMOD.lock().unwrap().mult = 5;
unset_numeric(1);
assert_eq!(ZMOD.lock().unwrap().mult, 1);
assert_eq!(ZMOD.lock().unwrap().flags, 0);
}
#[test]
fn unset_numeric_noop_when_exp_zero() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
ZMOD.lock().unwrap().flags |= MOD_MULT;
ZMOD.lock().unwrap().mult = 5;
unset_numeric(0);
assert_eq!(ZMOD.lock().unwrap().mult, 5);
assert_ne!(ZMOD.lock().unwrap().flags & MOD_MULT, 0);
}
}
#[cfg(test)]
mod suffix_tests {
use super::*;
#[test]
fn get_suffixactive_reads_suffixlen() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
SUFFIXLEN.store(7, Ordering::SeqCst);
assert_eq!(get_suffixactive(), 7);
SUFFIXLEN.store(0, Ordering::SeqCst);
assert_eq!(get_suffixactive(), 0);
}
#[test]
fn get_suffixend_reads_zlecs() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
ZLECS.store(11, Ordering::SeqCst);
assert_eq!(get_suffixend(), 11);
}
#[test]
fn get_suffixstart_subtracts_suffixlen() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
ZLECS.store(20, Ordering::SeqCst);
SUFFIXLEN.store(5, Ordering::SeqCst);
assert_eq!(get_suffixstart(), 15);
SUFFIXLEN.store(0, Ordering::SeqCst);
assert_eq!(get_suffixstart(), 20);
}
}
#[cfg(test)]
mod widget_tests {
use super::*;
#[test]
fn get_widget_reads_bindk_nam() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*BINDK.lock().unwrap() = Some(Thingy::new("self-insert"));
assert_eq!(get_widget(), "self-insert");
}
#[test]
fn get_widget_empty_when_no_bindk() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
assert_eq!(get_widget(), "");
}
#[test]
fn get_lwidget_reads_lbindk_nam() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*LBINDK.lock().unwrap() = Some(Thingy::new("forward-char"));
assert_eq!(get_lwidget(), "forward-char");
}
#[test]
fn get_lwidget_empty_when_no_lbindk() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert_eq!(get_lwidget(), "");
}
#[test]
fn get_recursive_reads_zle_recursive_field() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
ZLE_RECURSIVE.store(0, Ordering::SeqCst);
assert_eq!(get_recursive(), 0);
ZLE_RECURSIVE.store(5, Ordering::SeqCst);
assert_eq!(get_recursive(), 5);
}
}
#[cfg(test)]
mod isearch_tests {
use super::*;
#[test]
fn get_isearchmatchactive_reads_global() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
ISEARCH_ACTIVE.store(0, Ordering::SeqCst);
assert_eq!(get_isearchmatchactive(), 0);
ISEARCH_ACTIVE.store(1, Ordering::SeqCst);
assert_eq!(get_isearchmatchactive(), 1);
ISEARCH_ACTIVE.store(0, Ordering::SeqCst);
}
#[test]
fn get_isearchmatchstart_reads_global() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
ISEARCH_STARTPOS.store(7, Ordering::SeqCst);
assert_eq!(get_isearchmatchstart(), 7);
ISEARCH_STARTPOS.store(0, Ordering::SeqCst);
}
#[test]
fn get_isearchmatchend_reads_global() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
ISEARCH_ENDPOS.store(13, Ordering::SeqCst);
assert_eq!(get_isearchmatchend(), 13);
ISEARCH_ENDPOS.store(0, Ordering::SeqCst);
}
}
#[cfg(test)]
mod batch_getters_tests {
use super::*;
#[test]
fn get_histno_reads_history_cursor() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
history().lock().unwrap().cursor = 7;
assert_eq!(get_histno(), 8);
}
#[test]
fn get_keys_returns_keybuf_clone() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
*crate::ported::zle::zle_keymap::keybuf.lock().unwrap() = vec![0x1b, b'a'];
assert_eq!(get_keys(), vec![0x1b, b'a']);
}
#[test]
fn get_keys_queued_count_returns_unget_len() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
KUNGETBUF.lock().unwrap().push_back(b'a');
KUNGETBUF.lock().unwrap().push_back(b'b');
KUNGETBUF.lock().unwrap().push_back(b'c');
assert_eq!(get_keys_queued_count(), 3);
}
#[test]
fn get_yankstart_yankend_read_fields() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
YANKB.store(3, Ordering::SeqCst);
YANKE.store(8, Ordering::SeqCst);
assert_eq!(get_yankstart(), 3);
assert_eq!(get_yankend(), 8);
}
#[test]
fn set_yankstart_yankend_write_fields() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_yankstart(5);
set_yankend(11);
assert_eq!(YANKB.load(Ordering::SeqCst), 5);
assert_eq!(YANKE.load(Ordering::SeqCst), 11);
}
}
#[cfg(test)]
mod keybuf_tests {
use crate::zle::zle_keymap::{addkeybuf, freekeynode, KeyBinding};
use crate::zle::zle_main::zle_test_setup;
#[test]
fn addkeybuf_plain_byte() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let _tg = crate::ported::ztype_h::TYPTAB_TEST_LOCK
.lock()
.unwrap_or_else(|e| e.into_inner());
crate::ported::utils::inittyptab();
crate::ported::zle::zle_keymap::keybuf
.lock()
.unwrap()
.clear();
addkeybuf(b'a' as i32);
assert_eq!(
*crate::ported::zle::zle_keymap::keybuf.lock().unwrap(),
vec![b'a']
);
}
#[test]
fn addkeybuf_meta_quoted() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let _tg = crate::ported::ztype_h::TYPTAB_TEST_LOCK
.lock()
.unwrap_or_else(|e| e.into_inner());
crate::ported::utils::inittyptab();
crate::ported::zle::zle_keymap::keybuf
.lock()
.unwrap()
.clear();
addkeybuf(0xa0);
assert_eq!(
*crate::ported::zle::zle_keymap::keybuf.lock().unwrap(),
vec![0x83, 0x80]
);
}
#[test]
fn freekeynode_consumes_binding() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let kb = KeyBinding {
bind: None,
str: Some("send-string".to_string()),
prefixct: 0,
};
freekeynode(kb);
}
}
#[cfg(test)]
mod display_tests {
use super::*;
#[test]
fn get_set_predisplay_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_predisplay(Some("[hint] "));
assert_eq!(get_predisplay(), "[hint] ");
set_predisplay(None);
assert_eq!(get_predisplay(), "");
}
#[test]
fn get_set_postdisplay_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_postdisplay(Some("trailer"));
assert_eq!(get_postdisplay(), "trailer");
set_postdisplay(None);
assert_eq!(get_postdisplay(), "");
}
#[test]
fn free_prepostdisplay_clears_both() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_predisplay(Some("a"));
set_postdisplay(Some("b"));
free_prepostdisplay();
assert_eq!(get_predisplay(), "");
assert_eq!(get_postdisplay(), "");
}
#[test]
fn get_context_branches() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
ZLECONTEXT.store(ZLCON_LINE_START, Ordering::SeqCst);
assert_eq!(get_context(), "line");
ZLECONTEXT.store(ZLCON_LINE_CONT, Ordering::SeqCst);
assert_eq!(get_context(), "cont");
ZLECONTEXT.store(ZLCON_SELECT, Ordering::SeqCst);
assert_eq!(get_context(), "select");
ZLECONTEXT.store(ZLCON_VARED, Ordering::SeqCst);
assert_eq!(get_context(), "vared");
}
#[test]
fn get_lasearch_lsearch_default_empty() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
PREVIOUS_ABORTED_SEARCH
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.clear();
PREVIOUS_SEARCH
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.clear();
assert_eq!(get_lasearch(), "");
assert_eq!(get_lsearch(), "");
}
#[test]
fn get_prepost_truncates_to_len() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert_eq!(get_prepost("abcdef", 3), "abc");
assert_eq!(get_prepost("xyz", 99), "xyz"); }
#[test]
fn set_prepost_writes_and_clears() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut text = String::new();
let mut len = 0;
set_prepost(&mut text, &mut len, Some("hello"));
assert_eq!(text, "hello");
assert_eq!(len, 5);
set_prepost(&mut text, &mut len, None);
assert_eq!(text, "");
assert_eq!(len, 0);
}
}
#[cfg(test)]
mod widget_killring_tests {
use super::*;
#[test]
fn set_get_register_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_register('a', "hello");
let s: String = vibuf().lock().unwrap()[0].buf.clone();
assert_eq!(s, "hello");
assert_eq!(get_registers("a"), Some("hello".to_string()));
}
#[test]
fn set_register_digit_uses_offset_26() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_register('0', "zero");
let s: String = vibuf().lock().unwrap()[26].buf.clone();
assert_eq!(s, "zero");
assert_eq!(get_registers("0"), Some("zero".to_string()));
}
#[test]
fn set_register_invalid_returns_one() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert_eq!(set_register('!', "x"), 1);
}
#[test]
fn unset_register_clears_buffer() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_register('a', "hi");
unset_register('a', 1);
assert_eq!(get_registers("a"), Some(String::new()));
}
#[test]
fn set_get_killring_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let entries = vec!["first".to_string(), "second".to_string()];
set_killring(Some(&entries));
let got = get_killring();
assert_eq!(got, vec!["first".to_string(), "second".to_string()]);
}
#[test]
fn unset_killring_clears_when_exp_nonzero() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let entries = vec!["x".to_string()];
set_killring(Some(&entries));
unset_killring(1);
assert!(get_killring().is_empty());
}
#[test]
fn set_histno_clamps_to_entries_len() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
history().lock().unwrap().entries.push(HistEntry {
line: "ls".to_string(),
num: 1,
time: None,
});
history().lock().unwrap().entries.push(HistEntry {
line: "cd".to_string(),
num: 2,
time: None,
});
set_histno(1);
assert_eq!(history().lock().unwrap().cursor, 0);
set_histno(2);
assert_eq!(history().lock().unwrap().cursor, 1);
history().lock().unwrap().cursor = 7;
set_histno(99);
assert_eq!(history().lock().unwrap().cursor, 7);
}
#[test]
fn zle_params_corpus_buffer_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_buffer("hello");
assert_eq!(get_buffer(), "hello");
}
#[test]
fn zle_params_corpus_buffer_set_empty() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_buffer("anything");
set_buffer("");
assert_eq!(get_buffer(), "");
assert_eq!(ZLELL.load(Ordering::SeqCst), 0);
}
#[test]
fn zle_params_corpus_cursor_clamps_to_buffer_length() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_buffer("abc"); set_cursor(99);
assert_eq!(get_cursor(), 3, "cursor clamped to buf len");
}
#[test]
fn zle_params_corpus_cursor_within_buffer() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_buffer("abc");
set_cursor(2);
assert_eq!(get_cursor(), 2);
}
#[test]
fn zle_params_corpus_mark_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_buffer("abcdef");
set_mark(2);
assert_eq!(get_mark(), 2);
}
#[test]
fn zle_params_corpus_region_active_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_region_active(1);
assert_eq!(get_region_active(), 1);
set_region_active(0);
assert_eq!(get_region_active(), 0);
}
#[test]
fn zle_params_corpus_set_buffer_updates_zlell() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_buffer("ab");
assert_eq!(ZLELL.load(Ordering::SeqCst), 2);
set_buffer("abcdef");
assert_eq!(ZLELL.load(Ordering::SeqCst), 6);
}
#[test]
fn zle_params_corpus_get_buffer_empty_returns_empty() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
set_buffer("");
assert_eq!(get_buffer(), "");
}
#[test]
fn set_buffer_then_get_buffer_round_trips() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("hello");
assert_eq!(get_buffer(), "hello", "round-trip preserves string");
}
#[test]
fn set_cursor_then_get_cursor_round_trips() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("0123456789");
set_cursor(5);
assert_eq!(get_cursor(), 5);
}
#[test]
fn set_mark_then_get_mark_round_trips() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("0123456789");
set_mark(3);
assert_eq!(get_mark(), 3);
}
#[test]
fn set_lbuffer_writes_before_cursor() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("AFTER");
set_cursor(0);
set_lbuffer("BEFORE");
let lbuf = get_lbuffer();
assert_eq!(lbuf, "BEFORE");
}
#[test]
fn set_rbuffer_writes_after_cursor() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("HELLO");
set_cursor(5);
set_rbuffer("POST");
assert_eq!(get_rbuffer(), "POST");
}
#[test]
fn set_cursor_past_eol_clamps_to_buffer_length() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("abc");
set_cursor(99);
assert!(get_cursor() <= 3, "cursor clamps to buffer length");
}
#[test]
fn set_get_buffer_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("hello world");
assert_eq!(get_buffer(), "hello world");
}
#[test]
fn set_buffer_updates_zlell() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("12345");
assert_eq!(
ZLELL.load(Ordering::SeqCst),
5,
"ZLELL must reflect new length"
);
set_buffer("");
assert_eq!(ZLELL.load(Ordering::SeqCst), 0, "empty buffer → ZLELL=0");
}
#[test]
fn set_buffer_clamps_existing_cursor_to_new_length() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("hello world");
set_cursor(10);
set_buffer("abc");
assert!(get_cursor() <= 3, "cursor clamped after shrink");
}
#[test]
fn set_buffer_signals_reset_needed() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
ZLE_RESET_NEEDED.store(0, Ordering::SeqCst);
set_buffer("trigger");
assert_eq!(
ZLE_RESET_NEEDED.load(Ordering::SeqCst),
1,
"set_buffer must signal reset"
);
}
#[test]
fn set_cursor_within_buffer_stores_exact_value() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("hello");
set_cursor(2);
assert_eq!(get_cursor(), 2);
set_cursor(0);
assert_eq!(get_cursor(), 0);
set_cursor(5);
assert_eq!(get_cursor(), 5, "cursor at EOL exact");
}
#[test]
fn set_cursor_signals_reset_needed() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("hello");
ZLE_RESET_NEEDED.store(0, Ordering::SeqCst);
set_cursor(2);
assert_eq!(ZLE_RESET_NEEDED.load(Ordering::SeqCst), 1);
}
#[test]
fn set_mark_clamps_past_zlell() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("hi");
set_mark(99);
assert!(get_mark() <= 2, "mark must clamp to ZLELL=2");
}
#[test]
fn set_mark_within_buffer_stores_exact() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("abcdef");
set_mark(3);
assert_eq!(get_mark(), 3);
set_mark(0);
assert_eq!(get_mark(), 0);
}
#[test]
fn set_region_active_clears_on_zero() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_region_active(1);
assert_eq!(get_region_active(), 1);
set_region_active(0);
assert_eq!(get_region_active(), 0);
}
#[test]
fn set_region_active_coerces_nonzero_to_one() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_region_active(99);
assert_eq!(get_region_active(), 1, "99 → 1 (not 99)");
set_region_active(-5);
assert_eq!(get_region_active(), 1, "negative non-zero → 1");
set_region_active(i64::MAX);
assert_eq!(get_region_active(), 1);
}
#[test]
fn set_lbuffer_places_cursor_at_new_lbuffer_end() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("XYZworld");
set_cursor(3); set_lbuffer("PREFIX");
assert_eq!(get_lbuffer(), "PREFIX");
assert_eq!(get_rbuffer(), "world", "rbuffer preserved");
assert_eq!(get_cursor(), 6, "cursor at end of new lbuffer");
}
#[test]
fn set_rbuffer_preserves_cursor() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_buffer("hello world");
set_cursor(5);
set_rbuffer(" everyone");
assert_eq!(get_lbuffer(), "hello", "lbuffer preserved");
assert_eq!(get_cursor(), 5, "cursor unchanged");
}
#[test]
fn get_buffer_returns_string_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: String = get_buffer();
}
#[test]
fn get_cursor_returns_usize_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: usize = get_cursor();
}
#[test]
fn get_mark_returns_usize_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: usize = get_mark();
}
#[test]
fn get_region_active_in_canonical_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = get_region_active();
assert!(
(0..=2).contains(&r),
"region_active must be in {{0,1,2}}, got {}",
r
);
}
#[test]
fn get_widget_returns_string_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: String = get_widget();
}
#[test]
fn get_widgetfunc_returns_string_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: String = get_widgetfunc();
}
#[test]
fn get_keymap_returns_string_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: String = get_keymap();
}
#[test]
fn get_keys_returns_vec_u8_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: Vec<u8> = get_keys();
}
#[test]
fn get_keys_queued_count_non_negative() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let n = get_keys_queued_count();
assert!(n >= 0, "queued count must be ≥ 0, got {}", n);
}
#[test]
fn get_numeric_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = get_numeric();
}
#[test]
fn set_unset_numeric_round_trip_safe() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_numeric(42);
unset_numeric(0);
}
#[test]
fn get_prebuffer_returns_string_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: String = get_prebuffer();
}
#[test]
fn makezleparams_returns_void_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: () = makezleparams(0);
}
#[test]
fn makezleparams_idempotent() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
for _ in 0..5 {
makezleparams(0);
}
}
#[test]
fn set_killring_none_safe() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_killring(None);
}
#[test]
fn set_killring_empty_slice_safe() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_killring(Some(&[]));
}
#[test]
fn get_killring_returns_vec_string_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: Vec<String> = get_killring();
}
#[test]
fn unset_killring_zero_exp_safe() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
unset_killring(0);
}
#[test]
fn set_predisplay_none_safe() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_predisplay(None);
}
#[test]
fn set_postdisplay_none_safe() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_postdisplay(None);
}
#[test]
fn set_get_predisplay_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_predisplay(Some("hello"));
assert_eq!(get_predisplay(), "hello", "set then get round-trips");
set_predisplay(None);
assert_eq!(get_predisplay(), "", "None clears predisplay");
}
#[test]
fn set_get_postdisplay_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
set_postdisplay(Some("world"));
assert_eq!(get_postdisplay(), "world", "set then get round-trips");
set_postdisplay(None);
assert_eq!(get_postdisplay(), "", "None clears postdisplay");
}
#[test]
fn get_zle_state_returns_string_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: String = get_zle_state();
}
#[test]
fn is_insert_mode_returns_bool_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: bool = is_insert_mode();
}
#[test]
fn is_region_active_returns_bool_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: bool = is_region_active();
}
#[test]
fn scan_registers_none_callback_safe() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
scan_registers(0, None, 0);
}
#[test]
fn makezleparams_arms_writeback_only_when_writable() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
crate::zle_param_sync::clear_snapshot();
makezleparams(1);
assert!(
!crate::zle_param_sync::active(),
"makezleparams(1) creates PM_READONLY params — arming the \
write-back diff would let a stale line overwrite the editor"
);
crate::zle_param_sync::clear_snapshot();
makezleparams(0);
assert!(
crate::zle_param_sync::active(),
"makezleparams(0) is the widget call site (zle_main.c:1534); \
its params ARE writable and must sync back"
);
crate::zle_param_sync::clear_snapshot();
}
#[test]
fn zleparam_names_cover_every_published_param() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
for name in [
"BUFFER",
"LBUFFER",
"RBUFFER",
"CURSOR",
"NUMERIC",
"KEYMAP",
"BUFFERLINES",
"KEYS",
"WIDGET",
"LASTWIDGET",
"WIDGETFUNC",
"WIDGETSTYLE",
"HISTNO",
"CONTEXT",
"PENDING",
"PREDISPLAY",
"POSTDISPLAY",
"region_highlight",
"MARK",
"REGION_ACTIVE",
"CUTBUFFER",
"killring",
"PREBUFFER",
"KEYS_QUEUED_COUNT",
"YANK_START",
"YANK_END",
"YANK_ACTIVE",
"ISEARCHMATCH_START",
"ISEARCHMATCH_END",
"ISEARCHMATCH_ACTIVE",
"SUFFIX_START",
"SUFFIX_END",
"SUFFIX_ACTIVE",
"ZLE_RECURSIVE",
"ZLE_STATE",
"UNDO_CHANGE_NO",
"UNDO_LIMIT_NO",
] {
assert!(
ZLEPARAM_NAMES.contains(&name),
"{name} is published by makezleparams but missing from \
ZLEPARAM_NAMES — it would survive the completion scope"
);
}
}
}
#[cfg(test)]
mod scope_tests {
use super::*;
use crate::ported::params::{endparamscope, locallevel, paramtab};
use crate::ported::zle::zle_main::{zle_test_setup, ZMOD};
fn enter_widget_scope() -> i32 {
crate::zle_param_sync::clear_snapshot();
ZMOD.lock().unwrap().flags = 0;
locallevel.fetch_add(1, Ordering::Relaxed); makezleparams(0); locallevel.load(Ordering::Relaxed)
}
fn leave_widget_scope() {
crate::zle_param_sync::clear_snapshot();
endparamscope(); }
#[test]
fn published_params_are_scoped_and_endparamscope_collects_them() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let outer = locallevel.load(Ordering::Relaxed);
let level = enter_widget_scope();
let stamp = PM_SPECIAL | PM_REMOVABLE;
let mut checked = 0;
{
let tab = paramtab().read().unwrap();
for name in ZLEPARAM_NAMES {
let Some(pm) = tab.get(*name) else { continue };
assert_eq!(pm.level, level, "${name} not in the widget scope");
assert_eq!(
pm.node.flags as u32 & stamp,
stamp,
"${name} lacks PM_SPECIAL|PM_REMOVABLE — c:200"
);
checked += 1;
}
}
assert!(checked > 30, "only {checked} params published");
leave_widget_scope();
let tab = paramtab().read().unwrap();
for name in ZLEPARAM_NAMES {
if let Some(pm) = tab.get(*name) {
assert!(
pm.level <= outer,
"${name} survived endparamscope at level {} — c:zle_main.c:1540",
pm.level
);
}
}
}
#[test]
fn readonly_bits_match_the_c_zleparams_table() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
enter_widget_scope();
{
let tab = paramtab().read().unwrap();
for (name, ty) in ZLEPARAM_TABLE {
let Some(pm) = tab.get(*name) else { continue };
let want = ty & PM_READONLY != 0 && !ZLEPARAM_READONLY_EXEMPT.contains(name);
assert_eq!(
pm.node.flags as u32 & PM_READONLY != 0,
want,
"${name} read-only bit disagrees with zleparams[]"
);
}
}
leave_widget_scope();
}
#[test]
fn numeric_is_unset_without_a_prefix_and_set_with_one() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
enter_widget_scope();
assert!(
paramtab()
.read()
.unwrap()
.get("NUMERIC")
.unwrap()
.node
.flags as u32
& PM_UNSET
!= 0,
"$NUMERIC must be PM_UNSET with no numeric prefix — c:162"
);
ZMOD.lock().unwrap().flags = MOD_MULT;
makezleparams(0);
assert!(
paramtab()
.read()
.unwrap()
.get("NUMERIC")
.unwrap()
.node
.flags as u32
& PM_UNSET
== 0,
"$NUMERIC must be set once a numeric prefix is in effect — c:222"
);
ZMOD.lock().unwrap().flags = 0;
leave_widget_scope();
}
}