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(0); if c2 != -1 {
let cs = ZLECS.load(SeqCst) as i32;
forekill(
c2 - cs, CUT_RAW,
);
startvitext(1); 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(true) {
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 {
VIRANGEFLAG.store(1, Ordering::Relaxed); WORDFLAG.store(wf, Ordering::Relaxed); MARK.store(usize::MAX, Ordering::Relaxed);
ZLECS.load(SeqCst) as i32 }
pub fn dovilinerange() -> (usize, usize) {
let bol = findbol();
let eol = findeol();
let end = if eol < ZLELL.load(SeqCst) {
eol + 1
} else {
eol
};
(bol, end)
}
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 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;
}
let eol = findeol();
let count = (n as usize).min(eol - ZLECS.load(SeqCst));
if count > 0 {
let text: Vec<char> = ZLELINE
.lock()
.unwrap()
.drain(
ZLECS.load(SeqCst)
..ZLECS.load(SeqCst) + count,
)
.collect();
KILLRING.lock().unwrap().push_front(text);
if KILLRING.lock().unwrap().len() > KILLRINGMAX.load(SeqCst) {
KILLRING.lock().unwrap().pop_back();
}
ZLELL.fetch_sub(count, SeqCst);
}
startvitext(1);
0
}
pub fn vichangeeol() -> i32 {
let eol = findeol();
if eol > ZLECS.load(SeqCst) {
let text: Vec<char> = ZLELINE
.lock()
.unwrap()
.drain(ZLECS.load(SeqCst)..eol)
.collect();
KILLRING.lock().unwrap().push_front(text);
if KILLRING.lock().unwrap().len() > KILLRINGMAX.load(SeqCst) {
KILLRING.lock().unwrap().pop_back();
}
ZLELL.fetch_sub(
eol - ZLECS.load(SeqCst),
SeqCst,
);
}
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 text: Vec<char> =
ZLELINE.lock().unwrap()[ZLECS.load(SeqCst)..x].to_vec();
KILLRING.lock().unwrap().push_front(text);
if KILLRING.lock().unwrap().len() > KILLRINGMAX.load(SeqCst) {
KILLRING.lock().unwrap().pop_back();
}
0 }
pub fn viyankwholeline() -> i32 {
let bol = findbol();
let oldcs = ZLECS.load(SeqCst);
startvichange(-1);
let n = ZMOD.lock().unwrap().mult;
if n < 1 {
return 1;
}
for _ in 0..n {
ZLECS.store(
findeol() + 1,
SeqCst,
);
if ZLECS.load(SeqCst)
> ZLELL.load(SeqCst)
{
ZLECS.store(
ZLELL.load(SeqCst),
SeqCst,
);
}
}
let end = ZLECS.load(SeqCst);
let text: Vec<char> = ZLELINE.lock().unwrap()[bol..end].to_vec();
KILLRING.lock().unwrap().push_front(text);
if KILLRING.lock().unwrap().len() > KILLRINGMAX.load(SeqCst) {
KILLRING.lock().unwrap().pop_back();
}
ZLECS.store(oldcs, SeqCst);
0
}
pub fn vireplace() -> i32 {
startvitext(0);
0
}
pub fn vireplacechars() -> i32 {
startvichange(1);
let n = ZMOD.lock().unwrap().mult.max(1) as usize;
let eol = findeol();
let avail = eol.saturating_sub(ZLECS.load(SeqCst));
if n > avail {
return 1; }
if let Some(c) = char::from_u32(
crate::ported::zle::compcore::LASTCHAR.load(SeqCst) as u32,
) {
for i in 0..n {
ZLELINE.lock().unwrap()[ZLECS.load(SeqCst) + i] = c;
}
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;
}
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 {
ZLECS.store(
findeol(),
SeqCst,
);
ZLELINE
.lock()
.unwrap()
.insert(ZLECS.load(SeqCst), '\n');
ZLECS.fetch_add(1, SeqCst);
ZLELL.fetch_add(1, SeqCst);
startvitext(1);
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn viopenlineabove() -> i32 {
ZLECS.store(
findbol(),
SeqCst,
);
ZLELINE
.lock()
.unwrap()
.insert(ZLECS.load(SeqCst), '\n');
ZLELL.fetch_add(1, SeqCst);
startvitext(1);
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn vioperswapcase() -> i32 {
startvichange(1);
let eol = findeol();
let oldcs = ZLECS.load(SeqCst);
while ZLECS.load(SeqCst) < eol {
let c = ZLELINE.lock().unwrap()[ZLECS.load(SeqCst)];
ZLELINE.lock().unwrap()[ZLECS.load(SeqCst)] =
if c.is_ascii_uppercase() {
c.to_ascii_lowercase()
} else if c.is_ascii_lowercase() {
c.to_ascii_uppercase()
} else {
c
};
ZLECS.fetch_add(1, SeqCst);
}
ZLECS.store(oldcs, SeqCst);
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn viupcase() -> i32 {
startvichange(1);
let eol = findeol();
for i in ZLECS.load(SeqCst)..eol {
{
let mut __g = ZLELINE.lock().unwrap();
__g[i] = __g[i].to_ascii_uppercase();
}
}
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn vidowncase() -> i32 {
startvichange(1);
let eol = findeol();
for i in ZLECS.load(SeqCst)..eol {
{
let mut __g = ZLELINE.lock().unwrap();
__g[i] = __g[i].to_ascii_lowercase();
}
}
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
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 {
startvichange(1);
let bol = findbol();
for _ in 0..4 {
ZLELINE.lock().unwrap().insert(bol, ' ');
ZLELL.fetch_add(1, SeqCst);
}
if ZLECS.load(SeqCst) >= bol {
ZLECS.fetch_add(4, SeqCst);
}
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn viunindent() -> i32 {
startvichange(1);
let bol = findbol();
let mut removed = 0;
while removed < 4 && bol < ZLELINE.lock().unwrap().len() && ZLELINE.lock().unwrap()[bol] == ' '
{
ZLELINE.lock().unwrap().remove(bol);
ZLELL.fetch_sub(1, SeqCst);
removed += 1;
}
if ZLECS.load(SeqCst) >= bol + removed {
ZLECS.fetch_sub(removed, SeqCst);
}
ZLE_RESET_NEEDED.store(1, SeqCst);
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 {
startvichange(-1);
let n = ZMOD.lock().unwrap().mult.max(1);
for _ in 0..n {
let eol = findeol();
if eol >= ZLELL.load(SeqCst)
|| ZLELINE.lock().unwrap().get(eol) != Some(&'\n')
{
return 1;
}
ZLELINE.lock().unwrap()[eol] = ' ';
let mut p = eol + 1;
while p < ZLELINE.lock().unwrap().len() && ZLELINE.lock().unwrap()[p].is_whitespace() {
ZLELINE.lock().unwrap().remove(p);
ZLELL.fetch_sub(1, SeqCst);
}
let _ = p;
ZLECS.store(eol, SeqCst);
}
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn viswapcase() -> i32 {
startvichange(-1);
let n = ZMOD.lock().unwrap().mult;
if n < 1 {
return 1;
}
let eol = findeol();
for _ in 0..n {
if ZLECS.load(SeqCst) >= eol {
break;
}
let c = ZLELINE.lock().unwrap()[ZLECS.load(SeqCst)];
let swapped = if c.is_ascii_uppercase() {
c.to_ascii_lowercase()
} else if c.is_ascii_lowercase() {
c.to_ascii_uppercase()
} else {
c
};
ZLELINE.lock().unwrap()[ZLECS.load(SeqCst)] = swapped;
ZLECS.fetch_add(1, SeqCst);
}
ZLE_RESET_NEEDED.store(1, SeqCst);
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() -> i32 {
let c = (crate::ported::zle::compcore::LASTCHAR.load(SeqCst)
& 0xff) as u8;
let idx: i32 = if c.is_ascii_digit() {
(c - b'0') as i32 + 26
} else if c.is_ascii_lowercase() {
(c - b'a') as i32
} else if c.is_ascii_uppercase() {
ZMOD.lock().unwrap().flags |= MOD_VIAPP;
(c - b'A') as i32
} else {
return 1;
};
ZMOD.lock().unwrap().vibuf = idx;
ZMOD.lock().unwrap().flags |= MOD_VIBUF;
PREFIXFLAG.store(1, SeqCst);
0
}
pub fn vikilleol() -> i32 {
startvichange(1);
let eol = findeol();
if eol > ZLECS.load(SeqCst) {
let text: Vec<char> = ZLELINE
.lock()
.unwrap()
.drain(ZLECS.load(SeqCst)..eol)
.collect();
KILLRING.lock().unwrap().push_front(text);
if KILLRING.lock().unwrap().len() > KILLRINGMAX.load(SeqCst) {
KILLRING.lock().unwrap().pop_back();
}
ZLELL.fetch_sub(
eol - ZLECS.load(SeqCst),
SeqCst,
);
}
ZLE_RESET_NEEDED.store(1, SeqCst);
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();
}
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);
}
pub fn vi_get_range(op_char: char) -> Option<(usize, usize, bool)> {
let pos = ZLECS.load(SeqCst);
let n = vi_get_arg().max(1);
let motion = getfullchar(false)?;
if motion == op_char {
let bol = findbol();
let mut eol = findeol();
for _ in 1..n {
if eol >= ZLELL.load(SeqCst) {
break;
}
eol = findeol();
}
let end = if eol < ZLELL.load(SeqCst) {
eol + 1
} else {
eol
};
return Some((bol, end, true));
}
let other = match motion {
'w' => {
let mut p = pos;
for _ in 0..n {
let saved_cs = ZLECS.load(SeqCst);
ZLECS.store(p, SeqCst);
p = find_word_end(WordStyle::Vi);
ZLECS.store(saved_cs, SeqCst);
}
p
}
'W' => {
let mut p = pos;
for _ in 0..n {
let saved_cs = ZLECS.load(SeqCst);
ZLECS.store(p, SeqCst);
p = find_word_end(WordStyle::BlankDelimited);
ZLECS.store(saved_cs, SeqCst);
}
p
}
'b' => {
let mut p = pos;
for _ in 0..n {
let saved_cs = ZLECS.load(SeqCst);
ZLECS.store(p, SeqCst);
p = find_word_start(WordStyle::Vi);
ZLECS.store(saved_cs, SeqCst);
}
p
}
'B' => {
let mut p = pos;
for _ in 0..n {
let saved_cs = ZLECS.load(SeqCst);
ZLECS.store(p, SeqCst);
p = find_word_start(WordStyle::BlankDelimited);
ZLECS.store(saved_cs, SeqCst);
}
p
}
'e' => {
let saved_cs = ZLECS.load(SeqCst);
ZLECS.store(pos, SeqCst);
let mut p = find_word_end(WordStyle::Vi);
ZLECS.store(saved_cs, SeqCst);
if p < ZLELL.load(SeqCst) {
p += 1;
}
p
}
'E' => {
let saved_cs = ZLECS.load(SeqCst);
ZLECS.store(pos, SeqCst);
let mut p = find_word_end(WordStyle::BlankDelimited);
ZLECS.store(saved_cs, SeqCst);
if p < ZLELL.load(SeqCst) {
p += 1;
}
p
}
'0' => findbol(),
'^' => {
let bol = findbol();
let mut p = bol;
while p < ZLELL.load(SeqCst) && {
let __c = ZLELINE.lock().unwrap()[p];
__c.is_whitespace() && __c != '\n'
} {
p += 1;
}
p
}
'$' => findeol(),
'h' => pos.saturating_sub(n as usize),
'l' => (pos + n as usize).min(ZLELL.load(SeqCst)),
'j' => {
let mut p = findeol();
for _ in 0..n {
if p >= ZLELL.load(SeqCst) {
break;
}
p = findeol();
}
let bol = findbol();
let end = if p < ZLELL.load(SeqCst) {
p + 1
} else {
p
};
return Some((bol, end, true));
}
'k' => {
let mut bol = findbol();
for _ in 0..n {
if bol == 0 {
break;
}
bol = findbol();
}
let eol = findeol();
let end = if eol < ZLELL.load(SeqCst) {
eol + 1
} else {
eol
};
return Some((bol, end, true));
}
'f' | 'F' | 't' | 'T' => {
let next = getfullchar(false)?;
VFINDCHAR.store(next as i32, SeqCst);
VFINDDIR.store(
if motion == 'f' || motion == 't' {
1
} else {
-1
},
SeqCst,
);
TAILADD.store(
match motion {
'f' | 'F' => 0,
't' => -1,
'T' => 1,
_ => 0,
},
SeqCst,
);
let saved_mult = ZMOD.lock().unwrap().mult;
ZMOD.lock().unwrap().mult = n;
let ok = vi_find_char_inner(false) == 0;
ZMOD.lock().unwrap().mult = saved_mult;
if !ok {
return None;
}
let mut p = ZLECS.load(SeqCst);
if (motion == 'f' || motion == 't')
&& p < ZLELL.load(SeqCst)
{
p += 1;
}
ZLECS.store(pos, SeqCst);
p
}
_ => return None,
};
if other == pos {
return None;
}
let (start, end) = if other > pos {
(pos, other)
} else {
(other, pos)
};
Some((start, end, false))
}
fn vi_cut_into_killring(start: usize, end: usize) {
if end <= start || end > ZLELINE.lock().unwrap().len() {
return;
}
let killed: Vec<char> = ZLELINE.lock().unwrap()[start..end].to_vec();
KILLRING.lock().unwrap().push_front(killed);
if KILLRING.lock().unwrap().len() > KILLRINGMAX.load(SeqCst) {
KILLRING.lock().unwrap().pop_back();
}
}
pub fn vi_delete_op() -> i32 {
let (start, end, line_mode) = match vi_get_range('d') {
Some(r) => r,
None => return 1,
};
vi_cut_into_killring(start, end);
let drained = end - start;
ZLELINE.lock().unwrap().drain(start..end);
ZLELL.store(
ZLELINE.lock().unwrap().len(),
SeqCst,
);
ZLECS.store(
start.min(ZLELL.load(SeqCst)),
SeqCst,
);
if line_mode && ZLELL.load(SeqCst) > 0 {
LASTCOL.store(-1, SeqCst);
let bol = findbol();
let mut p = bol;
while p < ZLELL.load(SeqCst) && {
let __c = ZLELINE.lock().unwrap()[p];
__c.is_whitespace() && __c != '\n'
} {
p += 1;
}
ZLECS.store(p, SeqCst);
}
let _ = drained;
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn vi_change_op() -> i32 {
let (start, end, _) = match vi_get_range('c') {
Some(r) => r,
None => return 1,
};
vi_cut_into_killring(start, end);
ZLELINE.lock().unwrap().drain(start..end);
ZLELL.store(
ZLELINE.lock().unwrap().len(),
SeqCst,
);
ZLECS.store(
start.min(ZLELL.load(SeqCst)),
SeqCst,
);
VISTARTCHANGE.store(
UNDO_CHANGENO.load(SeqCst),
SeqCst,
);
selectkeymap("main", 1);
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
pub fn vi_yank_op() -> i32 {
let saved_lastcol = LASTCOL.load(SeqCst);
let (start, end, line_mode) = match vi_get_range('y') {
Some(r) => r,
None => return 1,
};
vi_cut_into_killring(start, end);
ZLECS.store(start, SeqCst);
if line_mode && saved_lastcol != -1 {
let eol = findeol();
ZLECS.fetch_add(saved_lastcol as usize, SeqCst);
if ZLECS.load(SeqCst) >= eol {
ZLECS.store(eol, SeqCst);
}
LASTCOL.store(-1, SeqCst);
}
ZLE_RESET_NEEDED.store(1, SeqCst);
0
}
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()));
}
fn feed(s: &str) {
ungetbytes(s.as_bytes());
}
#[test]
fn vi_get_range_dd_selects_whole_current_line() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("aaa\nbbb\nccc", 4); feed("d");
let (s, e, line) = vi_get_range('d').expect("range");
assert!(line);
assert_eq!(s, 4);
assert_eq!(e, 8); }
#[test]
fn vi_get_range_dw_selects_to_word_end() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("hello world", 0);
feed("w");
let (s, e, line) = vi_get_range('d').expect("range");
assert!(!line);
assert_eq!(s, 0);
assert_eq!(e, 6);
}
#[test]
fn vi_get_range_d_dollar_selects_to_eol() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("foo bar baz", 4);
feed("$");
let (s, e, _) = vi_get_range('d').expect("range");
assert_eq!(s, 4);
assert_eq!(e, 11);
}
#[test]
fn vi_delete_op_dw_removes_first_word() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("hello world", 0);
feed("w");
assert_eq!(vi_delete_op(), 0);
assert_eq!(ZLELINE.lock().unwrap().iter().collect::<String>(), "world");
assert_eq!(
KILLRING
.lock()
.unwrap()
.front()
.map(|v| v.iter().collect::<String>()),
Some("hello ".to_string())
);
}
#[test]
fn vi_yank_op_y_dollar_copies_without_removing() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("foo bar", 4);
feed("$");
assert_eq!(vi_yank_op(), 0);
assert_eq!(
ZLELINE.lock().unwrap().iter().collect::<String>(),
"foo bar"
);
assert_eq!(
KILLRING
.lock()
.unwrap()
.front()
.map(|v| v.iter().collect::<String>()),
Some("bar".to_string())
);
assert_eq!(ZLECS.load(SeqCst), 4);
}
#[test]
fn vi_change_op_cw_removes_word_and_clears_pending_change() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("foo bar", 0);
feed("w");
assert_eq!(vi_change_op(), 0);
assert_eq!(ZLELINE.lock().unwrap().iter().collect::<String>(), "bar");
assert_eq!(ZLECS.load(SeqCst), 0);
assert_eq!(
VISTARTCHANGE.load(SeqCst),
UNDO_CHANGENO.load(SeqCst)
);
}
#[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_get_range_unknown_motion_returns_none() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut zle = zle_with("abc", 0);
feed("Z"); assert!(vi_get_range('d').is_none());
}
#[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();
}
}