use std::collections::HashMap;
use std::sync::atomic::Ordering;
use crate::DPUTS2;
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};
#[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 => s.to_string(),
}
}
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)
}
#[allow(unused_variables)]
pub fn clnicezputs(do_colors: i32, s: &str, ml: i32) -> i32 {
let _ = do_colors;
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 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 as usize); }
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) as usize); }
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 mut acc = 0i32; let mut broken = 0i32;
loop {
complistmatches();
let cmd = crate::ported::zle::zle_keymap::getkeycmd();
let name = match &cmd {
None => String::new(),
Some(t) => t.nam.clone(),
};
if name.is_empty() || name == "send-break" {
crate::ported::utils::zbeep(); broken = 1;
break; }
if name == "accept-line" || name == "accept-search" {
if mode == 2 || mode == 3 {
mode = 0; continue; }
acc = 1; break; }
if name == "undo" {
break;
}
broken = 1;
break;
}
crate::ported::zle::zle_keymap::selectlocalmap(saved_localmap);
unqueue_signals();
let _ = (oe, step, mode, status);
if broken == 2 {
3
} else if acc != 0 {
1
} else {
2
}
}
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);
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 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");
}
}