#[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 fn cpatterns_same( mut a: Option<&crate::ported::zle::comp_h::Cpattern>,
mut b: Option<&crate::ported::zle::comp_h::Cpattern>,
) -> bool { use crate::ported::zle::comp_h::{CPAT_CCLASS, CPAT_CHAR, CPAT_EQUIV, CPAT_NCLASS};
while let Some(ap) = a { let bp = match b { None => return false, Some(p) => p,
};
if ap.tp != bp.tp { return false; }
match ap.tp { x if x == CPAT_CCLASS || x == CPAT_NCLASS || x == CPAT_EQUIV => { if ap.str != bp.str { return false; }
}
x if x == CPAT_CHAR => { if ap.chr != bp.chr { return false; }
}
_ => { }
}
a = ap.next.as_deref(); b = bp.next.as_deref(); }
b.is_none() }
pub fn cmatchers_same( a: &crate::ported::zle::comp_h::Cmatcher,
b: &crate::ported::zle::comp_h::Cmatcher,
) -> bool { use crate::ported::zle::comp_h::{CMF_LEFT, CMF_RIGHT};
if std::ptr::eq(a, b) {
return true;
}
if a.flags != b.flags || a.llen != b.llen || a.wlen != b.wlen {
return false;
}
if a.llen != 0 && !cpatterns_same(a.line.as_deref(), b.line.as_deref()) {
return false;
}
if a.wlen > 0 && !cpatterns_same(a.word.as_deref(), b.word.as_deref()) {
return false;
}
if (a.flags & (CMF_LEFT | CMF_RIGHT)) != 0 {
if a.lalen != b.lalen || a.ralen != b.ralen { return false;
}
if a.lalen != 0 && !cpatterns_same(a.left.as_deref(), b.left.as_deref()) {
return false; }
if a.ralen != 0 && !cpatterns_same(a.right.as_deref(), b.right.as_deref()) {
return false; }
}
true
}
pub fn cline_sublen(l: &crate::ported::zle::comp_h::Cline) -> i32 { use crate::ported::zle::comp_h::{CLF_LINE, CLF_SUF};
let mut len: i32 = if (l.flags & CLF_LINE) != 0 { l.llen } else { l.wlen };
let no_subs = if (l.flags & CLF_SUF) != 0 {
l.suffix.is_none()
} else {
l.prefix.is_none()
};
if l.olen != 0 && no_subs {
len += l.olen; } else { let mut p = l.prefix.as_deref();
while let Some(pp) = p {
len += if (pp.flags & CLF_LINE) != 0 { pp.llen } else { pp.wlen };
p = pp.next.as_deref();
}
let mut p = l.suffix.as_deref();
while let Some(pp) = p {
len += if (pp.flags & CLF_LINE) != 0 { pp.llen } else { pp.wlen };
p = pp.next.as_deref();
}
}
len }
pub fn cline_setlens(l: &mut Option<Box<crate::ported::zle::comp_h::Cline>>, both: i32) { let mut cur = l.as_deref_mut();
while let Some(node) = cur { let s = cline_sublen(node); node.min = s; if both != 0 { node.max = s; }
cur = node.next.as_deref_mut(); }
}
pub fn cline_matched(p: &mut Option<Box<crate::ported::zle::comp_h::Cline>>) { use crate::ported::zle::comp_h::CLF_MATCHED;
let mut cur = p.as_deref_mut();
while let Some(node) = cur { node.flags |= CLF_MATCHED; cline_matched(&mut node.prefix); cline_matched(&mut node.suffix); cur = node.next.as_deref_mut(); }
}
pub fn revert_cline( mut p: Option<Box<crate::ported::zle::comp_h::Cline>>,
) -> Option<Box<crate::ported::zle::comp_h::Cline>> { let mut r: Option<Box<crate::ported::zle::comp_h::Cline>> = None; while let Some(mut node) = p { let n = node.next.take(); node.next = r; r = Some(node); p = n; }
r }
pub fn cp_cline( l: Option<&crate::ported::zle::comp_h::Cline>,
deep: i32,
) -> Option<Box<crate::ported::zle::comp_h::Cline>> { let mut r: Option<Box<crate::ported::zle::comp_h::Cline>> = None; let mut tail: *mut Option<Box<crate::ported::zle::comp_h::Cline>> = &mut r;
let mut cur = l;
while let Some(node) = cur { let mut t: Box<crate::ported::zle::comp_h::Cline> = Box::new(node.clone());
t.next = None;
if deep != 0 { if let Some(pre) = node.prefix.as_deref() {
t.prefix = cp_cline(Some(pre), 0); }
if let Some(suf) = node.suffix.as_deref() {
t.suffix = cp_cline(Some(suf), 0); }
}
unsafe {
*tail = Some(t);
let new_node = (*tail).as_mut().unwrap();
tail = &mut new_node.next;
}
cur = node.next.as_deref(); }
r }
pub fn free_cline(l: Option<Box<crate::ported::zle::comp_h::Cline>>) { drop(l);
}
use std::sync::Mutex;
use std::sync::OnceLock;
pub static MATCHBUF: OnceLock<Mutex<String>> = OnceLock::new();
pub static MATCHPARTS: OnceLock<Mutex<Option<Box<crate::ported::zle::comp_h::Cline>>>> = OnceLock::new();
pub static MATCHSUBS: OnceLock<Mutex<Option<Box<crate::ported::zle::comp_h::Cline>>>> = OnceLock::new();
pub fn start_match() { MATCHBUF
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.clear();
*MATCHPARTS.get_or_init(|| Mutex::new(None)).lock().unwrap() = None;
*MATCHSUBS.get_or_init(|| Mutex::new(None)).lock().unwrap() = None;
}
pub fn abort_match() { let parts = MATCHPARTS
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap()
.take();
let subs = MATCHSUBS
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap()
.take();
free_cline(parts);
free_cline(subs);
}
pub fn pattern_match_equivalence(
lp: &crate::ported::zle::comp_h::Cpattern, wind: u32, wmtp: i32, wchr: u32,
) -> u32 {
use crate::ported::zsh_h::{PP_LOWER, PP_UPPER};
use crate::ported::zle::zle_h::{ZC_tolower, ZC_toupper};
let Some(ref s) = lp.str else { return u32::MAX; };
let Some(target_idx) = (wind as i64).checked_sub(1) else { return u32::MAX; };
if target_idx < 0 { return u32::MAX; }
let mut lchr: Option<u32> = None;
let mut lmtp: i32 = 0;
let mut idx: i64 = 0;
let mut chars = s.chars().peekable();
while let Some(ch) = chars.next() {
if let Some(&peek) = chars.peek() {
if peek == '-' {
chars.next();
if let Some(hi) = chars.next() {
let span = (hi as i64) - (ch as i64);
if span >= 0 && idx + span >= target_idx {
lchr = Some(((ch as i64) + (target_idx - idx)) as u32);
break;
}
idx += span + 1;
continue;
}
}
}
if idx == target_idx {
lchr = Some(ch as u32);
break;
}
idx += 1;
}
let lchr = match lchr { Some(c) => c, None => return u32::MAX };
if lchr != u32::MAX { return lchr; }
let _ = lmtp;
let wch = char::from_u32(wchr).unwrap_or('\0');
if wmtp == PP_UPPER && lmtp == PP_LOWER {
return ZC_tolower(wch) as u32;
}
if wmtp == PP_LOWER && lmtp == PP_UPPER {
return ZC_toupper(wch) as u32;
}
if wmtp == lmtp { return wchr; }
u32::MAX }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pattern_match_equivalence_case_cross() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
use crate::ported::zle::comp_h::{Cpattern, CPAT_EQUIV};
let lp = Cpattern { tp: CPAT_EQUIV, str: Some("ab".into()), chr: 0, next: None };
let r = pattern_match_equivalence(&lp, 1, 0, b'A' as u32);
assert_eq!(r, b'a' as u32);
}
use crate::ported::zle::comp_h::{
CLF_LINE, CLF_MATCHED, CLF_SUF, CMF_LEFT, CMF_RIGHT, CPAT_CCLASS, CPAT_CHAR, CPAT_NCLASS,
Cline, Cmatcher, Cpattern,
};
fn cpat_char(ch: u32) -> Cpattern {
Cpattern {
tp: CPAT_CHAR,
chr: ch,
..Default::default()
}
}
fn cpat_class(s: &str) -> Cpattern {
Cpattern {
tp: CPAT_CCLASS,
str: Some(s.to_string()),
..Default::default()
}
}
#[test]
fn cpatterns_same_chr_match() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = cpat_char('a' as u32);
let b = cpat_char('a' as u32);
assert!(cpatterns_same(Some(&a), Some(&b)));
}
#[test]
fn cpatterns_same_chr_mismatch() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = cpat_char('a' as u32);
let b = cpat_char('b' as u32);
assert!(!cpatterns_same(Some(&a), Some(&b)));
}
#[test]
fn cpatterns_same_tp_mismatch() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = cpat_char('a' as u32);
let b = Cpattern {
tp: CPAT_NCLASS,
str: Some("a".into()),
..Default::default()
};
assert!(!cpatterns_same(Some(&a), Some(&b)));
}
#[test]
fn cpatterns_same_class_match() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = cpat_class("a-z");
let b = cpat_class("a-z");
assert!(cpatterns_same(Some(&a), Some(&b)));
}
#[test]
fn cpatterns_same_length_mismatch() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = cpat_char('a' as u32);
let mut a_chain = a.clone();
a_chain.next = Some(Box::new(cpat_char('b' as u32)));
let b = cpat_char('a' as u32);
assert!(!cpatterns_same(Some(&a_chain), Some(&b)));
}
#[test]
fn cpatterns_same_both_empty() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert!(cpatterns_same(None, None));
}
#[test]
fn cmatchers_same_pointer_eq() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let m = Cmatcher::default();
assert!(cmatchers_same(&m, &m));
}
#[test]
fn cmatchers_same_flags_diff() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = Cmatcher { flags: 0, ..Default::default() };
let b = Cmatcher { flags: 1, ..Default::default() };
assert!(!cmatchers_same(&a, &b));
}
#[test]
fn cmatchers_same_anchor_lengths() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let a = Cmatcher {
flags: CMF_LEFT,
lalen: 2,
..Default::default()
};
let b = Cmatcher {
flags: CMF_LEFT,
lalen: 3,
..Default::default()
};
assert!(!cmatchers_same(&a, &b));
let a = Cmatcher {
flags: CMF_RIGHT,
ralen: 1,
..Default::default()
};
let b = Cmatcher {
flags: CMF_RIGHT,
ralen: 1,
..Default::default()
};
assert!(cmatchers_same(&a, &b));
}
#[test]
fn cline_sublen_simple() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let l = Cline {
flags: CLF_LINE,
llen: 5,
wlen: 999,
..Default::default()
};
assert_eq!(cline_sublen(&l), 5);
}
#[test]
fn cline_sublen_with_olen() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let l = Cline {
flags: 0,
llen: 0,
wlen: 3,
olen: 7,
..Default::default()
};
assert_eq!(cline_sublen(&l), 10);
}
#[test]
fn cline_sublen_with_prefix() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let pre = Cline {
flags: CLF_LINE,
llen: 4,
..Default::default()
};
let l = Cline {
flags: 0,
wlen: 2,
olen: 99, prefix: Some(Box::new(pre)),
..Default::default()
};
assert_eq!(cline_sublen(&l), 6);
}
#[test]
fn cline_sublen_clf_suf() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let suf = Cline {
flags: CLF_LINE,
llen: 3,
..Default::default()
};
let l = Cline {
flags: CLF_SUF,
wlen: 1,
olen: 99,
suffix: Some(Box::new(suf)),
..Default::default()
};
assert_eq!(cline_sublen(&l), 4);
}
#[test]
fn cline_setlens_propagates() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut head: Option<Box<Cline>> = Some(Box::new(Cline {
flags: CLF_LINE,
llen: 5,
next: Some(Box::new(Cline {
flags: CLF_LINE,
llen: 3,
..Default::default()
})),
..Default::default()
}));
cline_setlens(&mut head, 1);
let h = head.as_ref().unwrap();
assert_eq!(h.min, 5);
assert_eq!(h.max, 5);
let n = h.next.as_ref().unwrap();
assert_eq!(n.min, 3);
assert_eq!(n.max, 3);
}
#[test]
fn cline_matched_sets_flag_recursively() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut head: Option<Box<Cline>> = Some(Box::new(Cline {
prefix: Some(Box::new(Cline::default())),
suffix: Some(Box::new(Cline::default())),
next: Some(Box::new(Cline::default())),
..Default::default()
}));
cline_matched(&mut head);
let h = head.as_ref().unwrap();
assert!(h.flags & CLF_MATCHED != 0);
assert!(h.prefix.as_ref().unwrap().flags & CLF_MATCHED != 0);
assert!(h.suffix.as_ref().unwrap().flags & CLF_MATCHED != 0);
assert!(h.next.as_ref().unwrap().flags & CLF_MATCHED != 0);
}
#[test]
fn revert_cline_reverses_chain() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let head = Some(Box::new(Cline {
llen: 1,
next: Some(Box::new(Cline {
llen: 2,
next: Some(Box::new(Cline {
llen: 3,
..Default::default()
})),
..Default::default()
})),
..Default::default()
}));
let r = revert_cline(head);
let n = r.as_ref().unwrap();
assert_eq!(n.llen, 3);
let n = n.next.as_ref().unwrap();
assert_eq!(n.llen, 2);
let n = n.next.as_ref().unwrap();
assert_eq!(n.llen, 1);
assert!(n.next.is_none());
}
#[test]
fn cp_cline_shallow() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let src = Cline {
llen: 7,
wlen: 9,
next: Some(Box::new(Cline {
llen: 11,
..Default::default()
})),
..Default::default()
};
let dup = cp_cline(Some(&src), 0);
let n = dup.as_ref().unwrap();
assert_eq!(n.llen, 7);
assert_eq!(n.wlen, 9);
let n = n.next.as_ref().unwrap();
assert_eq!(n.llen, 11);
}
#[test]
fn start_match_clears_globals() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
MATCHBUF
.get_or_init(|| Mutex::new(String::new()))
.lock()
.unwrap()
.push_str("garbage");
*MATCHPARTS
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap() = Some(Box::new(Cline::default()));
start_match();
assert!(MATCHBUF.get().unwrap().lock().unwrap().is_empty());
assert!(MATCHPARTS.get().unwrap().lock().unwrap().is_none());
assert!(MATCHSUBS.get().unwrap().lock().unwrap().is_none());
}
#[test]
fn abort_match_drops_lists() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
*MATCHPARTS
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap() = Some(Box::new(Cline::default()));
*MATCHSUBS
.get_or_init(|| Mutex::new(None))
.lock()
.unwrap() = Some(Box::new(Cline::default()));
abort_match();
assert!(MATCHPARTS.get().unwrap().lock().unwrap().is_none());
assert!(MATCHSUBS.get().unwrap().lock().unwrap().is_none());
}
}
pub fn add_bmatchers(m: Option<&crate::ported::zle::comp_h::Cmatcher>) { use crate::ported::zle::comp_h::{Cmatcher, Cmlist, CMF_RIGHT};
let old = { let cell = crate::ported::zle::compcore::bmatchers
.get_or_init(|| std::sync::Mutex::new(None));
cell.lock().ok().and_then(|mut g| g.take())
};
let mut head: Option<Box<Cmlist>> = None; let mut tail_ref: *mut Option<Box<Cmlist>> = &mut head;
let mut cur = m;
while let Some(mat) = cur { let qual = (mat.flags == 0 && mat.wlen > 0 && mat.llen > 0) || (mat.flags == CMF_RIGHT && mat.wlen < 0 && mat.llen == 0);
if qual {
let n = Box::new(Cmlist {
next: None,
matcher: Box::new(Cmatcher {
refc: mat.refc,
next: mat.next.clone(),
flags: mat.flags,
line: mat.line.clone(),
llen: mat.llen,
word: mat.word.clone(),
wlen: mat.wlen,
left: mat.left.clone(),
lalen: mat.lalen,
right: mat.right.clone(),
ralen: mat.ralen,
}),
str: String::new(),
});
unsafe {
*tail_ref = Some(n);
if let Some(ref mut newnode) = *tail_ref {
tail_ref = &mut newnode.next as *mut _; }
}
}
cur = mat.next.as_deref(); }
unsafe { *tail_ref = old; }
if let Ok(mut g) = crate::ported::zle::compcore::bmatchers
.get_or_init(|| std::sync::Mutex::new(None)).lock()
{
*g = head;
}
}
pub fn add_match_part(
m: Option<&crate::ported::zle::comp_h::Cmatcher>, l: Option<&str>, _ll: i32,
w: &str, wl: i32,
o: Option<&str>, ol: i32,
s: &str, sl: i32,
osl: i32, sfx: i32,
) {
use crate::ported::zle::comp_h::{Cline, CMF_LEFT, CLF_NEW, CLF_SUF};
let l_eff: Option<String> = match l {
Some(lstr) if lstr.len() >= wl as usize
&& wl > 0
&& &lstr[..wl as usize] == &w[..wl as usize] => None,
Some(lstr) => Some(lstr.to_string()),
None => None,
};
let mut lp: Option<Box<Cline>> = None;
let mut lprem: Option<Box<Cline>> = None;
let mut p = bld_parts(s, sl, osl, Some(&mut lp), Some(&mut lprem));
if let Some(rem) = lprem.as_mut() {
if m.map(|mat| (mat.flags & CMF_LEFT) != 0).unwrap_or(false) {
rem.flags |= CLF_SUF; rem.suffix = rem.prefix.take(); }
}
if sfx != 0 {
if let Some(chain) = p.take() {
p = revert_cline(Some(chain));
}
}
let subs = MATCHSUBS.get_or_init(|| std::sync::Mutex::new(None))
.lock().ok().and_then(|mut g| g.take());
if let Some(subs_chain) = subs { if let Some(lp_node) = lp.as_mut() {
if sfx != 0 { let mut tail_ref: *mut Option<Box<Cline>> = &mut lp_node.prefix;
unsafe {
while let Some(ref mut next_node) = *tail_ref {
tail_ref = &mut next_node.next as *mut _;
}
*tail_ref = Some(subs_chain);
}
} else if let Some(ref mut p_node) = p { let old_prefix = p_node.prefix.take();
let mut new_head = subs_chain;
{
let mut tail_ref: *mut Option<Box<Cline>> = &mut new_head.next;
unsafe {
while let Some(ref mut nn) = *tail_ref {
tail_ref = &mut nn.next as *mut _;
}
*tail_ref = old_prefix;
}
}
p_node.prefix = Some(new_head);
}
}
if let Ok(mut g) = MATCHLASTSUB
.get_or_init(|| std::sync::Mutex::new(None)).lock()
{
*g = None;
}
}
if let Some(lp_node) = lp.as_mut() {
if lp_node.llen != 0 || lp_node.wlen != 0 { let next = get_cline(
l_eff.clone(), wl, Some(w.to_string()), wl,
o.map(|s| s.to_string()), ol, CLF_NEW,
);
lp_node.next = Some(next); } else { lp_node.line = l_eff.clone(); lp_node.llen = wl;
lp_node.word = Some(w.to_string()); lp_node.wlen = wl;
lp_node.orig = o.map(|s| s.to_string()); lp_node.olen = ol;
}
if o.is_some() || ol != 0 { lp_node.flags &= !CLF_NEW;
}
}
let last_present = MATCHLASTPART.get()
.and_then(|c| c.lock().ok().map(|g| g.is_some()))
.unwrap_or(false);
if last_present { if let Ok(mut tail) = MATCHLASTPART
.get_or_init(|| std::sync::Mutex::new(None)).lock()
{
if let Some(t) = tail.as_mut() {
t.next = p.clone();
}
}
} else if let Ok(mut head) = MATCHPARTS
.get_or_init(|| std::sync::Mutex::new(None)).lock()
{
*head = p.clone(); }
if let Some(lp_node) = lp {
if let Ok(mut tail) = MATCHLASTPART
.get_or_init(|| std::sync::Mutex::new(None)).lock()
{
*tail = Some(lp_node); }
}
}
pub static MATCHLASTPART: std::sync::OnceLock<std::sync::Mutex<Option<Box<crate::ported::zle::comp_h::Cline>>>>
= std::sync::OnceLock::new();
pub fn add_match_str(m: Option<&crate::ported::zle::comp_h::Cmatcher>, l: &str, w: &str, mut wl: i32, sfx: i32)
{
use crate::ported::zle::comp_h::CMF_LINE;
let (eff_w_owned, eff_w): (String, &str) = match m {
Some(mat) if (mat.flags & CMF_LINE) != 0 => {
wl = mat.llen;
let owned = l.to_string();
let s = owned.clone();
(owned, Box::leak(s.into_boxed_str()))
}
_ => (String::new(), w),
};
let _ = eff_w_owned;
if wl <= 0 { return; }
if let Ok(mut buf) = MATCHBUF.get_or_init(|| Mutex::new(String::new())).lock() {
let take_n = wl as usize;
let new_chunk: String = eff_w.chars().take(take_n).collect();
if sfx != 0 { *buf = format!("{}{}", new_chunk, *buf); } else { buf.push_str(&new_chunk);
}
MATCHBUFADDED.fetch_add(wl, std::sync::atomic::Ordering::Relaxed); }
}
pub static MATCHBUFADDED: std::sync::atomic::AtomicI32 =
std::sync::atomic::AtomicI32::new(0);
pub fn add_match_sub(
m: Option<&crate::ported::zle::comp_h::Cmatcher>, l: Option<&str>, ll: i32, w: Option<&str>, wl: i32,
) {
use crate::ported::zle::comp_h::{Cline, CLF_NEW};
let (eff_w, eff_wl) = match m {
Some(mat) if (mat.flags & crate::ported::zle::comp_h::CMF_LINE) != 0
=> (l, mat.llen),
_ => (w, wl),
};
if eff_wl <= 0 && ll <= 0 { return; }
let node = Box::new(Cline {
flags: CLF_NEW,
line: l.map(|s| s.to_string()),
llen: ll,
word: eff_w.map(|s| s.to_string()),
wlen: eff_wl,
..Default::default()
});
let last_cell = MATCHLASTSUB.get_or_init(|| Mutex::new(None));
let head_cell = MATCHSUBS.get_or_init(|| Mutex::new(None));
let last_present = last_cell.lock().ok().map(|g| g.is_some()).unwrap_or(false);
if last_present { if let Ok(mut tail) = last_cell.lock() {
if let Some(t) = tail.as_mut() {
t.next = Some(node.clone()); }
}
} else { if let Ok(mut h) = head_cell.lock() {
*h = Some(node.clone()); }
}
if let Ok(mut tail) = last_cell.lock() {
*tail = Some(node); }
}
pub static MATCHLASTSUB: std::sync::OnceLock<Mutex<Option<Box<crate::ported::zle::comp_h::Cline>>>>
= std::sync::OnceLock::new();
pub fn bld_line(
mp: &crate::ported::zle::comp_h::Cmatcher, line: &mut Vec<char>,
mword: &str,
word: &str,
wlen: i32,
_sfx: i32,
) -> i32 {
use crate::ported::zle::comp_h::CPAT_CHAR;
let _ = mword;
let _ = word;
let mut consumed: i32 = 0;
let mut lpat = Some(&*mp.line.as_deref().unwrap());
while let Some(p) = lpat {
if p.tp == CPAT_CHAR {
if let Some(ch) = char::from_u32(p.chr) {
line.push(ch); consumed += 1;
if consumed >= wlen { break; }
}
} else {
break;
}
lpat = p.next.as_deref();
}
consumed }
pub fn bld_parts(
str: &str, len: i32, mut plen: i32, lp: Option<&mut Option<Box<crate::ported::zle::comp_h::Cline>>>,
lprem: Option<&mut Option<Box<crate::ported::zle::comp_h::Cline>>>,
) -> Option<Box<crate::ported::zle::comp_h::Cline>> {
use crate::ported::zle::comp_h::{Cline, CLF_NEW};
let bytes = str.as_bytes();
let total: usize = (len as usize).min(bytes.len());
let mut op = plen;
let mut p_start = 0usize;
let mut str_pos = 0usize;
let mut remaining = total as i32;
let mut head: Option<Box<Cline>> = None;
let mut tail_ref: *mut Option<Box<Cline>> = &mut head;
let mut last_n: Option<Box<Cline>> = None;
while remaining > 0 { str_pos += 1;
remaining -= 1;
plen -= 1;
}
if p_start != str_pos { let olen = (str_pos - p_start) as i32;
let mut llen = if op < 0 { 0 } else { op };
if llen > olen { llen = olen; }
let flags = if plen <= 0 { CLF_NEW } else { 0 };
let mut node = Box::new(Cline {
flags,
..Default::default()
});
let prefix_word: String = std::str::from_utf8(
&bytes[p_start..p_start + olen as usize]
).unwrap_or("").into();
node.prefix = Some(Box::new(Cline {
llen,
word: Some(prefix_word.clone()),
wlen: olen,
..Default::default()
}));
if let Some(out) = lprem { *out = Some(node.clone()); } last_n = Some(node.clone());
unsafe {
*tail_ref = Some(node);
}
} else if head.is_none() { let flags = if plen <= 0 { CLF_NEW } else { 0 };
let node = Box::new(Cline {
flags,
..Default::default()
});
if let Some(out) = lprem { *out = Some(node.clone()); } last_n = Some(node.clone());
head = Some(node);
} else if let Some(out) = lprem { *out = None;
}
if let (Some(out_lp), Some(n)) = (lp, last_n) { *out_lp = Some(n);
}
let _ = p_start;
let _ = op;
head }
#[derive(Default, Clone, Debug)]
#[allow(non_camel_case_types)]
pub struct cmdata { pub cl: Option<Box<crate::ported::zle::comp_h::Cline>>, pub pcl: Option<Box<crate::ported::zle::comp_h::Cline>>, pub str: String, pub astr: String, pub len: i32, pub alen: i32, pub olen: i32, pub line: i32, }
pub fn check_cmdata(md: &mut cmdata, sfx: i32) -> i32 { use crate::ported::zle::comp_h::CLF_LINE;
if md.len != 0 { return 0; } let next = match md.cl.as_deref() { None => return 1,
Some(n) => n.clone(),
};
if (next.flags & CLF_LINE) != 0 { md.line = 1;
md.len = next.llen; md.str = next.line.clone().unwrap_or_default(); } else {
md.line = 0;
md.len = next.wlen; md.olen = next.wlen; if let Some(ref w) = next.word {
md.str = if sfx != 0 { w[md.len as usize..].to_string() } else { w.clone() };
}
md.alen = next.llen; if let Some(ref l) = next.line {
md.astr = if sfx != 0 { l[md.alen as usize..].to_string() } else { l.clone() };
}
}
md.pcl = Some(Box::new(next.clone())); md.cl = next.next.clone(); 0 }
pub fn cmp_anchors(o: &mut crate::ported::zle::comp_h::Cline, n: &crate::ported::zle::comp_h::Cline,
join: i32) -> i32 {
use crate::ported::zle::comp_h::{CLF_JOIN, CLF_LINE};
let strncmp_eq = |a: &Option<String>, b: &Option<String>, n: usize| -> bool {
match (a, b) {
(Some(x), Some(y)) => {
let xb = x.as_bytes();
let yb = y.as_bytes();
xb.len() >= n && yb.len() >= n && xb[..n] == yb[..n]
}
_ => false,
}
};
let word_match = (o.flags & CLF_LINE) == 0
&& o.wlen == n.wlen
&& (o.word.is_none()
|| strncmp_eq(&o.word, &n.word, o.wlen as usize));
let line_match = !word_match && {
let both_empty = o.line.is_none() && n.line.is_none()
&& o.wlen == 0 && n.wlen == 0;
let both_lines = o.llen == n.llen
&& o.line.is_some() && n.line.is_some()
&& strncmp_eq(&o.line, &n.line, o.llen as usize);
both_empty || both_lines };
if word_match || line_match { if line_match {
o.flags |= CLF_LINE;
o.word = None; o.wlen = 0; }
return 1; }
if join != 0 && (o.flags & CLF_JOIN) == 0
&& o.word.is_some() && n.word.is_some()
{
if let Some(j) = join_strs(
o.wlen,
o.word.as_deref().unwrap(),
n.wlen,
n.word.as_deref().unwrap(),
) {
o.flags |= CLF_JOIN; o.wlen = j.len() as i32; o.word = Some(j); return 2; }
}
0 }
pub fn get_cline(l: Option<String>, ll: i32, w: Option<String>, wl: i32, o: Option<String>, ol: i32, fl: i32)
-> Box<crate::ported::zle::comp_h::Cline>
{
use crate::ported::zle::comp_h::Cline;
Box::new(Cline {
next: None, line: l, llen: ll,
word: w, wlen: wl,
orig: o, olen: ol,
slen: 0, flags: fl, prefix: None, suffix: None,
min: 0, max: 0,
})
}
pub fn join_clines(o: i32, n: i32) -> i32 { if o == 0 { return n; }
n
}
pub fn join_mid(o: &mut crate::ported::zle::comp_h::Cline, n: &mut crate::ported::zle::comp_h::Cline)
{
use crate::ported::zle::comp_h::CLF_JOIN;
if (o.flags & CLF_JOIN) != 0 { let mut nr: Option<Box<crate::ported::zle::comp_h::Cline>> = None;
join_psfx(o, n, None, Some(&mut nr), 0);
n.suffix = nr.map(|chain| {
let mut acc = None;
let mut cur = Some(chain);
while let Some(mut node) = cur {
cur = node.next.take();
node.next = acc;
acc = Some(node);
}
acc
}).flatten();
join_psfx(o, n, None, None, 1);
} else { o.flags |= CLF_JOIN;
let mut or_: Option<Box<crate::ported::zle::comp_h::Cline>> = None;
let mut nr: Option<Box<crate::ported::zle::comp_h::Cline>> = None;
join_psfx(o, n, Some(&mut or_), Some(&mut nr), 0);
if let Some(ref mut or_node) = or_ { let new_llen = if o.slen > or_node.wlen { or_node.wlen } else { o.slen };
or_node.llen = new_llen;
}
let mut reversed_or = None;
let mut cur = or_;
while let Some(mut node) = cur {
cur = node.next.take();
node.next = reversed_or;
reversed_or = Some(node);
}
o.suffix = reversed_or;
let mut reversed_nr = None;
let mut cur = nr;
while let Some(mut node) = cur {
cur = node.next.take();
node.next = reversed_nr;
reversed_nr = Some(node);
}
n.suffix = reversed_nr;
join_psfx(o, n, None, None, 1); }
n.suffix = None; }
pub fn join_psfx(
ot: &mut crate::ported::zle::comp_h::Cline, nt: &mut crate::ported::zle::comp_h::Cline,
orest: Option<&mut Option<Box<crate::ported::zle::comp_h::Cline>>>,
nrest: Option<&mut Option<Box<crate::ported::zle::comp_h::Cline>>>,
sfx: i32,
) {
use crate::ported::zle::comp_h::CLF_MISS;
let (oref, nref) = if sfx != 0 { (ot.suffix.clone(), nt.suffix.clone())
} else {
(ot.prefix.clone(), nt.prefix.clone())
};
if oref.is_none() { if let Some(out) = orest { *out = None; } if let Some(out) = nrest { *out = nref.clone(); } if let Some(ref nn) = nref { if nn.wlen != 0 {
ot.flags |= CLF_MISS; }
}
return; }
if let Some(out) = orest { *out = oref; }
if let Some(out) = nrest { *out = nref; }
let _ = &nt;
}
pub fn join_strs(mut la: i32, sa: &str, mut lb: i32, sb: &str) -> Option<String>
{
let mut out = String::new();
let mut a_idx = 0usize;
let mut b_idx = 0usize;
let a_bytes = sa.as_bytes();
let b_bytes = sb.as_bytes();
while la > 0 && lb > 0 && a_idx < a_bytes.len() && b_idx < b_bytes.len() {
if a_bytes[a_idx] == b_bytes[b_idx] { out.push(a_bytes[a_idx] as char);
a_idx += 1;
b_idx += 1;
la -= 1;
lb -= 1;
} else {
let bmatchers = crate::ported::zle::compcore::bmatchers
.get_or_init(|| std::sync::Mutex::new(None))
.lock().ok().and_then(|g| g.clone());
let mut advanced = false;
let mut cur = bmatchers.as_deref();
while let Some(ms) = cur { let mp = &*ms.matcher;
let ok = mp.flags == 0 && mp.wlen > 0 && mp.llen > 0
&& mp.wlen <= la && mp.wlen <= lb;
if ok {
let mp_word = mp.word.as_deref();
let a_slice = &sa[a_idx..];
let b_slice = &sb[b_idx..];
let t = if pattern_match(mp_word, a_slice, None, "") != 0 {
1
} else if pattern_match(mp_word, b_slice, None, "") != 0 {
2
} else { 0 };
if t != 0 {
let mut line: Vec<char> = Vec::new();
let bl = bld_line(
mp, &mut line,
"", if t == 1 { b_slice } else { a_slice },
if t == 1 { lb } else { la },
0,
);
if bl > 0 { for ch in &line { out.push(*ch); }
if t == 1 {
a_idx += mp.wlen as usize;
la -= mp.wlen;
b_idx += bl as usize;
lb -= bl;
} else {
b_idx += mp.wlen as usize;
lb -= mp.wlen;
a_idx += bl as usize;
la -= bl;
}
advanced = true;
break;
}
}
}
cur = ms.next.as_deref();
}
if !advanced { break; }
}
}
if !out.is_empty() { Some(out) } else { None } }
pub fn join_sub(md: &mut cmdata, str: &str, len: i32, mlen: &mut i32, sfx: i32, join: i32) -> Option<Box<crate::ported::zle::comp_h::Cline>>
{
use crate::ported::zle::comp_h::CLF_JOIN;
if check_cmdata(md, sfx) != 0 {
return None;
}
let ow = str;
let nw = md.str.clone();
let ol = len;
let nl = md.len;
let bmatchers = crate::ported::zle::compcore::bmatchers
.get_or_init(|| std::sync::Mutex::new(None))
.lock().ok().and_then(|g| g.clone());
let mut cur = bmatchers.as_deref();
while let Some(ms) = cur { let mp = &*ms.matcher;
if mp.flags == 0 && mp.wlen > 0 && mp.llen > 0 { if mp.llen <= ol && mp.wlen <= nl { let ow_off = if sfx != 0 { ol - mp.llen } else { 0 };
let nw_off = if sfx != 0 { nl - mp.wlen } else { 0 };
let line_slice = &ow[ow_off as usize..];
let word_slice = &nw[nw_off as usize..];
if pattern_match(
mp.line.as_deref(), line_slice,
mp.word.as_deref(), word_slice,
) != 0
{
if sfx != 0 {
md.str = md.str.chars().take(
md.str.chars().count().saturating_sub(mp.wlen as usize),
).collect();
} else {
md.str = md.str.chars()
.skip(mp.wlen as usize).collect();
}
md.len -= mp.wlen;
*mlen = mp.llen; return Some(get_cline( None, 0,
Some(line_slice[..mp.llen as usize].to_string()),
mp.llen, None, 0, 0,
));
}
}
if join != 0 && mp.wlen <= ol && mp.wlen <= nl { let ow_off = if sfx != 0 { ol - mp.wlen } else { 0 };
let nw_off = if sfx != 0 { nl - mp.wlen } else { 0 };
let mp_word = mp.word.as_deref();
let ow_slice = &ow[ow_off as usize..];
let nw_slice = &nw[nw_off as usize..];
let t = if pattern_match(mp_word, ow_slice, None, "") != 0 {
1
} else if pattern_match(mp_word, nw_slice, None, "") != 0 {
2
} else { 0 };
if t != 0 { let (mw_slice, other_slice, other_len) = if t == 1 {
(ow_slice, nw_slice, nl)
} else {
(nw_slice, ow_slice, ol)
};
let _ = mw_slice;
let mut line: Vec<char> = Vec::new();
let bl = bld_line(
mp, &mut line, "", other_slice, other_len, sfx,
);
if bl > 0 { let new_nl = if t == 1 { bl } else { mp.wlen };
let new_ol = if t == 1 { mp.wlen } else { bl };
if sfx != 0 {
md.str = md.str.chars().take(
md.str.chars().count().saturating_sub(new_nl as usize),
).collect();
} else {
md.str = md.str.chars().skip(new_nl as usize).collect();
}
md.len -= new_nl; *mlen = new_ol;
let line_str: String = line.iter().collect();
return Some(get_cline( None, 0,
Some(line_str), mp.llen, None, 0, CLF_JOIN,
));
}
}
}
}
cur = ms.next.as_deref();
}
None }
pub fn pattern_match(
p: Option<&crate::ported::zle::comp_h::Cpattern>, s: &str,
wp: Option<&crate::ported::zle::comp_h::Cpattern>,
ws: &str,
) -> i32 {
use crate::ported::zle::comp_h::CPAT_ANY;
use crate::ported::zsh_h::{PP_LOWER, PP_UPPER};
use crate::ported::zle::zle_h::ZC_tolower;
let (mut p_cur, mut wp_cur) = (p, wp); let mut s_bytes = s.chars().peekable();
let mut ws_bytes = ws.chars().peekable();
while p_cur.is_some() && wp_cur.is_some() && s_bytes.peek().is_some() && ws_bytes.peek().is_some()
{
let pat = p_cur.unwrap();
let wpat = wp_cur.unwrap();
let wc = ws_bytes.next().unwrap() as u32; let mut wmt: i32 = 0;
let wind = pattern_match1(wpat, wc, &mut wmt); if wind == 0 { return 0; }
let c = s_bytes.next().unwrap() as u32; if pat.tp != CPAT_ANY || wpat.tp != CPAT_ANY { let mut mt: i32 = 0;
let ind = pattern_match1(pat, c, &mut mt); if ind == 0 { return 0; } if ind != wind { return 0; } if mt != wmt { let case_pair = (mt == PP_LOWER || mt == PP_UPPER)
&& (wmt == PP_LOWER || wmt == PP_UPPER);
if case_pair {
let cc = char::from_u32(c).unwrap_or('\0');
let wcc = char::from_u32(wc).unwrap_or('\0');
if ZC_tolower(cc) != ZC_tolower(wcc) { return 0;
}
} else {
return 0; }
}
}
p_cur = pat.next.as_deref(); wp_cur = wpat.next.as_deref();
}
if p_cur.is_none() && wp_cur.is_none()
&& s_bytes.peek().is_none() && ws_bytes.peek().is_none()
{
1 } else {
0 }
}
pub fn pattern_match_restrict(
p: Option<&crate::ported::zle::comp_h::Cpattern>, wp: Option<&crate::ported::zle::comp_h::Cpattern>,
wsc: &[u32],
prestrict: Option<&crate::ported::zle::comp_h::Cpattern>,
new_line: &mut Vec<char>,
) -> i32 {
use crate::ported::zle::comp_h::{CPAT_ANY, CPAT_CHAR, CPAT_EQUIV};
use crate::ported::zsh_h::{PP_LOWER, PP_UPPER};
use crate::ported::zle::zle_h::ZC_tolower;
let mut p_cur = p;
let mut wp_cur = wp;
let mut pr_cur = prestrict;
let mut wsc_idx = 0usize;
while p_cur.is_some() && wp_cur.is_some() && wsc_idx < wsc.len() && pr_cur.is_some()
{
let pat = p_cur.unwrap();
let wpat = wp_cur.unwrap();
let pre = pr_cur.unwrap();
let wc = wsc[wsc_idx];
let mut wmt: i32 = 0;
let wind = pattern_match1(wpat, wc, &mut wmt); if wind == 0 { return 0; }
let c: u32 = if pre.tp == CPAT_CHAR { pre.chr } else if pat.tp == CPAT_CHAR { pat.chr } else if pat.tp == CPAT_EQUIV { let r = pattern_match_equivalence(pat, wind, wmt, wc);
if r == u32::MAX { return 0; } r
} else { wc };
if pre.tp != CPAT_CHAR {
let mut mt: i32 = 0;
if pattern_match1(pre, c, &mut mt) == 0 { return 0; } }
if pat.tp != CPAT_ANY || wpat.tp != CPAT_ANY { let mut mt: i32 = 0;
let ind = pattern_match1(pat, c, &mut mt); if ind == 0 || ind != wind { return 0; } if mt != wmt {
let case_pair = (mt == PP_LOWER || mt == PP_UPPER)
&& (wmt == PP_LOWER || wmt == PP_UPPER);
if case_pair {
let cc = char::from_u32(c).unwrap_or('\0');
let wcc = char::from_u32(wc).unwrap_or('\0');
if ZC_tolower(cc) != ZC_tolower(wcc) { return 0; } } else {
return 0; }
}
}
if let Some(ch) = char::from_u32(c) {
new_line.push(ch);
}
pr_cur = pre.next.as_deref(); wsc_idx += 1;
p_cur = pat.next.as_deref();
wp_cur = wpat.next.as_deref();
}
while p_cur.is_some() && pr_cur.is_some() { let pat = p_cur.unwrap();
let pre = pr_cur.unwrap();
let c: u32 = if pre.tp == CPAT_CHAR {
pre.chr
} else if pat.tp == CPAT_CHAR {
pat.chr
} else {
return 0; };
let mut mt: i32 = 0;
if pre.tp != CPAT_CHAR && pattern_match1(pre, c, &mut mt) == 0 {
return 0;
}
if let Some(ch) = char::from_u32(c) {
new_line.push(ch);
}
pr_cur = pre.next.as_deref();
p_cur = pat.next.as_deref();
}
if p_cur.is_none() && pr_cur.is_none()
&& (wp_cur.is_none() || wsc_idx >= wsc.len())
{
1 } else {
0 }
}
pub fn pattern_match1(p: &crate::ported::zle::comp_h::Cpattern, c: u32, mtp: &mut i32) -> u32
{
use crate::ported::zle::comp_h::{CPAT_ANY, CPAT_CCLASS, CPAT_CHAR, CPAT_EQUIV, CPAT_NCLASS};
*mtp = 0; match p.tp { x if x == CPAT_CCLASS => { patmatchrange(p.str.as_deref(), c, None, None) as u32 }
x if x == CPAT_NCLASS => { if patmatchrange(p.str.as_deref(), c, None, None) { 0 } else { 1 } }
x if x == CPAT_EQUIV => { let mut ind: u32 = 0;
if patmatchrange(p.str.as_deref(), c, Some(&mut ind), Some(mtp)) {
ind + 1 } else {
0 }
}
x if x == CPAT_ANY => 1, x if x == CPAT_CHAR => if p.chr == c { c } else { 0 }, _ => 0, }
}
fn patmatchrange(s: Option<&str>, c: u32, indp: Option<&mut u32>, _mtp: Option<&mut i32>) -> bool {
let Some(s) = s else { return false; };
let mut idx: u32 = 0;
let mut chars = s.chars().peekable();
while let Some(ch) = chars.next() {
if let Some(&peek) = chars.peek() {
if peek == '-' {
chars.next();
if let Some(hi) = chars.next() {
if c >= ch as u32 && c <= hi as u32 {
if let Some(out) = indp { *out = idx; }
return true;
}
idx += 1;
continue;
}
}
}
if c == ch as u32 {
if let Some(out) = indp { *out = idx; }
return true;
}
idx += 1;
}
false
}
pub fn sub_join(a: &mut crate::ported::zle::comp_h::Cline, b: Option<Box<crate::ported::zle::comp_h::Cline>>,
e: &crate::ported::zle::comp_h::Cline,
_anew: i32) -> i32
{
if e.suffix.is_some() || a.prefix.is_none() { return 0; }
let mut min_total: i32 = 0; let mut max_total: i32 = 0;
let mut cur = b; while let Some(node) = cur { min_total += node.min; max_total += node.max;
if std::ptr::addr_eq(node.as_ref() as *const _, e as *const _) {
break;
}
cur = node.next.clone();
}
max_total - min_total }
pub fn sub_match(md: &mut cmdata, str: &str, len: i32, sfx: i32) -> i32 { let mut ret = 0i32;
let str_bytes = str.as_bytes();
let mut remaining = len as usize;
let start_idx: usize = if sfx != 0 { (len as usize).min(str_bytes.len()) } else { 0 };
while remaining > 0 { if check_cmdata(md, sfx) != 0 { return ret;
}
let md_bytes = md.str.as_bytes();
let mut l: usize = 0;
let md_len_usize = md.len as usize;
let cap = remaining.min(md_len_usize);
while l < cap {
let s_idx: isize = if sfx != 0 {
start_idx as isize - (l as isize) - 1 - (ret as isize)
} else {
(ret as isize) + (l as isize)
};
let m_len = md_bytes.len();
let m_idx: isize = if sfx != 0 {
m_len as isize - (l as isize) - 1
} else {
l as isize
};
if s_idx < 0 || m_idx < 0 { break; }
let s_pos = s_idx as usize;
let m_pos = m_idx as usize;
if s_pos >= str_bytes.len() || m_pos >= md_bytes.len() { break; }
if str_bytes[s_pos] != md_bytes[m_pos] { break; }
l += 1;
}
if l == 0 { return ret; }
const META_BYTE: u8 = 0x83;
let check_pos: isize = if sfx != 0 {
start_idx as isize - (l as isize) - (ret as isize)
} else {
(ret as isize) + (l as isize) - 1
};
if check_pos >= 0 && (check_pos as usize) < str_bytes.len()
&& str_bytes[check_pos as usize] == META_BYTE && l > 0
{
l -= 1;
}
md.len -= l as i32;
if sfx != 0 {
md.str = md.str.chars().take(
md.str.chars().count().saturating_sub(l),
).collect();
} else {
md.str = md.str.chars().skip(l).collect();
}
ret += l as i32; remaining = remaining.saturating_sub(l);
if remaining == 0 || md.len == 0 { break;
}
}
ret }
pub fn undo_cmdata(md: &cmdata, sfx: i32) -> Option<Box<crate::ported::zle::comp_h::Cline>> { use crate::ported::zle::comp_h::CLF_LINE;
let mut r = md.pcl.as_deref().cloned()?;
if md.line != 0 { r.word = None; r.wlen = 0; r.flags |= CLF_LINE; r.llen = md.len; let off = if sfx != 0 { md.len as usize } else { 0 };
r.line = Some(md.str.chars().skip(md.str.len().saturating_sub(off + md.len as usize)).collect());
} else if md.len != md.olen { r.wlen = md.len; let off = if sfx != 0 { md.len as usize } else { 0 };
r.word = Some(md.str.chars().skip(md.str.len().saturating_sub(off + md.len as usize)).collect());
}
Some(Box::new(r)) }