use super::zle_main::{ZleChar, ZleString};
#[allow(unused_imports)]
#[allow(unused_imports)]
use crate::ported::zle::zle_main::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_misc::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_hist::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_move::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_word::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_params::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_vi::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_refresh::*;
#[allow(unused_imports)]
use crate::ported::zle::zle_tricky::*;
#[allow(unused_imports)]
use crate::ported::zle::textobjects::*;
#[allow(unused_imports)]
use crate::ported::zle::deltochar::*;
use crate::zle::zle_main::{history, MARK, vibuf, CURCHANGE, KILLRING, KILLRINGMAX, LASTCS, LASTLINE, LASTLL, UNDO_CHANGENO, UNDO_LIMITNO, UNDO_STACK, VISTARTCHANGE, ZLECS, ZLELINE, ZLELL, ZLE_RESET_NEEDED, ZMOD};
pub fn insert_str(s: &str) {
for c in s.chars() {
ZLELINE.lock().unwrap().insert( ZLECS.load(std::sync::atomic::Ordering::SeqCst), c);
ZLECS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
ZLELL.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
pub fn insert_chars(chars: &[ZleChar]) {
for &c in chars {
ZLELINE.lock().unwrap().insert( ZLECS.load(std::sync::atomic::Ordering::SeqCst), c);
ZLECS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
ZLELL.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
pub fn delete_chars(n: usize) {
let n = n.min( ZLELL.load(std::sync::atomic::Ordering::SeqCst) - ZLECS.load(std::sync::atomic::Ordering::SeqCst));
for _ in 0..n {
if ZLECS.load(std::sync::atomic::Ordering::SeqCst) < ZLELL.load(std::sync::atomic::Ordering::SeqCst) {
ZLELINE.lock().unwrap().remove( ZLECS.load(std::sync::atomic::Ordering::SeqCst));
ZLELL.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
}
}
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
pub fn backspace_chars(n: usize) {
let n = n.min( ZLECS.load(std::sync::atomic::Ordering::SeqCst));
for _ in 0..n {
if ZLECS.load(std::sync::atomic::Ordering::SeqCst) > 0 {
ZLECS.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
ZLELINE.lock().unwrap().remove( ZLECS.load(std::sync::atomic::Ordering::SeqCst));
ZLELL.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
}
}
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
pub fn get_line() -> String {
ZLELINE.lock().unwrap().iter().collect()
}
pub fn set_line_keep_cursor(s: &str) {
* ZLELINE.lock().unwrap() = s.chars().collect();
ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
ZLECS.store( ZLECS.load(std::sync::atomic::Ordering::SeqCst).min( ZLELL.load(std::sync::atomic::Ordering::SeqCst)), std::sync::atomic::Ordering::SeqCst);
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
pub fn clear_line() {
ZLELINE.lock().unwrap().clear();
ZLELL.store(0, std::sync::atomic::Ordering::SeqCst);
ZLECS.store(0, std::sync::atomic::Ordering::SeqCst);
MARK.store(0, std::sync::atomic::Ordering::SeqCst);
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
pub fn get_region() -> Vec<ZleChar> {
let (start, end) = if ZLECS.load(std::sync::atomic::Ordering::SeqCst) < MARK.load(std::sync::atomic::Ordering::SeqCst) {
( ZLECS.load(std::sync::atomic::Ordering::SeqCst), MARK.load(std::sync::atomic::Ordering::SeqCst))
} else {
( MARK.load(std::sync::atomic::Ordering::SeqCst), ZLECS.load(std::sync::atomic::Ordering::SeqCst))
};
ZLELINE.lock().unwrap()[start..end].to_vec()
}
pub fn cut_to_buffer(buf: usize, append: bool) {
if buf < vibuf().lock().unwrap().len() {
let (start, end) = if ZLECS.load(std::sync::atomic::Ordering::SeqCst) < MARK.load(std::sync::atomic::Ordering::SeqCst) {
( ZLECS.load(std::sync::atomic::Ordering::SeqCst), MARK.load(std::sync::atomic::Ordering::SeqCst))
} else {
( MARK.load(std::sync::atomic::Ordering::SeqCst), ZLECS.load(std::sync::atomic::Ordering::SeqCst))
};
let text: ZleString = ZLELINE.lock().unwrap()[start..end].to_vec();
if append {
vibuf().lock().unwrap()[buf].extend(text);
} else {
vibuf().lock().unwrap()[buf] = text;
}
}
}
pub fn paste_from_buffer(buf: usize, after: bool) {
if buf < vibuf().lock().unwrap().len() {
let text = vibuf().lock().unwrap()[buf].clone();
if !text.is_empty() {
if after && ZLECS.load(std::sync::atomic::Ordering::SeqCst) < ZLELL.load(std::sync::atomic::Ordering::SeqCst) {
ZLECS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
insert_chars(&text);
}
}
}
pub fn pastebuf(s: &str) -> String {
s.to_string()
}
pub fn unmetafy(s: &str) -> String {
s.to_string()
}
pub fn set_last_line() {
setlastline();
}
pub fn handle_feep() {
print!("\x07"); }
pub fn get_zle_query() -> bool {
let c = match getfullchar(false) {
Some(c) => c,
None => return false, };
let resolved = if c == '\t' {
'y'
} else if c.is_control() {
'n'
} else {
c.to_ascii_lowercase()
};
if resolved != '\n' {
use std::sync::atomic::Ordering;
let fd = crate::ported::init::SHTTY.load(Ordering::Relaxed);
let out = if fd >= 0 { fd } else { 1 };
let mut buf = [0u8; 4];
let s = resolved.encode_utf8(&mut buf);
let _ = crate::ported::utils::write_loop(out, s.as_bytes());
}
resolved == 'y'
}
pub fn handle_suffix() {
call_hook("handle-suffix", None);
}
pub fn set_line(s: &str) {
* ZLELINE.lock().unwrap() = s.chars().collect();
ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
ZLECS.store( ZLELL.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
#[derive(Debug, Clone)]
#[allow(non_camel_case_types)]
pub struct zle_position { pub cs: usize,
pub mk: usize,
pub ll: usize,
}
pub fn bindztrdup(str: &[u8]) -> String {
let mut buf = String::new();
for &b in str {
let mut c = b;
if c & 0x80 != 0 {
buf.push('\\');
buf.push('M');
buf.push('-');
c &= 0x7f;
}
if c < 32 || c == 0x7f {
buf.push('^');
c ^= 64;
}
if c == b'\\' || c == b'^' {
buf.push('\\');
}
buf.push(c as char);
}
buf
}
pub fn printbind(seq: &[u8]) -> String {
let mut result = String::new();
for &b in seq {
match b {
0x1b => result.push_str("^["),
0..=31 => {
result.push('^');
result.push((b + 64) as char);
}
127 => result.push_str("^?"),
128..=159 => {
result.push_str("^[^");
result.push((b - 64) as char);
}
_ => result.push(b as char),
}
}
result
}
pub fn call_hook(name: &str, arg: Option<&str>) {
PENDING_HOOKS.lock().unwrap()
.push((name.to_string(), arg.map(|s| s.to_string())));
}
pub fn drain_hooks() -> Vec<(String, Option<String>)> {
std::mem::take(&mut * PENDING_HOOKS.lock().unwrap())
}
#[cfg(test)]
mod tests_hooks {
use super::*;
#[test]
fn call_hook_queues_for_host_dispatch() {
let _g = zle_test_setup();
call_hook("zle-line-init", None);
call_hook("zle-keymap-select", Some("vicmd"));
let drained = drain_hooks();
assert_eq!(drained.len(), 2);
assert_eq!(drained[0], ("zle-line-init".to_string(), None));
assert_eq!(
drained[1],
("zle-keymap-select".to_string(), Some("vicmd".to_string()))
);
assert!(drain_hooks().is_empty());
}
#[test]
fn redrawhook_queues_pre_redraw_hook() {
let _g = zle_test_setup();
redrawhook();
let drained = drain_hooks();
assert_eq!(drained, vec![("zle-line-pre-redraw".to_string(), None)]);
}
#[test]
fn reexpandprompt_re_runs_expansion_against_raw_templates() {
let _g = zle_test_setup();
* RAW_LP.lock().unwrap() = "%% > ".to_string();
* RAW_RP.lock().unwrap() = "[%%]".to_string();
reexpandprompt();
assert_eq!(prompt(), "% > ");
assert_eq!(rprompt(), "[%]");
}
}
#[cfg(test)]
mod tests_bindkey_format {
use crate::zle::zle_main::zle_test_setup;
use super::bindztrdup;
use super::printbind;
#[test]
fn bind_ztrdup_emits_caret_form_for_control_chars() {
let _g = zle_test_setup();
assert_eq!(bindztrdup(b"\x01"), "^A");
assert_eq!(bindztrdup(b"\x1f"), "^_");
assert_eq!(bindztrdup(b"\x7f"), "^?");
}
#[test]
fn bind_ztrdup_escapes_backslash_and_caret() {
let _g = zle_test_setup();
assert_eq!(bindztrdup(b"\\"), "\\\\");
assert_eq!(bindztrdup(b"^"), "\\^");
}
#[test]
fn bind_ztrdup_handles_high_bit_as_meta() {
let _g = zle_test_setup();
assert_eq!(bindztrdup(b"\xC1"), "\\M-A");
}
#[test]
fn printbind_caret_form_matches_describe_key_output() {
let _g = zle_test_setup();
assert_eq!(printbind(b"\x01"), "^A");
assert_eq!(printbind(b"\x1b"), "^[");
}
}
pub fn setlastline() {
let snapshot = ZLELINE.lock().unwrap().clone();
let mut ll = LASTLINE.lock().unwrap();
ll.clear();
ll.extend_from_slice(&snapshot);
drop(ll);
LASTLL.store( ZLELL.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
LASTCS.store( ZLECS.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
}
pub fn mkundoent() { if LASTLL.load(std::sync::atomic::Ordering::SeqCst) == ZLELL.load(std::sync::atomic::Ordering::SeqCst) && LASTLINE.lock().unwrap()[.. LASTLL.load(std::sync::atomic::Ordering::SeqCst)] == ZLELINE.lock().unwrap()[.. ZLELL.load(std::sync::atomic::Ordering::SeqCst)]
{
LASTCS.store( ZLECS.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
return;
}
let sh = LASTLL.load(std::sync::atomic::Ordering::SeqCst).min( ZLELL.load(std::sync::atomic::Ordering::SeqCst));
let mut pre = 0usize;
while pre < sh && ZLELINE.lock().unwrap()[pre] == LASTLINE.lock().unwrap()[pre] {
pre += 1;
}
let mut suf = 0usize;
while suf < sh - pre
&& ZLELINE.lock().unwrap()[ ZLELL.load(std::sync::atomic::Ordering::SeqCst) - 1 - suf] == LASTLINE.lock().unwrap()[ LASTLL.load(std::sync::atomic::Ordering::SeqCst) - 1 - suf]
{
suf += 1;
}
let del: ZleString = if suf + pre == LASTLL.load(std::sync::atomic::Ordering::SeqCst) {
Vec::new()
} else {
LASTLINE.lock().unwrap()[pre.. LASTLL.load(std::sync::atomic::Ordering::SeqCst) - suf].to_vec()
};
let ins: ZleString = if suf + pre == ZLELL.load(std::sync::atomic::Ordering::SeqCst) {
Vec::new()
} else {
ZLELINE.lock().unwrap()[pre.. ZLELL.load(std::sync::atomic::Ordering::SeqCst) - suf].to_vec()
};
UNDO_CHANGENO.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let del_str: String = del.iter().collect();
let ins_str: String = ins.iter().collect();
let dell = del_str.chars().count() as i32;
let insl = ins_str.chars().count() as i32;
let ch = crate::ported::zle::zle_h::change {
prev: None,
next: None,
flags: 0,
hist: history().lock().unwrap().cursor as i32,
off: pre as i32,
del: del_str,
dell,
ins: ins_str,
insl,
old_cs: LASTCS.load(std::sync::atomic::Ordering::SeqCst) as i32,
new_cs: ZLECS.load(std::sync::atomic::Ordering::SeqCst) as i32,
changeno: UNDO_CHANGENO.load(std::sync::atomic::Ordering::SeqCst) as i64,
};
UNDO_STACK.lock().unwrap().truncate( CURCHANGE.load(std::sync::atomic::Ordering::SeqCst));
UNDO_STACK.lock().unwrap().push(ch);
CURCHANGE.store( UNDO_STACK.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
}
pub fn handleundo() { setlastline();
}
pub fn unapply_change(idx: usize) -> bool {
if idx >= UNDO_STACK.lock().unwrap().len() {
return false;
}
let (off, dell, insl, old_cs);
let del_vec;
let ins_len;
{
let ch = & UNDO_STACK.lock().unwrap()[idx];
off = ch.off as usize;
dell = ch.dell as usize;
insl = ch.insl as usize;
ins_len = insl;
old_cs = ch.old_cs as usize;
del_vec = ch.del.chars().collect::<Vec<char>>();
}
let _ = ins_len;
ZLECS.store(off, std::sync::atomic::Ordering::SeqCst);
if insl > 0 {
ZLELINE.lock().unwrap().drain(off..off + insl);
}
if dell > 0 {
for (i, c) in del_vec.into_iter().enumerate() {
ZLELINE.lock().unwrap().insert(off + i, c);
}
}
ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
ZLECS.store(old_cs.min( ZLELL.load(std::sync::atomic::Ordering::SeqCst)), std::sync::atomic::Ordering::SeqCst);
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
true
}
pub fn apply_change(idx: usize) -> bool {
if idx >= UNDO_STACK.lock().unwrap().len() {
return false;
}
let (off, dell, insl, new_cs);
let ins_vec;
{
let ch = & UNDO_STACK.lock().unwrap()[idx];
off = ch.off as usize;
dell = ch.dell as usize;
insl = ch.insl as usize;
new_cs = ch.new_cs as usize;
ins_vec = ch.ins.chars().collect::<Vec<char>>();
}
ZLECS.store(off, std::sync::atomic::Ordering::SeqCst);
if dell > 0 {
ZLELINE.lock().unwrap().drain(off..off + dell);
}
if insl > 0 {
for (i, c) in ins_vec.into_iter().enumerate() {
ZLELINE.lock().unwrap().insert(off + i, c);
}
}
ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
ZLECS.store(new_cs.min( ZLELL.load(std::sync::atomic::Ordering::SeqCst)), std::sync::atomic::Ordering::SeqCst);
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
true
}
pub fn undo_widget() -> i32 { mkundoent();
if CURCHANGE.load(std::sync::atomic::Ordering::SeqCst) == 0 {
return 1;
}
let prev_idx = CURCHANGE.load(std::sync::atomic::Ordering::SeqCst) - 1;
if UNDO_STACK.lock().unwrap()[prev_idx].changeno <= UNDO_LIMITNO.load(std::sync::atomic::Ordering::SeqCst) as i64 {
return 1;
}
if unapply_change(prev_idx) {
CURCHANGE.store(prev_idx, std::sync::atomic::Ordering::SeqCst);
}
setlastline();
0
}
pub fn redo_widget() -> i32 { mkundoent();
if CURCHANGE.load(std::sync::atomic::Ordering::SeqCst) >= UNDO_STACK.lock().unwrap().len() {
return 1;
}
if apply_change( CURCHANGE.load(std::sync::atomic::Ordering::SeqCst)) {
CURCHANGE.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
setlastline();
0
}
pub fn applychange(ch: i32) -> i32 { use crate::ported::zle::zle_h::{CH_NEXT, CH_PREV};
let idx = ch as usize;
if idx >= UNDO_STACK.lock().unwrap().len() { return 0; }
let change = UNDO_STACK.lock().unwrap()[idx].clone();
let off = change.off as usize;
let del_n = change.dell as usize;
if off + del_n <= ZLELINE.lock().unwrap().len() {
ZLELINE.lock().unwrap().drain(off..off + del_n); }
for (i, c) in change.ins.chars().enumerate() {
if off + i <= ZLELINE.lock().unwrap().len() {
ZLELINE.lock().unwrap().insert(off + i, c);
} else {
ZLELINE.lock().unwrap().push(c);
}
}
ZLECS.store(change.new_cs as usize, std::sync::atomic::Ordering::SeqCst); ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
if change.flags & CH_NEXT != 0 { 1 } else { 0 }
}
pub fn backdel(ct: i32, _flags: i32) { let ct = ct as usize;
if ct == 0 || ZLECS.load(std::sync::atomic::Ordering::SeqCst) == 0 { return; }
let take_n = ct.min( ZLECS.load(std::sync::atomic::Ordering::SeqCst));
let start = ZLECS.load(std::sync::atomic::Ordering::SeqCst) - take_n;
ZLELINE.lock().unwrap().drain(start.. ZLECS.load(std::sync::atomic::Ordering::SeqCst)); ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
ZLECS.store(start, std::sync::atomic::Ordering::SeqCst);
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst); }
pub fn backkill(ct: i32, flags: i32) { let ct = ct as usize;
if ct == 0 || ZLECS.load(std::sync::atomic::Ordering::SeqCst) == 0 { return; }
let _ = flags; let take_n = ct.min( ZLECS.load(std::sync::atomic::Ordering::SeqCst));
let start = ZLECS.load(std::sync::atomic::Ordering::SeqCst) - take_n;
let cut_chars: Vec<char> = ZLELINE.lock().unwrap().drain(start.. ZLECS.load(std::sync::atomic::Ordering::SeqCst)).collect(); ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
ZLECS.store(start, std::sync::atomic::Ordering::SeqCst);
KILLRING.lock().unwrap().push_front(cut_chars);
if KILLRING.lock().unwrap().len() > KILLRINGMAX.load(std::sync::atomic::Ordering::SeqCst) {
KILLRING.lock().unwrap().pop_back();
}
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst); }
pub fn cut(i: i32, ct: i32, dir: i32) -> i32 {
if ct <= 0 || i < 0 {
return 0;
}
let start = i as usize;
let end = (start + ct as usize).min( ZLELINE.lock().unwrap().len());
if start >= end {
return 0;
}
let chunk: Vec<char> = ZLELINE.lock().unwrap()[start..end].to_vec();
cuttext(&chunk, dir);
0
}
pub fn cuttext(txt: &[char], dir: i32) {
use crate::ported::zle::zle_h::{MOD_MULT, MOD_TMULT, MOD_VIBUF, MOD_VIAPP, MOD_NEG, MOD_NULL, MOD_CHAR, MOD_LINE, MOD_PRI, MOD_CLIP, MOD_OSSEL};
let chars: Vec<char> = txt.to_vec();
if ZMOD.lock().unwrap().flags & MOD_VIBUF != 0 { let idx = ZMOD.lock().unwrap().vibuf as usize;
if idx < vibuf().lock().unwrap().len() {
if dir != 0 {
vibuf().lock().unwrap()[idx] = chars;
} else {
vibuf().lock().unwrap()[idx].extend(chars);
}
}
} else {
KILLRING.lock().unwrap().push_front(chars); let max = KILLRINGMAX.load(std::sync::atomic::Ordering::SeqCst);
if KILLRING.lock().unwrap().len() > max {
KILLRING.lock().unwrap().pop_back();
}
}
}
pub fn findbol() -> usize { let mut x = ZLECS.load(std::sync::atomic::Ordering::SeqCst); while x > 0 && ZLELINE.lock().unwrap().get(x - 1) != Some(&'\n') { x -= 1; }
x }
pub fn findeol() -> usize { let mut x = ZLECS.load(std::sync::atomic::Ordering::SeqCst); while x != ZLELL.load(std::sync::atomic::Ordering::SeqCst) && ZLELINE.lock().unwrap().get(x) != Some(&'\n') { x += 1; }
x }
pub fn findline() -> (usize, usize) { (findbol(), findeol()) }
pub fn foredel(ct: i32, _flags: i32) { let ct = ct as usize;
if ct == 0 || ZLECS.load(std::sync::atomic::Ordering::SeqCst) >= ZLELL.load(std::sync::atomic::Ordering::SeqCst) { return; }
let take_n = ct.min( ZLELL.load(std::sync::atomic::Ordering::SeqCst) - ZLECS.load(std::sync::atomic::Ordering::SeqCst));
let i = ZLECS.load(std::sync::atomic::Ordering::SeqCst);
ZLELINE.lock().unwrap().drain(i..i + take_n); ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst); }
pub fn forekill(ct: i32, flags: i32) { let ct = ct as usize;
if ct == 0 || ZLECS.load(std::sync::atomic::Ordering::SeqCst) >= ZLELL.load(std::sync::atomic::Ordering::SeqCst) { return; }
let _ = flags; let take_n = ct.min( ZLELL.load(std::sync::atomic::Ordering::SeqCst) - ZLECS.load(std::sync::atomic::Ordering::SeqCst));
let i = ZLECS.load(std::sync::atomic::Ordering::SeqCst);
let cut_chars: Vec<char> = ZLELINE.lock().unwrap().drain(i..i + take_n).collect(); ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
KILLRING.lock().unwrap().push_front(cut_chars);
if KILLRING.lock().unwrap().len() > KILLRINGMAX.load(std::sync::atomic::Ordering::SeqCst) {
KILLRING.lock().unwrap().pop_back();
}
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst); }
pub fn free_region_highlights_memos() { }
pub fn freechanges() { }
pub fn freeundo() { }
pub fn get_undo_current_change() -> i64 { -1
}
pub fn get_undo_limit_change() -> i64 { -1
}
pub fn getzlequery() -> i32 { -1
}
pub fn handlefeep() -> i32 { crate::ported::utils::zbeep(); 0 }
pub fn handlesuffix(c: i32) -> i32 { use std::sync::atomic::Ordering;
use crate::ported::zle::zle_misc::SUFFIXLEN;
let _ = c;
let len = SUFFIXLEN.load(Ordering::SeqCst);
if len > 0 {
SUFFIXLEN.store(0, Ordering::SeqCst);
}
0
}
pub fn initundo() { freeundo();
}
pub fn mergeundo() { use crate::ported::zle::zle_h::{CH_NEXT, CH_PREV};
if CURCHANGE.load(std::sync::atomic::Ordering::SeqCst) == 0 { return; }
let mut current = CURCHANGE.load(std::sync::atomic::Ordering::SeqCst) - 1; while current > 0
&& UNDO_STACK.lock().unwrap()[current].changeno > VISTARTCHANGE.load(std::sync::atomic::Ordering::SeqCst) as i64 + 1
{
UNDO_STACK.lock().unwrap()[current].flags |= CH_PREV; UNDO_STACK.lock().unwrap()[current - 1].flags |= CH_NEXT; current -= 1;
}
VISTARTCHANGE.store(u64::MAX, std::sync::atomic::Ordering::SeqCst); }
pub fn redo() -> i32 { use crate::ported::zle::zle_h::{CH_NEXT, CH_PREV};
loop {
if CURCHANGE.load(std::sync::atomic::Ordering::SeqCst) >= UNDO_STACK.lock().unwrap().len() { return 1; } let cur_idx = CURCHANGE.load(std::sync::atomic::Ordering::SeqCst);
if applychange(cur_idx as i32) == 0 { break; } CURCHANGE.store(cur_idx + 1, std::sync::atomic::Ordering::SeqCst);
let has_next = UNDO_STACK.lock().unwrap().get(cur_idx)
.map(|c| c.flags & CH_NEXT != 0)
.unwrap_or(false);
if !has_next { break; } }
CURCHANGE.fetch_add(1, std::sync::atomic::Ordering::SeqCst); 0 }
pub fn set_undo_limit_change(_n: i64) -> i32 { 0
}
pub fn setline(s: &str, flags: i32) {
ZLELINE.lock().unwrap().clear();
ZLELINE.lock().unwrap().extend(s.chars());
ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
if flags & 1 == 0 {
ZLECS.store( ZLELL.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst); }
ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
pub fn shiftchars(to: i32, cnt: i32) { let to = to as usize;
let cnt = cnt as usize;
if to + cnt > ZLELINE.lock().unwrap().len() {
return;
}
ZLELINE.lock().unwrap().drain(to..to + cnt);
ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
}
pub fn showmsg(msg: &str) { use std::sync::atomic::Ordering;
let fd = crate::ported::init::SHTTY.load(Ordering::Relaxed);
let out = if fd >= 0 { fd } else { 2 };
let _ = crate::ported::utils::write_loop(out, msg.as_bytes());
let _ = crate::ported::utils::write_loop(out, b"\n");
}
pub fn sizeline(sz: usize) { let mut __g_zleline = ZLELINE.lock().unwrap();
let cur_len = __g_zleline.len();
if sz > cur_len {
__g_zleline.reserve(sz - cur_len + 256);
}
}
pub fn spaceinline(ct: i32) { if ct <= 0 {
return;
}
let ct = ct as usize;
for _ in 0..ct {
ZLELINE.lock().unwrap().insert( ZLECS.load(std::sync::atomic::Ordering::SeqCst), '\0');
}
ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
}
pub fn splitundo() -> i32 { if VISTARTCHANGE.load(std::sync::atomic::Ordering::SeqCst) != u64::MAX { mergeundo(); VISTARTCHANGE.store( UNDO_CHANGENO.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst); }
handleundo(); 0 }
pub fn stringaszleline(s: &str) -> Vec<char> { let mut out = Vec::new();
let bytes = s.as_bytes();
let mut i = 0;
while i < bytes.len() {
let b = bytes[i];
if b == 0x83 && i + 1 < bytes.len() { i += 1;
out.push((bytes[i] ^ 32) as char);
} else {
out.push(b as char);
}
i += 1;
}
out
}
pub fn unapplychange(ch: i32) -> i32 { use crate::ported::zle::zle_h::{CH_NEXT, CH_PREV};
let idx = ch as usize;
if idx >= UNDO_STACK.lock().unwrap().len() { return 0; }
let change = UNDO_STACK.lock().unwrap()[idx].clone();
let off = change.off as usize;
let ins_n = change.insl as usize;
if off + ins_n <= ZLELINE.lock().unwrap().len() {
ZLELINE.lock().unwrap().drain(off..off + ins_n); }
for (i, c) in change.del.chars().enumerate() {
if off + i <= ZLELINE.lock().unwrap().len() {
ZLELINE.lock().unwrap().insert(off + i, c);
} else {
ZLELINE.lock().unwrap().push(c);
}
}
ZLECS.store(change.old_cs as usize, std::sync::atomic::Ordering::SeqCst); ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
if change.flags & CH_PREV != 0 { 1 } else { 0 }
}
pub fn undo(args: &[String]) -> i32 { use crate::ported::zle::zle_h::{CH_NEXT, CH_PREV};
let last_change: i64 = if !args.is_empty() { args[0].parse().unwrap_or(-1)
} else {
-1
};
loop {
if CURCHANGE.load(std::sync::atomic::Ordering::SeqCst) == 0 { return 1; } let prev_idx = CURCHANGE.load(std::sync::atomic::Ordering::SeqCst) - 1;
let prev_chno = UNDO_STACK.lock().unwrap()[prev_idx].changeno as i64;
if prev_chno <= last_change { break; } if (prev_chno as u64) <= UNDO_LIMITNO.load(std::sync::atomic::Ordering::SeqCst) && args.is_empty() { return 1;
}
if unapplychange(prev_idx as i32) == 0 { if last_change >= 0 {
unapplychange(prev_idx as i32); CURCHANGE.store(prev_idx, std::sync::atomic::Ordering::SeqCst); }
} else {
CURCHANGE.store(prev_idx, std::sync::atomic::Ordering::SeqCst); }
let has_prev = UNDO_STACK.lock().unwrap().get(prev_idx)
.map(|c| c.flags & CH_PREV != 0)
.unwrap_or(false);
if !(last_change >= 0 || has_prev) { break; } }
0 }
pub fn viundochange( args: &[String]) -> i32 {
handleundo(); if CURCHANGE.load(std::sync::atomic::Ordering::SeqCst) < UNDO_STACK.lock().unwrap().len() { while CURCHANGE.load(std::sync::atomic::Ordering::SeqCst) < UNDO_STACK.lock().unwrap().len() { let idx = CURCHANGE.load(std::sync::atomic::Ordering::SeqCst);
applychange(idx as i32); CURCHANGE.store(idx + 1, std::sync::atomic::Ordering::SeqCst); }
0 } else {
undo(args) }
}
#[derive(Debug, Clone)]
pub struct ZlePosition { pub cs: usize, pub mk: usize, pub ll: usize, }
pub static ZLE_POSITIONS: std::sync::Mutex<Vec<ZlePosition>> = std::sync::Mutex::new(Vec::new());
pub fn zle_save_positions() { let pos = ZlePosition { mk: MARK.load(std::sync::atomic::Ordering::SeqCst), cs: ZLECS.load(std::sync::atomic::Ordering::SeqCst), ll: ZLELL.load(std::sync::atomic::Ordering::SeqCst), };
if let Ok(mut s) = ZLE_POSITIONS.lock() { s.push(pos);
}
}
pub fn zle_restore_positions() { if let Ok(mut s) = ZLE_POSITIONS.lock() {
if let Some(oldpos) = s.pop() { MARK.store(oldpos.mk, std::sync::atomic::Ordering::SeqCst); ZLECS.store(oldpos.cs.min( ZLELL.load(std::sync::atomic::Ordering::SeqCst)), std::sync::atomic::Ordering::SeqCst); ZLELL.store(oldpos.ll, std::sync::atomic::Ordering::SeqCst); }
}
}
pub fn zle_free_positions() { if let Ok(mut s) = ZLE_POSITIONS.lock() {
s.pop(); }
}
pub fn zleaddtoline(ch: i32) { ZLELINE.lock().unwrap().push(ch as u8 as char);
ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
}
pub fn zlecallhook(name: &str, arg: Option<&str>) { use std::sync::atomic::Ordering;
use crate::ported::utils::errflag;
use crate::ported::zsh_h::ERRFLAG_INT;
use crate::ported::builtin::RETFLAG;
if !crate::ported::zle::zle_thingy::rthingy_nocreate(name) {
return;
}
let saverrflag = errflag.load(Ordering::Relaxed);
let savretflag = RETFLAG.load(Ordering::Relaxed);
let args: Vec<String> = match arg {
Some(a) => vec![a.to_string()],
None => Vec::new(),
};
let _ = crate::ported::zle::zle_main::execzlefunc(name, &args);
crate::ported::zle::zle_thingy::unrefthingy(name);
let cur_errflag = errflag.load(Ordering::Relaxed);
errflag.store(saverrflag | (cur_errflag & ERRFLAG_INT), Ordering::Relaxed);
RETFLAG.store(savretflag, Ordering::Relaxed); }
pub fn zlecharasstring(inchar: char, buf: &mut String) -> i32 { let start = buf.len();
buf.push(inchar);
(buf.len() - start) as i32
}
pub fn zlegetline( ll: &mut usize, cs: &mut usize) -> Vec<char> {
*ll = ZLELL.load(std::sync::atomic::Ordering::SeqCst);
*cs = ZLECS.load(std::sync::atomic::Ordering::SeqCst);
ZLELINE.lock().unwrap().clone()
}
pub fn zlelineasstring(line: &[char], ll: usize, _flags: i32) -> String { line.iter().take(ll).collect()
}
#[cfg(test)]
mod findbol_findeol_tests {
use crate::zle::zle_main::zle_test_setup;
use super::*;
fn zle_with(line: &str, cs: usize) {
zle_reset();
* ZLELINE.lock().unwrap() = line.chars().collect();
ZLELL.store( ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
ZLECS.store(cs, std::sync::atomic::Ordering::SeqCst);
}
#[test]
fn findbol_no_newline_returns_zero() {
let _g = zle_test_setup();
let z = zle_with("hello world", 7);
assert_eq!(findbol(), 0);
}
#[test]
fn findbol_finds_preceding_newline() {
let _g = zle_test_setup();
let z = zle_with("abc\ndef\nghi", 9);
assert_eq!(findbol(), 8);
}
#[test]
fn findbol_at_start_returns_zero() {
let _g = zle_test_setup();
let z = zle_with("anything", 0);
assert_eq!(findbol(), 0);
}
#[test]
fn findeol_no_newline_returns_end() {
let _g = zle_test_setup();
let z = zle_with("hello world", 0);
assert_eq!(findeol(), 11);
}
#[test]
fn findeol_finds_next_newline() {
let _g = zle_test_setup();
let z = zle_with("abc\ndef", 0);
assert_eq!(findeol(), 3);
}
#[test]
fn findeol_at_end_returns_zlell() {
let _g = zle_test_setup();
let z = zle_with("hello", 5);
assert_eq!(findeol(), 5);
}
#[test]
fn findline_returns_bol_eol_pair() {
let _g = zle_test_setup();
let z = zle_with("abc\ndef\nghi", 5);
let (bol, eol) = findline();
assert_eq!(bol, 4);
assert_eq!(eol, 7);
}
}