use crate::ported::zsh_h::hist_stack;
use crate::ported::zsh_h::{ZCONTEXT_HIST, ZCONTEXT_LEX, ZCONTEXT_PARSE};
use std::sync::Mutex;
use crate::zsh_h::lex_stack;
use super::parse::{ParseStack};
#[allow(non_camel_case_types)]
pub struct context_stack { pub next: Option<Box<context_stack>>, pub hist_stack: hist_stack, pub lex_stack: lex_stack, pub parse_stack: ParseStack, }
static cstack: Mutex<Option<Box<context_stack>>> = Mutex::new(None);
#[allow(non_snake_case)]
pub fn zcontext_save_partial(parts: i32) { crate::ported::signals::queue_signals();
let mut cs = Box::new(context_stack { next: None,
hist_stack: hist_stack {
histactive: 0, histdone: 0, stophist: 0, hlinesz: 0, defev: 0,
hline: None, hptr: None, chwords: Vec::new(),
chwordlen: 0, chwordpos: 0, csp: 0, hist_keep_comment: 0,
},
lex_stack: lex_stack::default(),
parse_stack: ParseStack::default(),
});
let mut head = cstack.lock().unwrap();
let toplevel: i32 = if head.is_none() { 1 } else { 0 }; if (parts & ZCONTEXT_HIST) != 0 { crate::ported::hist::hist_context_save(&mut cs.hist_stack, toplevel); }
if (parts & ZCONTEXT_LEX) != 0 { crate::ported::lex::lex_context_save(&mut cs.lex_stack);
}
if (parts & ZCONTEXT_PARSE) != 0 { crate::ported::parse::parse_context_save(&mut cs.parse_stack);
}
cs.next = head.take(); *head = Some(cs);
crate::ported::signals::unqueue_signals(); }
pub fn zcontext_save() { zcontext_save_partial(ZCONTEXT_HIST | ZCONTEXT_LEX | ZCONTEXT_PARSE);
}
pub fn zcontext_restore_partial(parts: i32) { let mut head = cstack.lock().unwrap();
let mut cs = match head.take() { Some(cs) => cs,
None => {
return;
}
};
crate::ported::signals::queue_signals(); *head = cs.next.take(); let toplevel: i32 = if head.is_none() { 1 } else { 0 };
if (parts & ZCONTEXT_HIST) != 0 { crate::ported::hist::hist_context_restore(&cs.hist_stack, toplevel); }
if (parts & ZCONTEXT_LEX) != 0 { crate::ported::lex::lex_context_restore(&mut cs.lex_stack);
}
if (parts & ZCONTEXT_PARSE) != 0 { crate::ported::parse::parse_context_restore(&cs.parse_stack);
}
drop(cs);
crate::ported::signals::unqueue_signals(); }
pub fn zcontext_restore() { zcontext_restore_partial(ZCONTEXT_HIST | ZCONTEXT_LEX | ZCONTEXT_PARSE);
}
#[cfg(test)]
mod tests {
use super::*;
fn reset_cstack() {
*cstack.lock().unwrap() = None;
}
#[test]
fn save_restore_balances_stack() {
reset_cstack();
crate::ported::lex::lex_init("");
zcontext_save();
assert!(cstack.lock().unwrap().is_some());
zcontext_restore();
assert!(cstack.lock().unwrap().is_none());
}
#[test]
fn nested_saves_pop_lifo() {
reset_cstack();
crate::ported::lex::lex_init("");
zcontext_save();
zcontext_save();
zcontext_restore();
assert!(cstack.lock().unwrap().is_some());
zcontext_restore();
assert!(cstack.lock().unwrap().is_none());
}
#[test]
fn restore_without_save_is_noop() {
reset_cstack();
crate::ported::lex::lex_init("");
zcontext_restore();
assert!(cstack.lock().unwrap().is_none());
}
#[test]
fn lex_save_restore_roundtrips_state() {
reset_cstack();
crate::ported::lex::lex_init("echo hello");
crate::ported::lex::LEX_DBPARENS.set(true);
crate::ported::lex::set_toklineno(42);
zcontext_save();
assert!(crate::ported::lex::LEX_DBPARENS.with(|c| c.get()));
assert_eq!(crate::ported::lex::toklineno(), 42);
zcontext_restore();
assert!(crate::ported::lex::LEX_DBPARENS.with(|c| c.get()));
assert_eq!(crate::ported::lex::toklineno(), 42);
}
}