Added an on-exit hook for autocmd

improved mode-switching autocmd hook logic

fixed escaping logic in single quote expansion
This commit is contained in:
2026-03-04 14:29:44 -05:00
parent fbadbebf8c
commit 12267716be
5 changed files with 30 additions and 7 deletions

View File

@@ -80,6 +80,7 @@ in
"post-prompt" "post-prompt"
"pre-mode-change" "pre-mode-change"
"post-mode-change" "post-mode-change"
"on-exit"
])) (list: list != []); ])) (list: list != []);
description = "The events that trigger this autocmd"; description = "The events that trigger this autocmd";
}; };

View File

@@ -1070,6 +1070,14 @@ pub fn unescape_str(raw: &str) -> String {
result.push(markers::SNG_QUOTE); result.push(markers::SNG_QUOTE);
while let Some(q_ch) = chars.next() { while let Some(q_ch) = chars.next() {
match q_ch { match q_ch {
'\\' => {
if chars.peek() == Some(&'\'') {
result.push('\'');
chars.next();
} else {
result.push('\\');
}
}
'\'' => { '\'' => {
result.push(markers::SNG_QUOTE); result.push(markers::SNG_QUOTE);
break; break;

View File

@@ -118,14 +118,17 @@ fn main() -> ExitCode {
} else { } else {
shed_interactive(args) shed_interactive(args)
} { } {
eprintln!("shed: {e}"); e.print_error();
}; };
if let Some(trap) = read_logic(|l| l.get_trap(TrapTarget::Exit)) if let Some(trap) = read_logic(|l| l.get_trap(TrapTarget::Exit))
&& let Err(e) = exec_input(trap, None, false, Some("trap".into())) { && let Err(e) = exec_input(trap, None, false, Some("trap".into())) {
eprintln!("shed: error running EXIT trap: {e}"); e.print_error();
} }
let on_exit_autocmds = read_logic(|l| l.get_autocmds(AutoCmdKind::OnExit));
on_exit_autocmds.exec();
write_jobs(|j| j.hang_up()); write_jobs(|j| j.hang_up());
ExitCode::from(QUIT_CODE.load(Ordering::SeqCst) as u8) ExitCode::from(QUIT_CODE.load(Ordering::SeqCst) as u8)
} }

View File

@@ -800,10 +800,16 @@ impl ShedVi {
} }
pub fn swap_mode(&mut self, mode: &mut Box<dyn ViMode>) { pub fn swap_mode(&mut self, mode: &mut Box<dyn ViMode>) {
let pre_mode_change = read_logic(|l| l.get_autocmds(AutoCmdKind::PreModeChange));
pre_mode_change.exec();
std::mem::swap(&mut self.mode, mode); std::mem::swap(&mut self.mode, mode);
self.editor.set_cursor_clamp(self.mode.clamp_cursor()); self.editor.set_cursor_clamp(self.mode.clamp_cursor());
write_vars(|v| v.set_var("SHED_VI_MODE", VarKind::Str(self.mode.report_mode().to_string()), VarFlags::NONE)).ok(); write_vars(|v| v.set_var("SHED_VI_MODE", VarKind::Str(self.mode.report_mode().to_string()), VarFlags::NONE)).ok();
self.prompt.refresh().ok(); self.prompt.refresh().ok();
let post_mode_change = read_logic(|l| l.get_autocmds(AutoCmdKind::PostModeChange));
post_mode_change.exec();
} }
pub fn exec_cmd(&mut self, mut cmd: ViCmd) -> ShResult<()> { pub fn exec_cmd(&mut self, mut cmd: ViCmd) -> ShResult<()> {
@@ -811,8 +817,6 @@ impl ShedVi {
let mut is_insert_mode = false; let mut is_insert_mode = false;
if cmd.is_mode_transition() { if cmd.is_mode_transition() {
let count = cmd.verb_count(); let count = cmd.verb_count();
let pre_mode_change = read_logic(|l| l.get_autocmds(AutoCmdKind::PreModeChange));
pre_mode_change.exec();
let mut mode: Box<dyn ViMode> = if let ModeReport::Ex = self.mode.report_mode() && cmd.flags.contains(CmdFlags::EXIT_CUR_MODE) { let mut mode: Box<dyn ViMode> = if let ModeReport::Ex = self.mode.report_mode() && cmd.flags.contains(CmdFlags::EXIT_CUR_MODE) {
if let Some(saved) = self.saved_mode.take() { if let Some(saved) = self.saved_mode.take() {
@@ -892,8 +896,6 @@ impl ShedVi {
write_vars(|v| v.set_var("SHED_VI_MODE", VarKind::Str(self.mode.report_mode().to_string()), VarFlags::NONE))?; write_vars(|v| v.set_var("SHED_VI_MODE", VarKind::Str(self.mode.report_mode().to_string()), VarFlags::NONE))?;
self.prompt.refresh()?; self.prompt.refresh()?;
let post_mode_change = read_logic(|l| l.get_autocmds(AutoCmdKind::PostModeChange));
post_mode_change.exec();
return Ok(()); return Ok(());
} else if cmd.is_cmd_repeat() { } else if cmd.is_cmd_repeat() {
@@ -1377,6 +1379,12 @@ pub fn annotate_token(token: Tk) -> Vec<(usize, Marker)> {
token_chars.next(); // consume the escaped char token_chars.next(); // consume the escaped char
} }
} }
'\\' if qt_state.in_single() => {
token_chars.next();
if let Some(&(_,'\'')) = token_chars.peek() {
token_chars.next(); // consume the escaped single quote
}
}
'<' | '>' if !qt_state.in_quote() && cmd_sub_depth == 0 && proc_sub_depth == 0 => { '<' | '>' if !qt_state.in_quote() && cmd_sub_depth == 0 && proc_sub_depth == 0 => {
token_chars.next(); token_chars.next();
if let Some((_, proc_sub_ch)) = token_chars.peek() if let Some((_, proc_sub_ch)) = token_chars.peek()

View File

@@ -532,7 +532,8 @@ pub enum AutoCmdKind {
PrePrompt, PrePrompt,
PostPrompt, PostPrompt,
PreModeChange, PreModeChange,
PostModeChange PostModeChange,
OnExit
} }
impl Display for AutoCmdKind { impl Display for AutoCmdKind {
@@ -547,6 +548,7 @@ impl Display for AutoCmdKind {
Self::PostPrompt => write!(f, "post-prompt"), Self::PostPrompt => write!(f, "post-prompt"),
Self::PreModeChange => write!(f, "pre-mode-change"), Self::PreModeChange => write!(f, "pre-mode-change"),
Self::PostModeChange => write!(f, "post-mode-change"), Self::PostModeChange => write!(f, "post-mode-change"),
Self::OnExit => write!(f, "on-exit"),
} }
} }
} }
@@ -564,6 +566,7 @@ impl FromStr for AutoCmdKind {
"post-prompt" => Ok(Self::PostPrompt), "post-prompt" => Ok(Self::PostPrompt),
"pre-mode-change" => Ok(Self::PreModeChange), "pre-mode-change" => Ok(Self::PreModeChange),
"post-mode-change" => Ok(Self::PostModeChange), "post-mode-change" => Ok(Self::PostModeChange),
"on-exit" => Ok(Self::OnExit),
_ => Err(ShErr::simple( _ => Err(ShErr::simple(
ShErrKind::ParseErr, ShErrKind::ParseErr,
format!("Invalid autocmd kind: {}", s), format!("Invalid autocmd kind: {}", s),