use std::sync::atomic::Ordering;
use std::sync::atomic::Ordering::SeqCst;
use super::zle_h::{modifier, vichange, MOD_MULT, MOD_TMULT, MOD_VIAPP, MOD_VIBUF};
use super::zle_keymap::selectkeymap;
use super::zle_misc::{TAILADD, VFINDCHAR, VFINDDIR};
#[allow(unused_imports)]
use crate::ported::zle::{
deltochar::*, textobjects::*, zle_h::*, zle_hist::*, zle_main, zle_main::*, zle_misc::*,
zle_move::*, zle_params::*, zle_refresh::*, zle_tricky::*, zle_utils::*, zle_word::*,
};
#[allow(unused_imports)]
#[allow(unused_imports)]
pub fn vichange() -> i32 {
startvichange(1); let c2 = getvirange(1); if c2 != -1 {
let cs = ZLECS.load(SeqCst) as i32;
forekill(c2 - cs, CUT_RAW); selectkeymap("main", 1); VIINSBEGIN.store(ZLECS.load(SeqCst), SeqCst); VISTARTCHANGE.store(UNDO_CHANGENO.load(SeqCst), SeqCst); return 0; }
1 }
pub fn startvichange(im: i32) {
use crate::ported::utils::unmetafy;
use crate::ported::zle::zle_keymap::{keybuf, keybuflen};
use crate::ported::zle::zle_main::{ZLECS, ZLELL};
if im > -1 {
INSMODE.store(if im != 0 { 1 } else { 0 }, SeqCst);
}
let in_repeat = VIINREPEAT.load(SeqCst) != 0;
if in_repeat && im != -2 {
let saved_mod = LASTVICHG.lock().unwrap().mod_.clone();
*ZMOD.lock().unwrap() = saved_mod;
VICHGFLAG.store(0, SeqCst);
} else if VICHGFLAG.load(SeqCst) == 0 {
let mut cur = CURVICHG.lock().unwrap();
cur.mod_ = ZMOD.lock().unwrap().clone();
let kblen = keybuflen.load(SeqCst).max(0) as usize;
cur.buf.clear();
cur.bufsz = (16 + kblen) as i32;
cur.bufptr = 0;
if im == -2 {
VICHGFLAG.store(1, SeqCst);
let zlell_v = ZLELL.load(SeqCst);
let zlecs_v = ZLECS.load(SeqCst);
let insmode_v = INSMODE.load(SeqCst) != 0;
let op = if zlell_v != 0 {
if insmode_v {
if zlecs_v < zlell_v {
b'i'
} else {
b'a'
}
} else {
b'R'
}
} else {
b'o'
};
cur.buf.push(op);
cur.bufptr = 1;
} else {
VICHGFLAG.store(2, SeqCst);
let kb = keybuf.lock().unwrap().clone();
let truncated_len = kb.iter().position(|&b| b == 0).unwrap_or(kb.len());
cur.buf = kb[..truncated_len].to_vec();
cur.bufptr = unmetafy(&mut cur.buf) as i32;
}
}
}
pub fn startvitext(im: i32) {
startvichange(im); selectkeymap("main", 1); VISTARTCHANGE.store(UNDO_CHANGENO.load(SeqCst), SeqCst); VIINSBEGIN.store(ZLECS.load(SeqCst), SeqCst); }
pub fn vigetkey() -> i32 {
let mn = crate::ported::zle::zle_keymap::openkeymap("main");
let byte = match getbyte(1) {
Some(b) => b,
None => return ZLEEOF, };
crate::ported::zle::compcore::LASTCHAR.store(byte as i32, SeqCst);
let cmd: Option<crate::ported::zle::zle_thingy::Thingy> = if let Some(km) = mn {
let (t, _str) = crate::ported::zle::zle_keymap::keybind(&km, &[byte]); t
} else {
None };
match cmd.as_ref().map(|t| t.nam.as_str()) {
None | Some("send-break") | Some("undefined-key") => {
return ZLEEOF;
}
Some("quoted-insert") => {
if getfullchar(false).is_none() {
return ZLEEOF; }
}
Some("vi-quoted-insert") => {
let zlecs = ZLECS.load(SeqCst);
let sav: Option<char> = {
let line = ZLELINE.lock().unwrap();
line.get(zlecs).copied() };
{
let mut line = ZLELINE.lock().unwrap();
if zlecs < line.len() {
line[zlecs] = '^';
}
}
zrefresh();
let _ = getfullchar(false);
if let Some(c) = sav {
let mut line = ZLELINE.lock().unwrap();
if zlecs < line.len() {
line[zlecs] = c;
}
}
}
Some("vi-cmd-mode") => {
return ZLEEOF; }
_ => {
}
}
byte as i32
}
pub fn getvirange(wf: i32) -> i32 {
let load_mark = || -> i32 {
let m = MARK.load(SeqCst);
if m == usize::MAX {
-1
} else {
m as i32
}
};
let store_mark = |v: i32| {
MARK.store(if v < 0 { usize::MAX } else { v as usize }, SeqCst);
};
let mut pos: i32 = ZLECS.load(SeqCst) as i32; let mpos: i32 = load_mark(); let mut ret: i32 = 0; let visual: i32 = REGION_ACTIVE.load(SeqCst) as i32; let mult1 = ZMOD.lock().unwrap().mult; let hist1 = histline.load(SeqCst);
if visual != 0 {
if ZLELL.load(SeqCst) == 0 {
return -1; }
pos = load_mark(); VILINERANGE.store(if visual == 2 { 1 } else { 0 }, SeqCst); REGION_ACTIVE.store(0, SeqCst); } else {
VIRANGEFLAG.store(1, SeqCst); WORDFLAG.store(wf, SeqCst); store_mark(-1); crate::ported::zle::termquery::cursor_form(); if let Some(km) = crate::ported::zle::zle_keymap::openkeymap("viopp") {
crate::ported::zle::zle_keymap::selectlocalmap(Some(km)); }
{
let mut zm = ZMOD.lock().unwrap();
zm.flags &= !MOD_TMULT;
}
loop {
VILINERANGE.store(0, SeqCst); PREFIXFLAG.store(0, SeqCst); let k2 = match crate::ported::zle::zle_keymap::getkeycmd() {
None => {
WORDFLAG.store(0, SeqCst);
VIRANGEFLAG.store(0, SeqCst);
store_mark(mpos);
return -1; }
Some(t)
if (t.flags & crate::ported::zsh_h::DISABLED) != 0 || t.nam == "send-break" =>
{
WORDFLAG.store(0, SeqCst); VIRANGEFLAG.store(0, SeqCst); store_mark(mpos); return -1; }
Some(t) => t,
};
let is_bindk =
BINDK.lock().unwrap().as_ref().map(|t| t.nam.clone()) == Some(k2.nam.clone());
if if is_bindk {
dovilinerange() } else {
execzlefunc(&k2.nam, &[], 1, 0) } != 0
{
ret = -1; }
if VIINREPEAT.load(SeqCst) != 0 {
ZMOD.lock().unwrap().mult = mult1; } else {
let tmult = ZMOD.lock().unwrap().tmult;
let zm = mult1 * tmult; ZMOD.lock().unwrap().mult = zm;
if VICHGFLAG.load(SeqCst) == 2 {
CURVICHG.lock().unwrap().mod_.mult = zm; }
}
if !(PREFIXFLAG.load(SeqCst) != 0 && ret == 0) {
break;
}
}
WORDFLAG.store(0, SeqCst); crate::ported::zle::zle_keymap::selectlocalmap(None);
let ll = ZLELL.load(SeqCst);
let modified = histline.load(SeqCst) != hist1 || ll != LASTLL.load(SeqCst) || {
let z = ZLELINE.lock().unwrap();
let last = LASTLINE.lock().unwrap();
z.get(..ll) != last.get(..ll)
};
if modified {
histline.store(hist1, SeqCst); let lastll = LASTLL.load(SeqCst);
{
let last = LASTLINE.lock().unwrap();
let mut z = ZLELINE.lock().unwrap();
*z = last[..lastll].to_vec();
}
ZLELL.store(lastll, SeqCst);
ZLECS.store(pos as usize, SeqCst); store_mark(mpos); VIRANGEFLAG.store(0, SeqCst); return -1; }
let ll = ZLELL.load(SeqCst);
let cs_now = ZLECS.load(SeqCst) as i32;
let mark_now = load_mark();
if ll == 0
|| (cs_now == pos
&& (mark_now == -1 || mark_now == cs_now)
&& VIRANGEFLAG.load(SeqCst) != 2)
|| ret == -1
{
store_mark(mpos); VIRANGEFLAG.store(0, SeqCst); return -1; }
VIRANGEFLAG.store(0, SeqCst);
if mark_now != -1 {
pos = mark_now; }
}
store_mark(mpos);
let mut cs = ZLECS.load(SeqCst) as i32;
if cs > pos {
std::mem::swap(&mut cs, &mut pos); }
ZLECS.store(cs as usize, SeqCst);
if visual == 1 && (pos as usize) < ZLELL.load(SeqCst) {
let kn = crate::ported::zle::zle_keymap::curkeymapname().clone();
if invicmdmode(&kn) {
let mut p = pos as usize;
incpos(&mut p);
pos = p as i32;
}
}
let zmod_flags = ZMOD.lock().unwrap().flags;
let vlr = (zmod_flags & MOD_LINE) != 0
|| (VILINERANGE.load(SeqCst) != 0 && (zmod_flags & MOD_CHAR) == 0);
VILINERANGE.store(if vlr { 1 } else { 0 }, SeqCst); if vlr {
let newcs = findbol() as i32; LASTCOL.store(ZLECS.load(SeqCst) as i32 - newcs, SeqCst); ZLECS.store(pos as usize, SeqCst); pos = findeol() as i32; ZLECS.store(newcs as usize, SeqCst); } else if visual == 0 {
let mut prev = pos as usize; decpos(&mut prev); if ZLELINE.lock().unwrap().get(prev) == Some(&'\n') {
pos = prev as i32; }
}
pos }
pub fn dovilinerange() -> i32 {
let pos = ZLECS.load(SeqCst) as i64;
let mut n = ZMOD.lock().unwrap().mult as i64; let mut cs: i64 = pos;
VILINERANGE.store(1, SeqCst); if n == 0 {
return 1; }
if n > 0 {
loop {
let t = n;
n -= 1;
if t == 0 || cs > ZLELL.load(SeqCst) as i64 {
break;
}
ZLECS.store(cs as usize, SeqCst);
cs = findeol() as i64 + 1;
}
if n != -1 {
ZLECS.store(pos as usize, SeqCst); return 1; }
ZLECS.store(cs as usize, SeqCst);
deccs(); } else {
loop {
let t = n;
n += 1;
if t == 0 || cs < 0 {
break;
}
ZLECS.store(cs as usize, SeqCst);
cs = findbol() as i64 - 1;
}
if n != 1 {
ZLECS.store(pos as usize, SeqCst); return 1; }
if cs < 0 {
ZLECS.store(0, SeqCst);
} else {
ZLECS.store(cs as usize, SeqCst);
inccs();
}
}
VIRANGEFLAG.store(2, SeqCst); 0 }
pub fn viaddnext() -> i32 {
let eol = findeol();
if ZLECS.load(SeqCst) != eol {
inccs();
}
startvitext(1);
0
}
pub fn viaddeol() -> i32 {
ZLECS.store(findeol(), SeqCst);
startvitext(1);
0
}
pub fn viinsert() -> i32 {
startvitext(1);
0
}
pub fn viinsert_init() {
startvitext(-2);
}
pub fn viinsertbol() -> i32 {
vifirstnonblank();
startvitext(1);
0
}
pub fn videlete() -> i32 {
startvichange(1); let c2 = getvirange(0); if c2 == -1 {
return 1;
}
let cs = ZLECS.load(SeqCst) as i32;
forekill(c2 - cs, CUT_RAW); if VILINERANGE.load(Ordering::Relaxed) != 0 {
let ll = ZLELL.load(SeqCst);
if ll != 0 {
LASTCOL.store(-1, Ordering::Relaxed);
let cs_now = ZLECS.load(SeqCst);
if cs_now == ll {
deccs(); }
foredel(1, 0); vifirstnonblank(); }
}
0 }
pub fn videletechar() -> i32 {
startvichange(-1);
let mut n = ZMOD.lock().unwrap().mult;
if n < 0 {
let saved = n;
ZMOD.lock().unwrap().mult = -n;
let ret = vibackwarddeletechar();
ZMOD.lock().unwrap().mult = saved;
return ret;
}
let cs = ZLECS.load(SeqCst);
let ll = ZLELL.load(SeqCst);
if cs == ll || ZLELINE.lock().unwrap().get(cs) == Some(&'\n') {
return 1;
}
let eol = findeol();
let max_n = eol.saturating_sub(cs) as i32;
let flags = if n > max_n {
n = max_n;
CUT_RAW } else {
0 };
forekill(n, flags);
0 }
pub fn visubstitute() -> i32 {
startvichange(1); let mut n = ZMOD.lock().unwrap().mult; if n < 0 {
return 1; }
if ZLECS.load(SeqCst) == ZLELL.load(SeqCst)
|| ZLELINE.lock().unwrap().get(ZLECS.load(SeqCst)) == Some(&'\n')
{
return 1; }
if REGION_ACTIVE.load(SeqCst) != 0 {
killregion(); } else {
let room = findeol() as i32 - ZLECS.load(SeqCst) as i32;
if n > room {
n = room; }
forekill(n, CUT_RAW); }
startvitext(1); 0 }
pub fn vichangeeol() -> i32 {
if REGION_ACTIVE.load(SeqCst) != 0 {
let (a, b) = regionlines(); ZLECS.store(a, SeqCst); REGION_ACTIVE.store(0, SeqCst); cut(a as i32, b as i32 - a as i32, CUT_RAW); shiftchars(a as i32, b as i32 - a as i32); } else {
forekill(findeol() as i32 - ZLECS.load(SeqCst) as i32, CUT_RAW); }
startvitext(1); 0 }
pub fn vichangewholeline() -> i32 {
vifirstnonblank();
vichangeeol()
}
pub fn viyank(_args: &[String]) -> i32 {
let mut ret = 1;
startvichange(1); let c2 = getvirange(0); if c2 != -1 {
let zlecs_now = ZLECS.load(SeqCst) as i32;
cut(
zlecs_now,
c2 - zlecs_now,
CUT_YANK,
);
ret = 0;
}
if VILINERANGE.load(SeqCst) != 0 && LASTCOL.load(SeqCst) != -1 {
let x = findeol() as i32;
let new_cs = ZLECS.load(SeqCst) as i32 + LASTCOL.load(SeqCst);
if new_cs >= x {
ZLECS.store(x as usize, SeqCst);
let bol = findbol() as i32;
let cmname = crate::ported::zle::zle_keymap::curkeymapname().clone();
if x > bol && invicmdmode(&cmname) {
deccs();
}
} else {
ZLECS.store(new_cs as usize, SeqCst);
}
LASTCOL.store(-1, SeqCst); }
ret
}
pub fn viyankeol() -> i32 {
let x = findeol(); startvichange(-1); if x == ZLECS.load(SeqCst) {
return 1; }
{
let cs = ZLECS.load(SeqCst) as i32;
cut(cs, x as i32 - cs, CUT_YANK); }
0 }
pub fn viyankwholeline() -> i32 {
let bol = findbol();
let oldcs = ZLECS.load(SeqCst);
startvichange(-1); let mut n = ZMOD.lock().unwrap().mult; if n < 1 {
return 1; }
while n > 0 {
if ZLECS.load(SeqCst) > ZLELL.load(SeqCst) {
ZLECS.store(oldcs, SeqCst); return 1; }
ZLECS.store(findeol() + 1, SeqCst); n -= 1;
}
VILINERANGE.store(1, SeqCst); let end = ZLECS.load(SeqCst) as i32;
cut(bol as i32, end - bol as i32 - 1, CUT_YANK); ZLECS.store(oldcs, SeqCst); 0 }
pub fn vireplace() -> i32 {
startvitext(0);
0
}
pub fn vireplacechars() -> i32 {
let mut n; let mut newchars = 0i32; let mut fail = false;
startvichange(1); n = ZMOD.lock().unwrap().mult; if n > 0 {
let ra = get_region_active(); if ra != 0 {
let a;
let mut b;
if ra == 1 {
let mark = zle_main::MARK.load(SeqCst);
let cs = ZLECS.load(SeqCst);
if mark > cs {
a = cs; b = mark; } else {
a = mark; b = cs; }
incpos(&mut b); } else {
let (ra2, rb2) = regionlines();
a = ra2;
b = rb2;
}
ZLECS.store(a, SeqCst); let zlell = ZLELL.load(SeqCst);
if b > zlell {
b = zlell; }
n = (b - a) as i32; let mut aa = a;
while aa < b {
newchars += 1; incpos(&mut aa); }
REGION_ACTIVE.store(0, SeqCst); } else {
let mut pos = ZLECS.load(SeqCst);
let zlell = ZLELL.load(SeqCst);
let line = ZLELINE.lock().unwrap();
while n > 0 {
if pos == zlell || line[pos] == '\n' {
fail = true; break; }
newchars += 1; incpos(&mut pos); n -= 1;
}
drop(line);
n = (pos - ZLECS.load(SeqCst)) as i32; }
}
if n < 1 || fail {
if VIINREPEAT.load(SeqCst) != 0 {
vigetkey(); }
return 1; }
let ch = vigetkey();
if ch == ZLEEOF {
return 1; }
if ch == '\r' as i32 || ch == '\n' as i32 {
ZLECS.fetch_add((n - 1) as usize, SeqCst); backkill(n - 1, CUT_RAW); let cs = ZLECS.load(SeqCst);
ZLELINE.lock().unwrap()[cs] = '\n'; ZLECS.store(cs + 1, SeqCst);
} else {
let cs = ZLECS.load(SeqCst);
if n > newchars {
shiftchars(cs as i32, n - newchars); } else if n < newchars {
spaceinline(newchars - n); }
let chr = char::from_u32(ch as u32).unwrap_or('\u{FFFD}');
let mut cs2 = cs;
let mut remaining = newchars;
{
let mut line = ZLELINE.lock().unwrap();
while remaining > 0 {
line[cs2] = chr; cs2 += 1;
remaining -= 1;
}
}
ZLECS.store(cs2 - 1, SeqCst); }
ZLE_RESET_NEEDED.store(1, SeqCst);
0 }
pub fn vicmdmode() -> i32 {
if *crate::ported::zle::zle_keymap::curkeymapname() == "vicmd" {
return 1;
}
if selectkeymap("vicmd", 0) != 0 {
return 1;
}
crate::ported::zle::zle_utils::mergeundo();
let overstrike_set = crate::ported::zsh_h::isset(crate::ported::zsh_h::OVERSTRIKE);
INSMODE.store(if overstrike_set { 0 } else { 1 }, SeqCst);
if VICHGFLAG.load(SeqCst) == 1 {
VICHGFLAG.store(0, SeqCst); let mut last = LASTVICHG.lock().unwrap();
let mut cur = CURVICHG.lock().unwrap();
last.mod_ = cur.mod_.clone();
last.buf = std::mem::take(&mut cur.buf);
last.bufsz = cur.bufsz;
last.bufptr = cur.bufptr;
cur.bufsz = 0;
cur.bufptr = 0;
}
if VIINREPEAT.load(SeqCst) == 1 {
VIINREPEAT.store(0, SeqCst);
}
let bol = findbol();
if ZLECS.load(SeqCst) != bol {
deccs();
}
0
}
pub fn viopenlinebelow() -> i32 {
use crate::ported::zle::zle_utils::spaceinline;
ZLECS.store(findeol(), SeqCst); spaceinline(1); {
let cs = ZLECS.load(SeqCst);
if let Some(slot) = ZLELINE.lock().unwrap().get_mut(cs) {
*slot = '\n'; }
}
ZLECS.fetch_add(1, SeqCst); startvitext(1); ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn viopenlineabove() -> i32 {
use crate::ported::zle::zle_utils::spaceinline;
ZLECS.store(findbol(), SeqCst); spaceinline(1); {
let cs = ZLECS.load(SeqCst);
if let Some(slot) = ZLELINE.lock().unwrap().get_mut(cs) {
*slot = '\n'; }
}
startvitext(1); ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn vioperswapcase() -> i32 {
let mut ret = 1; startvichange(1); let c2 = getvirange(0);
if c2 != -1 {
let oldcs = ZLECS.load(SeqCst); while (ZLECS.load(SeqCst) as i32) < c2 {
let cs = ZLECS.load(SeqCst);
let mut line = ZLELINE.lock().unwrap();
let c = line[cs];
if ZC_ilower(c) {
line[cs] = ZC_toupper(c); } else if ZC_iupper(c) {
line[cs] = ZC_tolower(c); }
drop(line);
inccs(); }
ZLECS.store(oldcs, SeqCst); ret = 0; }
ret }
pub fn viupcase() -> i32 {
let mut ret = 1; startvichange(1); let c2 = getvirange(0);
if c2 != -1 {
let oldcs = ZLECS.load(SeqCst); while (ZLECS.load(SeqCst) as i32) < c2 {
let cs = ZLECS.load(SeqCst);
{
let mut line = ZLELINE.lock().unwrap();
line[cs] = ZC_toupper(line[cs]); }
inccs(); }
ZLECS.store(oldcs, SeqCst); ret = 0; }
ret }
pub fn vidowncase() -> i32 {
let mut ret = 1; startvichange(1); let c2 = getvirange(0);
if c2 != -1 {
let oldcs = ZLECS.load(SeqCst); while (ZLECS.load(SeqCst) as i32) < c2 {
let cs = ZLECS.load(SeqCst);
{
let mut line = ZLELINE.lock().unwrap();
line[cs] = ZC_tolower(line[cs]); }
inccs(); }
ZLECS.store(oldcs, SeqCst); ret = 0; }
ret }
pub fn virepeatchange() -> i32 {
use crate::ported::zle::zle_main::ungetbytes;
let last = LASTVICHG.lock().unwrap();
if last.buf.is_empty() || VICHGFLAG.load(SeqCst) != 0 || VIRANGEFLAG.load(SeqCst) != 0 {
return 1;
}
drop(last);
let zmod_flags = ZMOD.lock().unwrap().flags;
let zmod_mult = ZMOD.lock().unwrap().mult;
let zmod_vibuf = ZMOD.lock().unwrap().vibuf;
let zmod_viapp = zmod_flags & MOD_VIAPP;
{
let mut lvc = LASTVICHG.lock().unwrap();
if zmod_flags & MOD_MULT != 0 {
lvc.mod_.mult = zmod_mult;
lvc.mod_.flags |= MOD_MULT;
}
if zmod_flags & MOD_VIBUF != 0 {
lvc.mod_.vibuf = zmod_vibuf;
lvc.mod_.flags = (lvc.mod_.flags & !MOD_VIAPP) | MOD_VIBUF | zmod_viapp;
} else if lvc.mod_.flags & MOD_VIBUF != 0 && lvc.mod_.vibuf >= 27 && lvc.mod_.vibuf <= 34 {
lvc.mod_.vibuf += 1;
}
}
VIINREPEAT.store(3, SeqCst);
let (bytes, ptr) = {
let l = LASTVICHG.lock().unwrap();
(l.buf.clone(), l.bufptr as usize)
};
let lo = ptr.min(bytes.len());
ungetbytes(&bytes[..lo]);
0 }
pub fn viindent() -> i32 {
let mut oldcs = ZLECS.load(SeqCst); startvichange(1); if REGION_ACTIVE.load(SeqCst) == 1 {
REGION_ACTIVE.store(2, SeqCst); }
let mut c2 = getvirange(0);
if c2 == -1 {
return 1; }
if VILINERANGE.load(SeqCst) == 0 {
ZLECS.store(oldcs, SeqCst); return 1; }
oldcs = ZLECS.load(SeqCst); while ZLECS.load(SeqCst) as i32 <= c2 {
let cs = ZLECS.load(SeqCst);
if ZLELINE.lock().unwrap().get(cs) == Some(&'\n') {
ZLECS.store(cs + 1, SeqCst); } else {
spaceinline(1); c2 += 1; ZLELINE.lock().unwrap()[cs] = '\t'; ZLECS.store(findeol() + 1, SeqCst); }
}
ZLECS.store(oldcs, SeqCst);
vifirstnonblank(); 0 }
pub fn viunindent() -> i32 {
let mut oldcs = ZLECS.load(SeqCst); startvichange(1); if REGION_ACTIVE.load(SeqCst) == 1 {
REGION_ACTIVE.store(2, SeqCst); }
let mut c2 = getvirange(0);
if c2 == -1 {
return 1; }
if VILINERANGE.load(SeqCst) == 0 {
ZLECS.store(oldcs, SeqCst); return 1; }
oldcs = ZLECS.load(SeqCst); while (ZLECS.load(SeqCst) as i32) < c2 {
let cs = ZLECS.load(SeqCst);
if ZLELINE.lock().unwrap().get(cs) == Some(&'\t') {
foredel(1, 0); c2 -= 1; }
ZLECS.store(findeol() + 1, SeqCst); }
ZLECS.store(oldcs, SeqCst);
vifirstnonblank(); 0 }
pub fn vibackwarddeletechar() -> i32 {
let curkm = crate::ported::zle::zle_keymap::curkeymapname()
.as_str()
.to_string();
let in_cmd = invicmdmode(&curkm);
if in_cmd {
startvichange(-1);
}
let n = ZMOD.lock().unwrap().mult as i32;
if n < 0 {
let prev = ZMOD.lock().unwrap().mult;
ZMOD.lock().unwrap().mult = (-n) as i32;
let ret = videletechar();
ZMOD.lock().unwrap().mult = prev;
return ret;
}
let bol = findbol();
let cs = ZLECS.load(SeqCst);
let viib = VIINSBEGIN.load(Ordering::Relaxed) as usize;
if cs == bol || (!in_cmd && cs.saturating_sub(n as usize) < viib) {
return 1;
}
let mut nn = n as usize;
if nn > cs - bol {
nn = cs - bol;
}
backkill(nn as i32, CUT_FRONT | CUT_RAW);
0
}
pub fn vikillline() -> i32 {
let zlecs = ZLECS.load(SeqCst) as i32;
let viinsbegin = VIINSBEGIN.load(SeqCst) as i32;
if viinsbegin > zlecs {
return 1;
} backdel(zlecs - viinsbegin, CUT_RAW); 0 }
pub fn vijoin() -> i32 {
let visual = REGION_ACTIVE.load(SeqCst); startvichange(-1); let mut n = ZMOD.lock().unwrap().mult; if n < 1 {
return 1; }
let zlell = || ZLELL.load(SeqCst);
let mark = || MARK.load(SeqCst);
let mut x: usize;
if visual != 0 && ZLECS.load(SeqCst) > mark() {
exchangepointandmark(); x = findeol(); if x >= mark() {
exchangepointandmark(); return 1; }
} else {
x = findeol(); if x == zlell() || (visual != 0 && x >= mark()) {
return 1; }
}
loop {
ZLECS.store(x + 1, SeqCst); let pos = ZLECS.load(SeqCst); while ZLECS.load(SeqCst) != zlell()
&& ZC_iblank(ZLELINE.lock().unwrap()[ZLECS.load(SeqCst)])
{
inccs();
}
x = 1 + (ZLECS.load(SeqCst) - pos); backdel(x as i32, CUT_RAW); let mut joined = false;
if ZLECS.load(SeqCst) != 0 {
let mut p = ZLECS.load(SeqCst); decpos(&mut p); if ZC_iblank(ZLELINE.lock().unwrap()[p]) {
ZLECS.store(p, SeqCst); joined = true; }
}
if !joined {
spaceinline(1); let cs = ZLECS.load(SeqCst);
ZLELINE.lock().unwrap()[cs] = ' '; }
if visual == 0 {
n -= 1;
if n < 2 {
break;
}
}
x = findeol();
if x == zlell() || (visual != 0 && x >= mark()) {
break;
}
}
0 }
pub fn viswapcase() -> i32 {
startvichange(-1); let mut n = ZMOD.lock().unwrap().mult; if n < 1 {
return 1; }
let eol = findeol(); if ZLECS.load(SeqCst) == eol {
return 1; }
while ZLECS.load(SeqCst) < eol && n > 0 {
n -= 1;
let cs = ZLECS.load(SeqCst);
{
let mut line = ZLELINE.lock().unwrap();
let c = line[cs];
if ZC_ilower(c) {
line[cs] = ZC_toupper(c); } else if ZC_iupper(c) {
line[cs] = ZC_tolower(c); }
}
inccs(); }
if ZLECS.load(SeqCst) != 0 && ZLECS.load(SeqCst) == eol {
deccs(); }
0 }
pub fn vicapslockpanic() -> i32 {
CLEARLIST.store(1, Ordering::Relaxed);
crate::ported::utils::zbeep();
let _ = crate::ported::params::setsparam("STATUSLINE", "press a lowercase key to continue");
let _ = crate::ported::params::setsparam("STATUSLINE", "");
0 }
pub fn visetbuffer(args: &[String]) -> i32 {
let registermod = [MOD_NULL, MOD_PRI, MOD_CLIP]; let ch: char = if let Some(a0) = args.first() {
let mut it = a0.chars();
let c = it.next().unwrap_or('\0'); if args.len() > 1 || (c != '\0' && it.next().is_some()) {
return 1; }
c
} else {
match crate::ported::zle::zle_main::getfullchar(false) {
Some(c) => c,
None => return 1, }
};
if let Some(found) = "_*+".find(ch) {
ZMOD.lock().unwrap().flags |= registermod[found]; PREFIXFLAG.store(1, SeqCst); return 0; } else {
ZMOD.lock().unwrap().flags &= !(MOD_NULL | MOD_OSSEL); }
if !ch.is_ascii_alphanumeric() {
return 1; }
let mut zm = ZMOD.lock().unwrap();
if ch.is_ascii_uppercase() {
zm.flags |= MOD_VIAPP; } else {
zm.flags &= !MOD_VIAPP; }
let lower = ZC_tolower(ch) as i32; zm.vibuf = if ch.is_ascii_digit() {
lower - '0' as i32 + 26 } else {
lower - 'a' as i32 };
zm.flags |= MOD_VIBUF; drop(zm);
PREFIXFLAG.store(1, SeqCst); 0 }
pub fn vikilleol() -> i32 {
let n = findeol() as i32 - ZLECS.load(SeqCst) as i32; startvichange(-1); if n == 0 {
return 1; }
forekill(findeol() as i32 - ZLECS.load(SeqCst) as i32, CUT_RAW); 0 }
pub fn vipoundinsert() -> i32 {
let mut oldcs = ZLECS.load(SeqCst);
startvichange(-1);
vifirstnonblank();
let cs = ZLECS.load(SeqCst);
let is_pound = ZLELINE.lock().unwrap().get(cs).copied() == Some('#');
let mut viib = VIINSBEGIN.load(SeqCst);
if !is_pound {
spaceinline(1);
if let Some(slot) = ZLELINE.lock().unwrap().get_mut(cs) {
*slot = '#';
}
if cs <= viib {
viib = viib.saturating_add(1);
}
if cs <= oldcs {
oldcs = oldcs.saturating_add(1);
}
ZLECS.store(oldcs.min(ZLELL.load(SeqCst)), SeqCst);
} else {
foredel(1, 0);
if cs < viib {
viib = viib.saturating_sub(1);
}
if cs < oldcs {
oldcs = oldcs.saturating_sub(1);
}
ZLECS.store(oldcs.min(ZLELL.load(SeqCst)), SeqCst);
}
VIINSBEGIN.store(viib, SeqCst);
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn viquotedinsert() -> i32 {
use crate::ported::zle::zle_h::ZLEEOF;
use crate::ported::zle::zle_main::LASTCHAR_WIDE;
use crate::ported::zle::zle_misc::selfinsert;
use crate::ported::zle::zle_refresh::zrefresh;
use crate::ported::zle::zle_utils::{foredel, spaceinline};
spaceinline(1);
let cs = ZLECS.load(SeqCst);
{
let mut g = ZLELINE.lock().unwrap();
if cs < g.len() {
g[cs] = '^';
}
}
zrefresh();
let _ = crate::ported::zle::zle_main::getfullchar(false);
foredel(1, 0);
if LASTCHAR_WIDE.load(SeqCst) == ZLEEOF {
return 1;
}
selfinsert(&[])
}
pub fn vidigitorbeginningofline() -> i32 {
if ZMOD.lock().unwrap().flags & MOD_TMULT != 0 {
return digitargument();
}
let _ = crate::ported::zle::zle_h::removesuffix(); crate::ported::zle::zle_h::invalidatelist(); vibeginningofline() }
pub static VIRANGEFLAG: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static WORDFLAG: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static VILINERANGE: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static VICHGFLAG: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static VIINREPEAT: std::sync::atomic::AtomicI32 = std::sync::atomic::AtomicI32::new(0);
pub static LASTVICHG: std::sync::Mutex<vichange> = std::sync::Mutex::new(vichange {
mod_: modifier {
flags: 0,
mult: 1,
tmult: 1,
vibuf: 0,
base: 10,
},
buf: Vec::new(),
bufsz: 0,
bufptr: 0,
});
pub static CURVICHG: std::sync::Mutex<vichange> = std::sync::Mutex::new(vichange {
mod_: modifier {
flags: 0,
mult: 1,
tmult: 1,
vibuf: 0,
base: 10,
},
buf: Vec::new(),
bufsz: 0,
bufptr: 0,
});
pub fn vi_get_arg() -> i32 {
if ZMOD.lock().unwrap().flags & MOD_MULT != 0 {
ZMOD.lock().unwrap().mult
} else {
1
}
}
pub fn vi_find_char(forward: bool, skip: bool) {
let c = match getfullchar(true) {
Some(c) => c,
None => return,
};
VFINDCHAR.store(c as i32, SeqCst);
VFINDDIR.store(if forward { 1 } else { -1 }, SeqCst);
TAILADD.store(
match (forward, skip) {
(_, false) => 0,
(true, true) => -1,
(false, true) => 1,
},
SeqCst,
);
let _ = vi_find_char_inner(false);
}
pub fn vi_find_char_inner(repeat: bool) -> i32 {
let target_raw = VFINDCHAR.load(SeqCst);
let target = match char::from_u32(target_raw as u32) {
Some(c) if target_raw != 0 => c,
_ => return 1,
};
if VFINDDIR.load(SeqCst) == 0 {
return 1;
}
let ocs = ZLECS.load(SeqCst);
let mut n = vi_get_arg();
if n < 0 {
n = -n;
VFINDDIR.store(-VFINDDIR.load(SeqCst), SeqCst);
TAILADD.store(-TAILADD.load(SeqCst), SeqCst);
let saved_mult = ZMOD.lock().unwrap().mult;
ZMOD.lock().unwrap().mult = n;
let ret = vi_find_char_inner(repeat);
ZMOD.lock().unwrap().mult = saved_mult;
VFINDDIR.store(-VFINDDIR.load(SeqCst), SeqCst);
TAILADD.store(-TAILADD.load(SeqCst), SeqCst);
return ret;
}
if repeat && TAILADD.load(SeqCst) != 0 {
if VFINDDIR.load(SeqCst) > 0 {
if ZLECS.load(SeqCst) < ZLELL.load(SeqCst)
&& ZLECS.load(SeqCst) + 1 < ZLELL.load(SeqCst)
&& ZLELINE.lock().unwrap()[ZLECS.load(SeqCst) + 1] == target
{
ZLECS.fetch_add(1, SeqCst);
}
} else if ZLECS.load(SeqCst) > 0
&& ZLELINE.lock().unwrap()[ZLECS.load(SeqCst) - 1] == target
{
ZLECS.fetch_sub(1, SeqCst);
}
}
let dir = VFINDDIR.load(SeqCst);
for _ in 0..n {
let found = if dir > 0 {
let mut p = ZLECS.load(SeqCst) + 1;
let mut hit = None;
while p < ZLELL.load(SeqCst) {
let ch = ZLELINE.lock().unwrap()[p];
if ch == '\n' {
break;
}
if ch == target {
hit = Some(p);
break;
}
p += 1;
}
hit
} else {
if ZLECS.load(SeqCst) == 0 {
None
} else {
let mut p = ZLECS.load(SeqCst) - 1;
let mut hit = None;
loop {
let ch = ZLELINE.lock().unwrap()[p];
if ch == '\n' {
break;
}
if ch == target {
hit = Some(p);
break;
}
if p == 0 {
break;
}
p -= 1;
}
hit
}
};
match found {
Some(p) => {
ZLECS.store(p, SeqCst);
}
None => {
ZLECS.store(ocs, SeqCst);
return 1;
}
}
}
let tail = TAILADD.load(SeqCst);
if tail > 0 && ZLECS.load(SeqCst) < ZLELL.load(SeqCst) {
ZLECS.fetch_add(1, SeqCst);
} else if tail < 0 && ZLECS.load(SeqCst) > 0 {
ZLECS.fetch_sub(1, SeqCst);
}
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn vi_match_bracket() {
let c = if ZLECS.load(SeqCst) < ZLELL.load(SeqCst) {
ZLELINE.lock().unwrap()[ZLECS.load(SeqCst)]
} else {
return;
};
let (target, forward) = match c {
'(' => (')', true),
')' => ('(', false),
'[' => (']', true),
']' => ('[', false),
'{' => ('}', true),
'}' => ('{', false),
'<' => ('>', true),
'>' => ('<', false),
_ => return,
};
let mut depth = 1;
let mut pos = ZLECS.load(SeqCst);
if forward {
pos += 1;
while pos < ZLELL.load(SeqCst) && depth > 0 {
if ZLELINE.lock().unwrap()[pos] == c {
depth += 1;
} else if ZLELINE.lock().unwrap()[pos] == target {
depth -= 1;
}
if depth > 0 {
pos += 1;
}
}
} else {
if pos > 0 {
pos -= 1;
loop {
if ZLELINE.lock().unwrap()[pos] == c {
depth += 1;
} else if ZLELINE.lock().unwrap()[pos] == target {
depth -= 1;
}
if depth == 0 || pos == 0 {
break;
}
pos -= 1;
}
}
}
if depth == 0 {
ZLECS.store(pos, SeqCst);
ZLE_RESET_NEEDED.store(1, SeqCst);
}
}
pub fn vi_replace_mode() {
selectkeymap("viins", 1);
INSMODE.store(0, SeqCst);
}
pub fn vi_swap_case() {
let count = vi_get_arg() as usize;
for _ in 0..count {
if ZLECS.load(SeqCst) < ZLELL.load(SeqCst) {
let c = ZLELINE.lock().unwrap()[ZLECS.load(SeqCst)];
ZLELINE.lock().unwrap()[ZLECS.load(SeqCst)] = if c.is_uppercase() {
c.to_lowercase().next().unwrap_or(c)
} else if c.is_lowercase() {
c.to_uppercase().next().unwrap_or(c)
} else {
c
};
ZLECS.fetch_add(1, SeqCst);
}
}
if ZLECS.load(SeqCst) > 0 && ZLECS.load(SeqCst) == ZLELL.load(SeqCst) {
ZLECS.fetch_sub(1, SeqCst);
}
ZLE_RESET_NEEDED.store(1, SeqCst);
}
pub fn vi_undo() {
let _ = undo_widget();
}
pub fn vi_visual_mode() {
match REGION_ACTIVE.load(SeqCst) {
1 => {
REGION_ACTIVE.store(0, SeqCst);
}
0 => {
MARK.store(ZLECS.load(SeqCst), SeqCst);
REGION_ACTIVE.store(1, SeqCst);
}
2 => {
REGION_ACTIVE.store(1, SeqCst);
}
_ => {}
}
}
pub fn vi_visual_line_mode() {
match REGION_ACTIVE.load(SeqCst) {
2 => {
REGION_ACTIVE.store(0, SeqCst);
}
0 => {
MARK.store(ZLECS.load(SeqCst), SeqCst);
REGION_ACTIVE.store(2, SeqCst);
}
1 => {
REGION_ACTIVE.store(2, SeqCst);
}
_ => {}
}
}
pub fn vi_visual_block_mode() {
if REGION_ACTIVE.load(SeqCst) == 0 {
MARK.store(ZLECS.load(SeqCst), SeqCst);
}
REGION_ACTIVE.store(1, SeqCst);
}
pub fn vi_deactivate_region() {
REGION_ACTIVE.store(0, SeqCst);
}
pub fn vi_set_mark(name: char) {
MARK.store(ZLECS.load(SeqCst), SeqCst);
if let Some(idx) = vimark_slot(name) {
vimarks().lock().unwrap()[idx] =
Some((ZLECS.load(SeqCst), history().lock().unwrap().cursor as i32));
}
}
pub fn vi_goto_mark(name: char) {
let idx = match vimark_slot(name) {
Some(i) => i,
None => return,
};
let (cs, hist) = match vimarks().lock().unwrap()[idx] {
Some(s) => s,
None => return,
};
vimarks().lock().unwrap()[26] =
Some((ZLECS.load(SeqCst), history().lock().unwrap().cursor as i32));
if hist >= 0 && (hist as usize) < history().lock().unwrap().entries.len() {
let target = hist as usize;
if target != history().lock().unwrap().cursor {
history().lock().unwrap().cursor = target;
*ZLELINE.lock().unwrap() = history().lock().unwrap().entries[target]
.line
.chars()
.collect();
ZLELL.store(ZLELINE.lock().unwrap().len(), SeqCst);
}
}
ZLECS.store(cs.min(ZLELL.load(SeqCst)), SeqCst);
ZLE_RESET_NEEDED.store(1, SeqCst);
}
pub fn vi_record_change(key: u8) {
VICHGBUF.lock().unwrap().push(key);
}
pub fn vi_start_change_recording() {
VICHGBUF.lock().unwrap().clear();
}
pub fn vi_repeat_change() {
if VICHGBUF.lock().unwrap().is_empty() {
return;
}
let bytes = VICHGBUF.lock().unwrap().clone();
ungetbytes(&bytes);
}
fn vimark_slot(name: char) -> Option<usize> {
if name.is_ascii_lowercase() {
Some(name as usize - 'a' as usize)
} else if name == '\'' || name == '`' {
Some(26)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
fn zle_with(line: &str, cs: usize) {
zle_reset();
*ZLELINE.lock().unwrap() = line.chars().collect();
ZLELL.store(ZLELINE.lock().unwrap().len(), SeqCst);
ZLECS.store(cs, SeqCst);
}
#[test]
fn vi_find_char_inner_lands_on_target_forward() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("abcdef", 0);
VFINDCHAR.store('d' as i32, SeqCst);
VFINDDIR.store(1, SeqCst);
TAILADD.store(0, SeqCst);
assert_eq!(vi_find_char_inner(false), 0);
assert_eq!(ZLECS.load(SeqCst), 3);
}
#[test]
fn vi_find_char_inner_skip_stops_one_short_forward() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("abcdef", 0);
VFINDCHAR.store('d' as i32, SeqCst);
VFINDDIR.store(1, SeqCst);
TAILADD.store(-1, SeqCst); assert_eq!(vi_find_char_inner(false), 0);
assert_eq!(ZLECS.load(SeqCst), 2);
}
#[test]
fn vi_find_char_inner_lands_on_target_backward() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("abcdef", 5);
VFINDCHAR.store('b' as i32, SeqCst);
VFINDDIR.store(-1, SeqCst);
TAILADD.store(0, SeqCst);
assert_eq!(vi_find_char_inner(false), 0);
assert_eq!(ZLECS.load(SeqCst), 1);
}
#[test]
fn vi_find_char_inner_returns_1_and_restores_when_missing() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("abcdef", 0);
VFINDCHAR.store('z' as i32, SeqCst);
VFINDDIR.store(1, SeqCst);
TAILADD.store(0, SeqCst);
assert_eq!(vi_find_char_inner(false), 1);
assert_eq!(ZLECS.load(SeqCst), 0);
}
#[test]
fn vi_find_char_inner_stops_at_newline() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("abc\ndef", 0);
VFINDCHAR.store('e' as i32, SeqCst);
VFINDDIR.store(1, SeqCst);
TAILADD.store(0, SeqCst);
assert_eq!(vi_find_char_inner(false), 1);
assert_eq!(ZLECS.load(SeqCst), 0);
}
#[test]
fn vi_repeat_find_walks_to_next_match_in_same_direction() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("a-b-c-d", 0);
VFINDCHAR.store('-' as i32, SeqCst);
VFINDDIR.store(1, SeqCst);
TAILADD.store(0, SeqCst);
assert_eq!(vi_find_char_inner(false), 0);
assert_eq!(ZLECS.load(SeqCst), 1);
assert_eq!(virepeatfind(), 0);
assert_eq!(ZLECS.load(SeqCst), 3);
assert_eq!(virepeatfind(), 0);
assert_eq!(ZLECS.load(SeqCst), 5);
}
#[test]
fn vi_set_and_goto_named_mark_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("hello world", 6);
vi_set_mark('a');
ZLECS.store(0, SeqCst);
vi_goto_mark('a');
assert_eq!(ZLECS.load(SeqCst), 6);
}
#[test]
fn vi_goto_mark_records_implicit_last_position() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("0123456789", 4);
vi_set_mark('m');
ZLECS.store(9, SeqCst);
vi_goto_mark('m'); assert_eq!(ZLECS.load(SeqCst), 4);
vi_goto_mark('\''); assert_eq!(ZLECS.load(SeqCst), 9);
}
#[test]
fn vi_set_mark_ignores_invalid_names() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("abc", 1);
vi_set_mark('A'); vi_set_mark('1'); assert!(vimarks().lock().unwrap().iter().all(|m| m.is_none()));
}
#[test]
fn vi_visual_mode_toggles_charwise() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("abcd", 2);
assert_eq!(REGION_ACTIVE.load(SeqCst), 0);
vi_visual_mode();
assert_eq!(REGION_ACTIVE.load(SeqCst), 1);
assert_eq!(MARK.load(SeqCst), 2);
vi_visual_mode();
assert_eq!(REGION_ACTIVE.load(SeqCst), 0);
}
#[test]
fn vi_visual_line_mode_toggles_linewise_and_swaps_with_charwise() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("abcd", 0);
vi_visual_line_mode();
assert_eq!(REGION_ACTIVE.load(SeqCst), 2);
vi_visual_mode();
assert_eq!(REGION_ACTIVE.load(SeqCst), 1);
vi_visual_line_mode();
assert_eq!(REGION_ACTIVE.load(SeqCst), 2);
vi_visual_line_mode();
assert_eq!(REGION_ACTIVE.load(SeqCst), 0);
}
#[test]
fn vi_deactivate_region_clears_active_state() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("abcd", 0);
REGION_ACTIVE.store(2, SeqCst);
vi_deactivate_region();
assert_eq!(REGION_ACTIVE.load(SeqCst), 0);
}
#[test]
fn vi_record_change_appends_to_replay_buffer() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("", 0);
vi_start_change_recording();
vi_record_change(b'd');
vi_record_change(b'w');
assert_eq!(*VICHGBUF.lock().unwrap(), vec![b'd', b'w']);
vi_start_change_recording();
assert!(VICHGBUF.lock().unwrap().is_empty());
}
#[test]
fn vi_undo_reverses_a_recorded_change() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("", 0);
setlastline();
*ZLELINE.lock().unwrap() = "abc".chars().collect();
ZLELL.store(3, SeqCst);
ZLECS.store(3, SeqCst);
mkundoent();
vi_undo();
assert_eq!(ZLELINE.lock().unwrap().iter().collect::<String>(), "");
}
#[test]
fn vi_rev_repeat_find_walks_back() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("a-b-c-d", 0);
VFINDCHAR.store('-' as i32, SeqCst);
VFINDDIR.store(1, SeqCst);
TAILADD.store(0, SeqCst);
assert_eq!(vi_find_char_inner(false), 0);
assert_eq!(ZLECS.load(SeqCst), 1);
assert_eq!(virepeatfind(), 0);
assert_eq!(ZLECS.load(SeqCst), 3);
assert_eq!(virevrepeatfind(), 0);
assert_eq!(ZLECS.load(SeqCst), 1);
}
#[test]
fn zle_vi_corpus_viaddeol_moves_to_eol() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_with("hello", 0);
let r = viaddeol();
assert_eq!(r, 0);
assert_eq!(
ZLECS.load(SeqCst),
5,
"cursor at end-of-line after viaddeol"
);
}
#[test]
fn zle_vi_corpus_viinsert_returns_zero_no_move() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_with("hello", 2);
let r = viinsert();
assert_eq!(r, 0);
assert_eq!(ZLECS.load(SeqCst), 2, "viinsert doesn't move cursor");
}
#[test]
fn zle_vi_corpus_viaddnext_advances_when_not_at_eol() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_with("hello", 0);
let r = viaddnext();
assert_eq!(r, 0);
assert_eq!(ZLECS.load(SeqCst), 1, "viaddnext from pos 0 → pos 1");
}
#[test]
fn zle_vi_corpus_viaddnext_at_eol_stays() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_with("hello", 5);
let r = viaddnext();
assert_eq!(r, 0);
assert_eq!(ZLECS.load(SeqCst), 5, "viaddnext at EOL stays at EOL");
}
#[test]
fn zle_vi_corpus_viinsert_init_no_panic() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_with("test", 0);
viinsert_init();
}
#[test]
fn viaddeol_moves_cursor_to_eol() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
zle_with("hello", 0); let ret = viaddeol();
assert_eq!(ret, 0, "viaddeol returns 0");
assert_eq!(
ZLECS.load(Ordering::SeqCst),
5,
"cursor at EOL (after 'hello' = pos 5)"
);
}
#[test]
fn viaddnext_increments_cursor_when_not_at_eol() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
zle_with("abc", 1); let ret = viaddnext();
assert_eq!(ret, 0);
assert_eq!(
ZLECS.load(Ordering::SeqCst),
2,
"cursor advances past current char (1 → 2)"
);
}
#[test]
fn viaddnext_at_eol_leaves_cursor_unchanged() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
zle_with("xyz", 3); let ret = viaddnext();
assert_eq!(ret, 0);
assert_eq!(ZLECS.load(Ordering::SeqCst), 3, "at EOL, cursor stays put");
}
#[test]
fn viinsertbol_moves_to_first_non_blank() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
zle_with(" abc", 5); let ret = viinsertbol();
assert_eq!(ret, 0);
assert_eq!(
ZLECS.load(Ordering::SeqCst),
3,
"cursor at first non-blank (pos 3 = 'a')"
);
}
#[test]
fn viaddeol_moves_cursor_to_zlell() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
zle_with("hello", 0); let ret = viaddeol();
assert_eq!(ret, 0);
assert_eq!(
ZLECS.load(Ordering::SeqCst),
5,
"viaddeol moves cursor to end of line"
);
}
#[test]
fn vicmdmode_already_in_vicmd_returns_one() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
if selectkeymap("vicmd", 0) == 0 {
let r = vicmdmode();
assert_eq!(r, 1, "vicmdmode on existing vicmd → 1 (no-op signal)");
}
}
#[test]
fn vicmdmode_from_emacs_succeeds() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
selectkeymap("emacs", 0);
zle_with("abc", 1);
let r = vicmdmode();
assert_eq!(r, 0, "vicmdmode from non-vicmd → 0 (selected)");
}
#[test]
fn viaddnext_always_returns_zero() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
zle_with("a", 0);
assert_eq!(viaddnext(), 0);
zle_with("", 0); assert_eq!(viaddnext(), 0);
}
#[test]
fn viaddeol_always_returns_zero() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
zle_with("xyz", 1);
assert_eq!(viaddeol(), 0);
zle_with("", 0);
assert_eq!(viaddeol(), 0);
}
#[test]
fn viinsert_always_returns_zero() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
zle_with("xyz", 0);
assert_eq!(viinsert(), 0);
}
#[test]
fn viinsertbol_always_returns_zero() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
zle_with("xyz", 1);
assert_eq!(viinsertbol(), 0);
}
#[test]
fn startvichange_im_zero_clears_insmode() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
INSMODE.store(1, SeqCst); startvichange(0);
assert_eq!(INSMODE.load(SeqCst), 0, "im=0 → insmode=0 (overwrite)");
}
#[test]
fn startvichange_im_one_sets_insmode() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
INSMODE.store(0, SeqCst); startvichange(1);
assert_eq!(INSMODE.load(SeqCst), 1, "im=1 → insmode=1 (insert)");
}
#[test]
fn startvichange_im_neg_one_preserves_insmode() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
INSMODE.store(1, SeqCst);
startvichange(-1);
assert_eq!(INSMODE.load(SeqCst), 1, "im=-1 must NOT change insmode");
INSMODE.store(0, SeqCst);
startvichange(-1);
assert_eq!(INSMODE.load(SeqCst), 0, "im=-1 must NOT change insmode");
}
#[test]
fn vichange_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = vichange();
}
#[test]
fn vigetkey_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = vigetkey();
}
#[test]
fn getvirange_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = getvirange(0);
}
#[test]
fn dovilinerange_single_line_lands_on_eol() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let line: Vec<char> = "echo aaa\necho bbb\necho ccc".chars().collect();
ZLELL.store(line.len(), SeqCst);
*ZLELINE.lock().unwrap() = line;
ZLECS.store(0, SeqCst);
ZMOD.lock().unwrap().mult = 1;
let ret: i32 = dovilinerange();
assert_eq!(ret, 0, "single-line range succeeds");
assert_eq!(ZLECS.load(SeqCst), 8, "cursor at eol of line 1");
assert_eq!(VILINERANGE.load(SeqCst), 1, "c:311");
assert_eq!(VIRANGEFLAG.load(SeqCst), 2, "c:331");
VIRANGEFLAG.store(0, SeqCst);
*ZLELINE.lock().unwrap() = Vec::new();
ZLELL.store(0, SeqCst);
ZLECS.store(0, SeqCst);
}
#[test]
fn dovilinerange_overrun_count_fails_and_restores() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let line: Vec<char> = "echo aaa\necho bbb".chars().collect();
ZLELL.store(line.len(), SeqCst);
*ZLELINE.lock().unwrap() = line;
ZLECS.store(3, SeqCst);
ZMOD.lock().unwrap().mult = 9;
let ret: i32 = dovilinerange();
assert_eq!(ret, 1, "count past last line fails (c:319)");
assert_eq!(ZLECS.load(SeqCst), 3, "zlecs restored (c:318)");
ZMOD.lock().unwrap().mult = 1;
*ZLELINE.lock().unwrap() = Vec::new();
ZLELL.store(0, SeqCst);
ZLECS.store(0, SeqCst);
}
#[test]
fn videletechar_returns_in_exit_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = videletechar();
assert!((0..256).contains(&r), "exit code {} must fit in u8", r);
}
#[test]
fn visubstitute_returns_in_exit_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = visubstitute();
assert!((0..256).contains(&r), "exit code {} must fit in u8", r);
}
#[test]
fn vichangeeol_returns_in_exit_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = vichangeeol();
assert!((0..256).contains(&r), "exit code {} must fit in u8", r);
}
#[test]
fn vichangewholeline_returns_in_exit_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = vichangewholeline();
assert!((0..256).contains(&r), "exit code {} must fit in u8", r);
}
#[test]
fn viyank_empty_args_returns_in_exit_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = viyank(&[]);
assert!((0..256).contains(&r), "exit code {} must fit in u8", r);
}
#[test]
fn viyankeol_returns_in_exit_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = viyankeol();
assert!((0..256).contains(&r), "exit code {} must fit in u8", r);
}
#[test]
fn viinsert_init_returns_void_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: () = viinsert_init();
}
#[test]
fn viinsert_init_idempotent() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
for _ in 0..5 {
viinsert_init();
}
}
#[test]
fn videlete_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = videlete();
}
#[test]
fn viyankwholeline_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = viyankwholeline();
}
#[test]
fn vireplace_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = vireplace();
}
#[test]
fn vireplacechars_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = vireplacechars();
}
#[test]
fn viopenlinebelow_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = viopenlinebelow();
}
#[test]
fn viopenlineabove_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = viopenlineabove();
}
#[test]
fn vioperswapcase_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = vioperswapcase();
}
#[test]
fn videlete_returns_in_exit_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = videlete();
assert!((0..256).contains(&r));
}
#[test]
fn viyankwholeline_returns_in_exit_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = viyankwholeline();
assert!((0..256).contains(&r));
}
#[test]
fn vioperswapcase_returns_in_exit_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = vioperswapcase();
assert!((0..256).contains(&r));
}
#[test]
fn vichange_returns_i32_pin_alt() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = vichange();
}
#[test]
fn vigetkey_returns_i32_pin_alt() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = vigetkey();
}
#[test]
fn getvirange_returns_i32_pin_alt() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = getvirange(0);
}
#[test]
fn dovilinerange_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = dovilinerange();
}
#[test]
fn viaddnext_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = viaddnext();
}
#[test]
fn viaddeol_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = viaddeol();
}
#[test]
fn viinsert_idempotent_returns_i32() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
for _ in 0..5 {
let _: i32 = viinsert();
}
}
#[test]
fn viinsertbol_returns_i32_type() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let _: i32 = viinsertbol();
}
#[test]
fn videletechar_returns_in_exit_range_alt() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = videletechar();
assert!((0..256).contains(&r));
}
#[test]
fn visubstitute_returns_in_exit_range_alt() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = visubstitute();
assert!((0..256).contains(&r));
}
#[test]
fn vichange_returns_in_exit_range() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = vichange();
assert!((0..256).contains(&r));
}
#[test]
fn vichangeeol_returns_in_exit_range_alt() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = vichangeeol();
assert!((0..256).contains(&r));
}
#[test]
fn vichangewholeline_returns_in_exit_range_alt() {
let _g = crate::test_util::global_state_lock();
let _g2 = zle_test_setup();
let r = vichangewholeline();
assert!((0..256).contains(&r));
}
}