Implement = (equalize/auto-indent) verb, fix dedent indexing, remove unimplemented screen-line motions, and clean up unreachable match arms

This commit is contained in:
2026-03-16 23:31:54 -04:00
parent db3f1b5108
commit ac8940f936
12 changed files with 244 additions and 229 deletions

View File

@@ -121,11 +121,7 @@ pub fn help(node: Node) -> ShResult<()> {
} }
pub fn open_help(content: &str, line: Option<usize>, file_name: Option<String>) -> ShResult<()> { pub fn open_help(content: &str, line: Option<usize>, file_name: Option<String>) -> ShResult<()> {
let pager = env::var("SHED_HPAGER") let pager = env::var("SHED_HPAGER").unwrap_or(env::var("PAGER").unwrap_or("less -R".into()));
.unwrap_or(
env::var("PAGER")
.unwrap_or("less -R".into()),
);
let line_arg = line.map(|ln| format!("+{ln}")).unwrap_or_default(); let line_arg = line.map(|ln| format!("+{ln}")).unwrap_or_default();
let prompt_arg = file_name let prompt_arg = file_name
.map(|name| format!("-Ps'{name}'")) .map(|name| format!("-Ps'{name}'"))

View File

@@ -38,7 +38,9 @@ use crate::prelude::*;
use crate::procio::borrow_fd; use crate::procio::borrow_fd;
use crate::readline::term::{LineWriter, RawModeGuard, raw_mode}; use crate::readline::term::{LineWriter, RawModeGuard, raw_mode};
use crate::readline::{Prompt, ReadlineEvent, ShedVi}; use crate::readline::{Prompt, ReadlineEvent, ShedVi};
use crate::signal::{GOT_SIGUSR1, GOT_SIGWINCH, JOB_DONE, QUIT_CODE, check_signals, sig_setup, signals_pending}; use crate::signal::{
GOT_SIGUSR1, GOT_SIGWINCH, JOB_DONE, QUIT_CODE, check_signals, sig_setup, signals_pending,
};
use crate::state::{ use crate::state::{
AutoCmdKind, read_logic, read_shopts, source_env, source_login, source_rc, write_jobs, AutoCmdKind, read_logic, read_shopts, source_env, source_login, source_rc, write_jobs,
write_meta, write_shopts, write_meta, write_shopts,

View File

@@ -32,13 +32,20 @@ use crate::{
test::double_bracket_test, test::double_bracket_test,
trap::{TrapTarget, trap}, trap::{TrapTarget, trap},
varcmds::{export, local, readonly, unset}, varcmds::{export, local, readonly, unset},
}, expand::{expand_aliases, expand_case_pattern, glob_to_regex}, jobs::{ChildProc, JobStack, attach_tty, dispatch_job}, libsh::{ },
expand::{expand_aliases, expand_case_pattern, glob_to_regex},
jobs::{ChildProc, JobStack, attach_tty, dispatch_job},
libsh::{
error::{ShErr, ShErrKind, ShResult, ShResultExt, next_color}, error::{ShErr, ShErrKind, ShResult, ShResultExt, next_color},
guards::{scope_guard, var_ctx_guard}, guards::{scope_guard, var_ctx_guard},
utils::RedirVecUtils, utils::RedirVecUtils,
}, prelude::*, procio::{IoMode, IoStack, PipeGenerator}, signal::{check_signals, signals_pending}, state::{ },
prelude::*,
procio::{IoMode, IoStack, PipeGenerator},
signal::{check_signals, signals_pending},
state::{
self, ShFunc, VarFlags, VarKind, read_logic, read_shopts, write_jobs, write_logic, write_vars, self, ShFunc, VarFlags, VarKind, read_logic, read_shopts, write_jobs, write_logic, write_vars,
} },
}; };
use super::{ use super::{

View File

@@ -12,15 +12,23 @@ use super::vicmd::{
ViCmd, Word, ViCmd, Word,
}; };
use crate::{ use crate::{
expand::expand_cmd_sub, libsh::{error::ShResult, guards::var_ctx_guard}, parse::{ expand::expand_cmd_sub,
Redir, RedirType, execute::exec_input, lex::{LexFlags, LexStream, QuoteState, Tk, TkFlags, TkRule} libsh::{error::ShResult, guards::var_ctx_guard},
}, prelude::*, procio::{IoFrame, IoMode, IoStack}, readline::{ parse::{
Redir, RedirType,
execute::exec_input,
lex::{LexFlags, LexStream, QuoteState, Tk, TkFlags, TkRule},
},
prelude::*,
procio::{IoFrame, IoMode, IoStack},
readline::{
history::History, history::History,
markers, markers,
register::{RegisterContent, write_register}, register::{RegisterContent, write_register},
term::RawModeGuard, term::{RawModeGuard, get_win_size},
vicmd::{ReadSrc, WriteDest}, vicmd::{ReadSrc, WriteDest},
}, state::{VarFlags, VarKind, read_shopts, write_meta, write_vars} },
state::{VarFlags, VarKind, read_shopts, write_meta, write_vars},
}; };
const PUNCTUATION: [&str; 3] = ["?", "!", "."]; const PUNCTUATION: [&str; 3] = ["?", "!", "."];
@@ -1012,12 +1020,31 @@ impl LineBuf {
if self.end_of_line() == self.cursor.max { if self.end_of_line() == self.cursor.max {
return None; return None;
} }
let target_line = self.cursor_line_number() + n; let target_line = self.cursor_line_number() + n - 1;
let start = self.start_of_line(); let start = self.start_of_line();
let (_, end) = self.line_bounds(target_line); let (_, end) = self.line_bounds(target_line);
Some((start, end)) Some((start, end))
} }
pub fn lines_in_range(&mut self, range: Range<usize>) -> Vec<(usize, usize)> {
let mut ranges = vec![];
let mut first_line = self.pos_line_number(range.start);
let mut last_line = self.pos_line_number(range.end);
(first_line, last_line) = ordered(first_line, last_line);
if first_line == last_line {
return vec![self.line_bounds(first_line)];
}
for line_no in first_line..last_line {
let (s, e) = self.line_bounds(line_no);
ranges.push((s, e));
}
ranges
}
pub fn line_bounds(&self, n: usize) -> (usize, usize) { pub fn line_bounds(&self, n: usize) -> (usize, usize) {
if n > self.total_lines() { if n > self.total_lines() {
panic!( panic!(
@@ -2129,12 +2156,16 @@ impl LineBuf {
self.buffer.replace_range(start..end, new); self.buffer.replace_range(start..end, new);
} }
pub fn calc_indent_level(&mut self) -> usize { pub fn calc_indent_level(&mut self) -> usize {
let to_cursor = self self.calc_indent_level_for_pos(self.cursor.get())
.slice_to_cursor() }
pub fn calc_indent_level_for_pos(&mut self, pos: usize) -> usize {
let slice = self
.slice_to(pos)
.map(|s| s.to_string()) .map(|s| s.to_string())
.unwrap_or(self.buffer.clone()); .unwrap_or(self.buffer.clone());
self.indent_ctx.calculate(&to_cursor) self.indent_ctx.calculate(&slice)
} }
pub fn eval_motion(&mut self, verb: Option<&Verb>, motion: MotionCmd) -> MotionKind { pub fn eval_motion(&mut self, verb: Option<&Verb>, motion: MotionCmd) -> MotionKind {
let buffer = self.buffer.clone(); let buffer = self.buffer.clone();
@@ -2338,7 +2369,7 @@ impl LineBuf {
let pos = if count == 1 { let pos = if count == 1 {
self.end_of_line_exclusive() self.end_of_line_exclusive()
} else if let Some((_, end)) = self.select_lines_down(count) { } else if let Some((_, end)) = self.select_lines_down(count) {
end end.saturating_sub(1)
} else { } else {
self.end_of_line_exclusive() self.end_of_line_exclusive()
}; };
@@ -2455,14 +2486,6 @@ impl LineBuf {
MotionKind::On(target_pos) MotionKind::On(target_pos)
} }
MotionCmd(_count, Motion::ScreenLineUp) => todo!(),
MotionCmd(_count, Motion::ScreenLineUpCharwise) => todo!(),
MotionCmd(_count, Motion::ScreenLineDown) => todo!(),
MotionCmd(_count, Motion::ScreenLineDownCharwise) => todo!(),
MotionCmd(_count, Motion::BeginningOfScreenLine) => todo!(),
MotionCmd(_count, Motion::FirstGraphicalOnScreenLine) => todo!(),
MotionCmd(_count, Motion::HalfOfScreen) => todo!(),
MotionCmd(_count, Motion::HalfOfScreenLineText) => todo!(),
MotionCmd(_count, Motion::WholeBuffer) => { MotionCmd(_count, Motion::WholeBuffer) => {
MotionKind::Exclusive((0, self.grapheme_indices().len())) MotionKind::Exclusive((0, self.grapheme_indices().len()))
} }
@@ -2503,8 +2526,9 @@ impl LineBuf {
final_end = final_end.min(self.cursor.max); final_end = final_end.min(self.cursor.max);
MotionKind::Exclusive((start, final_end)) MotionKind::Exclusive((start, final_end))
} }
MotionCmd(_count, Motion::RepeatMotion) => todo!(), MotionCmd(_count, Motion::RepeatMotion) | MotionCmd(_count, Motion::RepeatMotionRev) => {
MotionCmd(_count, Motion::RepeatMotionRev) => todo!(), unreachable!("already handled in readline/mod.rs")
}
MotionCmd(_count, Motion::Null) MotionCmd(_count, Motion::Null)
| MotionCmd(_count, Motion::Global(_)) | MotionCmd(_count, Motion::Global(_))
| MotionCmd(_count, Motion::NotGlobal(_)) => MotionKind::Null, | MotionCmd(_count, Motion::NotGlobal(_)) => MotionKind::Null,
@@ -3058,28 +3082,34 @@ impl LineBuf {
} }
#[allow(clippy::unnecessary_to_owned)] #[allow(clippy::unnecessary_to_owned)]
fn verb_dedent(&mut self, motion: MotionKind) -> ShResult<()> { fn verb_dedent(&mut self, motion: MotionKind) -> ShResult<()> {
let Some((start, mut end)) = self.range_from_motion(&motion) else { let Some((start, end)) = self.range_from_motion(&motion) else {
return Ok(()); return Ok(());
}; };
let end = end.min(self.grapheme_indices().len().saturating_sub(1));
// Collect tab positions to remove, then remove in reverse so indices stay valid
let mut to_remove = Vec::new();
if self.grapheme_at(start) == Some("\t") { if self.grapheme_at(start) == Some("\t") {
self.remove(start); to_remove.push(start);
} }
end = end.min(self.grapheme_indices().len().saturating_sub(1)); let range_indices = self.grapheme_indices()[start..end].to_vec();
let mut range_indices = self.grapheme_indices()[start..end].to_vec().into_iter(); let mut i = 0;
while let Some(idx) = range_indices.next() { while i < range_indices.len() {
let gr = self.grapheme_at(idx).unwrap(); let idx = range_indices[i];
if gr == "\n" { if self.grapheme_at(idx) == Some("\n") && i + 1 < range_indices.len() {
let Some(idx) = range_indices.next() else { let next_idx = range_indices[i + 1];
if self.grapheme_at(self.grapheme_indices().len().saturating_sub(1)) == Some("\t") { if self.grapheme_at(next_idx) == Some("\t") {
self.remove(self.grapheme_indices().len().saturating_sub(1)); to_remove.push(next_idx);
i += 1;
} }
break; }
}; i += 1;
if self.grapheme_at(idx) == Some("\t") { }
for idx in to_remove.into_iter().rev() {
self.remove(idx); self.remove(idx);
} }
}
}
match motion { match motion {
MotionKind::ExclusiveWithTargetCol((_, _), pos) MotionKind::ExclusiveWithTargetCol((_, _), pos)
| MotionKind::InclusiveWithTargetCol((_, _), pos) => { | MotionKind::InclusiveWithTargetCol((_, _), pos) => {
@@ -3091,6 +3121,29 @@ impl LineBuf {
} }
Ok(()) Ok(())
} }
fn verb_equalize(&mut self, motion: MotionKind) -> ShResult<()> {
let Some((s, e)) = self.range_from_motion(&motion) else {
return Ok(());
};
let lines = self.lines_in_range(s..e);
let target_col = self.cursor_col();
// reverse the list of line spans so that the spans stay valid
for (s, _) in lines.into_iter().rev() {
let indent_level = self.calc_indent_level_for_pos(s);
while self.grapheme_at(s).is_some_and(|c| c == "\t") {
self.remove(s)
}
for _ in 0..indent_level {
self.insert_at(s, '\t');
}
}
self.cursor.set(s);
self.cursor.add(target_col);
Ok(())
}
fn verb_insert_mode_line_break(&mut self, anchor: Anchor) -> ShResult<()> { fn verb_insert_mode_line_break(&mut self, anchor: Anchor) -> ShResult<()> {
let (mut start, end) = self.this_line_exclusive(); let (mut start, end) = self.this_line_exclusive();
let auto_indent = read_shopts(|o| o.prompt.auto_indent); let auto_indent = read_shopts(|o| o.prompt.auto_indent);
@@ -3296,7 +3349,7 @@ impl LineBuf {
Verb::ToLower => self.verb_case_transform(motion, CaseTransform::Lower)?, Verb::ToLower => self.verb_case_transform(motion, CaseTransform::Lower)?,
Verb::ToUpper => self.verb_case_transform(motion, CaseTransform::Upper)?, Verb::ToUpper => self.verb_case_transform(motion, CaseTransform::Upper)?,
Verb::Redo | Verb::Undo => self.verb_undo_redo(verb)?, Verb::Redo | Verb::Undo => self.verb_undo_redo(verb)?,
Verb::RepeatLast => todo!(), Verb::RepeatLast => unreachable!("already handled in readline.rs"),
Verb::Put(anchor) => self.verb_put(anchor, register)?, Verb::Put(anchor) => self.verb_put(anchor, register)?,
Verb::SwapVisualAnchor => self.verb_swap_visual_anchor(), Verb::SwapVisualAnchor => self.verb_swap_visual_anchor(),
Verb::JoinLines => self.verb_join_lines()?, Verb::JoinLines => self.verb_join_lines()?,
@@ -3304,7 +3357,7 @@ impl LineBuf {
Verb::Insert(string) => self.verb_insert(string), Verb::Insert(string) => self.verb_insert(string),
Verb::Indent => self.verb_indent(motion)?, Verb::Indent => self.verb_indent(motion)?,
Verb::Dedent => self.verb_dedent(motion)?, Verb::Dedent => self.verb_dedent(motion)?,
Verb::Equalize => todo!(), Verb::Equalize => self.verb_equalize(motion)?,
Verb::InsertModeLineBreak(anchor) => self.verb_insert_mode_line_break(anchor)?, Verb::InsertModeLineBreak(anchor) => self.verb_insert_mode_line_break(anchor)?,
Verb::AcceptLineOrNewline => self.verb_accept_line_or_newline()?, Verb::AcceptLineOrNewline => self.verb_accept_line_or_newline()?,
Verb::IncrementNumber(n) => self.verb_adjust_number(n as i64)?, Verb::IncrementNumber(n) => self.verb_adjust_number(n as i64)?,
@@ -3352,10 +3405,8 @@ impl LineBuf {
self.cursor.add(grapheme_count); self.cursor.add(grapheme_count);
} }
}, },
Verb::Write(dest) => { Verb::Write(dest) => match dest {
match dest { WriteDest::FileAppend(ref path_buf) | WriteDest::File(ref path_buf) => {
WriteDest::FileAppend(ref path_buf) |
WriteDest::File(ref path_buf) => {
let Ok(mut file) = (if matches!(dest, WriteDest::File(_)) { let Ok(mut file) = (if matches!(dest, WriteDest::File(_)) {
OpenOptions::new() OpenOptions::new()
.create(true) .create(true)
@@ -3363,10 +3414,7 @@ impl LineBuf {
.write(true) .write(true)
.open(path_buf) .open(path_buf)
} else { } else {
OpenOptions::new() OpenOptions::new().create(true).append(true).open(path_buf)
.create(true)
.append(true)
.open(path_buf)
}) else { }) else {
write_meta(|m| { write_meta(|m| {
m.post_system_message(format!("Failed to open file {}", path_buf.display())) m.post_system_message(format!("Failed to open file {}", path_buf.display()))
@@ -3375,7 +3423,10 @@ impl LineBuf {
}; };
if let Err(e) = file.write_all(self.as_str().as_bytes()) { if let Err(e) = file.write_all(self.as_str().as_bytes()) {
write_meta(|m| { write_meta(|m| {
m.post_system_message(format!("Failed to write to file {}: {e}", path_buf.display())) m.post_system_message(format!(
"Failed to write to file {}: {e}",
path_buf.display()
))
}); });
} }
return Ok(()); return Ok(());
@@ -3394,8 +3445,7 @@ impl LineBuf {
stack.push_frame(frame); stack.push_frame(frame);
exec_input(cmd, Some(stack), false, Some("ex write".into()))?; exec_input(cmd, Some(stack), false, Some("ex write".into()))?;
} }
} },
}
Verb::Edit(path) => { Verb::Edit(path) => {
let input = format!("$EDITOR {}", path.display()); let input = format!("$EDITOR {}", path.display());
exec_input(input, None, true, Some("ex edit".into()))?; exec_input(input, None, true, Some("ex edit".into()))?;

View File

@@ -675,7 +675,8 @@ impl ShedVi {
if let KeyEvent(KeyCode::Tab, mod_keys) = key { if let KeyEvent(KeyCode::Tab, mod_keys) = key {
if self.mode.report_mode() != ModeReport::Ex if self.mode.report_mode() != ModeReport::Ex
&& self.editor.attempt_history_expansion(&self.history) { && self.editor.attempt_history_expansion(&self.history)
{
// If history expansion occurred, don't attempt completion yet // If history expansion occurred, don't attempt completion yet
// allow the user to see the expanded command and accept or edit it before completing // allow the user to see the expanded command and accept or edit it before completing
return Ok(None); return Ok(None);
@@ -887,14 +888,12 @@ impl ShedVi {
let has_edit_verb = cmd.verb().is_some_and(|v| v.1.is_edit()); let has_edit_verb = cmd.verb().is_some_and(|v| v.1.is_edit());
let is_shell_cmd = cmd.verb().is_some_and(|v| matches!(v.1, Verb::ShellCmd(_))); let is_shell_cmd = cmd.verb().is_some_and(|v| matches!(v.1, Verb::ShellCmd(_)));
let is_ex_cmd = cmd.flags.contains(CmdFlags::IS_EX_CMD); let is_ex_cmd = cmd.flags.contains(CmdFlags::IS_EX_CMD);
log::debug!("is_ex_cmd: {is_ex_cmd}");
if is_shell_cmd { if is_shell_cmd {
self.old_layout = None; self.old_layout = None;
} }
if is_ex_cmd { if is_ex_cmd {
self.ex_history.push(cmd.raw_seq.clone()); self.ex_history.push(cmd.raw_seq.clone());
self.ex_history.reset(); self.ex_history.reset();
log::debug!("ex_history: {:?}", self.ex_history.entries());
} }
let before = self.editor.buffer.clone(); let before = self.editor.buffer.clone();
@@ -1416,11 +1415,7 @@ impl ShedVi {
self.editor.exec_cmd(cmd.clone())?; self.editor.exec_cmd(cmd.clone())?;
if self.mode.report_mode() == ModeReport::Visual if self.mode.report_mode() == ModeReport::Visual && cmd.verb().is_some() {
&& cmd
.verb()
.is_some_and(|v| v.1.is_edit() || v.1 == Verb::Yank)
{
self.editor.stop_selecting(); self.editor.stop_selecting();
let mut mode: Box<dyn ViMode> = Box::new(ViNormal::new()); let mut mode: Box<dyn ViMode> = Box::new(ViNormal::new());
self.swap_mode(&mut mode); self.swap_mode(&mut mode);

View File

@@ -338,16 +338,8 @@ pub enum Motion {
ForwardCharForced, ForwardCharForced,
LineUp, LineUp,
LineUpCharwise, LineUpCharwise,
ScreenLineUp,
ScreenLineUpCharwise,
LineDown, LineDown,
LineDownCharwise, LineDownCharwise,
ScreenLineDown,
ScreenLineDownCharwise,
BeginningOfScreenLine,
FirstGraphicalOnScreenLine,
HalfOfScreen,
HalfOfScreenLineText,
WholeBuffer, WholeBuffer,
StartOfBuffer, StartOfBuffer,
EndOfBuffer, EndOfBuffer,
@@ -387,12 +379,8 @@ impl Motion {
&self, &self,
Self::BeginningOfLine Self::BeginningOfLine
| Self::BeginningOfFirstWord | Self::BeginningOfFirstWord
| Self::BeginningOfScreenLine
| Self::FirstGraphicalOnScreenLine
| Self::LineDownCharwise | Self::LineDownCharwise
| Self::LineUpCharwise | Self::LineUpCharwise
| Self::ScreenLineUpCharwise
| Self::ScreenLineDownCharwise
| Self::ToColumn | Self::ToColumn
| Self::TextObj(TextObj::Sentence(_)) | Self::TextObj(TextObj::Sentence(_))
| Self::TextObj(TextObj::Paragraph(_)) | Self::TextObj(TextObj::Paragraph(_))
@@ -401,20 +389,13 @@ impl Motion {
| Self::ToBrace(_) | Self::ToBrace(_)
| Self::ToBracket(_) | Self::ToBracket(_)
| Self::ToParen(_) | Self::ToParen(_)
| Self::ScreenLineDown
| Self::ScreenLineUp
| Self::Range(_, _) | Self::Range(_, _)
) )
} }
pub fn is_linewise(&self) -> bool { pub fn is_linewise(&self) -> bool {
matches!( matches!(
self, self,
Self::WholeLineInclusive Self::WholeLineInclusive | Self::WholeLineExclusive | Self::LineUp | Self::LineDown
| Self::WholeLineExclusive
| Self::LineUp
| Self::LineDown
| Self::ScreenLineDown
| Self::ScreenLineUp
) )
} }
} }

View File

@@ -81,9 +81,15 @@ pub trait ViMode {
fn as_replay(&self) -> Option<CmdReplay>; fn as_replay(&self) -> Option<CmdReplay>;
fn cursor_style(&self) -> String; fn cursor_style(&self) -> String;
fn pending_seq(&self) -> Option<String>; fn pending_seq(&self) -> Option<String>;
fn pending_cursor(&self) -> Option<usize> { None } fn pending_cursor(&self) -> Option<usize> {
fn editor(&mut self) -> Option<&mut LineBuf> { None } None
fn history(&mut self) -> Option<&mut History> { None } }
fn editor(&mut self) -> Option<&mut LineBuf> {
None
}
fn history(&mut self) -> Option<&mut History> {
None
}
fn move_cursor_on_undo(&self) -> bool; fn move_cursor_on_undo(&self) -> bool;
fn clamp_cursor(&self) -> bool; fn clamp_cursor(&self) -> bool;
fn hist_scroll_start_pos(&self) -> Option<To>; fn hist_scroll_start_pos(&self) -> Option<To>;

View File

@@ -450,26 +450,10 @@ impl ViNormal {
Motion::WordMotion(To::End, Word::Big, Direction::Backward), Motion::WordMotion(To::End, Word::Big, Direction::Backward),
)); ));
} }
'k' => {
chars = chars_clone;
break 'motion_parse Some(MotionCmd(count, Motion::ScreenLineUp));
}
'j' => {
chars = chars_clone;
break 'motion_parse Some(MotionCmd(count, Motion::ScreenLineDown));
}
'_' => { '_' => {
chars = chars_clone; chars = chars_clone;
break 'motion_parse Some(MotionCmd(count, Motion::EndOfLastWord)); break 'motion_parse Some(MotionCmd(count, Motion::EndOfLastWord));
} }
'0' => {
chars = chars_clone;
break 'motion_parse Some(MotionCmd(count, Motion::BeginningOfScreenLine));
}
'^' => {
chars = chars_clone;
break 'motion_parse Some(MotionCmd(count, Motion::FirstGraphicalOnScreenLine));
}
_ => return self.quit_parse(), _ => return self.quit_parse(),
} }
} }

View File

@@ -376,16 +376,6 @@ impl ViVisual {
Motion::WordMotion(To::End, Word::Big, Direction::Backward), Motion::WordMotion(To::End, Word::Big, Direction::Backward),
)); ));
} }
'k' => {
chars_clone.next();
chars = chars_clone;
break 'motion_parse Some(MotionCmd(count, Motion::ScreenLineUp));
}
'j' => {
chars_clone.next();
chars = chars_clone;
break 'motion_parse Some(MotionCmd(count, Motion::ScreenLineDown));
}
_ => return self.quit_parse(), _ => return self.quit_parse(),
} }
} else { } else {

View File

@@ -3,7 +3,10 @@ use std::{
sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering}, sync::atomic::{AtomicBool, AtomicI32, AtomicU64, Ordering},
}; };
use nix::{sys::signal::{SaFlags, SigAction, sigaction}, unistd::getpid}; use nix::{
sys::signal::{SaFlags, SigAction, sigaction},
unistd::getpid,
};
use crate::{ use crate::{
builtin::trap::TrapTarget, builtin::trap::TrapTarget,
@@ -335,7 +338,8 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> {
for status in &statuses { for status in &statuses {
if let WtStat::Signaled(_, sig, _) = status if let WtStat::Signaled(_, sig, _) = status
&& *sig == Signal::SIGINT { && *sig == Signal::SIGINT
{
// Necessary to interrupt stuff like shell loops // Necessary to interrupt stuff like shell loops
kill(getpid(), Signal::SIGINT).ok(); kill(getpid(), Signal::SIGINT).ok();
} }