use std::sync::atomic::Ordering;
use crate::ported::zle::zle_h::{MOD_CHAR, MOD_LINE};
#[allow(unused_imports)]
use crate::ported::zle::{
deltochar::*, textobjects::*, zle_hist::*, zle_main::*, zle_misc::*, zle_params::*,
zle_refresh::*, zle_tricky::*, zle_utils::*, zle_vi::*, zle_word::*,
};
use crate::ported::zsh_h::{isset, COMBININGCHARS};
use crate::zsh_h::{IS_BASECHAR, IS_COMBINING};
#[allow(unused_imports)]
#[allow(unused_imports)]
pub fn alignmultiwordleft(pos: &mut usize, setpos: i32) -> i32 {
let mut loccs = *pos;
let zlell = ZLELL.load(Ordering::SeqCst) as usize;
if !isset(COMBININGCHARS)
|| loccs == zlell
|| loccs == 0
{
return 0;
}
let zleline = ZLELINE.lock().unwrap();
if loccs >= zleline.len() || !IS_COMBINING(zleline[loccs]) {
return 0;
}
loccs -= 1;
loop {
if loccs >= zleline.len() {
return 0;
}
if IS_BASECHAR(zleline[loccs]) {
if setpos != 0 {
*pos = loccs; }
return 1; } else if !IS_COMBINING(zleline[loccs]) {
return 0; }
if loccs == 0 {
return 0;
}
loccs -= 1;
}
}
pub fn alignmultiwordright(pos: &mut usize, setpos: i32) -> i32 {
if alignmultiwordleft(pos, 0) == 0 {
return 0;
}
let mut loccs = *pos + 1; let zlell = ZLELL.load(Ordering::SeqCst) as usize;
let zleline = ZLELINE.lock().unwrap();
while loccs < zlell {
if loccs >= zleline.len() {
break;
}
if !IS_COMBINING(zleline[loccs]) {
if setpos != 0 {
*pos = loccs; }
return 1; }
loccs += 1;
}
if setpos != 0 {
*pos = loccs;
}
1
}
pub fn inccs() {
let new_cs = ZLECS.fetch_add(1, Ordering::SeqCst) + 1; let mut p = new_cs;
alignmultiwordright(&mut p, 1); if p != new_cs {
ZLECS.store(p, Ordering::SeqCst);
}
}
pub fn deccs() {
let prev = ZLECS.fetch_sub(1, Ordering::SeqCst); let new_cs = prev.saturating_sub(1);
let mut p = new_cs;
alignmultiwordleft(&mut p, 1); if p != new_cs {
ZLECS.store(p, Ordering::SeqCst);
}
}
pub fn incpos(pos: &mut usize) {
*pos += 1; }
pub fn decpos(pos: &mut usize) {
*pos -= 1; }
pub const BMC_BUFSIZE: usize = 6;
pub fn backwardmetafiedchar() {
if ZLECS.load(Ordering::SeqCst) > 0 {
ZLECS.fetch_sub(1, Ordering::SeqCst);
}
}
pub fn beginningofline() -> i32 {
let n = ZMOD.lock().unwrap().mult;
if n < 0 {
let saved = n;
ZMOD.lock().unwrap().mult = -n;
let ret = endofline();
ZMOD.lock().unwrap().mult = saved;
return ret;
}
for _ in 0..n {
if ZLECS.load(Ordering::SeqCst) == 0 {
return 0;
}
if ZLECS.load(Ordering::SeqCst) > 0
&& ZLELINE
.lock()
.unwrap()
.get(ZLECS.load(Ordering::SeqCst) - 1)
== Some(&'\n')
{
ZLECS.fetch_sub(1, Ordering::SeqCst);
if ZLECS.load(Ordering::SeqCst) == 0 {
return 0;
}
}
while ZLECS.load(Ordering::SeqCst) > 0
&& ZLELINE
.lock()
.unwrap()
.get(ZLECS.load(Ordering::SeqCst) - 1)
!= Some(&'\n')
{
ZLECS.fetch_sub(1, Ordering::SeqCst);
}
}
0
}
pub fn endofline() -> i32 {
let n = ZMOD.lock().unwrap().mult;
if n < 0 {
let saved = n;
ZMOD.lock().unwrap().mult = -n;
let ret = beginningofline();
ZMOD.lock().unwrap().mult = saved;
return ret;
}
for _ in 0..n {
if ZLECS.load(Ordering::SeqCst)
>= ZLELL.load(Ordering::SeqCst)
{
ZLECS.store(
ZLELL.load(Ordering::SeqCst),
Ordering::SeqCst,
);
return 0;
}
if ZLELINE
.lock()
.unwrap()
.get(ZLECS.load(Ordering::SeqCst))
== Some(&'\n')
{
ZLECS.fetch_add(1, Ordering::SeqCst);
if ZLECS.load(Ordering::SeqCst)
== ZLELL.load(Ordering::SeqCst)
{
return 0;
}
}
while ZLECS.load(Ordering::SeqCst)
!= ZLELL.load(Ordering::SeqCst)
&& ZLELINE
.lock()
.unwrap()
.get(ZLECS.load(Ordering::SeqCst))
!= Some(&'\n')
{
ZLECS.fetch_add(1, Ordering::SeqCst);
}
}
0
}
pub fn beginningoflinehist() -> i32 {
use crate::ported::zle::zle_hist::uphistory;
let mut n = ZMOD.lock().unwrap().mult;
if n < 0 {
let saved = n;
ZMOD.lock().unwrap().mult = -n;
let ret = endoflinehist();
ZMOD.lock().unwrap().mult = saved;
return ret;
}
while n > 0 {
let cs = ZLECS.load(Ordering::SeqCst);
if cs == 0 {
break; }
let pos = cs - 1;
let at = ZLELINE.lock().unwrap().get(pos).copied();
if at == Some('\n') {
ZLECS.store(pos, Ordering::SeqCst); if pos == 0 {
break; }
}
loop {
let cs = ZLECS.load(Ordering::SeqCst);
if cs == 0 {
break;
}
if ZLELINE.lock().unwrap().get(cs - 1).copied() == Some('\n') {
break;
}
ZLECS.fetch_sub(1, Ordering::SeqCst);
}
n -= 1; }
if n > 0 {
let m = ZMOD.lock().unwrap().mult;
ZMOD.lock().unwrap().mult = n;
let ret = uphistory();
ZMOD.lock().unwrap().mult = m;
ZLECS.store(0, Ordering::SeqCst);
return ret;
}
0 }
pub fn endoflinehist() -> i32 {
let mut n = ZMOD.lock().unwrap().mult;
if n < 0 {
let saved = n;
ZMOD.lock().unwrap().mult = -n;
let ret = beginningoflinehist();
ZMOD.lock().unwrap().mult = saved;
return ret;
}
let line = ZLELINE.lock().unwrap().clone();
let ll = ZLELL.load(Ordering::SeqCst);
while n > 0 {
let mut cs = ZLECS.load(Ordering::SeqCst);
if cs >= ll {
ZLECS.store(ll, Ordering::SeqCst);
break;
}
cs += invicmdmode();
if cs == ll {
ZLECS.store(cs, Ordering::SeqCst);
break;
}
if cs < line.len() && line.get(cs) == Some(&'\n') {
cs += 1;
if cs == ll {
ZLECS.store(cs, Ordering::SeqCst);
break;
}
}
while cs != ll && line.get(cs) != Some(&'\n') {
cs += 1;
}
ZLECS.store(cs, Ordering::SeqCst);
n -= 1; }
if n > 0 {
let m = ZMOD.lock().unwrap().mult;
ZMOD.lock().unwrap().mult = n;
if let Some(_e) = history().lock().unwrap().down() {
ZLECS.store(ZLELL.load(Ordering::SeqCst), Ordering::SeqCst);
}
ZMOD.lock().unwrap().mult = m;
}
0
}
fn invicmdmode() -> usize {
if INSMODE.load(Ordering::SeqCst) == 0 {
1
} else {
0
}
}
pub fn forwardchar() -> i32 {
let mut n = ZMOD.lock().unwrap().mult; if n < 0 {
let saved = n;
ZMOD.lock().unwrap().mult = -n;
let ret = backwardchar();
ZMOD.lock().unwrap().mult = saved;
return ret;
}
while ZLECS.load(Ordering::SeqCst)
< ZLELL.load(Ordering::SeqCst)
&& n > 0
{
inccs(); n -= 1;
}
0 }
pub fn backwardchar() -> i32 {
let mut n = ZMOD.lock().unwrap().mult; if n < 0 {
let saved = n;
ZMOD.lock().unwrap().mult = -n;
let ret = forwardchar();
ZMOD.lock().unwrap().mult = saved;
return ret;
}
while ZLECS.load(Ordering::SeqCst) > 0 && n > 0 {
deccs(); n -= 1;
}
0 }
pub fn setmarkcommand() -> i32 {
if ZMOD.lock().unwrap().mult < 0 {
REGION_ACTIVE.store(0, Ordering::SeqCst); return 0; }
MARK.store(
ZLECS.load(Ordering::SeqCst),
Ordering::SeqCst,
); REGION_ACTIVE.store(1, Ordering::SeqCst); 0 }
pub fn exchangepointandmark() -> i32 {
if ZMOD.lock().unwrap().mult == 0 {
REGION_ACTIVE.store(1, Ordering::SeqCst); return 0; }
let x = MARK.load(Ordering::SeqCst); MARK.store(
ZLECS.load(Ordering::SeqCst),
Ordering::SeqCst,
); ZLECS.store(x, Ordering::SeqCst); if ZLECS.load(Ordering::SeqCst)
> ZLELL.load(Ordering::SeqCst)
{
ZLECS.store(
ZLELL.load(Ordering::SeqCst),
Ordering::SeqCst,
); }
if ZMOD.lock().unwrap().mult > 0 {
REGION_ACTIVE.store(1, Ordering::SeqCst); }
0 }
pub fn visualmode() -> i32 {
match REGION_ACTIVE.load(Ordering::SeqCst) {
1 => {
REGION_ACTIVE.store(0, Ordering::SeqCst);
} 0 => {
MARK.store(
ZLECS.load(Ordering::SeqCst),
Ordering::SeqCst,
); REGION_ACTIVE.store(1, Ordering::SeqCst); }
2 => {
REGION_ACTIVE.store(1, Ordering::SeqCst);
} _ => {}
}
let _ = MOD_CHAR;
0
}
pub fn visuallinemode() -> i32 {
match REGION_ACTIVE.load(Ordering::SeqCst) {
2 => {
REGION_ACTIVE.store(0, Ordering::SeqCst);
} 0 => {
MARK.store(
ZLECS.load(Ordering::SeqCst),
Ordering::SeqCst,
); REGION_ACTIVE.store(2, Ordering::SeqCst); }
1 => {
REGION_ACTIVE.store(2, Ordering::SeqCst);
} _ => {}
}
let _ = MOD_LINE;
0
}
pub fn deactivateregion() -> i32 {
REGION_ACTIVE.store(0, Ordering::SeqCst); 0 }
pub fn vigotocolumn() -> i32 {
let bol = findbol();
let eol = findeol();
let n = ZMOD.lock().unwrap().mult;
let target = if n >= 0 {
let off = if n > 0 { (n as usize) - 1 } else { 0 };
(bol + off).min(eol)
} else {
eol.saturating_sub((-n) as usize)
};
ZLECS.store(
target.max(bol).min(eol),
Ordering::SeqCst,
);
0
}
pub fn vimatchbracket() -> i32 {
let ocs = ZLECS.load(Ordering::SeqCst); if (ZLECS.load(Ordering::SeqCst) == ZLELL.load(Ordering::SeqCst) || ZLELINE.lock().unwrap().get(ZLECS.load(Ordering::SeqCst)) == Some(&'\n')) && ZLECS.load(Ordering::SeqCst) > 0
{
deccs(); }
if ZLECS.load(Ordering::SeqCst)
== ZLELL.load(Ordering::SeqCst)
|| ZLELINE
.lock()
.unwrap()
.get(ZLECS.load(Ordering::SeqCst))
== Some(&'\n')
{
ZLECS.store(ocs, Ordering::SeqCst); return 1; }
let me = ZLELINE.lock().unwrap()[ZLECS.load(Ordering::SeqCst)]; let (oth, dir) = match me {
'{' => ('}', 1),
'}' => ('{', -1),
'(' => (')', 1),
')' => ('(', -1),
'[' => (']', 1),
']' => ('[', -1),
'<' => ('>', 1),
'>' => ('<', -1),
_ => {
ZLECS.store(ocs, Ordering::SeqCst);
return 1;
}
};
let mut depth = 1i32; loop {
if dir > 0 {
if ZLECS.load(Ordering::SeqCst)
>= ZLELL.load(Ordering::SeqCst)
{
ZLECS.store(ocs, Ordering::SeqCst);
return 1;
}
ZLECS.fetch_add(1, Ordering::SeqCst);
} else {
if ZLECS.load(Ordering::SeqCst) == 0 {
ZLECS.store(ocs, Ordering::SeqCst);
return 1;
}
ZLECS.fetch_sub(1, Ordering::SeqCst);
}
let c = match ZLELINE
.lock()
.unwrap()
.get(ZLECS.load(Ordering::SeqCst))
{
Some(&c) => c,
None => {
ZLECS.store(ocs, Ordering::SeqCst);
return 1;
}
};
if c == me {
depth += 1;
} else if c == oth {
depth -= 1;
if depth == 0 {
return 0;
}
}
}
}
pub fn viforwardchar() -> i32 {
let mut lim = findeol(); let mut n = ZMOD.lock().unwrap().mult; if n < 0 {
let saved = n;
ZMOD.lock().unwrap().mult = -n;
let ret = vibackwardchar();
ZMOD.lock().unwrap().mult = saved;
return ret;
}
if *crate::ported::zle::zle_keymap::curkeymapname() == "vicmd" && lim > 0 {
lim -= 1;
}
if ZLECS.load(Ordering::SeqCst) >= lim {
return 1; }
while n > 0 && ZLECS.load(Ordering::SeqCst) < lim {
inccs(); n -= 1;
}
0 }
pub fn vibackwardchar() -> i32 {
let mut n = ZMOD.lock().unwrap().mult; if n < 0 {
let saved = n;
ZMOD.lock().unwrap().mult = -n;
let ret = viforwardchar();
ZMOD.lock().unwrap().mult = saved;
return ret;
}
if ZLECS.load(Ordering::SeqCst) == findbol() {
return 1; }
while n > 0 && ZLECS.load(Ordering::SeqCst) > 0 {
deccs(); if ZLELINE
.lock()
.unwrap()
.get(ZLECS.load(Ordering::SeqCst))
== Some(&'\n')
{
ZLECS.fetch_add(1, Ordering::SeqCst);
break;
}
n -= 1;
}
0 }
pub fn viendofline() -> i32 {
let oldcs = ZLECS.load(Ordering::SeqCst);
let n = ZMOD.lock().unwrap().mult;
if n < 1 {
return 1;
}
for _ in 0..n {
if ZLECS.load(Ordering::SeqCst)
> ZLELL.load(Ordering::SeqCst)
{
ZLECS.store(oldcs, Ordering::SeqCst);
return 1;
}
ZLECS.store(
findeol() + 1,
Ordering::SeqCst,
);
}
if ZLECS.load(Ordering::SeqCst) > 0 {
deccs();
}
0
}
pub fn vibeginningofline() -> i32 {
ZLECS.store(
findbol(),
Ordering::SeqCst,
); 0 }
pub fn vifindnextchar() -> i32 {
let c = vigetkey();
if c < 0 {
return 1;
}
VFINDCHAR.store(c, Ordering::SeqCst);
VFINDDIR.store(1, Ordering::SeqCst);
TAILADD.store(0, Ordering::SeqCst);
vifindchar(0)
}
pub fn vifindprevchar() -> i32 {
let c = vigetkey();
if c < 0 {
return 1;
}
VFINDCHAR.store(c, Ordering::SeqCst);
VFINDDIR.store(-1, Ordering::SeqCst);
TAILADD.store(0, Ordering::SeqCst);
vifindchar(0)
}
pub fn vifindnextcharskip() -> i32 {
let c = vigetkey();
if c < 0 {
return 1;
}
VFINDCHAR.store(c, Ordering::SeqCst);
VFINDDIR.store(1, Ordering::SeqCst);
TAILADD.store(-1, Ordering::SeqCst);
vifindchar(0)
}
pub fn vifindprevcharskip() -> i32 {
let c = vigetkey();
if c < 0 {
return 1;
}
VFINDCHAR.store(c, Ordering::SeqCst);
VFINDDIR.store(-1, Ordering::SeqCst);
TAILADD.store(1, Ordering::SeqCst);
vifindchar(0)
}
pub fn vifindchar(repeat: i32) -> i32 {
let vfind = VFINDCHAR.load(Ordering::Relaxed);
let vdir = VFINDDIR.load(Ordering::Relaxed);
let tail = TAILADD.load(Ordering::Relaxed);
let ocs = ZLECS.load(Ordering::SeqCst);
let n = ZMOD.lock().unwrap().mult;
if vdir == 0 {
return 1;
}
if n < 0 {
ZMOD.lock().unwrap().mult = -n;
let r = vifindchar(repeat);
ZMOD.lock().unwrap().mult = n;
return r;
}
if repeat != 0 && tail != 0 {
if vdir > 0 {
if ZLECS.load(Ordering::SeqCst) + 1
< ZLELL.load(Ordering::SeqCst)
&& (ZLELINE.lock().unwrap()[ZLECS.load(Ordering::SeqCst) + 1]
as i32)
== vfind
{
inccs();
}
} else if ZLECS.load(Ordering::SeqCst) > 0
&& (ZLELINE.lock().unwrap()[ZLECS.load(Ordering::SeqCst) - 1] as i32)
== vfind
{
deccs();
}
}
let mut nn = n;
while nn > 0 {
loop {
if vdir > 0 {
inccs();
} else {
if ZLECS.load(Ordering::SeqCst) == 0 {
break;
}
deccs();
}
if {
let __c = ZLELINE.lock().unwrap()[ZLECS.load(Ordering::SeqCst)];
ZLECS.load(Ordering::SeqCst)
>= ZLELL.load(Ordering::SeqCst)
|| (__c as i32) == vfind
|| __c == '\n'
} {
break;
}
}
if ZLECS.load(Ordering::SeqCst)
>= ZLELL.load(Ordering::SeqCst)
|| ZLELINE.lock().unwrap()[ZLECS.load(Ordering::SeqCst)] == '\n'
{
ZLECS.store(ocs, Ordering::SeqCst); return 1;
}
nn -= 1;
}
if tail > 0 {
inccs();
} else if tail < 0 {
deccs();
}
0
}
pub fn virepeatfind() -> i32 {
vifindchar(1)
}
pub fn virevrepeatfind() -> i32 {
if ZMOD.lock().unwrap().mult < 0 {
let mut __g_zmod = ZMOD.lock().unwrap();
__g_zmod.mult = -__g_zmod.mult;
let ret = vifindchar(1);
let mut __g_zmod = ZMOD.lock().unwrap();
__g_zmod.mult = -__g_zmod.mult;
return ret;
}
let t = TAILADD.load(Ordering::SeqCst);
let d = VFINDDIR.load(Ordering::SeqCst);
TAILADD.store(-t, Ordering::SeqCst);
VFINDDIR.store(-d, Ordering::SeqCst);
let ret = vifindchar(1);
TAILADD.store(t, Ordering::SeqCst);
VFINDDIR.store(d, Ordering::SeqCst);
ret
}
pub fn vifirstnonblank() -> i32 {
ZLECS.store(
findbol(),
Ordering::SeqCst,
); while ZLECS.load(Ordering::SeqCst)
!= ZLELL.load(Ordering::SeqCst)
{
let ch = ZLELINE.lock().unwrap()[ZLECS.load(Ordering::SeqCst)];
if !crate::ported::zle::zle_h::ZC_iblank(ch) {
break;
}
inccs(); }
0 }
pub fn visetmark(ch: char) -> i32 {
if !('a'..='z').contains(&ch) {
return 1;
}
let idx = (ch as u8 - b'a') as usize; vimarks().lock().unwrap()[idx] = Some((
ZLECS.load(Ordering::SeqCst),
history().lock().unwrap().cursor as i32,
)); 0
}
pub fn vigotomark(ch: char) -> i32 {
let idx = match ch {
'a'..='z' => (ch as u8 - b'a') as usize, '\'' | '`' => 26, _ => return 1,
};
if let Some((cs, hist)) = vimarks().lock().unwrap()[idx] {
ZLECS.store(
cs.min(ZLELL.load(Ordering::SeqCst)),
Ordering::SeqCst,
);
history().lock().unwrap().cursor = hist.max(0) as usize;
return 0;
}
1
}
pub fn vigotomarkline(ch: char) -> i32 {
vigotomark(ch); vifirstnonblank() }
pub fn move_to_bol() {
while ZLECS.load(Ordering::SeqCst) > 0
&& ZLELINE.lock().unwrap()[ZLECS.load(Ordering::SeqCst) - 1] != '\n'
{
ZLECS.fetch_sub(1, Ordering::SeqCst);
}
}
pub fn move_to_eol() {
while ZLECS.load(Ordering::SeqCst)
< ZLELL.load(Ordering::SeqCst)
&& ZLELINE.lock().unwrap()[ZLECS.load(Ordering::SeqCst)] != '\n'
{
ZLECS.fetch_add(1, Ordering::SeqCst);
}
}
pub fn move_up() -> bool {
let col = current_column();
let mut line_start = ZLECS.load(Ordering::SeqCst);
while line_start > 0 && ZLELINE.lock().unwrap()[line_start - 1] != '\n' {
line_start -= 1;
}
if line_start == 0 {
return false; }
ZLECS.store(line_start - 1, Ordering::SeqCst);
let mut prev_start = ZLECS.load(Ordering::SeqCst);
while prev_start > 0 && ZLELINE.lock().unwrap()[prev_start - 1] != '\n' {
prev_start -= 1;
}
ZLECS.store(
prev_start + col.min(ZLECS.load(Ordering::SeqCst) - prev_start),
Ordering::SeqCst,
);
true
}
pub fn move_down() -> bool {
let col = current_column();
let mut line_end = ZLECS.load(Ordering::SeqCst);
while line_end < ZLELL.load(Ordering::SeqCst)
&& ZLELINE.lock().unwrap()[line_end] != '\n'
{
line_end += 1;
}
if line_end >= ZLELL.load(Ordering::SeqCst) {
return false; }
ZLECS.store(line_end + 1, Ordering::SeqCst);
let mut next_end = ZLECS.load(Ordering::SeqCst);
while next_end < ZLELL.load(Ordering::SeqCst)
&& ZLELINE.lock().unwrap()[next_end] != '\n'
{
next_end += 1;
}
ZLECS.store(
(ZLECS.load(Ordering::SeqCst) + col).min(next_end),
Ordering::SeqCst,
);
true
}
pub fn current_column() -> usize {
let mut col = 0;
let mut i = ZLECS.load(Ordering::SeqCst);
while i > 0 && ZLELINE.lock().unwrap()[i - 1] != '\n' {
i -= 1;
col += 1;
}
col
}
pub fn current_line() -> usize {
ZLELINE.lock().unwrap()[..ZLECS.load(Ordering::SeqCst)]
.iter()
.filter(|&&c| c == '\n')
.count()
}
pub fn count_lines() -> usize {
ZLELINE
.lock()
.unwrap()
.iter()
.filter(|&&c| c == '\n')
.count()
+ 1
}
#[cfg(test)]
mod alignmultiword_tests {
use super::*;
fn setup_combining(chars: &[char]) {
crate::ported::options::opt_state_set("combiningchars", true);
let mut line = ZLELINE.lock().unwrap();
line.clear();
line.extend_from_slice(chars);
ZLELL.store(chars.len(), Ordering::SeqCst);
}
fn teardown_combining() {
crate::ported::options::opt_state_set("combiningchars", false);
ZLELINE.lock().unwrap().clear();
ZLELL.store(0, Ordering::SeqCst);
}
#[test]
fn alignmultiwordleft_returns_zero_without_combiningchars_option() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
crate::ported::options::opt_state_set("combiningchars", false);
let mut line = ZLELINE.lock().unwrap();
line.clear();
line.extend_from_slice(&['e', '\u{0301}']); ZLELL.store(2, Ordering::SeqCst);
drop(line);
let mut pos = 1usize;
assert_eq!(alignmultiwordleft(&mut pos, 1), 0,
"c:54 — !isset(COMBININGCHARS) short-circuits to 0");
assert_eq!(pos, 1, "pos unchanged");
teardown_combining();
}
#[test]
fn alignmultiwordleft_at_zlell_returns_zero() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
setup_combining(&['a', 'b']);
let mut pos = 2usize; assert_eq!(alignmultiwordleft(&mut pos, 1), 0, "c:54 — at zlell, return 0");
teardown_combining();
}
#[test]
fn alignmultiwordleft_at_zero_returns_zero() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
setup_combining(&['\u{0301}', 'b']); let mut pos = 0usize;
assert_eq!(alignmultiwordleft(&mut pos, 1), 0, "c:54 — loccs==0, return 0");
teardown_combining();
}
#[test]
fn alignmultiwordleft_returns_zero_when_not_on_combining_char() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
setup_combining(&['a', 'b', 'c']); let mut pos = 1usize; assert_eq!(alignmultiwordleft(&mut pos, 1), 0,
"c:58 — !IS_COMBINING(curr) → return 0");
assert_eq!(pos, 1, "pos unchanged");
teardown_combining();
}
#[test]
fn alignmultiwordleft_walks_back_to_base() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
setup_combining(&['a', '\u{0301}', '\u{0303}']);
assert!(crate::ported::zsh_h::isset(crate::ported::zsh_h::COMBININGCHARS),
"setup precondition: isset(COMBININGCHARS) must be true");
assert_eq!(crate::ported::compat::u9_wcwidth('\u{0303}'), 0,
"setup precondition: U+0303 must have width 0 for IS_COMBINING");
let mut pos = 2usize; assert_eq!(alignmultiwordleft(&mut pos, 1), 1,
"c:69 — found base char, return 1");
assert_eq!(pos, 0, "c:68 — pos set to base char index");
teardown_combining();
}
#[test]
fn alignmultiwordleft_setpos_zero_does_not_mutate() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
setup_combining(&['a', '\u{0301}']);
let mut pos = 1usize;
assert_eq!(alignmultiwordleft(&mut pos, 0), 1, "found base");
assert_eq!(pos, 1, "c:67 — setpos==0 skips assignment");
teardown_combining();
}
#[test]
fn alignmultiwordright_walks_forward_over_combining() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
setup_combining(&['a', '\u{0301}', '\u{0303}', 'b']);
let mut pos = 2usize; assert_eq!(alignmultiwordright(&mut pos, 1), 1,
"c:107 — found non-combining char, return 1");
assert_eq!(pos, 3, "c:106 — pos set to next base char index");
teardown_combining();
}
#[test]
fn alignmultiwordright_returns_zero_when_not_in_cluster() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
setup_combining(&['a', 'b']); let mut pos = 1usize;
assert_eq!(alignmultiwordright(&mut pos, 1), 0,
"c:96 — left-align failed → return 0");
assert_eq!(pos, 1, "pos unchanged");
teardown_combining();
}
#[test]
fn alignmultiwordright_runs_off_end_returns_one_with_end_pos() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
setup_combining(&['a', '\u{0301}', '\u{0303}']);
let mut pos = 1usize;
assert_eq!(alignmultiwordright(&mut pos, 1), 1,
"c:114 — fell off end, still return 1");
assert_eq!(pos, 3, "c:113 — pos set to zlell (end-of-buffer)");
teardown_combining();
}
}
#[cfg(test)]
mod region_tests {
use super::*;
#[test]
fn deactivateregion_clears_active() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
REGION_ACTIVE.store(1, Ordering::SeqCst);
let r = deactivateregion();
assert_eq!(r, 0);
assert_eq!(REGION_ACTIVE.load(Ordering::SeqCst), 0);
}
#[test]
fn setmarkcommand_sets_mark_to_cursor() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
ZLECS.store(7, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = 1;
let r = setmarkcommand();
assert_eq!(r, 0);
assert_eq!(
MARK.load(Ordering::SeqCst),
7
);
assert_eq!(REGION_ACTIVE.load(Ordering::SeqCst), 1);
}
#[test]
fn setmarkcommand_negative_mult_deactivates() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
REGION_ACTIVE.store(1, Ordering::SeqCst);
MARK.store(5, Ordering::SeqCst);
ZLECS.store(7, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = -1;
let r = setmarkcommand();
assert_eq!(r, 0);
assert_eq!(REGION_ACTIVE.load(Ordering::SeqCst), 0);
assert_eq!(
MARK.load(Ordering::SeqCst),
5
);
}
#[test]
fn exchangepointandmark_swaps() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "hello world".chars().collect();
ZLELL.store(11, Ordering::SeqCst);
ZLECS.store(3, Ordering::SeqCst);
MARK.store(8, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = 1;
let r = exchangepointandmark();
assert_eq!(r, 0);
assert_eq!(ZLECS.load(Ordering::SeqCst), 8);
assert_eq!(
MARK.load(Ordering::SeqCst),
3
);
assert_eq!(REGION_ACTIVE.load(Ordering::SeqCst), 1);
}
#[test]
fn exchangepointandmark_zero_mult_just_activates() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
ZLECS.store(3, Ordering::SeqCst);
MARK.store(8, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = 0;
let r = exchangepointandmark();
assert_eq!(r, 0);
assert_eq!(ZLECS.load(Ordering::SeqCst), 3);
assert_eq!(
MARK.load(Ordering::SeqCst),
8
);
assert_eq!(REGION_ACTIVE.load(Ordering::SeqCst), 1);
}
#[test]
fn exchangepointandmark_clamps_zlecs_to_zlell() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "hi".chars().collect();
ZLELL.store(2, Ordering::SeqCst);
ZLECS.store(1, Ordering::SeqCst);
MARK.store(99, Ordering::SeqCst); ZMOD.lock().unwrap().mult = 1;
exchangepointandmark();
assert_eq!(ZLECS.load(Ordering::SeqCst), 2);
}
#[test]
fn inccs_increments_zlecs() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "abc".chars().collect();
ZLELL.store(3, Ordering::SeqCst);
ZLECS.store(0, Ordering::SeqCst);
inccs();
assert_eq!(ZLECS.load(Ordering::SeqCst), 1);
inccs();
assert_eq!(ZLECS.load(Ordering::SeqCst), 2);
}
#[test]
fn deccs_decrements_zlecs() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "abc".chars().collect();
ZLELL.store(3, Ordering::SeqCst);
ZLECS.store(2, Ordering::SeqCst);
deccs();
assert_eq!(ZLECS.load(Ordering::SeqCst), 1);
deccs();
assert_eq!(ZLECS.load(Ordering::SeqCst), 0);
}
#[test]
fn incpos_decpos_round_trip() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
let mut p = 5;
incpos(&mut p);
assert_eq!(p, 6);
incpos(&mut p);
assert_eq!(p, 7);
decpos(&mut p);
assert_eq!(p, 6);
}
#[test]
fn forwardchar_moves_zmult_positions() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "hello world".chars().collect();
ZLELL.store(11, Ordering::SeqCst);
ZLECS.store(0, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = 3;
let r = forwardchar();
assert_eq!(r, 0);
assert_eq!(ZLECS.load(Ordering::SeqCst), 3);
}
#[test]
fn forwardchar_stops_at_zlell() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "ab".chars().collect();
ZLELL.store(2, Ordering::SeqCst);
ZLECS.store(0, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = 99;
forwardchar();
assert_eq!(ZLECS.load(Ordering::SeqCst), 2);
}
#[test]
fn backwardchar_moves_zmult_positions() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "hello world".chars().collect();
ZLELL.store(11, Ordering::SeqCst);
ZLECS.store(8, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = 3;
let r = backwardchar();
assert_eq!(r, 0);
assert_eq!(ZLECS.load(Ordering::SeqCst), 5);
}
#[test]
fn backwardchar_stops_at_zero() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "ab".chars().collect();
ZLELL.store(2, Ordering::SeqCst);
ZLECS.store(1, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = 99;
backwardchar();
assert_eq!(ZLECS.load(Ordering::SeqCst), 0);
}
#[test]
fn forwardchar_negative_count_delegates_to_backward() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "hello".chars().collect();
ZLELL.store(5, Ordering::SeqCst);
ZLECS.store(4, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = -2;
forwardchar();
assert_eq!(ZLECS.load(Ordering::SeqCst), 2);
assert_eq!(ZMOD.lock().unwrap().mult, -2);
}
#[test]
fn backwardchar_negative_count_delegates_to_forward() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
*ZLELINE.lock().unwrap() = "hello".chars().collect();
ZLELL.store(5, Ordering::SeqCst);
ZLECS.store(1, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = -2;
backwardchar();
assert_eq!(ZLECS.load(Ordering::SeqCst), 3);
assert_eq!(ZMOD.lock().unwrap().mult, -2);
}
#[test]
fn vibeginningofline_jumps_to_bol() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "abc\ndef\nghi".chars().collect();
ZLELL.store(11, Ordering::SeqCst);
ZLECS.store(9, Ordering::SeqCst); let r = vibeginningofline();
assert_eq!(r, 0);
assert_eq!(ZLECS.load(Ordering::SeqCst), 8); }
#[test]
fn vibackwardchar_stops_at_line_start() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "abc\ndef".chars().collect();
ZLELL.store(7, Ordering::SeqCst);
ZLECS.store(4, Ordering::SeqCst); ZMOD.lock().unwrap().mult = 1;
let r = vibackwardchar();
assert_eq!(r, 1);
assert_eq!(ZLECS.load(Ordering::SeqCst), 4); }
#[test]
fn vibackwardchar_moves_within_line() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
*ZLELINE.lock().unwrap() = "hello world".chars().collect();
ZLELL.store(11, Ordering::SeqCst);
ZLECS.store(8, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = 3;
vibackwardchar();
assert_eq!(ZLECS.load(Ordering::SeqCst), 5);
}
#[test]
fn viforwardchar_stops_at_eol() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "abc\ndef".chars().collect();
ZLELL.store(7, Ordering::SeqCst);
ZLECS.store(3, Ordering::SeqCst); ZMOD.lock().unwrap().mult = 1;
let r = viforwardchar();
assert_eq!(r, 1);
}
#[test]
fn viforwardchar_moves_within_line() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
*ZLELINE.lock().unwrap() = "hello world".chars().collect();
ZLELL.store(11, Ordering::SeqCst);
ZLECS.store(0, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = 3;
viforwardchar();
assert_eq!(ZLECS.load(Ordering::SeqCst), 3);
}
#[test]
fn viforwardchar_clamps_at_findeol() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "ab".chars().collect();
ZLELL.store(2, Ordering::SeqCst);
ZLECS.store(0, Ordering::SeqCst);
ZMOD.lock().unwrap().mult = 99;
viforwardchar();
assert_eq!(ZLECS.load(Ordering::SeqCst), 2);
}
#[test]
fn vifirstnonblank_skips_leading_spaces() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = " hello".chars().collect();
ZLELL.store(8, Ordering::SeqCst);
ZLECS.store(5, Ordering::SeqCst); let r = vifirstnonblank();
assert_eq!(r, 0);
assert_eq!(ZLECS.load(Ordering::SeqCst), 3); }
#[test]
fn vifirstnonblank_skips_tabs() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "\t\t\tfoo".chars().collect();
ZLELL.store(6, Ordering::SeqCst);
ZLECS.store(0, Ordering::SeqCst);
vifirstnonblank();
assert_eq!(ZLECS.load(Ordering::SeqCst), 3);
}
#[test]
fn vifirstnonblank_no_blanks() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "hello".chars().collect();
ZLELL.store(5, Ordering::SeqCst);
ZLECS.store(3, Ordering::SeqCst);
vifirstnonblank();
assert_eq!(ZLECS.load(Ordering::SeqCst), 0);
}
#[test]
fn vifirstnonblank_all_blanks() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = " ".chars().collect();
ZLELL.store(3, Ordering::SeqCst);
ZLECS.store(0, Ordering::SeqCst);
vifirstnonblank();
assert_eq!(ZLECS.load(Ordering::SeqCst), 3);
}
#[test]
fn vifirstnonblank_respects_findbol() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = "abc\n def".chars().collect();
ZLELL.store(10, Ordering::SeqCst);
ZLECS.store(8, Ordering::SeqCst); vifirstnonblank();
assert_eq!(ZLECS.load(Ordering::SeqCst), 7);
}
#[test]
fn vifirstnonblank_skips_wide_whitespace_per_wcsiblank() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
let buf: Vec<char> = vec!['\x0b', '\x0c', '\r', '\u{00A0}', 'x'];
*ZLELINE.lock().unwrap() = buf;
ZLELL.store(5, Ordering::SeqCst);
ZLECS.store(0, Ordering::SeqCst);
vifirstnonblank();
assert_eq!(
ZLECS.load(Ordering::SeqCst),
4,
"c:865 must skip CR/FF/VT/NBSP per wcsiblank"
);
}
#[test]
fn vifirstnonblank_stops_at_newline_per_wcsiblank() {
let _g = crate::test_util::global_state_lock();
let _g = zle_test_setup();
zle_reset();
*ZLELINE.lock().unwrap() = " \n x".chars().collect();
ZLELL.store(6, Ordering::SeqCst);
ZLECS.store(0, Ordering::SeqCst);
vifirstnonblank();
assert_eq!(
ZLECS.load(Ordering::SeqCst),
2,
"wcsiblank excludes '\\n' — cursor must stop at the newline"
);
}
}