Various bugfixes
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
pub mod highlight;
|
||||
pub mod readline;
|
||||
pub mod statusline;
|
||||
|
||||
|
||||
use readline::{FernVi, Readline};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
env,
|
||||
fmt::{Display, Write},
|
||||
fs::{self, OpenOptions},
|
||||
@@ -194,6 +195,22 @@ fn read_hist_file(path: &Path) -> ShResult<Vec<HistEntry>> {
|
||||
Ok(raw.parse::<HistEntries>()?.0)
|
||||
}
|
||||
|
||||
/// Deduplicate entries, keeping only the most recent occurrence of each command.
|
||||
/// Preserves chronological order (oldest to newest).
|
||||
fn dedupe_entries(entries: &[HistEntry]) -> Vec<HistEntry> {
|
||||
let mut seen = HashSet::new();
|
||||
// Iterate backwards (newest first), keeping first occurrence of each command
|
||||
entries
|
||||
.iter()
|
||||
.rev()
|
||||
.filter(|ent| seen.insert(ent.command.clone()))
|
||||
.cloned()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.rev() // Restore chronological order
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub struct History {
|
||||
path: PathBuf,
|
||||
entries: Vec<HistEntry>,
|
||||
@@ -222,7 +239,7 @@ impl History {
|
||||
new: true,
|
||||
})
|
||||
}
|
||||
let search_mask = entries.clone();
|
||||
let search_mask = dedupe_entries(&entries);
|
||||
let cursor = entries.len() - 1;
|
||||
let mut new = Self {
|
||||
path,
|
||||
@@ -288,15 +305,16 @@ impl History {
|
||||
match kind {
|
||||
SearchKind::Prefix => {
|
||||
if term.is_empty() {
|
||||
self.search_mask = self.entries.clone();
|
||||
self.search_mask = dedupe_entries(&self.entries);
|
||||
} else {
|
||||
let filtered = self
|
||||
let filtered: Vec<_> = self
|
||||
.entries
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|ent| ent.command().starts_with(&term));
|
||||
.iter()
|
||||
.filter(|ent| ent.command().starts_with(&term))
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
self.search_mask = filtered.collect();
|
||||
self.search_mask = dedupe_entries(&filtered);
|
||||
}
|
||||
self.cursor = self.search_mask.len().saturating_sub(1);
|
||||
}
|
||||
|
||||
@@ -362,6 +362,8 @@ impl LineBuf {
|
||||
let hint = hint.strip_prefix(&self.buffer).unwrap(); // If this ever panics, I will eat my hat
|
||||
if !hint.is_empty() {
|
||||
self.hint = Some(hint.to_string())
|
||||
} else {
|
||||
self.hint = None
|
||||
}
|
||||
} else {
|
||||
self.hint = None
|
||||
|
||||
@@ -57,7 +57,6 @@ impl Readline for FernVi {
|
||||
Err(_) | Ok(None) => {
|
||||
flog!(DEBUG, "EOF detected");
|
||||
raw_mode_guard.disable_for(|| self.writer.flush_write("\n"))?;
|
||||
std::mem::drop(raw_mode_guard);
|
||||
return Err(ShErr::simple(ShErrKind::ReadlineErr, "EOF"));
|
||||
}
|
||||
|
||||
@@ -68,7 +67,6 @@ impl Readline for FernVi {
|
||||
if self.should_accept_hint(&key) {
|
||||
self.editor.accept_hint();
|
||||
self.history.update_pending_cmd(self.editor.as_str());
|
||||
self.print_line()?;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -81,19 +79,22 @@ impl Readline for FernVi {
|
||||
|
||||
if self.should_grab_history(&cmd) {
|
||||
self.scroll_history(cmd);
|
||||
self.print_line()?;
|
||||
continue;
|
||||
}
|
||||
|
||||
if cmd.should_submit() {
|
||||
raw_mode_guard.disable_for(|| self.writer.flush_write("\n"))?;
|
||||
std::mem::drop(raw_mode_guard);
|
||||
return Ok(self.editor.take_buf());
|
||||
let buf = self.editor.take_buf();
|
||||
// Save command to history
|
||||
self.history.push(buf.clone());
|
||||
if let Err(e) = self.history.save() {
|
||||
eprintln!("Failed to save history: {e}");
|
||||
}
|
||||
return Ok(buf);
|
||||
}
|
||||
|
||||
if cmd.verb().is_some_and(|v| v.1 == Verb::EndOfFile) {
|
||||
if self.editor.buffer.is_empty() {
|
||||
std::mem::drop(raw_mode_guard);
|
||||
return Err(ShErr::simple(ShErrKind::CleanExit(0), "exit"));
|
||||
} else {
|
||||
self.editor.buffer.clear();
|
||||
|
||||
@@ -36,6 +36,9 @@ pub fn raw_mode() -> RawModeGuard {
|
||||
&raw,
|
||||
)
|
||||
.expect("Failed to set terminal to raw mode");
|
||||
|
||||
let (cols, rows) = get_win_size(STDIN_FILENO);
|
||||
|
||||
RawModeGuard {
|
||||
orig,
|
||||
fd: STDIN_FILENO,
|
||||
|
||||
1
src/prompt/statusline.rs
Normal file
1
src/prompt/statusline.rs
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
@@ -103,6 +103,11 @@ pub fn sig_setup() {
|
||||
flags,
|
||||
SigSet::empty(),
|
||||
),
|
||||
SigAction::new(
|
||||
SigHandler::Handler(handle_sigwinch),
|
||||
flags,
|
||||
SigSet::empty(),
|
||||
),
|
||||
];
|
||||
|
||||
|
||||
@@ -114,9 +119,19 @@ pub fn sig_setup() {
|
||||
sigaction(Signal::SIGINT, &actions[4]).unwrap();
|
||||
sigaction(Signal::SIGTTIN, &actions[5]).unwrap();
|
||||
sigaction(Signal::SIGTTOU, &actions[6]).unwrap();
|
||||
sigaction(Signal::SIGWINCH, &actions[7]).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn handle_sigwinch(_: libc::c_int) {
|
||||
/* do nothing
|
||||
* this exists for the sole purpose of interrupting readline
|
||||
* readline will be refreshed after the interruption,
|
||||
* which will cause window size calculations to be re-run
|
||||
* and we get window resize handling for free as a result
|
||||
*/
|
||||
}
|
||||
|
||||
extern "C" fn handle_sighup(_: libc::c_int) {
|
||||
GOT_SIGHUP.store(true, Ordering::SeqCst);
|
||||
SHOULD_QUIT.store(true, Ordering::SeqCst);
|
||||
|
||||
Reference in New Issue
Block a user