#[allow(unused_imports)]
#[allow(unused_imports)]
use crate::ported::zle::zle_main::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_misc::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_hist::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_move::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_word::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_params::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_vi::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_utils::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_refresh::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_tricky::*;
#[allow(unused_imports)]
use crate::ported::zle::textobjects::*;
#[allow(unused_imports)]
use crate::ported::zle::deltochar::*;
pub static INVCOUNT: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub fn instmatch( buffer: &str,
cursor: usize,
word_start: usize,
word_end: usize,
replacement: &str,
) -> (String, usize) {
let mut result = String::with_capacity(buffer.len() + replacement.len());
result.push_str(&buffer[..word_start]);
result.push_str(replacement);
result.push_str(&buffer[word_end..]);
let new_cursor = word_start + replacement.len();
(result, new_cursor)
}
pub fn unambig_data(matches: &[String]) -> String { if matches.is_empty() {
return String::new();
}
if matches.len() == 1 {
return matches[0].clone();
}
let first = &matches[0];
let mut prefix_len = first.len();
for m in &matches[1..] {
let common = first
.chars()
.zip(m.chars())
.take_while(|(a, b)| a == b)
.count();
prefix_len = prefix_len.min(common);
}
first[..first
.char_indices()
.nth(prefix_len)
.map(|(i, _)| i)
.unwrap_or(first.len())]
.to_string()
}
pub fn do_single( buffer: &str,
cursor: usize,
word_start: usize,
word_end: usize,
the_match: &str,
add_space: bool,
) -> (String, usize) {
let suffix = if add_space { " " } else { "" };
let replacement = format!("{}{}", the_match, suffix);
instmatch(buffer, cursor, word_start, word_end, &replacement)
}
pub fn do_ambiguous(matches: &[String]) -> i32 { let prefix = unambig_data(matches);
if prefix.is_empty() && matches.is_empty() {
return 0; }
if !prefix.is_empty() { 1 } else { 0 }
}
pub fn do_allmatches( buffer: &str,
cursor: usize,
word_start: usize,
word_end: usize,
matches: &[String],
separator: &str,
) -> (String, usize) {
let all = matches.join(separator);
instmatch(buffer, cursor, word_start, word_end, &all)
}
pub fn do_menucmp(matches: &[String], current: usize, forward: bool) -> (usize, &str) { if matches.is_empty() {
return (0, "");
}
let next = if forward {
(current + 1) % matches.len()
} else {
if current == 0 {
matches.len() - 1
} else {
current - 1
}
};
(next, &matches[next])
}
pub fn accept_last( buffer: &str,
cursor: usize,
word_start: usize,
word_end: usize,
selected: &str,
) -> (String, usize) {
do_single(buffer, cursor, word_start, word_end, selected, true)
}
pub fn valid_match(word: &str, prefix: &str, suffix: &str) -> bool { word.starts_with(prefix) && (suffix.is_empty() || word.ends_with(suffix))
}
pub fn hasbrpsfx(s: &str) -> bool { s.contains('{') || s.contains('}')
}
pub fn build_pos_string(current: usize, total: usize) -> String { format!("{}/{}", current + 1, total)
}
pub fn cut_cline(s: &str, max_len: usize) -> String { if s.len() <= max_len {
s.to_string()
} else {
format!("{}...", &s[..max_len.saturating_sub(3)])
}
}
pub fn cline_str( l: Option<&crate::ported::zle::comp_h::Cline>,
) -> String {
use crate::ported::zle::comp_h::{CLF_LINE, CLF_SUF};
let mut out = String::new();
let mut cur = l;
while let Some(node) = cur {
if node.olen != 0 && (node.flags & CLF_SUF) == 0 && node.prefix.is_none() {
if let Some(o) = &node.orig {
out.push_str(o);
}
} else {
let mut p = node.prefix.as_deref();
while let Some(part) = p {
let s = if (part.flags & CLF_LINE) != 0 {
part.line.as_deref()
} else {
part.word.as_deref()
};
if let Some(s) = s { out.push_str(s); }
p = part.next.as_deref();
}
}
let anchor = if (node.flags & CLF_LINE) != 0 {
node.line.as_deref()
} else {
node.word.as_deref()
};
if let Some(a) = anchor { out.push_str(a); }
if node.olen != 0 && (node.flags & CLF_SUF) != 0 && node.suffix.is_none() {
if let Some(o) = &node.orig {
out.push_str(o);
}
} else {
let mut p = node.suffix.as_deref();
while let Some(part) = p {
let s = if (part.flags & CLF_LINE) != 0 {
part.line.as_deref()
} else {
part.word.as_deref()
};
if let Some(s) = s { out.push_str(s); }
p = part.next.as_deref();
}
}
cur = node.next.as_deref();
}
out
}
pub fn list_lines(matches: &[String], columns: usize) -> usize { if columns == 0 {
return matches.len();
}
matches.len().div_ceil(columns)
}
pub fn skipnolist(p: &[crate::ported::zle::comp_h::Cmatch], showall: i32) -> usize { use crate::ported::zle::comp_h::{CMF_DISPLINE, CMF_HIDE, CMF_MULT, CMF_NOLIST};
let mask = if showall != 0 { 0 } else { CMF_NOLIST | CMF_MULT } | CMF_HIDE;
let mut i = 0usize; while i < p.len() { let m = &p[i];
let f = m.flags;
let skip_mask = (f & mask) != 0; let skip_disp = m.disp.is_some() && (f & (CMF_DISPLINE | CMF_HIDE)) != 0; if !(skip_mask || skip_disp) {
break;
}
i += 1; }
i }
pub fn comp_list(v: Option<&str>) { use std::sync::Mutex;
use std::sync::atomic::Ordering;
use crate::ported::zle::compcore::onlyexpl;
let complist = crate::ported::zle::complete::COMPLIST
.get_or_init(|| Mutex::new(String::new()));
{
let mut g = complist.lock().unwrap();
g.clear();
if let Some(s) = v {
g.push_str(s);
}
}
let val = match v {
None => 0,
Some(s) => {
(if s.contains("expl") { 1 } else { 0 })
| (if s.contains("messages") { 2 } else { 0 })
}
};
onlyexpl.store(val, Ordering::SeqCst);
}
pub fn comp_mod(mut v: i32, m: i32) -> i32 { if v >= 0 { v -= 1; }
if v >= 0 { v % m } else { while v < 0 { v += m; }
v }
}
pub fn asklist() -> i32 { use std::sync::atomic::Ordering;
use crate::ported::zle::compcore::MINFO;
use crate::ported::zle::complete::COMPLISTMAX;
use crate::ported::zle::zle_refresh::{LASTLISTLEN, CLEARFLAG};
use crate::ported::zle::zle_refresh::SHOWINGLIST;
use crate::ported::zsh_h::{USEZLE, isset};
crate::ported::zle::zle_main::trashzle(); SHOWINGLIST.store(0, Ordering::Relaxed);
crate::ported::zle::zle_refresh::LISTSHOWN.store(0, Ordering::Relaxed);
LASTLISTLEN.store(0, Ordering::Relaxed);
let usezle = isset(USEZLE);
let termflags = crate::ported::params::TERMFLAGS.load(Ordering::Relaxed);
let dolastprompt = true;
let clearflag = usezle && termflags == 0 && dolastprompt;
CLEARFLAG.store(if clearflag { 1 } else { 0 }, Ordering::Relaxed);
let listdat = crate::ported::zle::compcore::listdat
.get_or_init(|| std::sync::Mutex::new(Default::default()))
.lock().ok().map(|g| g.clone()).unwrap_or_default();
let zterm_lines = crate::ported::utils::adjustlines() as i32;
let cmax = COMPLISTMAX.load(Ordering::Relaxed) as i32;
let has_cur = MINFO.get().and_then(|m| m.lock().ok())
.map(|m| m.cur.is_some()).unwrap_or(false);
let already_asked = MINFO.get().and_then(|m| m.lock().ok())
.map(|m| m.asked).unwrap_or(0);
let over_threshold = (cmax > 0 && listdat.nlist >= cmax)
|| (cmax < 0 && listdat.nlines <= -cmax)
|| (cmax == 0 && listdat.nlines >= zterm_lines);
if (!has_cur || already_asked == 0) && over_threshold {
let prompt = if listdat.nlist > 0 {
format!(
"zsh: do you wish to see all {} possibilities ({} lines)? ",
listdat.nlist, listdat.nlines
)
} else {
format!(
"zsh: do you wish to see all {} lines? ",
listdat.nlines
)
};
let fd = crate::ported::init::SHTTY.load(Ordering::Relaxed);
let out = if fd >= 0 { fd } else { 1 };
let _ = crate::ported::utils::write_loop(out, prompt.as_bytes());
let said_yes = crate::ported::zle::zle_utils::getzlequery() != 0;
if !said_yes { let _ = crate::ported::utils::write_loop(out, b"\n");
if let Ok(mut m) = MINFO.get_or_init(
|| std::sync::Mutex::new(Default::default())
).lock() {
m.asked = 2;
}
return 1; }
let _ = crate::ported::utils::write_loop(out, b"\n");
if let Ok(mut m) = MINFO.get_or_init(
|| std::sync::Mutex::new(Default::default())
).lock() {
m.asked = 1;
}
}
let asked_now = MINFO.get().and_then(|m| m.lock().ok())
.map(|m| m.asked).unwrap_or(0);
if asked_now != 0 { asked_now - 1 } else { 0 }
}
pub fn ztat(path: &str, follow_symlink: bool) -> Option<std::fs::Metadata> { if follow_symlink { std::fs::symlink_metadata(path).ok()
} else { std::fs::metadata(path).ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unambig_data() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(unambig_data(&["foobar".into(), "foobaz".into()]), "fooba");
assert_eq!(unambig_data(&["abc".into()]), "abc");
assert_eq!(unambig_data(&[]), "");
}
#[test]
fn cline_str_none_returns_empty() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(cline_str(None), "");
}
#[test]
fn cline_str_emits_word_anchor() {
use crate::ported::zle::comp_h::Cline;
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut n = Cline::default();
n.word = Some("hello".to_string());
n.wlen = 5;
assert_eq!(cline_str(Some(&n)), "hello");
}
#[test]
fn cline_str_emits_line_anchor_when_clf_line_set() {
use crate::ported::zle::comp_h::{Cline, CLF_LINE};
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut n = Cline::default();
n.flags = CLF_LINE;
n.line = Some("LINE".to_string());
n.word = Some("word-should-not-emit".to_string());
assert_eq!(cline_str(Some(&n)), "LINE");
}
#[test]
fn cline_str_emits_orig_when_olen_set_and_no_prefix() {
use crate::ported::zle::comp_h::Cline;
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut n = Cline::default();
n.orig = Some("original".to_string());
n.olen = 8;
n.word = Some("anchor".to_string());
assert_eq!(cline_str(Some(&n)), "originalanchor");
}
#[test]
fn cline_str_walks_prefix_chain() {
use crate::ported::zle::comp_h::Cline;
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut p2 = Cline::default();
p2.word = Some("ond".to_string());
let mut p1 = Cline::default();
p1.word = Some("sec".to_string());
p1.next = Some(Box::new(p2));
let mut n = Cline::default();
n.prefix = Some(Box::new(p1));
n.word = Some("anchor".to_string());
assert_eq!(cline_str(Some(&n)), "secondanchor");
}
#[test]
fn cline_str_walks_next_chain() {
use crate::ported::zle::comp_h::Cline;
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut n2 = Cline::default();
n2.word = Some("B".to_string());
let mut n1 = Cline::default();
n1.word = Some("A".to_string());
n1.next = Some(Box::new(n2));
assert_eq!(cline_str(Some(&n1)), "AB");
}
#[test]
fn test_instmatch() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let (result, cursor) = instmatch("git co", 6, 4, 6, "commit");
assert_eq!(result, "git commit");
assert_eq!(cursor, 10);
}
#[test]
fn test_do_single() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let (result, cursor) = do_single("git co", 6, 4, 6, "commit", true);
assert_eq!(result, "git commit ");
assert_eq!(cursor, 11);
}
#[test]
fn test_do_menucmp() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let matches = vec!["commit".into(), "checkout".into(), "cherry-pick".into()];
let (next, word) = do_menucmp(&matches, 0, true);
assert_eq!(next, 1);
assert_eq!(word, "checkout");
let (next, word) = do_menucmp(&matches, 2, true);
assert_eq!(next, 0);
assert_eq!(word, "commit");
}
#[test]
fn test_valid_match() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert!(valid_match("foobar", "foo", ""));
assert!(valid_match("foobar", "foo", "bar"));
assert!(!valid_match("foobar", "baz", ""));
}
#[test]
fn test_build_pos_string() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(build_pos_string(0, 10), "1/10");
assert_eq!(build_pos_string(9, 10), "10/10");
}
#[test]
fn test_list_lines() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(list_lines(&vec!["a".into(); 10], 3), 4);
assert_eq!(list_lines(&vec!["a".into(); 6], 3), 2);
}
#[test]
fn comp_mod_positive() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(comp_mod(1, 5), 0); assert_eq!(comp_mod(3, 5), 2); assert_eq!(comp_mod(5, 5), 4); assert_eq!(comp_mod(6, 5), 0); }
#[test]
fn comp_mod_zero_branches_negative() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(comp_mod(0, 5), 4); assert_eq!(comp_mod(-1, 5), 4); assert_eq!(comp_mod(-5, 5), 0); assert_eq!(comp_mod(-6, 5), 4); }
#[test]
fn comp_list_sets_onlyexpl() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
use std::sync::atomic::Ordering;
use crate::ported::zle::compcore::onlyexpl;
comp_list(Some("expl"));
assert_eq!(onlyexpl.load(Ordering::SeqCst), 1);
comp_list(Some("messages"));
assert_eq!(onlyexpl.load(Ordering::SeqCst), 2);
comp_list(Some("expl messages"));
assert_eq!(onlyexpl.load(Ordering::SeqCst), 3);
comp_list(Some("nothing"));
assert_eq!(onlyexpl.load(Ordering::SeqCst), 0);
comp_list(None);
assert_eq!(onlyexpl.load(Ordering::SeqCst), 0);
}
#[test]
fn skipnolist_skips_hide_and_nolist() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
use crate::ported::zle::comp_h::{Cmatch, CMF_HIDE, CMF_NOLIST};
let mut a = Cmatch::default(); a.flags = CMF_NOLIST;
let mut b = Cmatch::default(); b.flags = CMF_HIDE;
let c = Cmatch::default(); let v = vec![a, b, c];
assert_eq!(skipnolist(&v, 0), 2);
}
#[test]
fn skipnolist_showall_keeps_nolist() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
use crate::ported::zle::comp_h::{Cmatch, CMF_NOLIST};
let mut a = Cmatch::default(); a.flags = CMF_NOLIST;
let v = vec![a];
assert_eq!(skipnolist(&v, 1), 0);
}
#[test]
fn skipnolist_skips_disp_displine() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
use crate::ported::zle::comp_h::{Cmatch, CMF_DISPLINE};
let mut a = Cmatch::default();
a.disp = Some("display".into());
a.flags = CMF_DISPLINE;
let b = Cmatch::default();
let v = vec![a, b];
assert_eq!(skipnolist(&v, 0), 1);
}
}
pub fn bld_all_str() -> String { use std::sync::atomic::Ordering;
use crate::ported::zle::comp_h::{CMF_ALL, CMF_HIDE};
let groups = crate::ported::zle::compcore::amatches
.get_or_init(|| std::sync::Mutex::new(Vec::new()))
.lock().ok().map(|g| g.clone()).unwrap_or_default();
let cols: i32 = crate::ported::utils::adjustcolumns() as i32;
let mut len: i32 = cols - 5; let mut add: i32 = 0;
let mut buf = String::new();
let mut g_idx = groups.iter().position(|g| g.mcount != 0);
'outer: while let Some(gi) = g_idx {
let g = &groups[gi];
let mut mp = 0usize;
while mp < g.matches.len() {
let m = &g.matches[mp];
let visible = (m.flags & (CMF_ALL | CMF_HIDE)) == 0
&& m.str.is_some();
if visible { let s = m.str.as_deref().unwrap();
let t = s.len() as i32 + add;
if len >= t { if add != 0 { buf.push(' '); } buf.push_str(s); len -= t;
add = 1;
} else { if len > add + 2 { if add != 0 { buf.push(' '); }
buf.push_str(&s[..((len - 2).max(0) as usize).min(s.len())]);
}
buf.push_str("..."); break 'outer; }
}
mp += 1;
if mp >= g.matches.len() { g_idx = (gi + 1..).find(|&i| i < groups.len()
&& groups[i].mcount != 0);
if g_idx.is_none() { break 'outer; }
continue 'outer;
}
}
let _ = Ordering::Relaxed;
g_idx = (gi + 1..).find(|&i| i < groups.len()
&& groups[i].mcount != 0);
}
buf }
thread_local! {
static LASTINVCOUNT: std::cell::Cell<i32> = const { std::cell::Cell::new(-1) };
}
pub fn calclist(showall: i32) -> i32 { use std::sync::atomic::Ordering::Relaxed;
use crate::ported::zle::comp_h::*;
let invcount = INVCOUNT.load(Relaxed);
let onlyexpl_v = crate::ported::zle::compcore::onlyexpl.load(Relaxed);
let menuacc_v = crate::ported::zle::compcore::menuacc.load(Relaxed);
let zterm_columns = crate::ported::utils::adjustcolumns() as i32; let zterm_lines = crate::ported::utils::adjustlines() as i32;
{
let ld = crate::ported::zle::compcore::listdat
.get_or_init(|| std::sync::Mutex::new(Cldata::default()));
let g = ld.lock().unwrap();
if LASTINVCOUNT.with(|c| c.get()) == invcount
&& g.valid != 0 && onlyexpl_v == g.onlyexpl
&& menuacc_v == g.menuacc && showall == g.showall
&& zterm_lines == g.zterm_lines
&& zterm_columns == g.zterm_columns
{
return 0; }
}
LASTINVCOUNT.with(|c| c.set(invcount));
let am = crate::ported::zle::compcore::amatches
.get_or_init(|| std::sync::Mutex::new(Vec::new()));
let mut groups = am.lock().unwrap();
let nmatches = crate::ported::zle::compcore::nmatches.load(Relaxed);
let mut mlens: Vec<i32> = vec![0; (nmatches + 1) as usize];
let mut hidden = 0i32;
let mut nlist = 0i32;
let mut nlines = 0i32;
let mut max = 0i32;
let listpacked = crate::ported::zsh_h::isset(crate::ported::zsh_h::LISTPACKED);
let listrowsfirst = crate::ported::zsh_h::isset(crate::ported::zsh_h::LISTROWSFIRST);
let listtypes = crate::ported::zsh_h::isset(crate::ported::zsh_h::LISTTYPES);
for g in groups.iter_mut() {
let mut nl = false;
let mut glong = 1i32;
let mut gshort = zterm_columns;
let mut ndisp = 0i32;
let mut totl = 0i32;
let mut hasf = false;
g.flags |= CGF_PACKED | CGF_ROWS;
if onlyexpl_v == 0 && !g.ylist.is_empty() {
if !listpacked { g.flags &= !CGF_PACKED; } if !listrowsfirst { g.flags &= !CGF_ROWS; }
hidden = 1; for s in g.ylist.iter() { if (s.chars().count() as i32) >= zterm_columns
|| s.contains('\n')
{
nl = true;
break;
}
}
if nl || g.ylist.len() < 2 { g.flags |= CGF_LINES; hidden = 1; for s in g.ylist.iter() { let mut acc = 0i32;
for chunk in s.split('\n') {
let w = chunk.chars().count().saturating_sub(1) as i32;
acc += 1 + w / zterm_columns;
}
nlines += acc;
}
} else {
for s in g.ylist.iter() { let l = s.chars().count() as i32;
ndisp += 1;
if l > glong { glong = l; }
if l < gshort { gshort = l; }
totl += l;
nlist += 1;
}
}
} else if onlyexpl_v == 0 {
for m in g.matches.iter_mut() {
if (m.flags & CMF_FILE) != 0 { hasf = true; }
if menuacc_v != 0 && !hasbrpsfx(m.str.as_deref().unwrap_or("")) {
m.flags |= CMF_HIDE;
continue;
}
m.flags &= !CMF_HIDE;
if showall != 0 || (m.flags & (CMF_NOLIST | CMF_MULT)) == 0 {
if (m.flags & (CMF_NOLIST | CMF_MULT)) != 0
&& m.str.as_deref().is_none_or(|s| s.is_empty())
{
m.flags |= CMF_HIDE;
continue;
}
if let Some(disp) = m.disp.clone() {
if (m.flags & CMF_DISPLINE) != 0 {
nlines += 1 + crate::ported::zle::zle_tricky::printfmt(&disp, 0, false, false);
g.flags |= CGF_HASDL;
} else {
let l = disp.chars().count() as i32
+ if m.modec != '\0' { 1 } else { 0 };
ndisp += 1;
if l > glong { glong = l; }
if l < gshort { gshort = l; }
totl += l;
mlens[m.gnum as usize] = l;
}
nlist += 1;
if (m.flags & CMF_PACKED) == 0 { g.flags &= !CGF_PACKED; }
if (m.flags & CMF_ROWS) == 0 { g.flags &= !CGF_ROWS; }
} else {
let s = m.str.as_deref().unwrap_or("");
let l = s.chars().count() as i32
+ if m.modec != '\0' { 1 } else { 0 };
ndisp += 1;
if l > glong { glong = l; }
if l < gshort { gshort = l; }
totl += l;
mlens[m.gnum as usize] = l;
nlist += 1;
if (m.flags & CMF_PACKED) == 0 { g.flags &= !CGF_PACKED; }
if (m.flags & CMF_ROWS) == 0 { g.flags &= !CGF_ROWS; }
}
} else {
hidden = 1;
}
}
}
for e in g.expls.iter() {
if (e.count != 0 || e.always != 0)
&& (onlyexpl_v == 0
|| (onlyexpl_v & if e.always > 0 { 2 } else { 1 }) != 0)
{
nlines += 1 + crate::ported::zle::zle_tricky::printfmt(
e.str.as_deref().unwrap_or(""),
if e.always != 0 { -1 } else { e.count },
false,
true,
);
}
}
if listtypes && hasf { g.flags |= CGF_FILES; } g.totl = totl + ndisp * CM_SPACE; g.dcount = ndisp; g.width = glong + CM_SPACE; g.shortest = gshort + CM_SPACE; if g.width > 0 {
g.cols = (zterm_columns / g.width).min(g.dcount); }
if g.cols > 0 {
let i = g.cols * g.width - CM_SPACE; if i > max { max = i; }
}
}
if onlyexpl_v == 0 {
for g in groups.iter_mut() {
let mut glines = 0i32;
g.widths.clear(); if !g.ylist.is_empty() {
if (g.flags & CGF_LINES) == 0 {
if g.cols > 0 {
glines += (g.ylist.len() as i32 + g.cols - 1) / g.cols;
if g.cols > 1 {
g.width += (max - (g.width * g.cols - CM_SPACE)) / g.cols;
}
} else {
g.cols = 1;
g.width = 1;
for s in g.ylist.iter() {
glines += 1 + s.chars().count() as i32 / zterm_columns;
}
}
}
} else if g.cols > 0 {
glines += (g.dcount + g.cols - 1) / g.cols;
if g.cols > 1 {
g.width += (max - (g.width * g.cols - CM_SPACE)) / g.cols;
}
} else if (g.flags & CGF_LINES) == 0 {
g.cols = 1;
g.width = 0;
for m in g.matches.iter() {
if (m.flags & CMF_HIDE) == 0 {
if m.disp.is_some() {
if (m.flags & CMF_DISPLINE) == 0 {
glines += 1 + (mlens[m.gnum as usize].saturating_sub(1)) / zterm_columns;
}
} else if showall != 0 || (m.flags & (CMF_NOLIST | CMF_MULT)) == 0 {
glines += 1 + (mlens[m.gnum as usize].saturating_sub(1)) / zterm_columns;
}
}
}
}
g.lins = glines;
nlines += glines;
}
for g in groups.iter_mut() {
if (g.flags & CGF_PACKED) == 0 { continue; } g.widths = vec![0i32; zterm_columns as usize];
let mut tlines = g.lins; let mut tcols = g.cols; let mut width: i32 = 0;
if !g.ylist.is_empty() { if (g.flags & CGF_LINES) == 0 { let ylens: Vec<i32> = g.ylist.iter()
.map(|s| s.chars().count() as i32 + CM_SPACE)
.collect();
if (g.flags & CGF_ROWS) != 0 {
let mut t = zterm_columns / (g.shortest + CM_SPACE);
while t > g.cols {
for w in &mut g.widths[..t as usize] { *w = 0; } let mut w = 0i32;
let mut nth = 0i32;
let mut tcol = 0i32;
let mut tl = 1i32;
while w < zterm_columns && nth < g.dcount { if tcol == t { tcol = 0; tl += 1; } let len = ylens[nth as usize]; if len > g.widths[tcol as usize] { w += len - g.widths[tcol as usize]; g.widths[tcol as usize] = len; }
nth += 1; tcol += 1;
}
width = w;
tcols = t;
tlines = tl;
if w < zterm_columns { break; } t -= 1;
}
} else {
let mut t = zterm_columns / (g.shortest + CM_SPACE);
while t > g.cols {
let mut tl = ((g.dcount + t - 1) / t).max(1); for w in &mut g.widths[..t as usize] { *w = 0; } let mut w = 0i32;
let mut nth = 0i32;
let mut tcol = 0i32;
let mut tline = 0i32;
while w < zterm_columns && nth < g.dcount { if tline == tl { tcol += 1; tline = 0; } if tcol == t { tcol = 0; tl += 1; } let len = ylens[nth as usize]; if len > g.widths[tcol as usize] { w += len - g.widths[tcol as usize];
g.widths[tcol as usize] = len;
}
nth += 1; tline += 1;
}
width = w;
tcols = t;
tlines = tl;
if w < zterm_columns { break; } t -= 1;
}
}
}
} else if g.width != 0 { if (g.flags & CGF_ROWS) != 0 {
let mut t = zterm_columns / (g.shortest + CM_SPACE);
while t > g.cols {
for w in &mut g.widths[..t as usize] { *w = 0; } let mut w = 0i32;
let mut tcol = 0i32;
let mut tl = 1i32;
let mut nth = 0i32;
let mut p_idx = skipnolist(&g.matches, showall);
while p_idx < g.matches.len() && w < zterm_columns && nth < g.dcount {
if tcol == t { tcol = 0; tl += 1; } let m = &g.matches[p_idx]; let len = mlens[m.gnum as usize]
+ if tcol == t - 1 { 0 } else { CM_SPACE }; if len > g.widths[tcol as usize] {
w += len - g.widths[tcol as usize];
g.widths[tcol as usize] = len;
}
nth += 1;
let nxt = p_idx + 1;
if nxt >= g.matches.len() {
p_idx = g.matches.len();
} else {
p_idx = nxt + skipnolist(&g.matches[nxt..], showall);
}
tcol += 1;
}
width = w;
tcols = t;
tlines = tl;
if w < zterm_columns { break; } t -= 1;
}
} else {
let mut t = zterm_columns / (g.shortest + CM_SPACE);
while t > g.cols {
let mut tl = ((g.dcount + t - 1) / t).max(1); for w in &mut g.widths[..t as usize] { *w = 0; } let mut w = 0i32;
let mut nth = 0i32;
let mut tcol = 0i32;
let mut tline = 0i32;
let mut p_idx = skipnolist(&g.matches, showall); while p_idx < g.matches.len() && w < zterm_columns && nth < g.dcount {
if tline == tl { tcol += 1; tline = 0; } if tcol == t { tcol = 0; tl += 1; } let m = &g.matches[p_idx]; let len = mlens[m.gnum as usize]
+ if tcol == t - 1 { 0 } else { CM_SPACE }; if len > g.widths[tcol as usize] {
w += len - g.widths[tcol as usize];
g.widths[tcol as usize] = len;
}
nth += 1;
let nxt = p_idx + 1;
if nxt >= g.matches.len() {
p_idx = g.matches.len();
} else {
p_idx = nxt + skipnolist(&g.matches[nxt..], showall);
}
tline += 1;
}
width = w;
tcols = t;
tlines = tl;
if w < zterm_columns { if tcol + 1 < tcols { tcols = tcol + 1; }
break;
}
t -= 1;
}
}
}
if tcols <= g.cols { tlines = g.lins; } if tlines == g.lins { g.widths.clear(); } else {
nlines += tlines - g.lins; g.lins = tlines; g.cols = tcols; g.totl = width; let width_adj = width - CM_SPACE; if width_adj > max { max = width_adj; } }
}
for g in groups.iter_mut() {
if g.widths.is_empty() && g.width != 0 && g.cols > 1 {
g.width += (max - (g.width * g.cols - CM_SPACE)) / g.cols;
}
}
} else {
for g in groups.iter_mut() {
g.widths.clear(); }
}
let ld = crate::ported::zle::compcore::listdat
.get_or_init(|| std::sync::Mutex::new(Cldata::default()));
let mut g = ld.lock().unwrap();
g.valid = 1;
g.hidden = hidden;
g.nlist = nlist;
g.nlines = nlines;
g.menuacc = menuacc_v;
g.onlyexpl = onlyexpl_v;
g.zterm_columns = zterm_columns;
g.zterm_lines = zterm_lines;
g.showall = showall;
1 }
pub fn do_ambig_menu() -> i32 { use std::sync::atomic::Ordering;
use crate::ported::zle::compcore::{
amatches, iforcemenu, insmnum, lastpermmnum, menuacc, oldins, oldlist,
MINFO,
};
use crate::ported::zle::zle_tricky::{MENUCMP, USEMENU};
if iforcemenu.load(Ordering::Relaxed) == -1 { let _ = do_ambiguous(&[]); }
let um = USEMENU.load(Ordering::Relaxed);
if um != 3 { MENUCMP.store(1, Ordering::Relaxed); menuacc.store(0, Ordering::Relaxed); if let Ok(mut m) = MINFO.get_or_init(|| std::sync::Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())).lock() {
m.cur = None; }
} else {
if oldlist.load(Ordering::Relaxed) != 0 { let has_cur = MINFO.get().and_then(|m| m.lock().ok())
.map(|m| m.cur.is_some()).unwrap_or(false);
if oldins.load(Ordering::Relaxed) != 0 && has_cur { let _ = accept_last("", 0, 0, 0, ""); }
} else {
if let Ok(mut m) = MINFO.get_or_init(
|| std::sync::Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())
).lock() {
m.cur = None; }
}
}
let mut idx = comp_mod(
insmnum.load(Ordering::Relaxed),
lastpermmnum.load(Ordering::Relaxed),
);
insmnum.store(idx, Ordering::Relaxed);
let groups = amatches.get_or_init(|| std::sync::Mutex::new(Vec::new()))
.lock().ok().map(|g| g.clone()).unwrap_or_default();
let mut chosen_group: Option<crate::ported::zle::comp_h::Cmgroup> = None;
for g in &groups {
if g.mcount > idx {
chosen_group = Some(g.clone());
break;
}
idx -= g.mcount;
}
let Some(g) = chosen_group else { if let Ok(mut m) = MINFO.get_or_init(
|| std::sync::Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())
).lock() {
m.cur = None;
m.asked = 0;
}
return 0;
};
let mc = g.matches.get(idx as usize).cloned();
if iforcemenu.load(Ordering::Relaxed) != -1 { if let Some(ref m) = mc {
if let Ok(mut g) = crate::ported::zle::compcore::MINFO.get_or_init(
|| std::sync::Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())
).lock() {
g.cur = Some(Box::new(m.clone()));
}
}
}
if let Ok(mut mst) = MINFO.get_or_init(
|| std::sync::Mutex::new(crate::ported::zle::comp_h::Menuinfo::default())
).lock() {
mst.cur = mc.map(Box::new); }
0
}
pub fn ilistmatches() -> i32 { use std::sync::atomic::Ordering;
use crate::ported::zle::zle_refresh::{LISTSHOWN, SHOWINGLIST};
let _ = calclist(0); let _ = SHOWINGLIST; let _ = LISTSHOWN;
let _ = printlist(0, 0); 0 }
pub fn invalidate_list() -> i32 { use std::sync::atomic::Ordering;
use crate::ported::zle::compcore::{
amatches, fromcomp, lastmatches, menuacc, nmatches as nmatches_g,
};
use crate::ported::zle::zle_refresh::SHOWINGLIST;
use crate::ported::zle::zle_tricky::{LASTAMBIG, MENUCMP, VALIDLIST};
INVCOUNT.fetch_add(1, Ordering::SeqCst); if VALIDLIST.load(Ordering::SeqCst) != 0 { if SHOWINGLIST.load(Ordering::SeqCst) == -2 { let _ = SHOWINGLIST.load(Ordering::SeqCst);
}
if let Ok(mut g) = lastmatches.get_or_init(
|| std::sync::Mutex::new(Vec::new())
).lock() {
g.clear();
}
crate::ported::zle::compcore::hasoldlist.store(0, Ordering::SeqCst); }
LASTAMBIG.store(0, Ordering::SeqCst);
MENUCMP.store(0, Ordering::SeqCst);
menuacc.store(0, Ordering::SeqCst);
VALIDLIST.store(0, Ordering::SeqCst);
SHOWINGLIST.store(0, Ordering::SeqCst);
fromcomp.store(0, Ordering::SeqCst);
if let Ok(mut ld) = crate::ported::zle::compcore::listdat
.get_or_init(|| std::sync::Mutex::new(Default::default())).lock()
{
ld.valid = 0;
}
use crate::ported::zle::zle_refresh::LISTSHOWN;
if LISTSHOWN.load(Ordering::SeqCst) < 0 {
LISTSHOWN.store(0, Ordering::SeqCst);
}
nmatches_g.store(0, Ordering::SeqCst); if let Ok(mut g) = amatches.get_or_init(
|| std::sync::Mutex::new(Vec::new())
).lock() {
g.clear(); }
0 }
#[allow(unused_variables)]
pub fn iprintm(
g: Option<&crate::ported::zle::comp_h::Cmgroup>,
mp: Option<&crate::ported::zle::comp_h::Cmatch>,
mc: i32, ml: i32, lastc: i32, width: i32,
) -> i32 { use crate::ported::zle::comp_h::{CGF_FILES, CMF_ALL, CMF_DISPLINE};
use std::sync::atomic::Ordering;
let m = match mp { None => return 0, Some(m) => m }; let mut disp_owned: String = String::new();
let disp_ref: Option<&str> = m.disp.as_deref();
if (m.flags & CMF_ALL) != 0 && disp_ref.map(|s| s.is_empty()).unwrap_or(true) {
disp_owned = bld_all_str(); }
let disp_now: Option<&str> = if !disp_owned.is_empty() {
Some(disp_owned.as_str())
} else {
disp_ref
};
let mut len: i32;
let fd = crate::ported::init::SHTTY.load(Ordering::Relaxed);
let out = if fd >= 0 { fd } else { 1 };
if let Some(d) = disp_now { if (m.flags & CMF_DISPLINE) != 0 { let _ = crate::ported::utils::write_loop(out, d.as_bytes());
let _ = crate::ported::utils::write_loop(out, b"\n");
return 0; }
let _ = crate::ported::utils::write_loop(out, d.as_bytes()); len = d.chars().count() as i32;
} else { let s = m.str.as_deref().unwrap_or("");
let _ = crate::ported::utils::write_loop(out, s.as_bytes()); len = s.chars().count() as i32;
if let Some(grp) = g {
if (grp.flags & CGF_FILES) != 0 && m.modec != '\0' {
let mut buf = [0u8; 4];
let mb = m.modec.encode_utf8(&mut buf);
let _ = crate::ported::utils::write_loop(out, mb.as_bytes());
len += 1;
}
}
}
if lastc == 0 { let pad = width - len;
if pad > 0 {
let spaces = vec![b' '; pad as usize];
let _ = crate::ported::utils::write_loop(out, &spaces);
}
}
len }
pub fn list_matches() -> i32 { use std::sync::atomic::Ordering;
use crate::ported::zle::compcore::{amatches, nmatches as nmatches_g};
use crate::ported::zle::zle_tricky::VALIDLIST;
if VALIDLIST.load(Ordering::SeqCst) == 0 { crate::ported::zle::zle_utils::showmsg("BUG: listmatches called with bogus list");
return 1; }
let groups = amatches.get_or_init(|| std::sync::Mutex::new(Vec::new()))
.lock().ok().map(|g| g.clone()).unwrap_or_default();
let mut dat = crate::ported::zle::comp_h::Chdata::default();
dat.matches = groups.into_iter().next().map(Box::new); dat.num = nmatches_g.load(Ordering::Relaxed); let _ = dat;
if let Ok(tab) = crate::ported::module::HOOKTAB.lock() {
if let Some(_fns) = tab.get("complist-matches") {
}
}
ilistmatches()
}
pub fn printlist(over: i32, showall: i32) -> i32 { use std::sync::atomic::Ordering;
use crate::ported::zle::comp_h::{CGF_LINES, CGF_ROWS, CMF_DISPLINE, CMF_HIDE, CMF_NOLIST};
let out_fd: i32 = {
let fd = crate::ported::init::SHTTY.load(Ordering::Relaxed);
if fd >= 0 { fd } else { 1 }
};
let listdat = crate::ported::zle::compcore::listdat
.get_or_init(|| std::sync::Mutex::new(Default::default()))
.lock().ok().map(|g| g.clone()).unwrap_or_default();
let mut cl: i32 = if over != 0 { listdat.nlines } else { -1 }; let mut pnl: i32 = 0; let mut ml: i32 = 0;
if cl < 2 { cl = -1;
crate::ported::zle::zle_refresh::tcoutclear(true); }
let groups = crate::ported::zle::compcore::amatches
.get_or_init(|| std::sync::Mutex::new(Vec::new()))
.lock().ok().map(|g| g.clone()).unwrap_or_default();
for g in &groups { for e in g.expls.iter() { let active = (e.count != 0 || e.always != 0) && (listdat.onlyexpl == 0
|| (listdat.onlyexpl
& (if e.always > 0 { 2 } else { 1 })) != 0);
if !active { continue; }
if pnl != 0 { let _ = crate::ported::utils::write_loop(out_fd, b"\n"); ml += 1;
cl -= 1;
if cl >= 0 && cl <= 1 { cl = -1;
crate::ported::zle::zle_refresh::tcoutclear(true);
}
}
let n = if e.always != 0 { -1 } else { e.count };
let l = crate::ported::zle::zle_tricky::printfmt(
e.str.as_deref().unwrap_or(""), n, true, true,
);
ml += l;
if cl >= 0 && (cl - l) <= 1 { cl = -1; }
pnl = 1;
}
if listdat.onlyexpl == 0 && !g.ylist.is_empty() { if pnl != 0 { let _ = crate::ported::utils::write_loop(out_fd, b"\n");
pnl = 0;
ml += 1;
if cl >= 0 && cl <= 1 { cl = -1; }
}
if (g.flags & CGF_LINES) != 0 { let last_idx = g.ylist.len().saturating_sub(1);
for (i, p) in g.ylist.iter().enumerate() {
let _ = crate::ported::utils::zputs(p);
if i != last_idx { let _ = crate::ported::utils::write_loop(out_fd, b"\n");
}
}
} else { for entry in &g.ylist {
let _ = crate::ported::utils::zputs(entry);
let _ = crate::ported::utils::write_loop(out_fd, b"\n");
ml += 1;
}
}
} else if listdat.onlyexpl == 0
&& (g.lcount != 0 || (showall != 0 && g.mcount != 0))
{
if pnl != 0 { let _ = crate::ported::utils::write_loop(out_fd, b"\n");
pnl = 0;
ml += 1;
}
for m in &g.matches { let visible = showall != 0
|| (m.flags & (CMF_HIDE | CMF_NOLIST)) == 0;
if !visible { continue; }
let _ = iprintm(Some(g), Some(m), 0, 0, 1, 0);
if (m.flags & CMF_DISPLINE) == 0 {
let _ = crate::ported::utils::write_loop(out_fd, b"\n");
}
ml += 1;
}
let _ = CGF_ROWS;
}
pnl = 1;
}
let _ = Ordering::Relaxed;
ml }