use std::sync::Mutex;
#[derive(Clone)]
pub struct ZleParamSnapshot {
buffer: String,
lbuffer: String,
rbuffer: String,
cursor: i64,
predisplay: String,
postdisplay: String,
region_highlight: Vec<String>,
mark: i64,
region_active: i64,
cutbuffer: String,
killring: Vec<String>,
}
static ZLE_PARAM_SNAPSHOT: Mutex<Option<ZleParamSnapshot>> = Mutex::new(None);
#[allow(clippy::too_many_arguments)]
pub fn arm_snapshot(
buffer: String,
lbuffer: String,
rbuffer: String,
cursor: i64,
predisplay: String,
postdisplay: String,
region_highlight: Vec<String>,
mark: i64,
region_active: i64,
cutbuffer: String,
killring: Vec<String>,
) {
*ZLE_PARAM_SNAPSHOT.lock().unwrap() = Some(ZleParamSnapshot {
buffer,
lbuffer,
rbuffer,
cursor,
predisplay,
postdisplay,
region_highlight,
mark,
region_active,
cutbuffer,
killring,
});
}
pub fn clear_snapshot() {
*ZLE_PARAM_SNAPSHOT.lock().unwrap() = None;
}
pub fn active() -> bool {
ZLE_PARAM_SNAPSHOT.lock().unwrap().is_some()
}
thread_local! {
static IN_LIVE_WRITE: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
}
pub fn in_live_write() -> bool {
IN_LIVE_WRITE.with(|c| c.get())
}
pub fn live_write(name: &str, val: &str) -> bool {
if !active() || in_live_write() {
return false;
}
use crate::ported::zle::zle_params as zp;
match name {
"BUFFER" => {
zp::set_buffer(val);
let len = val.chars().count();
if zp::get_cursor() > len {
zp::set_cursor(len);
}
}
"LBUFFER" => zp::set_lbuffer(val), "RBUFFER" => zp::set_rbuffer(val), "CURSOR" => {
let len = zp::get_buffer().chars().count() as i64;
let n = val.trim().parse::<i64>().unwrap_or(0).clamp(0, len);
zp::set_cursor(n as usize);
}
_ => return false,
}
IN_LIVE_WRITE.with(|c| c.set(true));
let buffer = zp::get_buffer();
let lbuffer = zp::get_lbuffer();
let rbuffer = zp::get_rbuffer();
let cursor = zp::get_cursor() as i64;
let _ = crate::ported::params::setsparam("BUFFER", &buffer);
let _ = crate::ported::params::setsparam("LBUFFER", &lbuffer);
let _ = crate::ported::params::setsparam("RBUFFER", &rbuffer);
let _ = crate::ported::params::setiparam("CURSOR", cursor);
IN_LIVE_WRITE.with(|c| c.set(false));
if let Some(snap) = ZLE_PARAM_SNAPSHOT.lock().unwrap().as_mut() {
snap.buffer = buffer;
snap.lbuffer = lbuffer;
snap.rbuffer = rbuffer;
snap.cursor = cursor;
}
true
}
pub fn sync_from_paramtab() {
let snap = ZLE_PARAM_SNAPSHOT.lock().unwrap().clone();
let Some(snap) = snap else {
return;
};
let post_buf = crate::ported::params::getsparam("BUFFER").unwrap_or_default();
let post_lbuf = crate::ported::params::getsparam("LBUFFER").unwrap_or_default();
let post_rbuf = crate::ported::params::getsparam("RBUFFER").unwrap_or_default();
let post_cur = crate::ported::params::getiparam("CURSOR");
if post_buf != snap.buffer {
crate::ported::zle::zle_params::set_buffer(&post_buf);
} else if post_lbuf != snap.lbuffer || post_rbuf != snap.rbuffer {
let joined = format!("{}{}", post_lbuf, post_rbuf);
crate::ported::zle::zle_params::set_buffer(&joined);
crate::ported::zle::zle_params::set_cursor(post_lbuf.chars().count());
}
if post_cur != snap.cursor && post_cur >= 0 {
crate::ported::zle::zle_params::set_cursor(post_cur as usize);
}
let post_predisp = crate::ported::params::getsparam("PREDISPLAY").unwrap_or_default();
if post_predisp != snap.predisplay {
crate::ported::zle::zle_params::set_predisplay(Some(&post_predisp));
}
let post_postdisp = crate::ported::params::getsparam("POSTDISPLAY").unwrap_or_default();
if post_postdisp != snap.postdisplay {
crate::ported::zle::zle_params::set_postdisplay(Some(&post_postdisp));
}
let post_rh = crate::ported::params::getaparam("region_highlight").unwrap_or_default();
if post_rh != snap.region_highlight {
crate::ported::zle::zle_refresh::set_region_highlight(Some(&post_rh));
}
let post_mark = crate::ported::params::getiparam("MARK");
if post_mark != snap.mark && post_mark >= 0 {
crate::ported::zle::zle_params::set_mark(post_mark as usize);
}
let post_ra = crate::ported::params::getiparam("REGION_ACTIVE");
if post_ra != snap.region_active {
let _ = crate::ported::zle::zle_params::set_region_active(post_ra);
}
let post_cutbuf = crate::ported::params::getsparam("CUTBUFFER").unwrap_or_default();
if post_cutbuf != snap.cutbuffer {
crate::ported::zle::zle_params::set_cutbuffer(&post_cutbuf);
}
let post_kr = crate::ported::params::getaparam("killring").unwrap_or_default();
if post_kr != snap.killring {
crate::ported::zle::zle_params::set_killring(Some(&post_kr));
}
}
pub fn end_vichg_frame(nestedvichg: i32, isrepeat: bool, ret: i32) {
use crate::ported::zle::zle_vi::{CURVICHG, LASTVICHG, VICHGFLAG, VIINREPEAT};
use std::sync::atomic::Ordering::SeqCst;
if VICHGFLAG.load(SeqCst) == 2 && nestedvichg == 0 {
let kn = crate::ported::zle::zle_keymap::curkeymapname().clone();
if crate::ported::zle::zle_h::invicmdmode(&kn) {
if ret != 0 {
let mut cur = CURVICHG.lock().unwrap();
cur.buf.clear();
cur.bufsz = 0;
cur.bufptr = 0;
} else {
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; }
VICHGFLAG.store(0, SeqCst); } else {
VICHGFLAG.store(1, SeqCst);
}
}
if isrepeat {
let kn = crate::ported::zle::zle_keymap::curkeymapname().clone();
VIINREPEAT.store(
if crate::ported::zle::zle_h::invicmdmode(&kn) {
0
} else {
1
},
SeqCst,
);
}
}
#[cfg(test)]
mod tests {
#[test]
fn zle_special_augment_appends_to_live_value() {
let _g = crate::test_util::global_state_lock();
use crate::ported::zle::zle_params as zp;
zp::set_buffer("echo hi ");
zp::set_cursor(8);
super::arm_snapshot(
"echo hi ".into(),
"echo hi ".into(),
String::new(),
8,
String::new(),
String::new(),
Vec::new(),
0,
0,
String::new(),
Vec::new(),
);
let _ = crate::ported::params::assignsparam(
"LBUFFER",
"\"",
crate::ported::zsh_h::ASSPM_AUGMENT,
);
let _ = crate::ported::params::assignsparam(
"RBUFFER",
"\"",
crate::ported::zsh_h::ASSPM_AUGMENT,
);
super::clear_snapshot();
assert_eq!(zp::get_buffer(), "echo hi \"\"", "append, not assign");
assert_eq!(zp::get_cursor(), 9, "cursor sits between the pair");
zp::set_buffer("");
}
}