use std::sync::atomic::AtomicI32;
use super::zle_main::{ZleString};
#[allow(unused_imports)]
use crate::ported::zle::zle_h::*;
#[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_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_utils::*;
#[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::*;
pub static ISEARCH_ACTIVE: AtomicI32 = AtomicI32::new(0);
pub static ISEARCH_STARTPOS: AtomicI32 = AtomicI32::new(0);
pub static ISEARCH_ENDPOS: AtomicI32 = AtomicI32::new(0);
#[derive(Debug, Clone)]
pub struct HistEntry {
pub line: String,
pub num: i64,
pub time: Option<i64>,
}
#[derive(Debug, Default)]
pub struct History {
pub entries: Vec<HistEntry>,
pub cursor: usize,
pub max_size: usize,
pub saved_line: Option<crate::ported::zle::zle_main::ZleString>,
pub saved_cs: usize,
pub search_pattern: String,
pub search_backward: bool,
pub originals: Vec<Option<String>>,
pub have_edits: bool,
pub hist_skip_flags: u32,
}
impl History {
pub fn new(max_size: usize) -> Self {
History {
entries: Vec::new(),
cursor: 0,
max_size,
saved_line: None,
saved_cs: 0,
search_pattern: String::new(),
search_backward: true,
originals: Vec::new(),
have_edits: false,
hist_skip_flags: 0,
}
}
pub fn add(&mut self, line: String) {
if line.is_empty() {
return;
}
if let Some(last) = self.entries.last() {
if last.line == line {
return;
}
}
self.entries.push(HistEntry {
line,
num: self.entries.len() as i64 + 1,
time: Some(
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs() as i64)
.unwrap_or(0),
),
});
while self.entries.len() > self.max_size {
self.entries.remove(0);
}
self.cursor = self.entries.len();
}
pub fn get(&self, index: usize) -> Option<&HistEntry> {
self.entries.get(index)
}
pub fn up(&mut self) -> Option<&HistEntry> {
if self.cursor > 0 {
self.cursor -= 1;
self.entries.get(self.cursor)
} else {
None
}
}
pub fn down(&mut self) -> Option<&HistEntry> {
if self.cursor < self.entries.len() {
self.cursor += 1;
self.entries.get(self.cursor)
} else {
None
}
}
pub fn search_backward(&mut self, pattern: &str) -> Option<&HistEntry> {
let start = if self.cursor > 0 {
self.cursor - 1
} else {
return None;
};
for i in (0..=start).rev() {
if self.entries[i].line.contains(pattern) {
self.cursor = i;
return self.entries.get(i);
}
}
None
}
pub fn search_forward(&mut self, pattern: &str) -> Option<&HistEntry> {
for i in (self.cursor + 1)..self.entries.len() {
if self.entries[i].line.contains(pattern) {
self.cursor = i;
return self.entries.get(i);
}
}
None
}
pub fn reset(&mut self) {
self.cursor = self.entries.len();
self.saved_line = None;
}
}
pub fn init_history(max_size: usize) {
let _ = max_size;
}
pub fn upline() -> i32 { let mut n = crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst);
if n < 0 {
crate::ported::zle::zle_main::MULT.store(-crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
let r = -downline();
crate::ported::zle::zle_main::MULT.store(-crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
return r;
}
if crate::ported::zle::zle_main::LASTCOL.load(std::sync::atomic::Ordering::SeqCst) == -1 {
crate::ported::zle::zle_main::LASTCOL.store((crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst) - findbol()) as i32, std::sync::atomic::Ordering::SeqCst);
}
crate::ported::zle::zle_main::ZLECS.store(findbol(), std::sync::atomic::Ordering::SeqCst);
while n > 0 {
if crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst) == 0 {
break;
}
crate::ported::zle::zle_main::ZLECS.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(findbol(), std::sync::atomic::Ordering::SeqCst);
n -= 1;
}
if n == 0 {
let x = findeol();
crate::ported::zle::zle_main::ZLECS.fetch_add(crate::ported::zle::zle_main::LASTCOL.load(std::sync::atomic::Ordering::SeqCst) as usize, std::sync::atomic::Ordering::SeqCst);
if crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst) >= x {
crate::ported::zle::zle_main::ZLECS.store(x, std::sync::atomic::Ordering::SeqCst);
}
}
n
}
pub fn downline() -> i32 { let mut n = crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst);
if n < 0 {
crate::ported::zle::zle_main::MULT.store(-crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
let r = -upline();
crate::ported::zle::zle_main::MULT.store(-crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
return r;
}
if crate::ported::zle::zle_main::LASTCOL.load(std::sync::atomic::Ordering::SeqCst) == -1 {
crate::ported::zle::zle_main::LASTCOL.store((crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst) - findbol()) as i32, std::sync::atomic::Ordering::SeqCst);
}
while n > 0 {
let x = findeol();
if x == crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst) {
break;
}
crate::ported::zle::zle_main::ZLECS.store(x + 1, std::sync::atomic::Ordering::SeqCst);
n -= 1;
}
if n == 0 {
let x = findeol();
crate::ported::zle::zle_main::ZLECS.fetch_add(crate::ported::zle::zle_main::LASTCOL.load(std::sync::atomic::Ordering::SeqCst) as usize, std::sync::atomic::Ordering::SeqCst);
if crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst) >= x {
crate::ported::zle::zle_main::ZLECS.store(x, std::sync::atomic::Ordering::SeqCst);
}
}
n
}
pub fn zle_goto_hist(n: i32, skipdups: bool) -> bool {
let len = crate::ported::zle::zle_main::history().lock().unwrap().entries.len() as i32;
if len == 0 {
return false;
}
let cur: i32 = if (crate::ported::zle::zle_main::history().lock().unwrap().cursor as i32) > len {
len
} else {
crate::ported::zle::zle_main::history().lock().unwrap().cursor as i32
};
let mut new_idx = cur + n;
if new_idx < 0 || new_idx > len {
return false;
}
if skipdups && n != 0 {
let cur_line: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect();
let step: i32 = if n < 0 { -1 } else { 1 };
while new_idx >= 0 && new_idx < len {
if crate::ported::zle::zle_main::history().lock().unwrap().entries[new_idx as usize].line != cur_line {
break;
}
new_idx += step;
}
if new_idx < 0 || new_idx > len {
return false;
}
}
if crate::ported::zle::zle_main::history().lock().unwrap().saved_line.is_none() && crate::ported::zle::zle_main::history().lock().unwrap().cursor as i32 == len {
crate::ported::zle::zle_main::history().lock().unwrap().saved_line = Some(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().clone());
crate::ported::zle::zle_main::history().lock().unwrap().saved_cs = crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst);
}
crate::ported::zle::zle_main::history().lock().unwrap().cursor = new_idx as usize;
let new_line: Option<ZleString> = if new_idx == len {
crate::ported::zle::zle_main::history().lock().unwrap().saved_line.clone()
} else {
Some(
crate::ported::zle::zle_main::history().lock().unwrap().entries[new_idx as usize]
.line
.chars()
.collect(),
)
};
if let Some(line) = new_line {
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = line;
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
let new_cs = if new_idx == len {
crate::ported::zle::zle_main::history().lock().unwrap().saved_cs.min(crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst))
} else {
crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst)
};
crate::ported::zle::zle_main::ZLECS.store(new_cs, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::LASTCOL.store(-1, std::sync::atomic::Ordering::SeqCst);
}
true
}
pub fn history_up(hist: &mut History) {
if hist.saved_line.is_none() {
hist.saved_line = Some(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().clone());
hist.saved_cs = crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst);
}
if let Some(entry) = hist.up() {
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = entry.line.chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
}
pub fn history_down(hist: &mut History) {
if let Some(entry) = hist.down() {
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = entry.line.chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
} else if let Some(saved) = hist.saved_line.take() {
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = saved;
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(hist.saved_cs, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
}
pub fn history_isearch_backward(hist: &mut History) {
hist.search_backward = true;
}
pub fn history_isearch_forward(hist: &mut History) {
hist.search_backward = false;
}
pub fn history_search_prefix(hist: &mut History) {
let prefix: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap()[..crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst)].iter().collect();
if let Some(entry) = hist.search_backward(&prefix) {
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = entry.line.chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
}
pub fn beginning_of_history(hist: &mut History) {
if hist.saved_line.is_none() {
hist.saved_line = Some(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().clone());
hist.saved_cs = crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst);
}
if !hist.entries.is_empty() {
hist.cursor = 0;
if let Some(entry) = hist.entries.first() {
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = entry.line.chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(0, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
}
}
pub fn push_line() {
let n = crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst);
if n < 0 {
return;
}
let line: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect();
crate::ported::zle::zle_main::BUFSTACK.lock().unwrap().push(line);
let mut remaining = n - 1;
while remaining > 0 {
crate::ported::zle::zle_main::BUFSTACK.lock().unwrap().push(String::new());
remaining -= 1;
}
crate::ported::zle::zle_main::STACKCS.store(crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().clear();
crate::ported::zle::zle_main::ZLELL.store(0, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(0, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
pub fn accept_line_and_down_history(hist: &mut History) -> Option<String> {
let line: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect();
if hist.cursor < hist.entries.len() {
hist.cursor += 1;
if let Some(entry) = hist.entries.get(hist.cursor) {
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = entry.line.chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
}
}
Some(line)
}
pub fn vi_fetch_history(hist: &mut History, num: usize) {
if num > 0 && num <= hist.entries.len() {
if hist.saved_line.is_none() {
hist.saved_line = Some(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().clone());
hist.saved_cs = crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst);
}
hist.cursor = num - 1;
if let Some(entry) = hist.entries.get(hist.cursor) {
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = entry.line.chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(0, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
}
}
}
pub fn vi_repeat_search(hist: &mut History) {
if hist.search_backward {
vihistorysearchbackward();
} else {
vihistorysearchforward();
}
}
pub fn set_local_history(hist: &mut History, has_mult: bool, mult: i32) {
const HIST_FOREIGN: u32 = 1;
if has_mult {
hist.hist_skip_flags = if mult != 0 { HIST_FOREIGN } else { 0 };
} else {
hist.hist_skip_flags ^= HIST_FOREIGN;
}
}
pub fn remember_edits(hist: &mut History) {
if hist.cursor < hist.entries.len() {
if hist.originals.len() < hist.entries.len() {
hist.originals.resize(hist.entries.len(), None);
}
let new_line: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect();
if hist.entries[hist.cursor].line != new_line {
if hist.originals[hist.cursor].is_none() {
hist.originals[hist.cursor] = Some(hist.entries[hist.cursor].line.clone());
}
hist.entries[hist.cursor].line = new_line;
hist.have_edits = true;
}
}
}
pub fn forget_edits(hist: &mut History) {
if !hist.have_edits {
return;
}
for (i, original) in hist.originals.iter_mut().enumerate() {
if let Some(text) = original.take() {
if let Some(entry) = hist.entries.get_mut(i) {
entry.line = text;
}
}
}
hist.have_edits = false;
}
#[cfg(test)]
mod tests {
use super::*;
fn zle_with_history(entries: &[&str]) {
crate::ported::zle::zle_main::zle_reset();
for line in entries {
crate::ported::zle::zle_main::history().lock().unwrap().add((*line).to_string());
}
}
#[test]
fn uphistory_skips_consecutive_dupes_when_histignoredups_set() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _zle = zle_with_history(&["unique", "dup", "dup"]);
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "dup".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(
"dup".len(),
std::sync::atomic::Ordering::SeqCst,
);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 3; crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult = 1;
crate::ported::options::opt_state_set("histignoredups", true);
let rc = super::uphistory();
assert_eq!(rc, 0);
assert_eq!(
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(),
"unique",
"with HISTIGNOREDUPS on, up must skip the 'dup' twins and land on 'unique'"
);
crate::ported::options::opt_state_set("histignoredups", false);
}
#[test]
fn uphistory_returns_1_on_exhaustion_when_histbeep_set() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _zle = zle_with_history(&["only"]);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 0;
crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult = 1;
crate::ported::options::opt_state_set("histbeep", true);
let rc = super::uphistory();
assert_eq!(rc, 1, "exhausted up + HISTBEEP must return 1 (beep signal)");
crate::ported::options::opt_state_set("histbeep", false);
}
#[test]
fn uphistory_returns_0_on_exhaustion_without_histbeep() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _zle = zle_with_history(&["only"]);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 0;
crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult = 1;
crate::ported::options::opt_state_set("histbeep", false);
let rc = super::uphistory();
assert_eq!(rc, 0, "exhausted up + !HISTBEEP must return 0");
}
#[test]
fn beginningofhistory_fills_buffer_from_oldest_entry() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _zle = zle_with_history(&["alpha", "bravo", "charlie"]);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 3; *crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "draft".chars().collect();
let rc = super::beginningofhistory();
assert_eq!(rc, 0, "successful move returns 0");
assert_eq!(
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(),
"alpha",
"buffer must hold the oldest entry"
);
assert_eq!(
crate::ported::zle::zle_main::history().lock().unwrap().cursor,
0,
"cursor must land on entry 0"
);
}
#[test]
fn endofhistory_fills_buffer_with_saved_live_line() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let _zle = zle_with_history(&["one", "two"]);
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "myDraft".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(),
std::sync::atomic::Ordering::SeqCst,
);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 2;
assert!(zle_goto_hist(-1, false));
assert_eq!(
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(),
"two"
);
let rc = super::endofhistory();
assert_eq!(rc, 0);
assert_eq!(
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(),
"myDraft",
"saved live buffer must be restored at sentinel"
);
}
#[test]
fn zle_goto_hist_walks_backwards_then_forwards() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&["echo a", "echo b", "echo c"]);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 3;
assert!(zle_goto_hist(-1, false));
assert_eq!(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(), "echo c");
assert!(zle_goto_hist(-2, false));
assert_eq!(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(), "echo a");
assert!(!zle_goto_hist(-1, false));
assert!(zle_goto_hist(2, false));
assert_eq!(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(), "echo c");
}
#[test]
fn zle_goto_hist_restores_saved_line_when_returning_to_sentinel() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&["one", "two"]);
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "draft".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 2; assert!(zle_goto_hist(-1, false));
assert!(zle_goto_hist(-1, false));
assert!(zle_goto_hist(2, false));
assert_eq!(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(), "draft");
}
#[test]
fn zle_goto_hist_skipdups_skips_consecutive_dupes() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&["dup", "dup", "uniq"]);
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "uniq".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 3;
assert!(zle_goto_hist(-1, true));
assert_eq!(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(), "dup");
}
#[test]
fn upline_in_single_line_buffer_returns_remaining_count() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "echo hi".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(4, std::sync::atomic::Ordering::SeqCst);
let leftover = upline();
assert_eq!(leftover, 1);
}
#[test]
fn upline_in_two_line_buffer_moves_cursor_to_first_line() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "first\nsecond".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(9, std::sync::atomic::Ordering::SeqCst); let leftover = upline();
assert_eq!(leftover, 0);
assert_eq!(crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst), 3);
}
#[test]
fn up_line_or_history_falls_through_to_history_when_at_top() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&["prev cmd"]);
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "current".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(0, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 1;
let ret = uplineorhistory();
assert_eq!(ret, 0);
assert_eq!(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(), "prev cmd");
}
#[test]
fn undo_redo_round_trip() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
setlastline();
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "abc".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(3, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(3, std::sync::atomic::Ordering::SeqCst);
mkundoent();
assert_eq!(undo_widget(), 0);
assert_eq!(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(), "");
assert_eq!(crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst), 0);
assert_eq!(redo_widget(), 0);
assert_eq!(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(), "abc");
}
#[test]
fn undo_returns_one_when_stack_empty() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
setlastline();
assert_eq!(undo_widget(), 1);
}
#[test]
fn push_line_pushes_buffer_and_clears_editor() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&[]);
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "in flight".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(9, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLECS.store(4, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::MULT.store(1, std::sync::atomic::Ordering::SeqCst);
push_line();
assert_eq!(*crate::ported::zle::zle_main::BUFSTACK.lock().unwrap(), vec!["in flight".to_string()]);
assert!(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().is_empty());
assert_eq!(crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst), 0);
assert_eq!(crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst), 0);
assert_eq!(crate::ported::zle::zle_main::STACKCS.load(std::sync::atomic::Ordering::SeqCst), 4);
}
#[test]
fn push_line_with_count_pushes_extra_empty_strings() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&[]);
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "x".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(1, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::MULT.store(3, std::sync::atomic::Ordering::SeqCst);
push_line();
assert_eq!(crate::ported::zle::zle_main::BUFSTACK.lock().unwrap().len(), 3);
assert_eq!(crate::ported::zle::zle_main::BUFSTACK.lock().unwrap()[0], "x");
assert_eq!(crate::ported::zle::zle_main::BUFSTACK.lock().unwrap()[1], "");
assert_eq!(crate::ported::zle::zle_main::BUFSTACK.lock().unwrap()[2], "");
}
#[test]
fn push_line_negative_count_is_no_op() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&[]);
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "abc".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(3, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::MULT.store(-1, std::sync::atomic::Ordering::SeqCst);
push_line();
assert!(crate::ported::zle::zle_main::BUFSTACK.lock().unwrap().is_empty());
assert_eq!(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect::<String>(), "abc");
}
#[test]
fn remember_edits_saves_original_then_forget_restores() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&["echo a", "echo b"]);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 0;
*crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "echo Z".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(6, std::sync::atomic::Ordering::SeqCst);
{
let mut hist = crate::ported::zle::zle_main::history().lock().unwrap();
remember_edits(&mut hist);
}
{
let hist = crate::ported::zle::zle_main::history().lock().unwrap();
assert!(hist.have_edits);
assert_eq!(hist.entries[0].line, "echo Z");
assert_eq!(hist.originals[0].as_deref(), Some("echo a"));
}
{
let mut hist = crate::ported::zle::zle_main::history().lock().unwrap();
forget_edits(&mut hist);
}
{
let hist = crate::ported::zle::zle_main::history().lock().unwrap();
assert!(!hist.have_edits);
assert_eq!(hist.entries[0].line, "echo a");
assert!(hist.originals[0].is_none());
}
}
#[test]
fn set_local_history_mult_sets_or_clears_foreign_skip() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&[]);
let mut hist = crate::ported::zle::zle_main::history().lock().unwrap();
set_local_history(&mut hist, true, 2);
assert_eq!(hist.hist_skip_flags, 1);
set_local_history(&mut hist, true, 0);
assert_eq!(hist.hist_skip_flags, 0);
}
#[test]
fn set_local_history_no_mult_xor_toggles() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&[]);
let mut hist = crate::ported::zle::zle_main::history().lock().unwrap();
set_local_history(&mut hist, false, 0);
assert_eq!(hist.hist_skip_flags, 1);
set_local_history(&mut hist, false, 0);
assert_eq!(hist.hist_skip_flags, 0);
}
#[test]
fn accept_line_and_down_history_pushes_next_entry_on_bufstack() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
let mut zle = zle_with_history(&["one", "two", "three"]);
crate::ported::zle::zle_main::history().lock().unwrap().cursor = 0; *crate::ported::zle::zle_main::ZLELINE.lock().unwrap() = "one".chars().collect();
crate::ported::zle::zle_main::ZLELL.store(3, std::sync::atomic::Ordering::SeqCst);
let len = crate::ported::zle::zle_main::history().lock().unwrap().entries.len();
let next_idx = crate::ported::zle::zle_main::history().lock().unwrap().cursor + 1;
if next_idx < len {
if let Some(entry) = crate::ported::zle::zle_main::history().lock().unwrap().entries.get(next_idx) {
crate::ported::zle::zle_main::BUFSTACK.lock().unwrap().push(entry.line.clone());
crate::ported::zle::zle_main::STACKHIST.store((entry.num as i32).max(0), std::sync::atomic::Ordering::SeqCst);
}
}
crate::ported::zle::zle_misc::DONE.store(1, std::sync::atomic::Ordering::SeqCst);
assert!(crate::ported::zle::zle_misc::DONE.load(std::sync::atomic::Ordering::SeqCst) != 0);
assert_eq!(*crate::ported::zle::zle_main::BUFSTACK.lock().unwrap(), vec!["two".to_string()]);
}
}
pub fn acceptandinfernexthistory() -> i32 { crate::ported::zle::zle_misc::DONE.store(1, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::history().lock().unwrap().search_pattern.clear();
0
}
pub fn acceptlineanddownhistory() -> i32 { crate::ported::zle::zle_misc::DONE.store(1, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::STACKHIST.store((crate::ported::zle::zle_main::history().lock().unwrap().cursor as i32) + 1, std::sync::atomic::Ordering::SeqCst);
0
}
pub fn beginningofbufferorhistory() -> i32 { let bol = crate::ported::zle::zle_utils::findbol();
if bol > 0 {
crate::ported::zle::zle_main::ZLECS.store(0, std::sync::atomic::Ordering::SeqCst);
0
} else {
beginningofhistory()
}
}
pub fn beginningofhistory() -> i32 { use std::sync::atomic::Ordering;
use crate::ported::zsh_h::HISTBEEP;
let cur = crate::ported::zle::zle_main::history().lock().unwrap().cursor as i32;
let delta = 0 - cur;
let moved = zle_goto_hist(delta, false);
if !moved && crate::ported::zsh_h::isset(HISTBEEP) {
return 1;
}
0 }
pub fn doisearch(dir: i32) -> i32 { use std::sync::atomic::Ordering;
ISEARCH_ACTIVE.store(1, Ordering::SeqCst);
let pat = crate::ported::zle::zle_main::history().lock().unwrap().search_pattern.clone();
let r = if pat.is_empty() {
0
} else if dir < 0 {
if crate::ported::zle::zle_main::history().lock().unwrap().search_backward(&pat).is_some() { 0 } else { 1 }
} else {
if crate::ported::zle::zle_main::history().lock().unwrap().search_forward(&pat).is_some() { 0 } else { 1 }
};
ISEARCH_ACTIVE.store(0, Ordering::SeqCst);
r
}
pub fn downhistory() -> i32 { use crate::ported::zsh_h::{HISTBEEP, HISTIGNOREDUPS, isset};
let nodups = isset(HISTIGNOREDUPS);
let zmult = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1);
if !zle_goto_hist(zmult, nodups) && isset(HISTBEEP) {
return 1;
}
0 }
pub fn downlineorhistory() -> i32 { let ocs = crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst);
let n = downline();
if n != 0 {
crate::ported::zle::zle_main::ZLECS.store(ocs, std::sync::atomic::Ordering::SeqCst);
if (crate::ported::zle::zle_main::ZLEREADFLAGS.load(std::sync::atomic::Ordering::SeqCst) & crate::ported::zsh_h::ZLRF_HISTORY) == 0 {
return 1;
}
let saved_mult = crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::MULT.store(n, std::sync::atomic::Ordering::SeqCst);
let ret = if zle_goto_hist(crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst), false) {
0
} else {
1
};
crate::ported::zle::zle_main::MULT.store(saved_mult, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
ret
} else {
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
0
}
}
pub fn downlineorsearch() -> i32 { let ocs = crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst);
let n = downline();
if n != 0 {
crate::ported::zle::zle_main::ZLECS.store(ocs, std::sync::atomic::Ordering::SeqCst);
let saved = crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::MULT.store(n, std::sync::atomic::Ordering::SeqCst);
let r = historysearchforward();
crate::ported::zle::zle_main::MULT.store(saved, std::sync::atomic::Ordering::SeqCst);
return r;
}
0
}
pub fn endofbufferorhistory() -> i32 { let eol = crate::ported::zle::zle_utils::findeol();
if eol != crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst) {
crate::ported::zle::zle_main::ZLECS.store(crate::ported::zle::zle_main::ZLELL.load(std::sync::atomic::Ordering::SeqCst), std::sync::atomic::Ordering::SeqCst);
0
} else {
endofhistory()
}
}
pub fn endofhistory() -> i32 { let (cur, end): (i32, i32) = {
let h = crate::ported::zle::zle_main::history().lock().unwrap();
(h.cursor as i32, h.entries.len() as i32)
};
let delta = end - cur;
let _ = zle_goto_hist(delta, false);
0 }
#[derive(Debug, Default, Clone, Copy)]
#[allow(non_camel_case_types)]
pub struct isrch_spot { pub hl: i32,
pub pos: u16,
pub pat_hl: i32,
pub pat_pos: u16,
pub end_pos: u16,
pub cs: u16,
pub len: u16,
pub flags: u16,
}
pub static ISRCH_SPOTS: std::sync::OnceLock<std::sync::Mutex<Vec<isrch_spot>>> =
std::sync::OnceLock::new();
fn isrch_spots() -> &'static std::sync::Mutex<Vec<isrch_spot>> {
ISRCH_SPOTS.get_or_init(|| std::sync::Mutex::new(Vec::new()))
}
pub const ISEARCH_PROMPT: &str = "XXXXXXX XXX-i-search: ";
pub const FAILING_TEXT: &str = "failing";
pub const INVALID_TEXT: &str = "invalid";
pub const BAD_TEXT_LEN: usize = 7;
pub const NORM_PROMPT_POS: usize = BAD_TEXT_LEN + 1;
pub const FIRST_SEARCH_CHAR: usize = NORM_PROMPT_POS + 14;
pub const ISS_FORWARD: u16 = 1;
pub const ISS_NOMATCH_SHIFT: u16 = 1;
pub fn free_isrch_spots() { isrch_spots().lock().unwrap().clear();
}
#[allow(clippy::too_many_arguments)]
pub fn set_isrch_spot( num: usize,
hl: i32,
pos: i32,
pat_hl: i32,
pat_pos: i32,
end_pos: i32,
cs: i32,
len: i32,
dir: i32,
nomatch: i32,
) {
let mut spots = isrch_spots().lock().unwrap();
if num >= spots.len() {
spots.resize(num + 64, isrch_spot::default());
}
spots[num] = isrch_spot {
hl,
pos: pos as u16,
pat_hl,
pat_pos: pat_pos as u16,
end_pos: end_pos as u16,
cs: cs as u16,
len: len as u16,
flags: (if dir > 0 { ISS_FORWARD } else { 0 })
| ((nomatch as u16) << ISS_NOMATCH_SHIFT),
};
}
pub fn get_isrch_spot(num: usize) -> Option<(i32, i32, i32, i32, i32, i32, i32, i32, i32)> { let spots = isrch_spots().lock().unwrap();
let s = spots.get(num)?;
Some((
s.hl,
s.pos as i32,
s.pat_hl,
s.pat_pos as i32,
s.end_pos as i32,
s.cs as i32,
s.len as i32,
if (s.flags & ISS_FORWARD) != 0 { 1 } else { -1 },
(s.flags >> ISS_NOMATCH_SHIFT) as i32,
))
}
pub fn getvisrchstr() -> i32 { let snap: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect();
if snap.is_empty() {
return 0;
}
crate::ported::zle::zle_main::history().lock().unwrap().search_pattern = snap;
1
}
pub fn historybeginningsearchbackward() -> i32 { let prefix: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap()[..crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst)].iter().collect();
let n = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1);
for _ in 0..n {
if crate::ported::zle::zle_main::history().lock().unwrap().search_backward(&prefix).is_none() {
return 1;
}
}
0
}
pub fn historybeginningsearchforward() -> i32 { let prefix: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap()[..crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst)].iter().collect();
let n = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1);
for _ in 0..n {
if crate::ported::zle::zle_main::history().lock().unwrap().search_forward(&prefix).is_none() {
return 1;
}
}
0
}
pub fn historyincrementalpatternsearchbackward() -> i32 { doisearch(-1)
}
pub fn historyincrementalpatternsearchforward() -> i32 { doisearch(1)
}
pub fn historyincrementalsearchbackward() -> i32 { doisearch(-1)
}
pub fn historyincrementalsearchforward() -> i32 { doisearch(1)
}
pub fn historysearchbackward() -> i32 { let prefix: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap()[..crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst)].iter().collect();
let n = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1);
for _ in 0..n {
if crate::ported::zle::zle_main::history().lock().unwrap().search_backward(&prefix).is_none() {
return 1;
}
}
0
}
pub fn historysearchforward() -> i32 { let prefix: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap()[..crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst)].iter().collect();
let n = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1);
for _ in 0..n {
if crate::ported::zle::zle_main::history().lock().unwrap().search_forward(&prefix).is_none() {
return 1;
}
}
0
}
pub fn infernexthist() -> i32 { if crate::ported::zle::zle_main::history().lock().unwrap().cursor + 1 >= crate::ported::zle::zle_main::history().lock().unwrap().entries.len() {
return 1;
}
let cur_first: String = crate::ported::zle::zle_main::history().lock().unwrap().entries[crate::ported::zle::zle_main::history().lock().unwrap().cursor]
.line
.split_whitespace()
.next()
.unwrap_or("")
.to_string();
if cur_first.is_empty() {
return 1;
}
let (start, len) = {
let h = crate::ported::zle::zle_main::history().lock().unwrap();
(h.cursor + 1, h.entries.len())
};
for i in start..len {
let first = {
let h = crate::ported::zle::zle_main::history().lock().unwrap();
h.entries[i].line.split_whitespace().next().unwrap_or("").to_string()
};
if first == cur_first {
crate::ported::zle::zle_main::history().lock().unwrap().cursor = i;
return 0;
}
}
1
}
pub fn infernexthistory() -> i32 { infernexthist()
}
pub fn insertlastword() -> i32 { let n = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1) as usize;
if crate::ported::zle::zle_main::history().lock().unwrap().cursor < n {
return 1;
}
let idx = crate::ported::zle::zle_main::history().lock().unwrap().cursor - n;
let entry = match crate::ported::zle::zle_main::history().lock().unwrap().entries.get(idx) {
Some(e) => e.line.clone(),
None => return 1,
};
let word = match entry.split_whitespace().last() {
Some(w) => w.to_string(),
None => return 1,
};
for ch in word.chars() {
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().insert(crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst), ch);
crate::ported::zle::zle_main::ZLECS.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
}
0
}
pub fn isearch_newpos(curpos: i32, dir: i32, end: &mut i32) -> i32 { let _ = (curpos, dir, end);
-1
}
pub fn pushinput() -> i32 { let snapshot: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect();
crate::ported::zle::zle_main::history().lock().unwrap().entries.push(HistEntry {
line: snapshot,
num: 0,
time: None,
});
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().clear();
crate::ported::zle::zle_main::ZLECS.store(0, std::sync::atomic::Ordering::SeqCst);
0
}
pub fn pushline() -> i32 { let snapshot: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect();
if snapshot.is_empty() {
return 1;
}
crate::ported::zle::zle_main::history().lock().unwrap().entries.push(HistEntry {
line: snapshot,
num: 0,
time: None,
});
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().clear();
crate::ported::zle::zle_main::ZLECS.store(0, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_misc::DONE.store(1, std::sync::atomic::Ordering::SeqCst);
0
}
pub fn pushlineoredit() -> i32 { let snapshot: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect();
if snapshot.is_empty() {
return 0;
}
crate::ported::zle::zle_main::history().lock().unwrap().entries.push(HistEntry {
line: snapshot,
num: 0,
time: None,
});
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().clear();
crate::ported::zle::zle_main::ZLECS.store(0, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_misc::DONE.store(1, std::sync::atomic::Ordering::SeqCst);
0
}
pub fn save_isearch_buffer() -> i32 { let snap: String = crate::ported::zle::zle_main::ZLELINE.lock().unwrap().iter().collect();
crate::ported::zle::zle_main::history().lock().unwrap().search_pattern = snap;
0
}
pub fn setlocalhistory() -> i32 { crate::ported::zle::zle_main::history().lock().unwrap().hist_skip_flags ^= 1;
0
}
pub fn uphistory() -> i32 { use crate::ported::zsh_h::{HISTBEEP, HISTIGNOREDUPS, isset};
let nodups = isset(HISTIGNOREDUPS);
let zmult = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1);
if !zle_goto_hist(-zmult, nodups) && isset(HISTBEEP) {
return 1;
}
0 }
pub fn uplineorhistory() -> i32 { let ocs = crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst);
let n = upline();
if n != 0 {
crate::ported::zle::zle_main::ZLECS.store(ocs, std::sync::atomic::Ordering::SeqCst);
if (crate::ported::zle::zle_main::ZLEREADFLAGS.load(std::sync::atomic::Ordering::SeqCst) & crate::ported::zsh_h::ZLRF_HISTORY) == 0 {
return 1;
}
let saved_mult = crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::MULT.store(n, std::sync::atomic::Ordering::SeqCst);
let ret = if zle_goto_hist(-crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst), false) {
0
} else {
1
};
crate::ported::zle::zle_main::MULT.store(saved_mult, std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
ret
} else {
crate::ported::zle::zle_main::ZLE_RESET_NEEDED.store(1, std::sync::atomic::Ordering::SeqCst);
0
}
}
pub fn uplineorsearch() -> i32 { let ocs = crate::ported::zle::zle_main::ZLECS.load(std::sync::atomic::Ordering::SeqCst);
let n = upline();
if n != 0 {
crate::ported::zle::zle_main::ZLECS.store(ocs, std::sync::atomic::Ordering::SeqCst);
let saved = crate::ported::zle::zle_main::MULT.load(std::sync::atomic::Ordering::SeqCst);
crate::ported::zle::zle_main::MULT.store(n, std::sync::atomic::Ordering::SeqCst);
let r = historysearchbackward();
crate::ported::zle::zle_main::MULT.store(saved, std::sync::atomic::Ordering::SeqCst);
return r;
}
0
}
pub fn vidownlineorhistory() -> i32 { downlineorhistory()
}
pub fn vifetchhistory() -> i32 { let n = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult;
if n <= 0 {
if crate::ported::zle::zle_main::history().lock().unwrap().entries.is_empty() {
return 1;
}
crate::ported::zle::zle_main::history().lock().unwrap().cursor = crate::ported::zle::zle_main::history().lock().unwrap().entries.len() - 1;
return 0;
}
if (n as usize) > crate::ported::zle::zle_main::history().lock().unwrap().entries.len() {
return 1;
}
crate::ported::zle::zle_main::history().lock().unwrap().cursor = (n as usize).saturating_sub(1);
0
}
pub fn vihistorysearchbackward() -> i32 { if crate::ported::zle::zle_main::history().lock().unwrap().search_pattern.is_empty() {
return 1;
}
let pat = crate::ported::zle::zle_main::history().lock().unwrap().search_pattern.clone();
let n = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1);
for _ in 0..n {
if crate::ported::zle::zle_main::history().lock().unwrap().search_backward(&pat).is_none() {
return 1;
}
}
crate::ported::zle::zle_main::history().lock().unwrap().search_backward = true;
0
}
pub fn vihistorysearchforward() -> i32 { if crate::ported::zle::zle_main::history().lock().unwrap().search_pattern.is_empty() {
return 1;
}
let pat = crate::ported::zle::zle_main::history().lock().unwrap().search_pattern.clone();
let n = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1);
for _ in 0..n {
if crate::ported::zle::zle_main::history().lock().unwrap().search_forward(&pat).is_none() {
return 1;
}
}
crate::ported::zle::zle_main::history().lock().unwrap().search_backward = false;
0
}
pub fn virepeatsearch() -> i32 { let (pat, backward) = {
let h = crate::ported::zle::zle_main::history().lock().unwrap();
if h.search_pattern.is_empty() { return 1; }
(h.search_pattern.clone(), h.search_backward)
};
let n = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1);
for _ in 0..n {
let hit_found = {
let mut h = crate::ported::zle::zle_main::history().lock().unwrap();
if backward { h.search_backward(&pat).is_some() } else { h.search_forward(&pat).is_some() }
};
if !hit_found { return 1; }
}
0
}
pub fn virevrepeatsearch() -> i32 { let (pat, backward) = {
let h = crate::ported::zle::zle_main::history().lock().unwrap();
if h.search_pattern.is_empty() { return 1; }
(h.search_pattern.clone(), h.search_backward)
};
let n = crate::ported::zle::zle_main::ZMOD.lock().unwrap().mult.max(1);
for _ in 0..n {
let hit_found = {
let mut h = crate::ported::zle::zle_main::history().lock().unwrap();
if backward { h.search_forward(&pat).is_some() } else { h.search_backward(&pat).is_some() }
};
if !hit_found { return 1; }
}
0
}
pub fn viuplineorhistory() -> i32 { uplineorhistory()
}
pub fn zgetline() -> i32 { let entry = match crate::ported::zle::zle_main::history().lock().unwrap().entries.pop() {
Some(e) => e.line,
None => return 1,
};
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().clear();
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().extend(entry.chars());
crate::ported::zle::zle_main::ZLECS.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
0
}
pub fn zle_setline() -> i32 { if let Some(entry) = crate::ported::zle::zle_main::history().lock().unwrap().entries.get(crate::ported::zle::zle_main::history().lock().unwrap().cursor) {
let line = entry.line.clone();
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().clear();
crate::ported::zle::zle_main::ZLELINE.lock().unwrap().extend(line.chars());
crate::ported::zle::zle_main::ZLECS.store(crate::ported::zle::zle_main::ZLELINE.lock().unwrap().len(), std::sync::atomic::Ordering::SeqCst);
return 0;
}
1
}
pub fn zlinecmp(histp: &str, inputp: &str) -> i32 { let h_bytes = histp.as_bytes();
let i_bytes = inputp.as_bytes();
let mut hi = 0;
let mut ii = 0;
while ii < i_bytes.len() && hi < h_bytes.len() && h_bytes[hi] == i_bytes[ii] {
hi += 1;
ii += 1;
}
if ii >= i_bytes.len() {
if hi >= h_bytes.len() {
return 0; } else {
return -1; }
}
let mut hi = 0;
let mut ii = 0;
while hi < h_bytes.len() && ii < i_bytes.len() { if h_bytes[hi].to_ascii_lowercase() != i_bytes[ii] {
return 3;
}
hi += 1;
ii += 1;
}
if ii >= i_bytes.len() {
if hi >= h_bytes.len() {
return 1; } else {
return 2; }
}
3 }
pub fn zlinefind(haystack: &str, pos: usize, needle: &str, dir: i32, sens: i32) -> Option<usize> { let bytes = haystack.as_bytes();
let mut s = pos; if dir > 0 { while s < bytes.len() { if zlinecmp(&haystack[s..], needle) < sens {
return Some(s);
}
s += 1; }
} else {
loop { if zlinecmp(&haystack[s..], needle) < sens {
return Some(s);
}
if s == 0 { break;
}
s -= 1; }
}
None }
#[cfg(test)]
mod zlinecmp_zlinefind_tests {
use super::*;
#[test]
fn zlinecmp_same() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(zlinecmp("hello", "hello"), 0);
}
#[test]
fn zlinecmp_input_prefix() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(zlinecmp("hello world", "hello"), -1);
}
#[test]
fn zlinecmp_lowercase_same() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(zlinecmp("HELLO", "hello"), 1);
}
#[test]
fn zlinecmp_lowercase_prefix() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(zlinecmp("HELLO World", "hello"), 2);
}
#[test]
fn zlinecmp_different() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(zlinecmp("apple", "orange"), 3);
}
#[test]
fn zlinecmp_empty_input() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(zlinecmp("foo", ""), -1);
assert_eq!(zlinecmp("", ""), 0);
}
#[test]
fn zlinefind_forward_exact() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(zlinefind("hello world hello", 0, "world", 1, 0), Some(6));
}
#[test]
fn zlinefind_backward_exact() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(
zlinefind("hello world hello", 16, "hello", -1, 1),
Some(12)
);
}
#[test]
fn zlinefind_not_found() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(zlinefind("hello", 0, "xyz", 1, 0), None);
}
#[test]
fn zlinefind_starts_at_pos() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(zlinefind("abcabc", 1, "a", 1, 0), Some(3));
}
}
#[cfg(test)]
mod isearch_prompt_tests {
use super::*;
#[test]
fn bad_text_strings_are_seven_chars() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(FAILING_TEXT.len(), BAD_TEXT_LEN);
assert_eq!(INVALID_TEXT.len(), BAD_TEXT_LEN);
}
#[test]
fn norm_prompt_pos_after_bad_text_marker() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(NORM_PROMPT_POS, 8);
}
#[test]
fn first_search_char_after_isearch_label() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert_eq!(FIRST_SEARCH_CHAR, 22);
}
#[test]
fn isearch_prompt_skeleton_has_correct_shape() {
let _g = crate::ported::zle::zle_main::zle_test_setup();
assert!(ISEARCH_PROMPT.starts_with("XXXXXXX "));
assert!(ISEARCH_PROMPT.contains("XXX-i-search:"));
}
}