use crate::ported::init::SHTTY;
use crate::ported::mem::popheap;
use crate::ported::params::getsparam;
use crate::ported::signals::unqueue_signals;
use crate::ported::utils::{adjustcolumns, adjustlines, errflag, write_loop};
use crate::ported::zle::comp_h::{
Cmatch, Cmgroup, CGF_HASDL, CGF_LINES, CGF_ROWS, CMF_DISPLINE, CMF_HIDE, CMF_NOLIST,
};
use crate::ported::zle::compcore::{listdat, MINFO, ZLEMETACS, ZLEMETALINE, ZLEMETALL};
use crate::ported::zle::zle_refresh::{tcmultout, tcout, CLEARFLAG, NLNCT};
use crate::ported::zsh_h::{isset, Patprog, EXTENDEDGLOB, TCCLEAREOD, TCCLEAREOL, USEZLE};
use crate::DPUTS2;
use std::collections::HashMap;
use std::sync::atomic::Ordering;
#[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::*,
};
pub const MMARK: u32 = 1;
pub const MAX_POS: usize = 11;
pub const COL_NO: usize = 0; pub const COL_FI: usize = 1; pub const COL_DI: usize = 2; pub const COL_LN: usize = 3; pub const COL_PI: usize = 4; pub const COL_SO: usize = 5; pub const COL_BD: usize = 6; pub const COL_CD: usize = 7; pub const COL_OR: usize = 8; pub const COL_MI: usize = 9; pub const COL_SU: usize = 10; pub const COL_SG: usize = 11; pub const COL_TW: usize = 12; pub const COL_OW: usize = 13; pub const COL_ST: usize = 14; pub const COL_EX: usize = 15; pub const COL_LC: usize = 16; pub const COL_RC: usize = 17; pub const COL_EC: usize = 18; pub const COL_TC: usize = 19; pub const COL_SP: usize = 20; pub const COL_MA: usize = 21; pub const COL_HI: usize = 22; pub const COL_DU: usize = 23; pub const COL_SA: usize = 24; pub const NUM_COLS: usize = 25;
pub fn filecol(col: &str) -> filecol {
filecol {
prog: None, col: col.to_string(), next: None, } }
#[derive(Default)]
#[allow(non_camel_case_types)]
pub struct filecol {
pub prog: Option<Patprog>, pub col: String, pub next: Option<Box<filecol>>, }
#[derive(Default)]
#[allow(non_camel_case_types)]
pub struct patcol {
pub prog: Option<Patprog>, pub pat: Option<Patprog>, pub cols: Vec<String>, pub next: Option<Box<patcol>>, }
#[derive(Default)]
#[allow(non_camel_case_types)]
pub struct extcol {
pub prog: Option<Patprog>, pub ext: String, pub col: String, pub next: Option<Box<extcol>>, }
#[derive(Default)]
#[allow(non_camel_case_types)]
pub struct listcols {
pub files: Vec<filecol>, pub pats: Option<Box<patcol>>, pub exts: Option<Box<extcol>>, pub flags: i32, }
pub fn getcolval(s: &str, multi: i32) -> (String, &str) {
use crate::ported::init::tcstr;
let _ = tcstr;
let bytes = s.as_bytes();
let mut p: Vec<u8> = Vec::with_capacity(bytes.len());
let mut i = 0usize;
while i < bytes.len() {
let c = bytes[i];
if c == b':' || (multi != 0 && c == b'=') {
break;
}
if c == b'\\' && i + 1 < bytes.len() {
i += 1;
let n = bytes[i];
i += 1;
match n {
b'a' => p.push(0x07),
b'n' => p.push(b'\n'),
b'b' => p.push(0x08),
b't' => p.push(b'\t'),
b'v' => p.push(0x0b),
b'f' => p.push(0x0c),
b'r' => p.push(b'\r'),
b'e' => p.push(0x1b),
b'_' => p.push(b' '),
b'?' => p.push(0x7f),
d if (b'0'..=b'7').contains(&d) => {
let mut val = (d - b'0') as i32;
if i < bytes.len() && (b'0'..=b'7').contains(&bytes[i]) {
val = val * 8 + (bytes[i] - b'0') as i32;
i += 1;
if i < bytes.len() && (b'0'..=b'7').contains(&bytes[i]) {
val = val * 8 + (bytes[i] - b'0') as i32;
i += 1;
}
}
p.push(val as u8);
}
_ => p.push(n),
}
} else if c == b'^' && i + 1 < bytes.len() {
let n = bytes[i + 1];
if (b'@'..=b'_').contains(&n) || (b'a'..=b'z').contains(&n) {
p.push(n & !0x60);
} else if n == b'?' {
p.push(0x7f);
} else {
p.push(c);
p.push(n);
}
i += 2;
} else {
p.push(c);
i += 1;
}
}
let consumed = i as i32;
if consumed > MAX_CAPLEN.load(Ordering::Relaxed) {
MAX_CAPLEN.store(consumed, Ordering::Relaxed);
}
let decoded = String::from_utf8_lossy(&p).into_owned();
(decoded, &s[i..])
}
pub fn getcoldef(s: &str) -> Option<String> {
s.split_once(':').map(|(_, rest)| rest.to_string())
}
pub fn getcols(_unused: &str) -> i32 {
MAX_CAPLEN.store(0, Ordering::SeqCst); LR_CAPLEN.store(0, Ordering::SeqCst); {
let mut mc = MCOLORS.lock().unwrap();
mc.flags = 0; }
crate::ported::signals::queue_signals();
let s_opt = getsparam("ZLS_COLORS").or_else(|| getsparam("ZLS_COLOURS"));
if s_opt.is_none() {
let mut mc = MCOLORS.lock().unwrap();
mc.files.clear();
for _i in 0..NUM_COLS {
mc.files.push(filecol("")); }
mc.pats = None; mc.exts = None;
let tcstr_guard = crate::ported::init::tcstr.lock().unwrap();
let so_beg = tcstr_guard[crate::ported::zsh_h::TCSTANDOUTBEG as usize].clone();
let so_end = tcstr_guard[crate::ported::zsh_h::TCSTANDOUTEND as usize].clone();
drop(tcstr_guard);
if !so_beg.is_empty() {
mc.files[COL_MA] = filecol(&so_beg); mc.files[COL_EC] = filecol(&so_end); } else {
mc.files[COL_MA] = filecol("7"); }
let ma_len = mc.files[COL_MA].col.len() as i32;
let ec_len = mc.files[COL_EC].col.len() as i32;
let max_len = if ma_len < ec_len { ec_len } else { ma_len };
MAX_CAPLEN.store(max_len, Ordering::SeqCst); unqueue_signals(); return 0; }
{
let mut mc = MCOLORS.lock().unwrap();
*mc = listcols::default(); }
let mut s = s_opt.unwrap(); while !s.is_empty() {
if s.starts_with(':') {
s = s[1..].to_string(); } else {
s = match getcoldef(&s) {
Some(rest) => rest,
None => break,
};
}
}
unqueue_signals();
let defcols: [&str; NUM_COLS] = [
"0",
"0",
"01;34",
"01;36",
"33",
"01;35",
"01;33",
"01;33",
"01;05;37;41",
"01;05;37;41",
"37;41",
"30;43",
"30;42",
"34;42",
"37;44",
"01;32",
"\x1b[",
"m",
"0",
"0",
"0",
"7",
"0",
"0",
"0",
];
let mut mc = MCOLORS.lock().unwrap();
while mc.files.len() < NUM_COLS {
mc.files.push(filecol(""));
}
let mut max_len = MAX_CAPLEN.load(Ordering::SeqCst);
for i in 0..NUM_COLS {
if mc.files[i].col.is_empty() {
mc.files[i] = filecol(defcols[i]); }
let l = mc.files[i].col.len() as i32; if l > max_len {
max_len = l;
} }
MAX_CAPLEN.store(max_len, Ordering::SeqCst);
let lr_len = (mc.files[COL_LC].col.len() + mc.files[COL_RC].col.len()) as i32;
LR_CAPLEN.store(lr_len, Ordering::SeqCst);
if mc.files[COL_OR].col.is_empty() {
let ln = mc.files[COL_LN].col.clone();
mc.files[COL_OR] = filecol(&ln); }
if mc.files[COL_MI].col.is_empty() {
let fi = mc.files[COL_FI].col.clone();
mc.files[COL_MI] = filecol(&fi); }
0 }
pub fn zlrputs(cap: &str) -> i32 {
if cap.is_empty() {
return 0;
}
let fd = SHTTY.load(Ordering::Relaxed);
let out = if fd >= 0 { fd } else { 1 };
let s = format!("\x1b[{}m", cap);
let _ = write_loop(out, s.as_bytes());
0
}
pub fn zcputs(s: &str, color: Option<&str>) -> String {
match color {
Some(c) => format!("\x1b[{}m{}\x1b[0m", c, s),
None => String::new(),
}
}
pub fn zcoff() { }
pub fn cleareol() {
if MLBEG.load(Ordering::Relaxed) < 0 {
return;
}
let fd = SHTTY.load(Ordering::Relaxed);
let out = if fd >= 0 { fd } else { 1 };
if !LAST_CAP.lock().map(|s| s.is_empty()).unwrap_or(true) {
let _ = write_loop(out, b"\x1b[0m");
LAST_CAP.lock().ok().map(|mut s| s.clear());
}
let _ = write_loop(out, b"\x1b[K");
}
pub fn initiscol() -> i32 {
let first_cap = PATCOLS
.lock()
.ok()
.and_then(|p| p.first().cloned())
.unwrap_or_default();
if !first_cap.is_empty() {
let _ = zlrputs(&first_cap);
}
if let Ok(mut cs) = CURISCOLS.lock() {
if !cs.is_empty() {
cs[0] = first_cap.clone();
}
}
CURISCOL.store(0, Ordering::Relaxed);
PATCOLS_IDX.store(1, Ordering::Relaxed);
CURISBEG.store(0, Ordering::Relaxed);
CURISSEND.store(0, Ordering::Relaxed);
let nrefs = NREFS.load(Ordering::Relaxed) as usize;
if let Ok(mut sp) = SENDPOS.lock() {
for i in 0..MAX_POS {
sp[i] = 0xfffffff;
}
for i in 0..nrefs.min(MAX_POS) {
sp[i] = 0xfffffff; }
}
if let Ok(mut bp) = BEGPOS.lock() {
for i in nrefs..MAX_POS {
bp[i] = 0xfffffff; }
}
if let Ok(mut ep) = ENDPOS.lock() {
for i in nrefs..MAX_POS {
ep[i] = 0xfffffff; }
}
0
}
pub fn doiscol(pos: i32) -> i32 {
let fd = SHTTY.load(Ordering::Relaxed);
let out = if fd >= 0 { fd } else { 1 };
loop {
let curissend = CURISSEND.load(Ordering::Relaxed) as usize;
let sp = SENDPOS
.lock()
.ok()
.and_then(|s| s.get(curissend).copied())
.unwrap_or(0xfffffff);
if pos <= sp {
break;
}
CURISSEND.fetch_add(1, Ordering::Relaxed);
let curiscol = CURISCOL.load(Ordering::Relaxed);
if curiscol > 0 {
let _ = write_loop(out, b"\x1b[0m");
let new_idx = curiscol - 1;
CURISCOL.store(new_idx, Ordering::Relaxed);
let restore_cap = CURISCOLS
.lock()
.ok()
.and_then(|c| c.get(new_idx as usize).cloned())
.unwrap_or_default();
if !restore_cap.is_empty() {
let _ = zlrputs(&restore_cap);
}
}
}
loop {
let curisbeg = CURISBEG.load(Ordering::Relaxed) as usize;
if curisbeg >= MAX_POS {
break;
}
let (bp, ep) = {
let bp_lock = BEGPOS.lock().ok();
let ep_lock = ENDPOS.lock().ok();
match (bp_lock, ep_lock) {
(Some(b), Some(e)) => (
b.get(curisbeg).copied().unwrap_or(0xfffffff),
e.get(curisbeg).copied().unwrap_or(0xfffffff),
),
_ => break,
}
};
let fi = ep < bp || bp == -1;
if !(fi || pos == bp) {
break;
}
let patcols_idx = PATCOLS_IDX.load(Ordering::Relaxed);
let cap_now = PATCOLS
.lock()
.ok()
.and_then(|p| p.get(patcols_idx).cloned())
.unwrap_or_default();
if cap_now.is_empty() {
break;
}
if !fi {
let e = ep;
if let Ok(mut sp) = SENDPOS.lock() {
let curissend = CURISSEND.load(Ordering::Relaxed) as usize;
let mut i = curissend;
while i < MAX_POS && sp[i] <= e {
i += 1;
}
let mut j = MAX_POS - 1;
while j > i {
sp[j] = sp[j - 1];
j -= 1;
}
if i < MAX_POS {
sp[i] = e;
}
}
let _ = write_loop(out, b"\x1b[0m");
let _ = zlrputs(&cap_now);
let new_idx = CURISCOL.fetch_add(1, Ordering::Relaxed) + 1;
if let Ok(mut cs) = CURISCOLS.lock() {
if (new_idx as usize) < cs.len() {
cs[new_idx as usize] = cap_now;
}
}
}
PATCOLS_IDX.fetch_add(1, Ordering::Relaxed);
CURISBEG.fetch_add(1, Ordering::Relaxed);
}
0
}
pub fn clprintfmt(p: &str, ml: i32) -> i32 {
printfmt(p, ml, true, true)
}
pub fn clnicezputs(do_colors: i32, s: &str, ml_in: i32) -> i32 {
use crate::ported::lex::untokenize;
use crate::ported::utils::{nicechar, unmetafy_str, wcs_nicechar, zwcwidth};
let oml = ml_in;
let mut ml = ml_in;
let mut col: i32 = 0;
let mut i: i32 = 0;
let zterm_columns = adjustcolumns() as i32;
let mlend = MLEND.load(Ordering::SeqCst);
let mscroll = MSCROLL.load(Ordering::SeqCst) != 0;
let fd = SHTTY.load(Ordering::Relaxed);
let out_fd = if fd >= 0 { fd } else { 1 };
let ums = untokenize(s);
let ubytes = unmetafy_str(&ums);
if do_colors != 0 {
initiscol();
}
let mut idx = 0usize;
while idx < ubytes.len() {
let b0 = ubytes[idx];
let seq_len = if b0 < 0x80 {
1
} else if b0 >> 5 == 0b110 {
2
} else if b0 >> 4 == 0b1110 {
3
} else if b0 >> 3 == 0b11110 {
4
} else {
0 };
let (rep, cnt): (String, usize) = if seq_len >= 1 && idx + seq_len <= ubytes.len() {
match std::str::from_utf8(&ubytes[idx..idx + seq_len]) {
Ok(cs) => {
let cc = cs.chars().next().unwrap();
(wcs_nicechar(cc, None, None), seq_len)
}
Err(_) => (nicechar(b0 as char), 1),
}
} else {
(nicechar(b0 as char), 1)
};
idx += cnt;
if do_colors != 0 {
for _ in 0..cnt {
doiscol(i);
i += 1;
}
}
let mut buf = [0u8; 4];
for ch in rep.chars() {
if ml == mlend - 1 && col == zterm_columns - 1 {
MLPRINTED.store(ml - oml, Ordering::SeqCst);
return 0;
}
let _ = write_loop(out_fd, ch.encode_utf8(&mut buf).as_bytes());
col += if (ch as u32) < 0x80 {
1
} else {
zwcwidth(ch) as i32
};
if col > zterm_columns {
ml += 1;
if mscroll {
let rest = MRESTLINES.fetch_sub(1, Ordering::SeqCst) - 1;
if rest == 0 {
let ask = asklistscroll(ml);
if ask != 0 {
MLPRINTED.store(ml - oml, Ordering::SeqCst);
return ask;
}
}
}
col -= zterm_columns;
if do_colors != 0 {
let _ = write_loop(out_fd, b" \x08");
}
}
}
}
MLPRINTED.store(ml - oml, Ordering::SeqCst);
0
}
pub fn putmatchcol(group: &str, n: &str) -> i32 {
let mc = MCOLORS.lock().unwrap();
let _ = group;
let _ = n;
if let Some(no_col) = mc.files.get(COL_NO) {
zlrputs(&no_col.col);
}
0 }
pub fn putfilecol(group: &str, filename: &str, m: u32, _special: i32) -> i32 {
use crate::ported::zle::complist as cl;
let mc = MCOLORS.lock().unwrap();
let mut cur = mc.exts.as_deref();
while let Some(ec) = cur {
if filename.ends_with(&ec.ext) {
zlrputs(&ec.col);
return 0;
}
cur = ec.next.as_deref();
}
let pick = if (m & 0o170000) == 0o040000 {
cl::COL_DI
} else if (m & 0o170000) == 0o120000 {
cl::COL_LN
} else if (m & 0o170000) == 0o010000 {
cl::COL_PI
} else if (m & 0o170000) == 0o140000 {
cl::COL_SO
} else if (m & 0o170000) == 0o060000 {
cl::COL_BD
} else if (m & 0o170000) == 0o020000 {
cl::COL_CD
} else if m & 0o111 != 0 {
cl::COL_EX
} else {
cl::COL_FI
};
if let Some(col) = mc.files.get(pick) {
if !col.col.is_empty() {
zlrputs(&col.col);
return 0;
}
}
let _ = group;
if let Some(no_col) = mc.files.get(COL_NO) {
zlrputs(&no_col.col);
}
0
}
pub fn asklistscroll(ml: i32) -> i32 {
use crate::ported::utils::{adjustcolumns, adjustlines};
use crate::ported::zle::zle_keymap::{getkeycmd, selectlocalmap, ungetkeycmd};
use crate::ported::zle::zle_main::zsetterm;
let mut _stop = 0i32;
let _ = compprintfmt("", 1, 1, 1, ml, &mut _stop);
let fd = SHTTY.load(Ordering::Relaxed);
let out_fd = if fd >= 0 { fd } else { 1 };
let _ = zsetterm();
menuselect_bindings();
let lsk = crate::ported::zle::zle_keymap::openkeymap("listscroll");
selectlocalmap(lsk);
let ret;
let cmd = getkeycmd();
let nm = cmd.as_ref().map(|t| t.nam.as_str()).unwrap_or("");
match nm {
"" | "send-break" => {
ret = 1; }
"accept-line"
| "down-history"
| "down-line-or-history"
| "down-line-or-search"
| "vi-down-line-or-history" => {
MRESTLINES.store(1, Ordering::Relaxed);
ret = 0;
}
"complete-word"
| "expand-or-complete"
| "expand-or-complete-prefix"
| "menu-complete"
| "menu-expand-or-complete"
| "menu-select" => {
MRESTLINES.store(adjustlines() as i32 - 1, Ordering::Relaxed);
ret = 0;
}
"accept-search" => {
ret = 1;
}
_ => {
ungetkeycmd();
ret = 1;
}
}
selectlocalmap(None);
let _ = write_loop(out_fd, b"\r");
let cols = adjustcolumns().saturating_sub(1);
let blank = vec![b' '; cols];
let _ = write_loop(out_fd, &blank);
let _ = write_loop(out_fd, b"\r");
ret }
#[allow(unused_variables)]
pub fn compprintnl(ml: i32) -> i32 {
let fd = SHTTY.load(Ordering::Relaxed);
let out_fd = if fd >= 0 { fd } else { 1 };
let _ = write_loop(out_fd, b"\x1b[K\n");
0
}
pub fn compprintfmt(
fmt: &str,
n: i32,
dopr: i32,
doesc: i32,
ml: i32,
stop: &mut i32,
) -> i32 {
use std::sync::atomic::Ordering;
let mut l = 0i32; let mut cc = 0i32;
let _ = doesc;
let _ = ml;
let _ = stop;
let owned: String;
let fmt_str: &str = if fmt.is_empty() {
if MLBEG.load(Ordering::SeqCst) >= 0 {
owned = MSTATUS.lock().unwrap().clone();
if owned.is_empty() {
MLPRINTED.store(0, Ordering::SeqCst); return 0; }
cc = -1; &owned
} else {
owned = MLISTP.lock().unwrap().clone();
&owned
}
} else {
fmt
};
let mut chars = fmt_str.chars().peekable();
while let Some(c) = chars.next() {
if c == '%' {
let mut arg = 0i32;
while let Some(&d) = chars.peek() {
if d.is_ascii_digit() {
arg = arg * 10 + (d as i32 - '0' as i32);
chars.next();
} else {
break;
}
}
match chars.next() {
Some('%') => {
if dopr == 1 {
l += 1;
}
cc += 1;
} Some('n') => {
let s = n.to_string();
if dopr == 1 {
let fd = SHTTY.load(Ordering::Relaxed);
let out_fd = if fd >= 0 { fd } else { 1 };
let _ = write_loop(out_fd, s.as_bytes());
}
l += s.len() as i32;
cc += s.len() as i32;
}
Some('p') => {
let mlbeg = MLBEG.load(Ordering::SeqCst);
let mlines = MLINES.load(Ordering::SeqCst);
let s = if mlbeg <= 0 && mlines < MLEND.load(Ordering::SeqCst) {
"Top".to_string()
} else if mlbeg + MLEND.load(Ordering::SeqCst) - MLBEG.load(Ordering::SeqCst)
>= mlines
{
"Bot".to_string()
} else {
format!("{}%", mlbeg.max(0) * 100 / mlines.max(1))
};
if dopr == 1 {
let fd = SHTTY.load(Ordering::Relaxed);
let out_fd = if fd >= 0 { fd } else { 1 };
let _ = write_loop(out_fd, s.as_bytes());
}
l += s.len() as i32;
cc += s.len() as i32;
}
Some(_) => {
let _ = arg;
} None => break,
}
} else {
if dopr == 1 {
let fd = SHTTY.load(Ordering::Relaxed);
let out_fd = if fd >= 0 { fd } else { 1 };
let mut buf = [0u8; 4];
let bs = c.encode_utf8(&mut buf).as_bytes();
let _ = write_loop(out_fd, bs);
}
l += 1;
cc += 1;
}
}
let _ = l;
cc }
pub static MSTATUS: std::sync::LazyLock<std::sync::Mutex<String>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(String::new()));
pub static MLISTP: std::sync::LazyLock<std::sync::Mutex<String>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(String::new()));
#[allow(unused_variables)]
pub fn compzputs(s: &str, ml: i32) -> i32 {
let bytes = s.as_bytes();
let mut out: Vec<u8> = Vec::with_capacity(bytes.len());
let mut i = 0;
while i < bytes.len() {
let c = bytes[i];
if c == 0x83 {
i += 1;
if i < bytes.len() {
out.push(bytes[i] ^ 32);
}
} else if (0x80..0xa0).contains(&c) { } else {
out.push(c);
}
i += 1;
}
if out.is_empty() {
return 0;
}
let fd = SHTTY.load(Ordering::Relaxed);
let out_fd = if fd >= 0 { fd } else { 1 };
let _ = write_loop(out_fd, &out); 0
}
pub fn compprintlist(showall: i32) -> i32 {
let mut pnl = 0i32; let mut cl: i32;
let mut ml: i32 = 0;
let mut mc: i32;
let mut printed = 0i32;
let mut stop = 0i32;
let _asked = 1i32;
let mut lastused = 0i32;
let mlbeg = MLBEG.load(Ordering::SeqCst);
let mlend = MLEND.load(Ordering::SeqCst);
let mnew = MNEW.load(Ordering::SeqCst);
let mhasstat = MHASSTAT.load(Ordering::SeqCst);
let zterm_lines = adjustlines() as i32;
let nlnct = NLNCT.load(Ordering::SeqCst);
let invcount = crate::ported::zle::compresult::INVCOUNT.load(Ordering::SeqCst);
MFIRSTL.store(-1, Ordering::SeqCst);
let mut last_type = LAST_TYPE.load(Ordering::SeqCst);
let last_invcount = LAST_INVCOUNT.load(Ordering::SeqCst);
let last_beg = LAST_BEG.load(Ordering::SeqCst);
if mnew != 0 || last_invcount != invcount || last_beg != mlbeg || mlbeg < 0 {
last_type = 0; LAST_TYPE.store(0, Ordering::SeqCst);
LAST_NLNCT.store(-1, Ordering::SeqCst);
}
let listdat_nlines = listdat
.get()
.and_then(|m| m.lock().ok().map(|g| g.nlines))
.unwrap_or(0);
cl = if listdat_nlines > zterm_lines - nlnct - mhasstat {
zterm_lines - nlnct - mhasstat
} else {
listdat_nlines
} - if LAST_NLNCT.load(Ordering::SeqCst) > nlnct {
1
} else {
0
};
LAST_NLNCT.store(nlnct, Ordering::SeqCst); MRESTLINES.store(zterm_lines - 1, Ordering::SeqCst); LAST_INVCOUNT.store(invcount, Ordering::SeqCst);
let tcd_avail = crate::ported::init::tclen.lock().unwrap()[TCCLEAREOD as usize] != 0; let tceol_avail = crate::ported::init::tclen.lock().unwrap()[TCCLEAREOL as usize] != 0;
if cl < 2 {
cl = -1; if tcd_avail {
tcout(TCCLEAREOD); }
} else if mlbeg >= 0 && !tceol_avail && tcd_avail {
tcout(TCCLEAREOD); }
let groups: Vec<Cmgroup> = {
crate::ported::zle::compcore::amatches
.get_or_init(|| std::sync::Mutex::new(Vec::new()))
.lock()
.ok()
.map(|g| g.clone())
.unwrap_or_default()
};
let dolist = |x: i32| -> bool { x >= mlbeg && x < mlend }; let dolistcl = |x: i32| -> bool { x >= mlbeg && x < mlend + 1 }; let dolistnl = |x: i32| -> bool { x >= mlbeg && x < mlend - 1 };
'outer: for g in &groups {
if errflag.load(Ordering::SeqCst) != 0 {
break;
}
let pp = &g.ylist;
let onlyexpl: i32 = listdat
.get()
.and_then(|m| m.lock().ok().map(|g| g.onlyexpl))
.unwrap_or(0);
if !g.expls.is_empty() {
for e in &g.expls {
if errflag.load(Ordering::SeqCst) != 0 {
break 'outer;
}
let valid = (e.count != 0 || e.always != 0) && (onlyexpl == 0
|| (onlyexpl & if e.always > 0 { 2 } else { 1 }) != 0);
if valid {
if pnl != 0 {
if dolistnl(ml) && compprintnl(ml) != 0 {
break 'outer;
}
pnl = 0; ml += 1; if dolistcl(ml) && cl >= 0 {
cl -= 1;
if cl <= 1 {
cl = -1; if tcd_avail {
tcout(TCCLEAREOD);
}
}
}
}
if mlbeg < 0 && MFIRSTL.load(Ordering::SeqCst) < 0 {
MFIRSTL.store(ml, Ordering::SeqCst); }
let n = if e.always != 0 { -1 } else { e.count };
let estr = e.str.clone().unwrap_or_default();
let _ = compprintfmt(
&estr,
n,
if dolist(ml) { 1 } else { 0 },
1,
ml,
&mut stop,
);
if stop != 0 {
break 'outer;
} if last_type == 0 && ml >= mlbeg {
last_type = 1; LAST_TYPE.store(1, Ordering::SeqCst);
LAST_BEG.store(mlbeg, Ordering::SeqCst);
LAST_ML.store(ml, Ordering::SeqCst);
lastused = 1;
}
ml += MLPRINTED.load(Ordering::SeqCst); if dolistcl(ml) && cl >= 0 {
cl -= MLPRINTED.load(Ordering::SeqCst);
if cl <= 1 {
cl = -1;
if tcd_avail {
tcout(TCCLEAREOD);
}
}
}
pnl = 1; }
if mnew == 0 && ml > mlend {
break 'outer;
} }
}
if onlyexpl == 0 && mlbeg < 0 && !pp.is_empty() {
if pnl != 0 {
if dolistnl(ml) && compprintnl(ml) != 0 {
break 'outer;
} pnl = 0;
ml += 1;
if cl >= 0 {
cl -= 1;
if cl <= 1 {
cl = -1;
if tcd_avail {
tcout(TCCLEAREOD);
}
}
}
}
if mlbeg < 0 && MFIRSTL.load(Ordering::SeqCst) < 0 {
MFIRSTL.store(ml, Ordering::SeqCst);
}
if (g.flags & CGF_LINES) != 0 {
for s in pp {
if compzputs(s, ml) != 0 {
break 'outer;
} if compprintnl(ml) != 0 {
break 'outer;
} }
} else {
for s in pp {
if compzputs(s, MSCROLL.load(Ordering::SeqCst)) != 0 {
break 'outer;
}
if compprintnl(ml) != 0 {
break 'outer;
} ml += 1;
}
}
} else if onlyexpl == 0 && (g.lcount != 0 || (showall != 0 && g.mcount != 0)) {
let n_total = g.dcount;
let _ = n_total;
let nc = g.lins;
if (g.flags & CGF_HASDL) != 0 {
for m in &g.matches {
let displine = m.disp.is_some() && (m.flags & CMF_DISPLINE) != 0;
let visible = showall != 0 || (m.flags & (CMF_HIDE | CMF_NOLIST)) == 0;
if displine && visible {
if pnl != 0 {
if dolistnl(ml) && compprintnl(ml) != 0 {
break 'outer;
}
pnl = 0;
ml += 1;
if dolistcl(ml) && cl >= 0 {
cl -= 1;
if cl <= 1 {
cl = -1;
if tcd_avail {
tcout(TCCLEAREOD);
}
}
}
}
if last_type == 0 && ml >= mlbeg {
last_type = 2;
LAST_TYPE.store(2, Ordering::SeqCst);
LAST_BEG.store(mlbeg, Ordering::SeqCst);
LAST_ML.store(ml, Ordering::SeqCst);
lastused = 1;
}
if MFIRSTL.load(Ordering::SeqCst) < 0 {
MFIRSTL.store(ml, Ordering::SeqCst);
}
if dolist(ml) {
printed += 1;
} if clprintm(Some(g), Some(m), 0, ml, 1, 0) != 0 {
break 'outer;
}
ml += MLPRINTED.load(Ordering::SeqCst); if dolistcl(ml) {
cl -= MLPRINTED.load(Ordering::SeqCst);
if cl <= 1 {
cl = -1;
if tcd_avail {
tcout(TCCLEAREOD);
}
}
}
pnl = 1; }
if mnew == 0 && ml > mlend {
break 'outer;
} }
}
if pnl != 0 {
if dolistnl(ml) && compprintnl(ml) != 0 {
break 'outer;
}
pnl = 0;
ml += 1;
if dolistcl(ml) && cl >= 0 {
cl -= 1;
if cl <= 1 {
cl = -1;
if tcd_avail {
tcout(TCCLEAREOD);
}
}
}
}
let mut nl_cnt = nc;
let mut p_idx: usize = 0;
while p_idx < g.matches.len() {
let m = &g.matches[p_idx];
if (m.flags & CMF_HIDE) != 0 || (showall == 0 && (m.flags & CMF_NOLIST) != 0) {
p_idx += 1;
} else {
break;
}
}
let mut n = g.dcount;
while n > 0 && nl_cnt > 0 && errflag.load(Ordering::SeqCst) == 0 {
if last_type == 0 && ml >= mlbeg {
last_type = 3;
LAST_TYPE.store(3, Ordering::SeqCst);
LAST_BEG.store(mlbeg, Ordering::SeqCst);
LAST_ML.store(ml, Ordering::SeqCst);
lastused = 1;
}
let mut i = g.cols; mc = 0;
let mut q_idx = p_idx;
while n > 0 && i > 0 && errflag.load(Ordering::SeqCst) == 0 {
i -= 1;
let wid = if !g.widths.is_empty() {
g.widths.get(mc as usize).copied().unwrap_or(g.width)
} else {
g.width
};
let m_at_q = g.matches.get(q_idx); match m_at_q {
None => {
if clprintm(
Some(g),
None,
mc,
ml, if i == 0 { 1 } else { 0 },
wid,
) != 0
{
break 'outer;
}
break;
}
Some(m) => {
if clprintm(Some(g), Some(m), mc, ml, if i == 0 { 1 } else { 0 }, wid)
!= 0
{
break 'outer;
}
if dolist(ml) {
printed += 1;
} ml += MLPRINTED.load(Ordering::SeqCst); if dolistcl(ml) {
cl -= MLPRINTED.load(Ordering::SeqCst);
if cl < 1 {
cl = -1;
if tcd_avail {
tcout(TCCLEAREOD);
}
}
}
if MFIRSTL.load(Ordering::SeqCst) < 0 {
MFIRSTL.store(ml, Ordering::SeqCst);
}
n -= 1; if n > 0 {
let step = if (g.flags & CGF_ROWS) != 0 {
1
} else {
nc as usize
};
for _j in 0..step {
if q_idx < g.matches.len() {
q_idx += 1;
}
while q_idx < g.matches.len() {
let m2 = &g.matches[q_idx];
if (m2.flags & CMF_HIDE) != 0
|| (showall == 0 && (m2.flags & CMF_NOLIST) != 0)
{
q_idx += 1;
} else {
break;
}
}
}
}
mc += 1; }
}
}
while i > 0 {
i -= 1;
let wid = if !g.widths.is_empty() {
g.widths.get(mc as usize).copied().unwrap_or(g.width)
} else {
g.width
};
if clprintm(Some(g), None, mc, ml, if i == 0 { 1 } else { 0 }, wid) != 0 {
break 'outer;
}
mc += 1;
}
if n > 0 {
if dolistnl(ml) && compprintnl(ml) != 0 {
break 'outer;
}
ml += 1; if dolistcl(ml) && cl >= 0 {
cl -= 1;
if cl <= 1 {
cl = -1;
if tcd_avail {
tcout(TCCLEAREOD);
}
}
}
if nl_cnt > 0 {
let step = if (g.flags & CGF_ROWS) != 0 {
g.cols as usize
} else {
1
};
for _j in 0..step {
if p_idx < g.matches.len() {
p_idx += 1;
}
while p_idx < g.matches.len() {
let m2 = &g.matches[p_idx];
if (m2.flags & CMF_HIDE) != 0
|| (showall == 0 && (m2.flags & CMF_NOLIST) != 0)
{
p_idx += 1;
} else {
break;
}
}
}
}
}
if mnew == 0 && ml > mlend {
break 'outer;
} nl_cnt -= 1;
}
}
if g.lcount != 0 || (showall != 0 && g.mcount != 0) {
pnl = 1; }
}
MSTATPRINTED.store(0, Ordering::SeqCst); LASTLISTLEN.store(0, Ordering::SeqCst); if nlnct <= 1 {
MSCROLL.store(0, Ordering::SeqCst);
}
let _ = lastused;
printed }
pub static LAST_TYPE: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static LAST_BEG: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static LAST_ML: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static LAST_INVCOUNT: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1);
pub static LAST_NLNCT: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1);
pub fn clprintm(
g: Option<&Cmgroup>,
m: Option<&Cmatch>,
mc: i32,
ml: i32,
lastc: i32,
width: i32,
) -> i32 {
use std::sync::atomic::Ordering;
let mselect = MSELECT.load(Ordering::SeqCst);
let mcols = MCOLS.load(Ordering::SeqCst);
let zterm_columns = adjustcolumns() as i32;
let mlines_v = MLINES.load(Ordering::SeqCst);
DPUTS2!(
mselect >= 0 && ml >= mlines_v, "clprintm called with ml too large ({}/{})",
ml,
mlines_v );
{
let mut lg = LAST_GROUP.lock().unwrap();
let g_name = g.and_then(|grp| grp.name.clone()).unwrap_or_default();
if *lg != g_name {
*LAST_CAP.lock().unwrap() = String::new(); *lg = g_name; }
}
let m_ref = match m {
Some(m_real) => m_real,
None => {
if let Some(grp) = g {
let _ = grp;
let pad = (width - 2).max(0) as usize;
let pad_str = " ".repeat(pad);
let fd = SHTTY.load(Ordering::Relaxed);
let out_fd = if fd >= 0 { fd } else { 1 };
let _ = write_loop(out_fd, pad_str.as_bytes());
}
MLPRINTED.store(0, Ordering::SeqCst); return 0; }
};
let _ = crate::ported::zle::comp_h::CMF_ALL;
MLASTM.store(m_ref.gnum, Ordering::SeqCst);
let displine = m_ref.disp.is_some() && (m_ref.flags & CMF_DISPLINE) != 0;
if displine {
if mselect >= 0 {
let mm = mcols * ml; let mut mtab_guard = MTAB.lock().unwrap();
let mut mgtab_guard = MGTAB.lock().unwrap();
for i in 0..mcols {
let idx = (mm + i) as usize;
if idx < mtab_guard.len() {
mtab_guard[idx] = Some(m_ref.clone()); if let Some(grp) = g {
mgtab_guard[idx] = Some(grp.clone()); }
}
}
}
if m_ref.gnum == mselect {
let mm = mcols * ml;
MLINE.store(ml, Ordering::SeqCst); MCOL.store(0, Ordering::SeqCst); MMTABP.store(mm.max(0) as usize, Ordering::SeqCst); MGTABP.store(mm.max(0) as usize, Ordering::SeqCst); }
let disp = m_ref.disp.as_deref().unwrap_or("");
let _ = compprintfmt(disp, 0, 1, 0, ml, &mut 0); } else {
let mx = if !g.is_some_and(|grp| grp.widths.is_empty()) {
g.map(|grp| grp.widths.iter().take(mc as usize).sum::<i32>())
.unwrap_or(0)
} else {
mc * g.map(|grp| grp.width).unwrap_or(0)
};
if mselect >= 0 {
let mm = mcols * ml;
let mut mtab_guard = MTAB.lock().unwrap();
let mut mgtab_guard = MGTAB.lock().unwrap();
let n = if width != 0 { width } else { mcols };
for i in 0..n {
let idx = (mx + mm + i) as usize;
if idx < mtab_guard.len() {
mtab_guard[idx] = Some(m_ref.clone()); if let Some(grp) = g {
mgtab_guard[idx] = Some(grp.clone()); }
}
}
}
if m_ref.gnum == mselect {
let mm = mcols * ml;
MCOL.store(mx, Ordering::SeqCst); MLINE.store(ml, Ordering::SeqCst); MMTABP.store((mx + mm).max(0) as usize, Ordering::SeqCst); MGTABP.store((mx + mm).max(0) as usize, Ordering::SeqCst); }
let display = m_ref
.disp
.as_deref()
.unwrap_or_else(|| m_ref.str.as_deref().unwrap_or(""));
let fd = SHTTY.load(Ordering::Relaxed);
let out_fd = if fd >= 0 { fd } else { 1 };
let _ = write_loop(out_fd, display.as_bytes());
let len_str = display.chars().count() as i32;
let lines = if len_str > 0 {
(len_str - 1) / zterm_columns
} else {
0
};
MLPRINTED.store(lines, Ordering::SeqCst);
let cgf_files = g
.map(|grp| (grp.flags & crate::ported::zle::comp_h::CGF_FILES) != 0)
.unwrap_or(false);
let modec = m_ref.modec as u8;
let mut emitted_marker = 0i32;
if cgf_files && modec != 0 {
let _ = write_loop(out_fd, &[modec]); emitted_marker = 1; }
let total_len = len_str + emitted_marker;
let pad = (width - total_len - 2).max(0) as usize; if pad > 0 {
let pad_str = " ".repeat(pad);
let _ = write_loop(out_fd, pad_str.as_bytes());
}
}
let _ = lastc;
0 }
pub static LAST_GROUP: std::sync::LazyLock<std::sync::Mutex<String>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(String::new()));
pub fn singlecalc(cp: &mut i32, l: i32, lcp: &mut i32) -> i32 {
let zterm_columns = crate::ported::utils::adjustcolumns() as i32;
if zterm_columns <= 0 {
*lcp = 1;
return 0;
}
let mtab_guard = MTAB.lock().unwrap();
let row_base = (l * zterm_columns) as usize;
let mut c = *cp;
if c < 0 {
c = 0;
}
let mp = mtab_guard.get(row_base + c as usize).cloned().flatten();
let cm_eq = |a: Option<&Cmatch>, b: Option<&Cmatch>| -> bool {
match (a, b) {
(None, None) => true,
(Some(x), Some(y)) => x.str == y.str,
_ => false,
}
};
let mut n = 0i32;
let mut op: Option<Cmatch> = None;
let mut first = true;
let mut j = c;
while j >= 0 {
let p = mtab_guard.get(row_base + j as usize).cloned().flatten();
if cm_eq(p.as_ref(), mp.as_ref()) {
c = j;
}
if !first && !cm_eq(p.as_ref(), op.as_ref()) {
n += 1;
}
op = p;
first = false;
j -= 1;
}
*cp = c;
*lcp = 1;
let mut k = c;
while k < zterm_columns {
let p = mtab_guard.get(row_base + k as usize).cloned().flatten();
if p.is_some() && !cm_eq(p.as_ref(), mp.as_ref()) {
*lcp = 0;
}
k += 1;
}
n }
pub fn singledraw() -> i32 {
let mline = MLINE.load(Ordering::SeqCst);
let mcol = MCOL.load(Ordering::SeqCst);
let mlbeg = MLBEG.load(Ordering::SeqCst);
let moline = MOLINE.load(Ordering::SeqCst);
let mocol = MOCOL.load(Ordering::SeqCst);
let molbeg = MOLBEG.load(Ordering::SeqCst);
let zterm_columns = adjustcolumns() as i32;
let t1 = mline - mlbeg; let t2 = moline - molbeg;
let (mc1, ml1, md1, mc2, ml2, md2);
if t2 < t1 {
mc1 = mocol;
ml1 = moline;
md1 = t2; mc2 = mcol;
ml2 = mline;
md2 = t1; } else {
mc1 = mcol;
ml1 = mline;
md1 = t1; mc2 = mocol;
ml2 = moline;
md2 = t2; }
let mcc1 = mc1;
let _lc1 = 0i32;
let mcc2 = mc2;
let _lc2 = 0i32;
if md1 != 0 {
tc_downcurs(md1); }
if mc1 != 0 {
tcmultout(
crate::ported::zsh_h::TCRIGHT,
crate::ported::zsh_h::TCMULTRIGHT,
mc1,
); }
let idx1 = (ml1 * zterm_columns + mc1) as usize;
let g_at1 = MGTAB.lock().unwrap().get(idx1).cloned().flatten();
let m_at1 = MTAB.lock().unwrap().get(idx1).cloned().flatten();
let width_at1 = g_at1
.as_ref()
.map(|g| {
if g.widths.is_empty() {
g.width
} else {
g.widths.get(mcc1 as usize).copied().unwrap_or(g.width)
}
})
.unwrap_or(0);
clprintm(g_at1.as_ref(), m_at1.as_ref(), mcc1, ml1, 0, width_at1);
let mlprinted = MLPRINTED.load(Ordering::SeqCst);
if mlprinted != 0 {
tcmultout(
crate::ported::zsh_h::TCUP,
crate::ported::zsh_h::TCMULTUP,
mlprinted,
); }
let fd = SHTTY.load(Ordering::Relaxed);
let out_fd = if fd >= 0 { fd } else { 1 };
let _ = write_loop(out_fd, b"\r");
if md2 != md1 {
tc_downcurs(md2 - md1); }
if mc2 != 0 {
tcmultout(
crate::ported::zsh_h::TCRIGHT,
crate::ported::zsh_h::TCMULTRIGHT,
mc2,
); }
let idx2 = (ml2 * zterm_columns + mc2) as usize;
let g_at2 = MGTAB.lock().unwrap().get(idx2).cloned().flatten();
let m_at2 = MTAB.lock().unwrap().get(idx2).cloned().flatten();
let width_at2 = g_at2
.as_ref()
.map(|g| {
if g.widths.is_empty() {
g.width
} else {
g.widths.get(mcc2 as usize).copied().unwrap_or(g.width)
}
})
.unwrap_or(0);
clprintm(g_at2.as_ref(), m_at2.as_ref(), mcc2, ml2, 0, width_at2);
if mlprinted != 0 {
tcmultout(
crate::ported::zsh_h::TCUP,
crate::ported::zsh_h::TCMULTUP,
mlprinted,
); }
let _ = write_loop(out_fd, b"\r");
let _ = (mcc1, mcc2);
0 }
pub fn complistmatches() -> i32 {
if NOSELECT.load(Ordering::SeqCst) > 0 {
NOSELECT.store(0, Ordering::SeqCst); }
let zterm_lines = adjustlines() as i32;
let zterm_columns = adjustcolumns() as i32;
let nlnct = NLNCT.load(Ordering::SeqCst);
let mselect = MSELECT.load(Ordering::SeqCst);
let minfo_asked = MINFO
.get()
.and_then(|m| m.lock().ok().map(|g| g.asked))
.unwrap_or(0);
let errflag_v = errflag.load(Ordering::SeqCst);
if (minfo_asked == 2 && mselect < 0) || nlnct >= zterm_lines
|| errflag_v != 0
{
SHOWINGLIST.store(0, Ordering::SeqCst); NOSELECT.store(1, Ordering::SeqCst); return 1;
}
crate::ported::mem::pushheap();
let extendedglob = isset(EXTENDEDGLOB);
getcols("");
let calc_changed = crate::ported::zle::compresult::calclist(if mselect >= 0 { 1 } else { 0 });
let mlastcols = MLASTCOLS.load(Ordering::SeqCst);
let mlastlines = MLASTLINES.load(Ordering::SeqCst);
let listdat_nlines: i32 = listdat
.get()
.and_then(|m| m.lock().ok().map(|g| g.nlines))
.unwrap_or(0);
let mnew = (calc_changed != 0 || mlastcols != zterm_columns || mlastlines != listdat_nlines)
&& mselect >= 0;
MNEW.store(if mnew { 1 } else { 0 }, Ordering::SeqCst);
let usezle = isset(USEZLE);
if listdat_nlines == 0 || (mselect >= 0 && !(usezle))
{
SHOWINGLIST.store(0, Ordering::SeqCst);
LISTSHOWN.store(0, Ordering::SeqCst);
NOSELECT.store(1, Ordering::SeqCst);
popheap();
return 1;
}
if INSELECT.load(Ordering::SeqCst) != 0 || MLBEG.load(Ordering::SeqCst) >= 0 {
CLEARFLAG.store(0, Ordering::SeqCst);
}
MSCROLL.store(0, Ordering::SeqCst);
let listprompt = getsparam("LISTPROMPT");
if mselect >= 0 || MLBEG.load(Ordering::SeqCst) >= 0 || listprompt.is_some() {
trashzle();
SHOWINGLIST.store(0, Ordering::SeqCst);
LISTSHOWN.store(0, Ordering::SeqCst);
LASTLISTLEN.store(0, Ordering::SeqCst);
if listprompt.is_some() {
CLEARFLAG.store(if usezle { 1 } else { 0 }, Ordering::SeqCst);
MSCROLL.store(1, Ordering::SeqCst); } else {
CLEARFLAG.store(1, Ordering::SeqCst); if let Some(m) = MINFO.get() {
if let Ok(mut g) = m.lock() {
g.asked = if listdat_nlines + nlnct <= zterm_lines {
1
} else {
0
};
}
}
}
} else {
let r = crate::ported::zle::compresult::asklist();
if r != 0 {
popheap();
NOSELECT.store(1, Ordering::SeqCst);
return 1;
}
}
let mlbeg = MLBEG.load(Ordering::SeqCst);
if mlbeg >= 0 {
let mhasstat = MHASSTAT.load(Ordering::SeqCst);
let mut new_mlend = mlbeg + zterm_lines - nlnct - mhasstat; let mline = MLINE.load(Ordering::SeqCst);
let mut adjusted_mlbeg = mlbeg;
while mline >= new_mlend {
adjusted_mlbeg += 1; new_mlend += 1;
}
MLBEG.store(adjusted_mlbeg, Ordering::SeqCst);
MLEND.store(new_mlend, Ordering::SeqCst);
} else {
MLEND.store(9_999_999, Ordering::SeqCst); }
if mnew {
MTAB_BEEN_REALLOCATED.store(1, Ordering::SeqCst); let i = (zterm_columns * listdat_nlines) as usize; *MTAB.lock().unwrap() = vec![None; i]; *MGTAB.lock().unwrap() = vec![None; i]; MGTABSIZE.store(i as i32, Ordering::SeqCst); MLASTCOLS.store(zterm_columns, Ordering::SeqCst); MCOLS.store(zterm_columns, Ordering::SeqCst);
MLASTLINES.store(listdat_nlines, Ordering::SeqCst); MLINES.store(listdat_nlines, Ordering::SeqCst);
MMTABP.store(0, Ordering::SeqCst); }
let cap_size = (MAX_CAPLEN.load(Ordering::SeqCst) + 1).max(1) as usize;
*LAST_CAP.lock().unwrap() = String::with_capacity(cap_size);
let cur_onlnct = ONLNCT.load(Ordering::SeqCst);
let inselect = INSELECT.load(Ordering::SeqCst);
let mlbeg_cur = MLBEG.load(Ordering::SeqCst);
let molbeg = MOLBEG.load(Ordering::SeqCst);
let clearflag = CLEARFLAG.load(Ordering::SeqCst);
if !mnew && inselect != 0 && cur_onlnct == nlnct && mlbeg_cur >= 0 && mlbeg_cur == molbeg
{
if NOSELECT.load(Ordering::SeqCst) == 0 {
singledraw(); }
} else if compprintlist(if mselect >= 0 { 1 } else { 0 }) == 0 || clearflag == 0
{
NOSELECT.store(1, Ordering::SeqCst); }
ONLNCT.store(nlnct, Ordering::SeqCst); MOLBEG.store(MLBEG.load(Ordering::SeqCst), Ordering::SeqCst); MOCOL.store(MCOL.load(Ordering::SeqCst), Ordering::SeqCst); MOLINE.store(MLINE.load(Ordering::SeqCst), Ordering::SeqCst);
popheap();
let _ = extendedglob;
NOSELECT.load(Ordering::SeqCst) }
pub static ONLNCT: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1);
pub fn adjust_mcol(wish: i32, tabp: &mut i32, grp: &mut i32) -> i32 {
wish.max(0)
}
#[derive(Default)]
#[allow(non_camel_case_types)]
pub struct menustack {
pub line: String, pub brbeg: Option<Box<crate::ported::zle::comp_h::Brinfo>>, pub brend: Option<Box<crate::ported::zle::comp_h::Brinfo>>, pub nbrbeg: i32, pub nbrend: i32, pub cs: i32, pub acc: i32, pub nmatches: i32, pub mline: i32, pub mlbeg: i32, pub nolist: i32, pub origline: String, pub origcs: i32, pub origll: i32, pub status: String, pub mode: i32, }
#[derive(Default)]
#[allow(non_camel_case_types)]
pub struct menusearch {
pub str: String, pub line: i32, pub col: i32, pub back: i32, pub state: i32, pub ptr: usize, }
pub const MS_OK: i32 = 0; pub const MS_FAILED: i32 = 1; pub const MS_WRAPPED: i32 = 2;
pub const MAX_STATUS: usize = 128;
pub fn setmstatus(
status: &mut String,
sline: &str,
sll: i32,
scs: i32,
csp: Option<&mut i32>,
llp: Option<&mut i32>,
lenp: Option<&mut i32>,
) -> Option<String> {
use std::sync::atomic::Ordering;
let mut ret: Option<String> = None;
let zlemetacs = ZLEMETACS.load(Ordering::SeqCst);
let zlemetall = ZLEMETALL.load(Ordering::SeqCst);
let lastend = crate::ported::zle::compcore::LASTEND.load(Ordering::SeqCst);
let wb = crate::ported::zle::compcore::WB.load(Ordering::SeqCst);
let mut p: String;
let mut s: String;
if let Some(csp_ref) = csp {
*csp_ref = zlemetacs; if let Some(llp_ref) = llp {
*llp_ref = zlemetall;
} if let Some(lenp_ref) = lenp {
*lenp_ref = lastend - wb;
}
let zml = ZLEMETALINE
.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default();
ret = Some(zml.clone());
let wb_u = wb.max(0) as usize;
let cs_u = zlemetacs.max(0) as usize;
p = zml.get(wb_u..cs_u).unwrap_or("").to_string();
if lastend < zlemetacs {
s = String::new(); } else {
let le_u = lastend.max(0) as usize;
s = zml.get(cs_u..le_u).unwrap_or("").to_string(); }
ZLEMETACS.store(0, Ordering::SeqCst); foredel(zlemetall, 0); spaceinline(sll); if let Some(zml_mutex) = ZLEMETALINE.get() {
if let Ok(mut g) = zml_mutex.lock() {
if g.len() >= sll as usize {
let head: String = sline.chars().take(sll as usize).collect();
g.replace_range(..sll as usize, &head); } else {
*g = sline.chars().take(sll as usize).collect();
}
}
}
ZLEMETACS.store(scs, Ordering::SeqCst); } else {
p = crate::ported::zle::complete::COMPLASTPREFIX
.get_or_init(|| std::sync::Mutex::new(String::new()))
.lock()
.unwrap()
.clone();
s = crate::ported::zle::complete::COMPLASTSUFFIX
.get_or_init(|| std::sync::Mutex::new(String::new()))
.lock()
.unwrap()
.clone();
}
let pl = p.len() as i32; let sl = s.len() as i32; let zterm_columns = adjustcolumns() as i32;
let max = if zterm_columns < MAX_STATUS as i32 {
zterm_columns
} else {
MAX_STATUS as i32
} - 14;
if max > 12 {
let h = (max - 2) >> 1;
status.clear();
status.push_str("interactive: "); if pl > h - 3 {
status.push_str("..."); let skip = (pl - h - 3).max(0) as usize;
status.push_str(&p[skip..]); } else {
status.push_str(&p); }
status.push_str("[]"); if sl > h - 3 {
let take = (h - 3).max(0) as usize;
status.push_str(&s.chars().take(take).collect::<String>()); status.push_str("..."); } else {
status.push_str(&s); }
}
ret }
pub fn msearchpush() -> i32 {
0
}
pub fn msearchpop() -> i32 {
let mut stack = MSEARCHSTACK.lock().unwrap();
let popped = match stack.pop() {
Some(s) => s,
None => return 0,
};
*MSEARCHSTR.lock().unwrap() = popped.str.clone();
MLINE.store(popped.line, Ordering::Relaxed);
MCOL.store(popped.col, Ordering::Relaxed);
MSEARCHSTATE.store(popped.state, Ordering::Relaxed);
popped.back
}
pub fn msearch() -> i32 {
let mut x = MCOL.load(Ordering::SeqCst);
let mut y = MLINE.load(Ordering::SeqCst);
let mcols = MCOLS.load(Ordering::SeqCst);
let listdat_nlines = listdat
.get()
.and_then(|m| m.lock().ok().map(|g| g.nlines))
.unwrap_or(0);
let mut wrap = 0i32;
let owrap = MSEARCHSTATE.load(Ordering::SeqCst) & MS_WRAPPED;
let back = 0i32; let (mut ex, mut ey) = if back != 0 {
(mcols - 1, -1i32)
} else {
(0i32, listdat_nlines)
};
let mut p = (y * mcols + x).max(0) as usize;
let needle = MSEARCHSTR.lock().unwrap().clone();
let mtab_snapshot: Vec<Option<Cmatch>> = MTAB.lock().unwrap().clone();
loop {
if let Some(Some(m)) = mtab_snapshot.get(p) {
let hay = m
.disp
.as_deref()
.unwrap_or_else(|| m.str.as_deref().unwrap_or(""));
if !needle.is_empty() && hay.contains(needle.as_str()) {
MCOL.store(x, Ordering::SeqCst); MLINE.store(y, Ordering::SeqCst); return p as i32; }
}
if back != 0 {
if p == 0 {
p = mtab_snapshot.len().saturating_sub(1);
} else {
p -= 1;
}
x -= 1;
if x < 0 {
x = mcols - 1; y -= 1; }
} else {
p += 1; x += 1;
if x == mcols {
x = 0; y += 1; }
}
if x == ex && y == ey {
if back != 0 {
x = mcols - 1; y = listdat_nlines - 1; p = (y * mcols + x).max(0) as usize; } else {
x = 0;
y = 0; p = 0; }
ex = MCOL.load(Ordering::SeqCst); ey = MLINE.load(Ordering::SeqCst);
if wrap != 0 || (x == ex && y == ey) {
MSEARCHSTATE.store(MS_FAILED | owrap, Ordering::SeqCst); break; }
MSEARCHSTATE.fetch_or(MS_WRAPPED, Ordering::SeqCst); wrap = 1; }
if p >= mtab_snapshot.len() {
break;
}
}
-1 }
pub static MSEARCHSTR: std::sync::LazyLock<std::sync::Mutex<String>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(String::new()));
pub static MSEARCHSTATE: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(MS_OK);
pub static MSEARCHSTACK: std::sync::LazyLock<std::sync::Mutex<Vec<menusearch>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(Vec::new()));
pub fn domenuselect() -> i32 {
let mut _i: i32 = 0; let mut _acc: i32 = 0; let mut _wishcol: i32 = 0; let _setwish: i32 = 0; let oe = crate::ported::zle::compcore::onlyexpl.load(Ordering::SeqCst); let mut _wasnext: i32 = 0; let _space: i32 = 0; let _lbeg: i32 = 0; let mut step: i32 = 1; let _wrap: i32 = 0; let _pl = NLNCT.load(Ordering::SeqCst); let _broken: i32 = 0; let _first: i32 = 1; let mut _nolist: i32 = 0; let mut mode: i32 = 0; let _modecs: i32 = 0; let _modell: i32 = 0; let _modelen: i32 = 0; let _wasmeta: i32; let mut status = String::new();
if crate::ported::zle::compcore::hasoldlist.load(std::sync::atomic::Ordering::Relaxed) == 0 {
return 2; }
*MSEARCHSTR.lock().unwrap() = String::new(); MSEARCHSTATE.store(MS_OK, Ordering::SeqCst);
crate::ported::signals::queue_signals();
_wasmeta = if ZLEMETALINE.get().is_some() {
1
} else {
0
};
if let Some(s) = getsparam("MENUSCROLL") {
let parsed: i32 = s.trim().parse().unwrap_or(0);
if parsed == 0 {
let zterm_lines = adjustlines() as i32;
let nlnct = NLNCT.load(Ordering::SeqCst);
step = (zterm_lines - nlnct) >> 1; } else if parsed < 0 {
let zterm_lines = adjustlines() as i32;
let nlnct = NLNCT.load(Ordering::SeqCst);
step = parsed + zterm_lines - nlnct;
if step < 0 {
step = 1;
} } else {
step = parsed;
}
}
if let Some(s) = getsparam("MENUMODE") {
if s == "interactive" {
mode = 1;
let origline = ORIGLINE
.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default();
let l = origline.len() as i32;
ZLEMETACS.store(0, Ordering::SeqCst);
foredel(
ZLEMETALL.load(Ordering::SeqCst),
0,
);
spaceinline(l); if let Some(m) = ZLEMETALINE.get() {
if let Ok(mut g) = m.lock() {
if g.len() >= l as usize {
g.replace_range(..l as usize, &origline); } else {
*g = origline.clone();
}
}
}
ZLEMETACS.store(
ORIGCS.load(Ordering::SeqCst),
Ordering::SeqCst,
);
let _ = setmstatus(&mut status, "", 0, 0, None, None, None); } else if s.starts_with("search") {
mode = if s.contains("back") { 3 } else { 2 }; }
}
let saved_localmap = {
let g = crate::ported::zle::zle_keymap::LOCALKEYMAP.lock().unwrap();
g.clone()
};
if let Some(mskeymap) = crate::ported::zle::zle_keymap::openkeymap("menuselect") {
crate::ported::zle::zle_keymap::selectlocalmap(Some(mskeymap)); }
let _ = &saved_localmap;
{
let mstatus = match getsparam("MENUPROMPT") {
None => String::new(),
Some(s) if s.is_empty() => "%SScrolling active: current selection at %p%s".to_string(),
Some(s) => s,
};
MHASSTAT.store(if mstatus.is_empty() { 0 } else { 1 }, Ordering::SeqCst); *MSTATUS.lock().unwrap() = mstatus;
}
unqueue_signals();
let mut acc = 0i32; let mut broken = 0i32; let mut wishcol = 0i32; let mut setwish = 0i32; let mut wasnext = 0i32; let mut lbeg = 0i32; let mut first = 1i32; let mut nolist = 0i32; let pl = NLNCT.load(Ordering::SeqCst); let mut do_last_key = 0i32; let mut modeline: Option<String> = None; let mut modecs = 0i32; let mut modell = 0i32; let mut modelen = 0i32; let mut lastsearch: Option<String> = None; let mut p: i32 = 0; let mut cmd: Option<crate::ported::zle::zle_thingy::Thingy> = None; let mut goto_getk = false; let mut i_flag = 0i32;
struct MFrame {
line: String, cs: i32, mline: i32, mlbeg: i32, info: crate::ported::zle::comp_h::Menuinfo, amatches: Option<Vec<Cmgroup>>, pmatches: Option<Vec<Cmgroup>>, lastmatches: Option<Vec<Cmgroup>>, lastlmatches: Option<Cmgroup>, nolist: i32, acc: i32, brbeg: Option<Box<crate::ported::zle::comp_h::Brinfo>>, brend: Option<Box<crate::ported::zle::comp_h::Brinfo>>, nbrbeg: i32, nbrend: i32, nmatches: i32, origline: String, origcs: i32, origll: i32, status: String, mode: i32, }
let mut u: Vec<MFrame> = Vec::new();
#[derive(Clone, Copy, PartialEq)]
enum Move {
Down,
Up,
Right,
Left,
Top,
Bottom,
FwdWord,
BwdWord,
BlankFwd,
BlankBwd,
BegLine,
EndLine,
}
let cell = |i: i32| -> Option<Cmatch> {
if i < 0 {
return None;
}
MTAB.lock().unwrap().get(i as usize).cloned().flatten()
};
let gcell = |i: i32| -> Option<Cmgroup> {
if i < 0 {
return None;
}
MGTAB.lock().unwrap().get(i as usize).cloned().flatten()
};
let skipcell = |i: i32| -> bool {
if i < 0 {
return true; }
let mc = MCOLS.load(Ordering::SeqCst);
let t = MTAB.lock().unwrap();
match t.get(i as usize).cloned().flatten() {
None => true, Some(a) => {
if mc > 0 && i % mc != 0 {
if let Some(b) = t.get((i - 1) as usize).cloned().flatten() {
return a.gnum == b.gnum; }
}
false
}
}
};
let same = |a: i32, b: i32| -> bool {
match (cell(a), cell(b)) {
(Some(x), Some(y)) => x.gnum == y.gnum,
_ => false,
}
};
let adjust_mcol = |wish: i32, pin: i32| -> (i32, i32) {
let mc = MCOLS.load(Ordering::SeqCst);
let mcol = MCOL.load(Ordering::SeqCst);
let base = pin - mcol; let mut pp = wish;
while pp >= 0 && skipcell(base + pp) {
pp -= 1;
} let mut n = wish;
while n < mc && skipcell(base + n) {
n += 1;
} if n == mc {
n = -1;
} let c;
if pp < 0 {
if n < 0 {
return (pin, 1);
} c = n; } else if n < 0 {
c = pp; } else {
c = if (mcol - pp) < (n - mcol) { pp } else { n }; }
MCOL.store(c, Ordering::SeqCst); (base + c, 0) };
let cur_gnum = || -> i32 {
MINFO
.get()
.and_then(|g| g.lock().ok())
.and_then(|g| g.cur.as_ref().map(|c| c.gnum))
.unwrap_or(-1)
};
let do_menucmp0 = || {
let mut zm = crate::ported::zle::compcore::ZMULT.load(Ordering::SeqCst);
while zm != 0 {
let ci = MINFO
.get()
.and_then(|g| g.lock().ok())
.map(|g| g.cur_idx)
.unwrap_or(0);
let m = crate::ported::zle::compresult::valid_match(ci, 1);
if let (Some(mm), Some(lk)) = (m, MINFO.get()) {
if let Ok(mut mi) = lk.lock() {
mi.cur = Some(Box::new(mm)); }
}
zm -= (if 0 < zm { 1 } else { 0 }) - (if zm < 0 { 1 } else { 0 });
}
let cur = MINFO
.get()
.and_then(|g| g.lock().ok())
.and_then(|g| g.cur.as_ref().map(|c| (**c).clone()));
if let Some(c) = cur {
crate::ported::zle::compresult::do_single(&c); }
};
let msearch_fn = |pin: i32, ins: Option<&str>, back: bool, rep0: bool| -> (Option<i32>, i32) {
let mc = MCOLS.load(Ordering::SeqCst);
let mut x = MCOL.load(Ordering::SeqCst); let mut y = MLINE.load(Ordering::SeqCst);
let mut wrap = 0i32;
let owrap = MSEARCHSTATE.load(Ordering::SeqCst) & MS_WRAPPED; {
let mut st = MSEARCHSTACK.lock().unwrap();
st.push(menusearch {
str: MSEARCHSTR.lock().unwrap().clone(),
line: MLINE.load(Ordering::SeqCst),
col: MCOL.load(Ordering::SeqCst),
back: if back { 1 } else { 0 },
state: MSEARCHSTATE.load(Ordering::SeqCst),
ptr: pin.max(0) as usize,
});
}
if let Some(s) = ins {
MSEARCHSTR.lock().unwrap().push_str(s); }
let nlines = listdat
.get()
.and_then(|m| m.lock().ok().map(|g| g.nlines))
.unwrap_or(0);
let (mut ex, mut ey) = if back {
(mc - 1, -1) } else {
(0, nlines) };
let mut pp = pin; let mut l_gnum: Option<i32> = None;
let mut rep = rep0;
if rep {
l_gnum = cell(pp).map(|c| c.gnum); }
let needle = MSEARCHSTR.lock().unwrap().clone();
loop {
if !rep {
if let Some(m) = cell(pp) {
if l_gnum != Some(m.gnum) {
l_gnum = Some(m.gnum);
let hay = m
.disp
.as_deref()
.unwrap_or_else(|| m.str.as_deref().unwrap_or(""));
if hay.contains(needle.as_str()) {
MCOL.store(x, Ordering::SeqCst); MLINE.store(y, Ordering::SeqCst); return (Some(pp), wrap); }
}
}
}
rep = false; if back {
pp -= 1;
x -= 1;
if x < 0 {
x = mc - 1;
y -= 1;
}
} else {
pp += 1;
x += 1;
if x == mc {
x = 0;
y += 1;
}
}
if x == ex && y == ey {
if back {
x = mc - 1;
y = nlines - 1;
pp = y * mc + x; } else {
x = 0;
y = 0;
pp = 0; }
ex = MCOL.load(Ordering::SeqCst); ey = MLINE.load(Ordering::SeqCst); if wrap != 0 || (x == ex && y == ey) {
MSEARCHSTATE.store(MS_FAILED | owrap, Ordering::SeqCst); break;
}
MSEARCHSTATE.fetch_or(MS_WRAPPED, Ordering::SeqCst); wrap = 1; }
}
(None, wrap) };
NOSELECT.store(1, Ordering::SeqCst);
loop {
let cur = MINFO
.get()
.and_then(|g| g.lock().ok())
.and_then(|g| g.cur.as_ref().map(|c| (**c).clone()));
let (prebr, postbr) = MINFO
.get()
.and_then(|g| g.lock().ok())
.map(|g| (g.prebr.clone(), g.postbr.clone()))
.unwrap_or((None, None));
let need = match cur {
Some(c) => {
let ma = crate::ported::zle::compcore::menuacc.load(Ordering::SeqCst);
(ma != 0
&& !crate::ported::zle::compresult::hasbrpsfx(
&c,
prebr.as_deref(),
postbr.as_deref(),
))
|| (c.flags & crate::ported::zle::comp_h::CMF_DUMMY) != 0
|| ((c.flags & (CMF_NOLIST | crate::ported::zle::comp_h::CMF_MULT)) != 0
&& c.str.as_deref().map_or(true, |s| s.is_empty()))
}
None => false,
};
if !need {
break;
}
do_menucmp0(); }
MSELECT.store(cur_gnum(), Ordering::SeqCst); MLINE.store(0, Ordering::SeqCst); MLINES.store(999999, Ordering::SeqCst); MLBEG.store(0, Ordering::SeqCst); MOLBEG.store(-42, Ordering::SeqCst); MTAB_BEEN_REALLOCATED.store(0, Ordering::SeqCst);
loop {
if !goto_getk {
if MLINE.load(Ordering::SeqCst) < 0 || MTAB_BEEN_REALLOCATED.load(Ordering::SeqCst) != 0
{
let mcols = MCOLS.load(Ordering::SeqCst);
let mlines = MLINES.load(Ordering::SeqCst);
let msel = MSELECT.load(Ordering::SeqCst);
let mut idx = 0i32;
let mut found_y = mlines;
'yl: for y in 0..mlines {
let mut xx = mcols;
while xx > 0 {
if !skipcell(idx) {
if let Some(c) = cell(idx) {
if c.gnum == msel {
MCOL.store(mcols - xx, Ordering::SeqCst); found_y = y;
break 'yl;
}
}
}
xx -= 1;
idx += 1;
}
}
if found_y < mlines {
MLINE.store(found_y, Ordering::SeqCst); }
}
MTAB_BEEN_REALLOCATED.store(0, Ordering::SeqCst);
while MLINE.load(Ordering::SeqCst) < MLBEG.load(Ordering::SeqCst) {
let nb = MLBEG.load(Ordering::SeqCst) - step;
MLBEG.store(nb, Ordering::SeqCst); if nb < 0 {
MLBEG.store(0, Ordering::SeqCst);
if MLINE.load(Ordering::SeqCst) < 0 {
break;
}
}
}
if MLBEG.load(Ordering::SeqCst) != 0 && lbeg != MLBEG.load(Ordering::SeqCst) {
let cols = MCOLS.load(Ordering::SeqCst);
let mut base = (MLBEG.load(Ordering::SeqCst) - 1) * cols;
while MLBEG.load(Ordering::SeqCst) != 0 {
let mut c = cols;
let mut q = base;
while c > 0 {
if !skipcell(q) {
break;
}
q += 1;
c -= 1;
}
if c != 0 {
break;
}
base -= cols;
MLBEG.store(MLBEG.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
}
}
let zterm_lines = adjustlines() as i32;
let space = zterm_lines - pl - MHASSTAT.load(Ordering::SeqCst);
if space > 0 {
while MLINE.load(Ordering::SeqCst) >= MLBEG.load(Ordering::SeqCst) + space {
let nb = MLBEG.load(Ordering::SeqCst) + step;
MLBEG.store(nb, Ordering::SeqCst);
if nb + space > MLINES.load(Ordering::SeqCst) {
MLBEG.store(MLINES.load(Ordering::SeqCst) - space, Ordering::SeqCst);
}
}
}
if lbeg != MLBEG.load(Ordering::SeqCst) {
let cols = MCOLS.load(Ordering::SeqCst);
let mut base = MLBEG.load(Ordering::SeqCst) * cols;
while MLBEG.load(Ordering::SeqCst) < MLINES.load(Ordering::SeqCst) {
let mut c = cols;
let mut q = base;
while c > 0 {
if cell(q).is_some() {
break;
}
q += 1;
c -= 1;
}
if c != 0 {
break;
}
base += cols;
MLBEG.store(MLBEG.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
}
}
lbeg = MLBEG.load(Ordering::SeqCst);
crate::ported::zle::compcore::onlyexpl.store(0, Ordering::SeqCst); SHOWINGLIST.store(-2, Ordering::SeqCst);
if first != 0
&& LISTSHOWN.load(Ordering::SeqCst) == 0
&& isset(crate::ported::zsh_h::LISTBEEP)
{
crate::ported::utils::zbeep();
}
if first != 0 {
modeline = Some(
ZLEMETALINE
.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default(),
);
modecs = ZLEMETACS.load(Ordering::SeqCst);
modell = ZLEMETALL.load(Ordering::SeqCst);
modelen = MINFO
.get()
.and_then(|g| g.lock().ok())
.map(|g| g.len)
.unwrap_or(0);
}
first = 0;
if mode == 1 {
*STATUSLINE.lock().unwrap() = Some(status.clone());
} else if mode != 0 {
let st = MSEARCHSTATE.load(Ordering::SeqCst);
let failed = if st & MS_FAILED != 0 { "failed " } else { "" };
let wrapped = if st & MS_WRAPPED != 0 { "wrapped " } else { "" };
let dir = if mode == 2 { "" } else { " backward" };
status = format!(
"{}{}isearch{}: {}",
failed,
wrapped,
dir,
MSEARCHSTR.lock().unwrap()
);
*STATUSLINE.lock().unwrap() = Some(status.clone());
} else {
*STATUSLINE.lock().unwrap() = None;
}
if NOSELECT.load(Ordering::SeqCst) < 0 {
SHOWINGLIST.store(0, Ordering::SeqCst);
CLEARLIST.store(0, Ordering::SeqCst);
CLEARFLAG.store(1, Ordering::SeqCst);
}
complistmatches(); *STATUSLINE.lock().unwrap() = None;
INSELECT.store(1, Ordering::SeqCst); SELECTED.store(1, Ordering::SeqCst);
let nosel = NOSELECT.load(Ordering::SeqCst);
if nosel != 0 {
if nosel < 0 {
NOSELECT.store(0, Ordering::SeqCst); goto_getk = true; continue;
}
broken = 1; break; }
if i_flag == 0 {
let total = MCOLS.load(Ordering::SeqCst) * MLINES.load(Ordering::SeqCst);
let mut k = total;
let mut any = false;
while k > 0 {
k -= 1;
if cell(k).is_some() {
any = true;
break;
}
}
if !any {
break; }
i_flag = 1;
}
p = MMTABP.load(Ordering::SeqCst) as i32; if let Some(c) = cell(p) {
let g = gcell(p);
if let Ok(mut mi) = MINFO
.get_or_init(|| {
std::sync::Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())
})
.lock()
{
mi.cur = Some(Box::new(c)); mi.group = g.map(Box::new); }
}
let cg = cur_gnum();
if setwish != 0 {
wishcol = MCOL.load(Ordering::SeqCst); } else if MCOL.load(Ordering::SeqCst) > wishcol {
while MCOL.load(Ordering::SeqCst) > 0 && cell(p - 1).map_or(false, |c| c.gnum == cg)
{
MCOL.store(MCOL.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
p -= 1;
}
} else if MCOL.load(Ordering::SeqCst) < wishcol {
while MCOL.load(Ordering::SeqCst) < MCOLS.load(Ordering::SeqCst) - 1
&& cell(p + 1).map_or(false, |c| c.gnum == cg)
{
MCOL.store(MCOL.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
p += 1;
}
}
setwish = 0;
wasnext = 0; }
goto_getk = false;
if do_last_key == 0 {
crate::ported::zle::compcore::ZMULT.store(1, Ordering::SeqCst); cmd = crate::ported::zle::zle_keymap::getkeycmd(); if MTAB_BEEN_REALLOCATED.load(Ordering::SeqCst) != 0 {
do_last_key = 1; continue;
}
}
do_last_key = 0; let was_inter = mode == 1; let name = cmd.as_ref().map(|t| t.nam.clone()).unwrap_or_default();
let mut movement: Option<Move> = None;
let mut wrap = 0i32;
if name.is_empty() || name == "send-break" {
crate::ported::utils::zbeep();
MOLBEG.store(-1, Ordering::SeqCst);
broken = 1;
break;
} else if nolist != 0
&& name != "undo"
&& (mode == 0
|| (name != "backward-delete-char"
&& name != "self-insert"
&& name != "self-insert-unmeta"))
{
crate::ported::zle::zle_keymap::ungetkeycmd();
break;
} else if name == "accept-line" || name == "accept-search" {
if mode == 2 || mode == 3 {
mode = 0;
continue;
}
acc = 1;
break;
} else if name == "vi-insert" {
if mode == 1 {
mode = 0; } else {
mode = 1;
let origline = ORIGLINE
.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default();
let l = origline.len() as i32;
ZLEMETACS.store(0, Ordering::SeqCst);
foredel(ZLEMETALL.load(Ordering::SeqCst), 0);
spaceinline(l);
if let Some(m) = ZLEMETALINE.get() {
if let Ok(mut g) = m.lock() {
if g.len() >= l as usize {
g.replace_range(..l as usize, &origline);
} else {
*g = origline.clone();
}
}
}
ZLEMETACS.store(ORIGCS.load(Ordering::SeqCst), Ordering::SeqCst);
let _ = setmstatus(&mut status, "", 0, 0, None, None, None);
continue;
}
} else if name == "accept-and-infer-next-history"
|| (mode == 1 && (name == "self-insert" || name == "self-insert-unmeta"))
{
continue;
} else if name == "accept-and-hold" || name == "accept-and-menu-complete" {
if mode == 1 {
let cur = MINFO
.get()
.and_then(|g| g.lock().ok())
.and_then(|g| g.cur.as_ref().map(|c| (**c).clone()));
if let Some(lk) = MINFO.get() {
if let Ok(mut mi) = lk.lock() {
mi.cur = None;
}
}
if let Some(c) = &cur {
crate::ported::zle::compresult::do_single(c);
}
if let (Some(lk), Some(c)) = (MINFO.get(), cur) {
if let Ok(mut mi) = lk.lock() {
mi.cur = Some(Box::new(c));
}
}
}
mode = 0;
let info = MINFO
.get()
.and_then(|g| g.lock().ok())
.map(|g| g.clone())
.unwrap_or_default();
u.push(MFrame {
line: ZLEMETALINE
.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default(),
cs: ZLEMETACS.load(Ordering::SeqCst),
mline: MLINE.load(Ordering::SeqCst),
mlbeg: MLBEG.load(Ordering::SeqCst),
info,
amatches: None, pmatches: None,
lastmatches: None,
lastlmatches: None,
nolist,
acc: crate::ported::zle::compcore::menuacc.load(Ordering::SeqCst),
brbeg: crate::ported::zle::compcore::BRBEG
.get()
.and_then(|m| m.lock().ok())
.and_then(|g| g.clone()),
brend: crate::ported::zle::compcore::BREND
.get()
.and_then(|m| m.lock().ok())
.and_then(|g| g.clone()),
nbrbeg: NBRBEG.load(Ordering::SeqCst),
nbrend: NBREND.load(Ordering::SeqCst),
nmatches: crate::ported::zle::compcore::nmatches.load(Ordering::SeqCst),
origline: ORIGLINE
.get()
.and_then(|m| m.lock().ok().map(|g| g.clone()))
.unwrap_or_default(),
origcs: ORIGCS.load(Ordering::SeqCst),
origll: ORIGLL.load(Ordering::SeqCst),
status: status.clone(),
mode,
});
crate::ported::zle::compresult::accept_last(); handleundo();
COMPRECURSIVE.store(1, Ordering::SeqCst); do_menucmp0(); MSELECT.store(cur_gnum(), Ordering::SeqCst);
p -= MCOL.load(Ordering::SeqCst);
MCOL.store(0, Ordering::SeqCst);
let ol = MLINE.load(Ordering::SeqCst);
let cg = cur_gnum();
loop {
MCOL.store(0, Ordering::SeqCst);
let mcols = MCOLS.load(Ordering::SeqCst);
let mut found = false;
while MCOL.load(Ordering::SeqCst) < mcols {
if cell(p).map_or(false, |c| c.gnum == cg) {
found = true;
break;
}
MCOL.store(MCOL.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
p += 1;
}
if found {
break;
}
MLINE.store(MLINE.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
if MLINE.load(Ordering::SeqCst) == MLINES.load(Ordering::SeqCst) {
MLINE.store(0, Ordering::SeqCst);
p -= MLINES.load(Ordering::SeqCst) * mcols;
}
if MLINE.load(Ordering::SeqCst) == ol {
break;
}
}
if !cell(p).map_or(false, |c| c.gnum == cg) {
NOSELECT.store(1, Ordering::SeqCst);
CLEARLIST.store(1, Ordering::SeqCst);
LISTSHOWN.store(1, Ordering::SeqCst);
crate::ported::zle::compcore::onlyexpl.store(0, Ordering::SeqCst);
complistmatches();
break;
}
setwish = 1; continue;
} else if name == "undo" || (mode == 1 && name == "backward-delete-char") {
let frame = match u.pop() {
Some(f) => f,
None => break, };
handleundo();
ZLEMETACS.store(0, Ordering::SeqCst);
foredel(ZLEMETALL.load(Ordering::SeqCst), 0);
let l = frame.line.len() as i32;
spaceinline(l);
if let Some(m) = ZLEMETALINE.get() {
if let Ok(mut g) = m.lock() {
if g.len() >= l as usize {
g.replace_range(..l as usize, &frame.line);
} else {
*g = frame.line.clone();
}
}
}
ZLEMETACS.store(frame.cs, Ordering::SeqCst);
crate::ported::zle::compcore::menuacc.store(frame.acc, Ordering::SeqCst);
if let Ok(mut mi) = MINFO
.get_or_init(|| {
std::sync::Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())
})
.lock()
{
*mi = frame.info.clone(); }
MLINE.store(frame.mline, Ordering::SeqCst);
MLBEG.store(frame.mlbeg, Ordering::SeqCst);
if let Some(am) = frame.amatches {
if let Some(m) = crate::ported::zle::compcore::amatches.get() {
*m.lock().unwrap() = am;
}
if let (Some(pm), Some(m)) =
(frame.pmatches, crate::ported::zle::compcore::pmatches.get())
{
*m.lock().unwrap() = pm;
}
if let (Some(lm), Some(m)) = (
frame.lastmatches,
crate::ported::zle::compcore::lastmatches.get(),
) {
*m.lock().unwrap() = lm;
}
if let Some(m) = crate::ported::zle::compcore::lastlmatches.get() {
*m.lock().unwrap() = frame.lastlmatches;
}
crate::ported::zle::compcore::nmatches.store(frame.nmatches, Ordering::SeqCst);
crate::ported::zle::compcore::hasoldlist.store(1, Ordering::SeqCst);
VALIDLIST.store(1, Ordering::SeqCst);
}
if let Some(m) = crate::ported::zle::compcore::BRBEG.get() {
*m.lock().unwrap() = frame.brbeg;
}
if let Some(m) = crate::ported::zle::compcore::BREND.get() {
*m.lock().unwrap() = frame.brend;
}
NBRBEG.store(frame.nbrbeg, Ordering::SeqCst);
NBREND.store(frame.nbrend, Ordering::SeqCst);
if let Some(m) = ORIGLINE.get() {
*m.lock().unwrap() = frame.origline.clone();
}
ORIGCS.store(frame.origcs, Ordering::SeqCst);
ORIGLL.store(frame.origll, Ordering::SeqCst);
status = frame.status.clone();
mode = frame.mode;
nolist = frame.nolist;
CLEARLIST.store(1, Ordering::SeqCst); setwish = 1;
if let Some(m) = listdat.get() {
if let Ok(mut g) = m.lock() {
g.valid = 0;
}
}
MOLBEG.store(-42, Ordering::SeqCst);
if nolist != 0 {
if mode == 1 {
*STATUSLINE.lock().unwrap() = Some(status.clone());
} else {
*STATUSLINE.lock().unwrap() = None;
}
zrefresh();
*STATUSLINE.lock().unwrap() = None;
goto_getk = true;
continue;
}
if mode != 0 {
continue; }
let cur = MINFO
.get()
.and_then(|g| g.lock().ok())
.and_then(|g| g.cur.as_ref().map(|c| (**c).clone()));
if let Some(c) = cur {
crate::ported::zle::compresult::do_single(&c);
MSELECT.store(c.gnum, Ordering::SeqCst);
}
continue;
} else if name == "redisplay" {
redisplay();
MOLBEG.store(-42, Ordering::SeqCst);
continue;
} else if name == "clear-screen" {
clearscreen();
MOLBEG.store(-42, Ordering::SeqCst);
continue;
} else if name == "down-history"
|| name == "down-line-or-history"
|| name == "down-line-or-search"
|| name == "vi-down-line-or-history"
{
mode = 0;
wrap = 0;
movement = Some(Move::Down);
} else if name == "up-history"
|| name == "up-line-or-history"
|| name == "up-line-or-search"
|| name == "vi-up-line-or-history"
{
mode = 0;
wrap = 0;
movement = Some(Move::Up);
} else if name == "emacs-forward-word"
|| name == "vi-forward-word"
|| name == "vi-forward-word-end"
|| name == "forward-word"
{
mode = 0;
movement = Some(Move::FwdWord);
} else if name == "emacs-backward-word"
|| name == "vi-backward-word"
|| name == "backward-word"
{
mode = 0;
movement = Some(Move::BwdWord);
} else if name == "beginning-of-history" {
mode = 0;
movement = Some(Move::Top);
} else if name == "end-of-history" {
mode = 0;
movement = Some(Move::Bottom);
} else if name == "forward-char" || name == "vi-forward-char" {
mode = 0;
wrap = 0;
movement = Some(Move::Right);
} else if name == "backward-char" || name == "vi-backward-char" {
mode = 0;
wrap = 0;
movement = Some(Move::Left);
} else if name == "beginning-of-buffer-or-history"
|| name == "beginning-of-line"
|| name == "beginning-of-line-hist"
|| name == "vi-beginning-of-line"
{
mode = 0;
movement = Some(Move::BegLine);
} else if name == "end-of-buffer-or-history"
|| name == "end-of-line"
|| name == "end-of-line-hist"
|| name == "vi-end-of-line"
{
mode = 0;
movement = Some(Move::EndLine);
} else if name == "vi-forward-blank-word" || name == "vi-forward-blank-word-end" {
mode = 0;
movement = Some(Move::BlankFwd);
} else if name == "vi-backward-blank-word" {
mode = 0;
movement = Some(Move::BlankBwd);
} else if name == "complete-word"
|| name == "expand-or-complete"
|| name == "expand-or-complete-prefix"
|| name == "menu-complete"
|| name == "menu-expand-or-complete"
|| name == "menu-select"
{
if mode == 1 {
let ml = modeline.clone().unwrap_or_default();
if let Some(m) = ORIGLINE.get() {
*m.lock().unwrap() = ml.clone();
}
ORIGCS.store(modecs, Ordering::SeqCst);
ORIGLL.store(modell, Ordering::SeqCst);
ZLEMETACS.store(0, Ordering::SeqCst);
foredel(ZLEMETALL.load(Ordering::SeqCst), 0);
spaceinline(modell);
if let Some(m) = ZLEMETALINE.get() {
if let Ok(mut g) = m.lock() {
if g.len() >= modell as usize {
g.replace_range(..modell as usize, &ml);
} else {
*g = ml.clone();
}
}
}
ZLEMETACS.store(modecs, Ordering::SeqCst);
if let Some(lk) = MINFO.get() {
if let Ok(mut mi) = lk.lock() {
mi.len = modelen;
}
}
crate::ported::zle::compcore::WE.store(
crate::ported::zle::compcore::WB.load(Ordering::SeqCst) + modelen,
Ordering::SeqCst,
);
} else {
mode = 0;
COMPRECURSIVE.store(1, Ordering::SeqCst); do_menucmp0(); MSELECT.store(cur_gnum(), Ordering::SeqCst);
setwish = 1;
MLINE.store(-1, Ordering::SeqCst);
}
continue;
} else if name == "reverse-menu-complete" {
mode = 0;
COMPRECURSIVE.store(1, Ordering::SeqCst); crate::ported::zle::compcore::ZMULT.store(
-crate::ported::zle::compcore::ZMULT.load(Ordering::SeqCst),
Ordering::SeqCst,
);
do_menucmp0(); MSELECT.store(cur_gnum(), Ordering::SeqCst);
setwish = 1;
MLINE.store(-1, Ordering::SeqCst);
continue;
} else if name == "history-incremental-search-forward"
|| name == "history-incremental-search-backward"
|| ((mode == 2 || mode == 3)
&& (name == "self-insert"
|| name == "self-insert-unmeta"
|| name == "bracketed-paste"))
{
let op = p;
let was = mode == 2 || mode == 3;
let ins =
name == "self-insert" || name == "self-insert-unmeta" || name == "bracketed-paste";
let back = name == "history-incremental-search-backward";
loop {
let mut toins: Option<String> = None;
if was {
p += wishcol - MCOL.load(Ordering::SeqCst);
MCOL.store(wishcol, Ordering::SeqCst);
}
if !ins {
if was {
let empty = MSEARCHSTR.lock().unwrap().is_empty();
if empty {
if let Some(ls) = lastsearch.clone() {
if back == (mode == 3) {
*MSEARCHSTR.lock().unwrap() = ls;
mode = 0;
}
}
}
} else {
*MSEARCHSTR.lock().unwrap() = String::new();
MSEARCHSTACK.lock().unwrap().clear();
MSEARCHSTATE.store(MS_OK, Ordering::SeqCst);
}
} else {
if name == "self-insert-unmeta" {
fixunmeta();
}
if name == "bracketed-paste" {
toins = Some(bracketedstring());
} else {
let lc = crate::ported::zle::compcore::LASTCHAR.load(Ordering::SeqCst);
toins = Some(((lc as u8) as char).to_string());
}
}
let (np, wrapf) = msearch_fn(
p,
toins.as_deref(),
if ins { mode == 3 } else { back },
was && !ins,
);
if !ins {
mode = if back { 3 } else { 2 };
}
if !MSEARCHSTR.lock().unwrap().is_empty() {
lastsearch = Some(MSEARCHSTR.lock().unwrap().clone());
}
if let Some(npi) = np {
wishcol = MCOL.load(Ordering::SeqCst);
p = npi;
}
let (np2, _) = adjust_mcol(wishcol, p);
p = np2;
let cont = (back || name == "history-incremental-search-forward")
&& np.is_some()
&& wrapf == 0
&& was
&& same(p, op);
if !cont {
break;
}
}
} else if (mode == 2 || mode == 3) && name == "backward-delete-char" {
let mut back = 1i32;
let mut ptr: Option<i32> = None;
{
let mut st = MSEARCHSTACK.lock().unwrap();
let info = st
.last()
.map(|s| (s.str.clone(), s.line, s.col, s.state, s.back, s.ptr));
match info {
Some((str_, line, col, state, bk, pr)) => {
*MSEARCHSTR.lock().unwrap() = str_;
MLINE.store(line, Ordering::SeqCst);
MCOL.store(col, Ordering::SeqCst);
MSEARCHSTATE.store(state, Ordering::SeqCst);
if st.len() > 1 {
st.pop();
}
back = bk;
ptr = Some(pr as i32);
}
None => {
back = 1;
ptr = None;
}
}
}
mode = if back != 0 { 3 } else { 2 };
wishcol = MCOL.load(Ordering::SeqCst);
if let Some(pi) = ptr {
p = pi;
let (np, _) = adjust_mcol(wishcol, p);
p = np;
}
} else if name == "undefined-key" {
mode = 0;
continue;
} else {
crate::ported::zle::zle_keymap::ungetkeycmd();
let ncomp = cmd
.as_ref()
.and_then(|t| t.widget.as_ref())
.map(|w| (w.flags & crate::ported::zle::zle_h::WIDGET_NCOMP) != 0)
.unwrap_or(false);
if ncomp {
acc = 0;
broken = 2;
} else {
acc = 1;
}
break;
}
if let Some(mv0) = movement {
let mut mv = mv0;
'nav: loop {
match mv {
Move::Down => {
let omline = MLINE.load(Ordering::SeqCst);
let op = p;
loop {
if MLINE.load(Ordering::SeqCst) == MLINES.load(Ordering::SeqCst) - 1 {
if wrap & 2 != 0 {
MLINE.store(omline, Ordering::SeqCst);
p = op;
break;
}
p -= MLINE.load(Ordering::SeqCst) * MCOLS.load(Ordering::SeqCst);
MLINE.store(0, Ordering::SeqCst);
wrap |= 1;
} else {
MLINE.store(MLINE.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
p += MCOLS.load(Ordering::SeqCst);
}
let (np, r) = adjust_mcol(wishcol, p);
p = np;
if r != 0 {
continue;
}
if skipcell(p) {
continue;
}
break;
}
if wrap == 1 {
mv = Move::Right;
continue 'nav;
}
break 'nav;
}
Move::Up => {
let omline = MLINE.load(Ordering::SeqCst);
let op = p;
loop {
if MLINE.load(Ordering::SeqCst) == 0 {
if wrap & 2 != 0 {
MLINE.store(omline, Ordering::SeqCst);
p = op;
break;
}
MLINE.store(MLINES.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
p += MLINE.load(Ordering::SeqCst) * MCOLS.load(Ordering::SeqCst);
wrap |= 1;
} else {
MLINE.store(MLINE.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
p -= MCOLS.load(Ordering::SeqCst);
}
let (np, r) = adjust_mcol(wishcol, p);
p = np;
if r != 0 {
continue;
}
if skipcell(p) {
continue;
}
break;
}
if wrap == 1 {
if MCOL.load(Ordering::SeqCst) == wishcol {
mv = Move::Left;
continue 'nav;
}
wishcol = MCOL.load(Ordering::SeqCst);
}
break 'nav;
}
Move::Right => {
let omcol = MCOL.load(Ordering::SeqCst);
let op = p;
loop {
if MCOL.load(Ordering::SeqCst) == MCOLS.load(Ordering::SeqCst) - 1 {
if wrap & 1 != 0 {
p = op;
MCOL.store(omcol, Ordering::SeqCst);
break;
}
p -= MCOL.load(Ordering::SeqCst);
MCOL.store(0, Ordering::SeqCst);
wrap |= 2;
} else {
MCOL.store(MCOL.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
p += 1;
}
if skipcell(p) {
continue;
}
if MCOL.load(Ordering::SeqCst) != omcol && same(p, op) {
continue;
}
break;
}
wishcol = MCOL.load(Ordering::SeqCst);
if wrap == 2 {
mv = Move::Down;
continue 'nav;
}
break 'nav;
}
Move::Left => {
let omcol = MCOL.load(Ordering::SeqCst);
let op = p;
loop {
if MCOL.load(Ordering::SeqCst) == 0 {
if wrap & 1 != 0 {
p = op;
MCOL.store(omcol, Ordering::SeqCst);
break;
}
MCOL.store(MCOLS.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
p += MCOL.load(Ordering::SeqCst);
wrap |= 2;
} else {
MCOL.store(MCOL.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
p -= 1;
}
if skipcell(p) {
continue;
}
if MCOL.load(Ordering::SeqCst) != omcol && same(p, op) {
continue;
}
break;
}
wishcol = MCOL.load(Ordering::SeqCst);
if wrap == 2 {
p += MCOLS.load(Ordering::SeqCst) - 1 - MCOL.load(Ordering::SeqCst);
MCOL.store(MCOLS.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
wishcol = MCOLS.load(Ordering::SeqCst) - 1;
let (np, _) = adjust_mcol(wishcol, p);
p = np;
mv = Move::Up;
continue 'nav;
}
break 'nav;
}
Move::Top => {
let mut ll = MLINE.load(Ordering::SeqCst);
let mut lp = p;
while MLINE.load(Ordering::SeqCst) != 0 {
MLINE.store(MLINE.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
p -= MCOLS.load(Ordering::SeqCst);
let (np, r) = adjust_mcol(wishcol, p);
p = np;
if r != 0 {
continue;
}
if !skipcell(p) {
lp = p;
ll = MLINE.load(Ordering::SeqCst);
}
}
MLINE.store(ll, Ordering::SeqCst);
p = lp;
break 'nav;
}
Move::Bottom => {
let mut ll = MLINE.load(Ordering::SeqCst);
let mut lp = p;
while MLINE.load(Ordering::SeqCst) < MLINES.load(Ordering::SeqCst) - 1 {
MLINE.store(MLINE.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
p += MCOLS.load(Ordering::SeqCst);
let (np, r) = adjust_mcol(wishcol, p);
p = np;
if r != 0 {
continue;
}
if !skipcell(p) {
lp = p;
ll = MLINE.load(Ordering::SeqCst);
}
}
MLINE.store(ll, Ordering::SeqCst);
p = lp;
break 'nav;
}
Move::FwdWord => {
let zl = adjustlines() as i32;
let oi = zl - pl - 1;
let mut ic = oi;
let mut ll = 0i32;
let mut lp: Option<i32> = None;
if MLINE.load(Ordering::SeqCst) == MLINES.load(Ordering::SeqCst) - 1 {
mv = Move::Top;
continue 'nav;
}
let mut goto_top = false;
while ic > 0 {
if MLINE.load(Ordering::SeqCst) == MLINES.load(Ordering::SeqCst) - 1 {
if ic != oi && lp.is_some() {
break;
}
goto_top = true;
break;
} else {
MLINE.store(MLINE.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
p += MCOLS.load(Ordering::SeqCst);
}
let (np, r) = adjust_mcol(wishcol, p);
p = np;
if r != 0 {
continue;
}
if !skipcell(p) {
ic -= 1;
lp = Some(p);
ll = MLINE.load(Ordering::SeqCst);
}
}
if goto_top {
mv = Move::Top;
continue 'nav;
}
if let Some(x) = lp {
p = x;
}
MLINE.store(ll, Ordering::SeqCst);
break 'nav;
}
Move::BwdWord => {
let zl = adjustlines() as i32;
let oi = zl - pl - 1;
let mut ic = oi;
let mut ll = 0i32;
let mut lp: Option<i32> = None;
if MLINE.load(Ordering::SeqCst) == 0 {
mv = Move::Bottom;
continue 'nav;
}
let mut goto_bottom = false;
while ic > 0 {
if MLINE.load(Ordering::SeqCst) == 0 {
if ic != oi && lp.is_some() {
break;
}
goto_bottom = true;
break;
} else {
MLINE.store(MLINE.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
p -= MCOLS.load(Ordering::SeqCst);
}
let (np, r) = adjust_mcol(wishcol, p);
p = np;
if r != 0 {
continue;
}
ic -= 1;
lp = Some(p);
ll = MLINE.load(Ordering::SeqCst);
}
if goto_bottom {
mv = Move::Bottom;
continue 'nav;
}
if let Some(x) = lp {
p = x;
}
MLINE.store(ll, Ordering::SeqCst);
break 'nav;
}
Move::BlankFwd => {
let g0 = gcell(p).map(|x| x.num);
let ol = MLINE.load(Ordering::SeqCst);
loop {
if MLINE.load(Ordering::SeqCst) == MLINES.load(Ordering::SeqCst) - 1 {
p -= MLINE.load(Ordering::SeqCst) * MCOLS.load(Ordering::SeqCst);
MLINE.store(0, Ordering::SeqCst);
} else {
MLINE.store(MLINE.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
p += MCOLS.load(Ordering::SeqCst);
}
let (np, _) = adjust_mcol(wishcol, p);
p = np;
let grp_eq = gcell(p).map(|x| x.num) == g0;
if ol != MLINE.load(Ordering::SeqCst) && (grp_eq || skipcell(p)) {
continue;
}
break;
}
break 'nav;
}
Move::BlankBwd => {
let g0 = gcell(p).map(|x| x.num);
let ol = MLINE.load(Ordering::SeqCst);
loop {
if MLINE.load(Ordering::SeqCst) == 0 {
MLINE.store(MLINES.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
p += MLINE.load(Ordering::SeqCst) * MCOLS.load(Ordering::SeqCst);
} else {
MLINE.store(MLINE.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
p -= MCOLS.load(Ordering::SeqCst);
}
let (np, _) = adjust_mcol(wishcol, p);
p = np;
let grp_eq = gcell(p).map(|x| x.num) == g0;
if ol != MLINE.load(Ordering::SeqCst) && (grp_eq || skipcell(p)) {
continue;
}
break;
}
break 'nav;
}
Move::BegLine => {
p -= MCOL.load(Ordering::SeqCst);
MCOL.store(0, Ordering::SeqCst);
while skipcell(p) {
MCOL.store(MCOL.load(Ordering::SeqCst) + 1, Ordering::SeqCst);
p += 1;
}
wishcol = 0;
break 'nav;
}
Move::EndLine => {
p += MCOLS.load(Ordering::SeqCst) - MCOL.load(Ordering::SeqCst) - 1;
MCOL.store(MCOLS.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
while skipcell(p) {
MCOL.store(MCOL.load(Ordering::SeqCst) - 1, Ordering::SeqCst);
p -= 1;
}
wishcol = MCOLS.load(Ordering::SeqCst) - 1;
break 'nav;
}
}
}
}
if was_inter {
if let Some(lk) = MINFO.get() {
if let Ok(mut mi) = lk.lock() {
mi.cur = None;
}
}
}
if let Some(c) = cell(p) {
crate::ported::zle::compresult::do_single(&c); MSELECT.store(c.gnum, Ordering::SeqCst); }
}
u.clear();
crate::ported::zle::zle_keymap::selectlocalmap(None); MSELECT.store(-1, Ordering::SeqCst); MLASTCOLS.store(-1, Ordering::SeqCst);
MLASTLINES.store(-1, Ordering::SeqCst);
*MSTATUS.lock().unwrap() = String::new(); INSELECT.store(0, Ordering::SeqCst); MHASSTAT.store(0, Ordering::SeqCst);
if nolist != 0 {
CLEARLIST.store(1, Ordering::SeqCst);
LISTSHOWN.store(1, Ordering::SeqCst);
}
let validlist = VALIDLIST.load(Ordering::SeqCst);
let cur = MINFO
.get()
.and_then(|g| g.lock().ok())
.and_then(|g| g.cur.as_ref().map(|c| (**c).clone()));
if acc != 0 && validlist != 0 && cur.is_some() {
MENUCMP.store(0, Ordering::SeqCst);
LASTAMBIG.store(0, Ordering::SeqCst);
crate::ported::zle::compcore::hasoldlist.store(0, Ordering::SeqCst);
if mode == 1 {
if let Some(lk) = MINFO.get() {
if let Ok(mut mi) = lk.lock() {
mi.cur = None;
}
}
}
if let Some(c) = &cur {
crate::ported::zle::compresult::do_single(c);
}
}
if wasnext != 0 || broken != 0 {
MENUCMP.store(1, Ordering::SeqCst);
SHOWINGLIST.store(
if validlist != 0 && nolist == 0 { -2 } else { 0 },
Ordering::SeqCst,
);
if let Some(lk) = MINFO.get() {
if let Ok(mut mi) = lk.lock() {
mi.asked = 0;
}
}
if NOSELECT.load(Ordering::SeqCst) == 0 {
let nos = NOSELECT.load(Ordering::SeqCst);
zrefresh();
NOSELECT.store(nos, Ordering::SeqCst);
}
}
if NOSELECT.load(Ordering::SeqCst) == 0 {
MLBEG.store(-1, Ordering::SeqCst);
SHOWINGLIST.store(
if validlist != 0 && nolist == 0 { -2 } else { 0 },
Ordering::SeqCst,
);
crate::ported::zle::compcore::onlyexpl.store(oe, Ordering::SeqCst);
if acc != 0 && LISTSHOWN.load(Ordering::SeqCst) != 0 {
CLEARLIST.store(1, Ordering::SeqCst);
LISTSHOWN.store(1, Ordering::SeqCst);
SHOWINGLIST.store(1, Ordering::SeqCst);
} else if crate::ported::zle::compcore::smatches.load(Ordering::SeqCst) == 0 {
CLEARLIST.store(1, Ordering::SeqCst);
LISTSHOWN.store(1, Ordering::SeqCst);
}
zrefresh();
}
MLBEG.store(-1, Ordering::SeqCst);
let _ = step;
if broken == 2 {
3
} else {
((NOSELECT.load(Ordering::SeqCst) == 0) as i32) ^ acc
}
}
pub fn menuselect() -> i32 {
menucomplete(&[])
}
pub fn setup_() -> i32 {
0
}
pub fn features_() -> i32 {
0
}
pub fn enables_() -> i32 {
0
}
pub fn menuselect_bindings() -> i32 {
use crate::ported::zle::zle_keymap::{linkkeymap, newkeymap, openkeymap, Keymap};
use crate::ported::zle::zle_thingy::Thingy;
use std::sync::Arc;
let bind = |km: &mut Keymap, seq: &[u8], name: &str| {
km.bind_seq(seq, Thingy::builtin(name));
};
if openkeymap("menuselect").is_none() {
let mut mskeymap = newkeymap(None, "menuselect");
let km = Arc::get_mut(&mut mskeymap).unwrap();
bind(km, b"\t", "complete-word"); bind(km, b"\n", "accept-line"); bind(km, b"\r", "accept-line"); bind(km, b"\x1b[A", "up-line-or-history"); bind(km, b"\x1b[B", "down-line-or-history"); bind(km, b"\x1b[C", "forward-char"); bind(km, b"\x1b[D", "backward-char"); bind(km, b"\x1bOA", "up-line-or-history"); bind(km, b"\x1bOB", "down-line-or-history"); bind(km, b"\x1bOC", "forward-char"); bind(km, b"\x1bOD", "backward-char"); linkkeymap(mskeymap, "menuselect", 1); }
if openkeymap("listscroll").is_none() {
let mut lskeymap = newkeymap(None, "listscroll");
let km = Arc::get_mut(&mut lskeymap).unwrap();
bind(km, b"\t", "complete-word"); bind(km, b" ", "complete-word"); bind(km, b"\n", "accept-line"); bind(km, b"\r", "accept-line"); bind(km, b"\x1b[B", "down-line-or-history"); bind(km, b"\x1bOB", "down-line-or-history"); linkkeymap(lskeymap, "listscroll", 1); }
0
}
pub fn boot_() -> i32 {
MTAB.lock().unwrap().clear(); MGTAB.lock().unwrap().clear(); MSELECT.store(-1, Ordering::Relaxed); INSELECT.store(0, Ordering::Relaxed);
if crate::ported::zle::zle_keymap::keymapnamtab()
.lock()
.map(|t| !t.contains_key("main"))
.unwrap_or(false)
{
crate::ported::zle::zle_keymap::default_bindings();
}
menuselect_bindings();
0 }
pub fn cleanup_() -> i32 {
0
}
pub fn finish_() -> i32 {
0
}
pub static LAST_CAP: std::sync::LazyLock<std::sync::Mutex<String>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(String::new()));
pub static PATCOLS: std::sync::LazyLock<std::sync::Mutex<Vec<String>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(Vec::new()));
pub static PATCOLS_IDX: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
pub static BEGPOS: std::sync::LazyLock<std::sync::Mutex<Vec<i32>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(vec![0xfffffff_i32; 11]));
pub static ENDPOS: std::sync::LazyLock<std::sync::Mutex<Vec<i32>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(vec![0xfffffff_i32; 11]));
pub static SENDPOS: std::sync::LazyLock<std::sync::Mutex<Vec<i32>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(vec![0xfffffff_i32; 11]));
pub static CURISCOLS: std::sync::LazyLock<std::sync::Mutex<Vec<String>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(vec![String::new(); 11]));
pub static COLNAMES: &[&str] = &[
"no", "fi", "di", "ln", "pi", "so", "bd", "cd", "or", "mi", "su", "sg", "tw", "ow", "st", "ex",
"lc", "rc", "ec", "tc", "sp", "ma", "hi", "du", "sa",
];
pub static DEFCOLS: &[Option<&str>] = &[
Some("0"),
Some("0"),
Some("1;31"),
Some("1;36"),
Some("33"),
Some("1;35"),
Some("1;33"),
Some("1;33"),
None,
None,
Some("37;41"),
Some("30;43"),
Some("30;42"),
Some("34;42"),
Some("37;44"),
Some("1;32"),
Some("\x1b["),
Some("m"),
None,
Some("0"),
Some("0"),
Some("7"),
None,
None,
Some("0"),
];
pub const LC_FOLLOW_SYMLINKS: i32 = 0x0001;
pub static COMPRECURSIVE: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static NOSELECT: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MSELECT: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1); pub static INSELECT: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MCOL: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MLINE: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MCOLS: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MLINES: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static SELECTED: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MLBEG: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-1); pub static MLEND: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(9_999_999); pub static MSCROLL: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MRESTLINES: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static MNEW: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MLASTCOLS: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MLASTLINES: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MHASSTAT: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MFIRSTL: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MLASTM: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static MLPRINTED: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MOLBEG: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(-2); pub static MOCOL: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MOLINE: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MSTATPRINTED: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static MTAB_BEEN_REALLOCATED: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub static MGTABSIZE: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static NREFS: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static CURISBEG: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static CURISSEND: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static CURISCOL: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static LR_CAPLEN: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0); pub static MAX_CAPLEN: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static MCOLORS: std::sync::LazyLock<std::sync::Mutex<listcols>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(listcols::default()));
pub static MTAB: std::sync::LazyLock<std::sync::Mutex<Vec<Option<Cmatch>>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(Vec::new()));
pub static MMTABP: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
pub static MGTAB: std::sync::LazyLock<std::sync::Mutex<Vec<Option<Cmgroup>>>> =
std::sync::LazyLock::new(|| std::sync::Mutex::new(Vec::new()));
pub static MGTABP: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
pub fn calclist(showall: i32) -> i32 {
let r = crate::ported::zle::compresult::calclist(showall);
let _ = showall;
r
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compprintfmt() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut stop = 0i32;
let cc = compprintfmt("hello", 0, 0, 0, 0, &mut stop);
assert_eq!(cc, 5);
}
#[test]
fn col_indices_match_c() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert_eq!(COL_NO, 0);
assert_eq!(COL_DI, 2);
assert_eq!(COL_EX, 15);
assert_eq!(COL_LC, 16);
assert_eq!(COL_EC, 18);
assert_eq!(COL_SA, 24);
}
#[test]
fn num_cols_matches_c() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert_eq!(NUM_COLS, 25);
assert_eq!(COLNAMES.len(), 25);
assert_eq!(DEFCOLS.len(), 25);
}
#[test]
fn colnames_match_c() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert_eq!(COLNAMES[COL_NO], "no");
assert_eq!(COLNAMES[COL_DI], "di");
assert_eq!(COLNAMES[COL_LN], "ln");
assert_eq!(COLNAMES[COL_EX], "ex");
assert_eq!(COLNAMES[COL_MA], "ma");
}
#[test]
fn defcols_match_c() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
assert_eq!(DEFCOLS[COL_NO], Some("0"));
assert_eq!(DEFCOLS[COL_DI], Some("1;31"));
assert_eq!(DEFCOLS[COL_EX], Some("1;32"));
assert_eq!(DEFCOLS[COL_OR], None); assert_eq!(DEFCOLS[COL_MI], None); assert_eq!(DEFCOLS[COL_LC], Some("\x1b["));
assert_eq!(DEFCOLS[COL_RC], Some("m"));
}
#[test]
fn filecol_allocates_with_defaults() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let fc = filecol("0;32");
assert_eq!(fc.col, "0;32");
assert!(fc.prog.is_none());
assert!(fc.next.is_none());
}
#[test]
fn filecol_empty_string() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let fc = filecol("");
assert_eq!(fc.col, "");
assert!(fc.prog.is_none());
assert!(fc.next.is_none());
}
#[test]
fn col_indices_full_set_matches_c_layout() {
let _g = crate::test_util::global_state_lock();
assert_eq!(COL_FI, 1);
assert_eq!(COL_LN, 3);
assert_eq!(COL_PI, 4);
assert_eq!(COL_SO, 5);
assert_eq!(COL_BD, 6);
assert_eq!(COL_CD, 7);
assert_eq!(COL_OR, 8);
assert_eq!(COL_MI, 9);
assert_eq!(COL_SU, 10);
assert_eq!(COL_SG, 11);
assert_eq!(COL_TW, 12);
assert_eq!(COL_OW, 13);
assert_eq!(COL_ST, 14);
assert_eq!(COL_RC, 17);
assert_eq!(COL_TC, 19);
assert_eq!(COL_SP, 20);
assert_eq!(COL_MA, 21); assert_eq!(COL_HI, 22); assert_eq!(COL_DU, 23); }
#[test]
fn colnames_entries_are_two_lowercase_letters() {
let _g = crate::test_util::global_state_lock();
for (i, &name) in COLNAMES.iter().enumerate() {
assert_eq!(
name.len(),
2,
"COLNAMES[{}] = {:?} must be exactly 2 chars",
i,
name
);
for c in name.chars() {
assert!(
c.is_ascii_lowercase(),
"COLNAMES[{}] = {:?} contains non-lowercase char {:?}",
i,
name,
c
);
}
}
}
#[test]
fn colnames_has_no_duplicates() {
let _g = crate::test_util::global_state_lock();
let unique: std::collections::HashSet<_> = COLNAMES.iter().copied().collect();
assert_eq!(unique.len(), COLNAMES.len(), "duplicate entry in COLNAMES");
}
#[test]
fn defcols_and_colnames_have_equal_lengths() {
let _g = crate::test_util::global_state_lock();
assert_eq!(
DEFCOLS.len(),
COLNAMES.len(),
"DEFCOLS and COLNAMES must have parallel indices"
);
assert_eq!(
NUM_COLS,
COLNAMES.len(),
"NUM_COLS must equal COLNAMES.len()"
);
}
#[test]
fn filecol_owns_its_col_string() {
let _g = crate::test_util::global_state_lock();
let original = "0;31".to_string();
let fc = filecol(&original);
drop(original);
assert_eq!(fc.col, "0;31");
}
#[test]
fn filecol_distinct_calls_produce_independent_nodes() {
let _g = crate::test_util::global_state_lock();
let a = filecol("red");
let b = filecol("blue");
assert_eq!(a.col, "red");
assert_eq!(b.col, "blue");
assert!(a.prog.is_none());
assert!(b.next.is_none());
}
#[test]
fn getcolval_empty_input_returns_empty() {
let _g = crate::test_util::global_state_lock();
let (decoded, rest) = getcolval("", 0);
assert_eq!(decoded, "");
assert_eq!(rest, "");
}
#[test]
fn compprintnl_does_not_panic_outside_zle() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let _ = compprintnl(0);
}
#[test]
fn complist_corpus_getcolval_stops_at_colon() {
let _g = crate::test_util::global_state_lock();
let (decoded, rest) = getcolval("red:rest", 0);
assert_eq!(decoded, "red");
assert_eq!(rest, ":rest");
}
#[test]
fn complist_corpus_getcolval_multi_stops_at_equals() {
let _g = crate::test_util::global_state_lock();
let (decoded, rest) = getcolval("foo=bar", 1);
assert_eq!(decoded, "foo");
assert_eq!(rest, "=bar");
}
#[test]
fn complist_corpus_getcolval_no_multi_keeps_equals() {
let _g = crate::test_util::global_state_lock();
let (decoded, _) = getcolval("foo=bar", 0);
assert_eq!(decoded, "foo=bar");
}
#[test]
fn complist_corpus_getcolval_backslash_n_is_newline() {
let _g = crate::test_util::global_state_lock();
let (decoded, _) = getcolval(r"a\nb", 0);
assert_eq!(decoded, "a\nb");
}
#[test]
fn complist_corpus_getcolval_backslash_e_is_esc() {
let _g = crate::test_util::global_state_lock();
let (decoded, _) = getcolval(r"\e[1m", 0);
assert_eq!(decoded, "\x1b[1m");
}
#[test]
fn complist_corpus_getcolval_octal_033_is_esc() {
let _g = crate::test_util::global_state_lock();
let (decoded, _) = getcolval(r"\033", 0);
assert_eq!(decoded, "\x1b");
}
#[test]
fn complist_corpus_getcolval_caret_shorthand() {
let _g = crate::test_util::global_state_lock();
let (decoded, _) = getcolval("^A", 0);
assert_eq!(decoded.as_bytes(), b"\x01");
}
#[test]
fn complist_corpus_getcolval_underscore_is_space() {
let _g = crate::test_util::global_state_lock();
let (decoded, _) = getcolval(r"a\_b", 0);
assert_eq!(decoded, "a b");
}
#[test]
fn getcolval_empty_returns_empty_pair() {
let _g = crate::test_util::global_state_lock();
let (decoded, rest) = getcolval("", 0);
assert!(decoded.is_empty(), "empty in → empty decoded");
assert!(rest.is_empty(), "empty in → empty rest");
}
#[test]
fn getcolval_plain_chars_pass_through() {
let _g = crate::test_util::global_state_lock();
let (decoded, _rest) = getcolval("abc", 0);
assert_eq!(decoded, "abc", "plain chars pass through unchanged");
}
#[test]
fn getcoldef_empty_input_returns_none() {
let _g = crate::test_util::global_state_lock();
let r = getcoldef("");
assert!(r.is_none(), "empty input → no coldef");
}
#[test]
fn zcoff_runs_without_panic() {
let _g = crate::test_util::global_state_lock();
zcoff();
zcoff();
}
#[test]
fn cleareol_runs_without_panic() {
let _g = crate::test_util::global_state_lock();
cleareol();
}
#[test]
fn zcputs_no_color_returns_empty() {
let _g = crate::test_util::global_state_lock();
let r = zcputs("group", None);
assert!(r.is_empty(), "None color → empty SGR");
}
#[test]
fn getcolval_stops_at_colon() {
let _g = crate::test_util::global_state_lock();
let (val, rest) = getcolval("abc:def", 0);
assert_eq!(val, "abc");
assert_eq!(rest, ":def", "tail preserves colon for caller");
}
#[test]
fn getcolval_multi_zero_ignores_equals() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("a=b:c", 0);
assert_eq!(val, "a=b", "= is verbatim when multi=0");
}
#[test]
fn getcolval_multi_nonzero_stops_at_equals() {
let _g = crate::test_util::global_state_lock();
let (val, rest) = getcolval("key=val", 1);
assert_eq!(val, "key");
assert_eq!(rest, "=val");
}
#[test]
fn getcolval_backslash_a_is_bell() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("\\a", 0);
assert_eq!(val.as_bytes(), b"\x07");
}
#[test]
fn getcolval_backslash_n_is_newline() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("\\n", 0);
assert_eq!(val.as_bytes(), b"\n");
}
#[test]
fn getcolval_backslash_b_is_backspace() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("\\b", 0);
assert_eq!(val.as_bytes(), b"\x08");
}
#[test]
fn getcolval_backslash_t_is_tab() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("\\t", 0);
assert_eq!(val.as_bytes(), b"\t");
}
#[test]
fn getcolval_backslash_e_is_escape() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("\\e", 0);
assert_eq!(val.as_bytes(), b"\x1b");
}
#[test]
fn getcolval_backslash_underscore_is_space() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("\\_", 0);
assert_eq!(val.as_bytes(), b" ");
}
#[test]
fn getcolval_backslash_question_is_del() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("\\?", 0);
assert_eq!(val.as_bytes(), b"\x7f");
}
#[test]
fn getcolval_octal_033_is_escape() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("\\033", 0);
assert_eq!(val.as_bytes(), b"\x1b", "octal \\033 → 0x1b");
}
#[test]
fn getcolval_single_digit_octal() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("\\7", 0);
assert_eq!(val.as_bytes(), b"\x07");
}
#[test]
fn getcolval_two_digit_octal() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("\\77", 0);
assert_eq!(val.as_bytes(), &[0o77]);
}
#[test]
fn getcolval_caret_uppercase_ctrl_a() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("^A", 0);
assert_eq!(val.as_bytes(), b"\x01");
}
#[test]
fn getcolval_caret_bracket_is_escape() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("^[", 0);
assert_eq!(val.as_bytes(), b"\x1b", "^[ is Ctrl-[ = ESC");
}
#[test]
fn getcolval_caret_lowercase_ctrl_a() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("^a", 0);
assert_eq!(val.as_bytes(), b"\x01", "^a is Ctrl-A (case-insensitive)");
}
#[test]
fn getcolval_caret_question_is_del() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("^?", 0);
assert_eq!(val.as_bytes(), b"\x7f");
}
#[test]
fn getcolval_plain_ascii_verbatim() {
let _g = crate::test_util::global_state_lock();
let (val, _) = getcolval("hello", 0);
assert_eq!(val, "hello");
}
#[test]
fn filecol_constructs_with_col_set_others_none() {
let _g = crate::test_util::global_state_lock();
let f = filecol("01;31");
assert_eq!(f.col, "01;31");
assert!(f.prog.is_none());
assert!(f.next.is_none());
}
#[test]
fn getcolval_empty_returns_empty_pair_corpus_pin() {
let _g = crate::test_util::global_state_lock();
let (val, rest) = getcolval("", 0);
assert!(val.is_empty());
assert!(rest.is_empty());
}
#[test]
fn getcolval_is_deterministic() {
let _g = crate::test_util::global_state_lock();
for s in ["", "01", "01;31", "01;32:something"] {
let first = getcolval(s, 0);
for _ in 0..3 {
assert_eq!(
getcolval(s, 0),
first,
"getcolval({:?}, 0) must be deterministic",
s
);
}
}
}
#[test]
fn getcolval_returns_tuple_string_str_type() {
let _g = crate::test_util::global_state_lock();
let _: (String, &str) = getcolval("abc", 0);
}
#[test]
fn getcoldef_empty_returns_none() {
let _g = crate::test_util::global_state_lock();
assert!(getcoldef("").is_none(), "empty input → None");
}
#[test]
fn getcoldef_returns_option_string_type() {
let _g = crate::test_util::global_state_lock();
let _: Option<String> = getcoldef("foo");
}
#[test]
fn zcoff_idempotent() {
let _g = crate::test_util::global_state_lock();
for _ in 0..5 {
zcoff();
}
}
#[test]
fn cleareol_idempotent() {
let _g = crate::test_util::global_state_lock();
for _ in 0..5 {
cleareol();
}
}
#[test]
fn putmatchcol_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = putmatchcol("", "");
}
#[test]
fn putfilecol_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = putfilecol("", "", 0, 0);
}
#[test]
fn compprintnl_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = compprintnl(0);
}
#[test]
fn compzputs_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = compzputs("", 0);
}
#[test]
fn zlrputs_empty_no_panic() {
let _g = crate::test_util::global_state_lock();
let _ = zlrputs("");
}
#[test]
fn getcols_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = getcols("");
}
#[test]
fn zcputs_returns_string_type() {
let _g = crate::test_util::global_state_lock();
let _: String = zcputs("", None);
}
#[test]
fn initiscol_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = initiscol();
}
#[test]
fn doiscol_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = doiscol(0);
}
#[test]
fn asklistscroll_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = asklistscroll(0);
}
#[test]
fn compprintlist_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = compprintlist(0);
}
#[test]
fn complistmatches_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = complistmatches();
}
#[test]
fn msearchpush_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = msearchpush();
}
#[test]
fn msearchpop_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = msearchpop();
}
#[test]
fn menuselect_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = menuselect();
}
#[test]
fn complist_lifecycle_all_zero() {
let _g = crate::test_util::global_state_lock();
assert_eq!(setup_(), 0);
assert_eq!(features_(), 0);
assert_eq!(enables_(), 0);
assert_eq!(boot_(), 0);
assert_eq!(cleanup_(), 0);
assert_eq!(finish_(), 0);
}
#[test]
fn menuselect_bindings_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = menuselect_bindings();
}
#[test]
fn getcolval_returns_string_str_tuple_type() {
let _: (String, &str) = getcolval("", 0);
}
#[test]
fn getcolval_empty_deterministic() {
let (a, _) = getcolval("", 0);
let (b, _) = getcolval("", 0);
assert_eq!(a, b, "getcolval('') must be pure");
}
#[test]
fn getcoldef_returns_option_string_pin_alt() {
let _: Option<String> = getcoldef("");
}
#[test]
fn getcoldef_empty_returns_none_alt() {
assert!(getcoldef("").is_none(), "empty colour-def → None");
}
#[test]
fn zlrputs_empty_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = zlrputs("");
}
#[test]
fn zcputs_returns_string_pin_alt() {
let _g = crate::test_util::global_state_lock();
let _: String = zcputs("", None);
}
#[test]
fn zcputs_empty_deterministic() {
let _g = crate::test_util::global_state_lock();
let a = zcputs("", None);
let b = zcputs("", None);
assert_eq!(a, b, "zcputs('', None) must be pure");
}
#[test]
fn zcoff_idempotent_10_call_alt() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
zcoff();
}
}
#[test]
fn cleareol_idempotent_10_call_alt() {
let _g = crate::test_util::global_state_lock();
for _ in 0..10 {
cleareol();
}
}
#[test]
fn clprintfmt_empty_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = clprintfmt("", 0);
}
#[test]
fn compzputs_empty_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = compzputs("", 0);
}
#[test]
fn compprintnl_returns_i32_pin_alt() {
let _g = crate::test_util::global_state_lock();
let _: i32 = compprintnl(0);
}
#[test]
fn putmatchcol_empty_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = putmatchcol("", "");
}
#[test]
fn putfilecol_empty_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _: i32 = putfilecol("", "", 0, 0);
}
}