Implement = (equalize/auto-indent) verb, fix dedent indexing, remove unimplemented screen-line motions, and clean up unreachable match arms
This commit is contained in:
@@ -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}'"))
|
||||||
|
|||||||
12
src/jobs.rs
12
src/jobs.rs
@@ -873,12 +873,12 @@ pub fn wait_fg(job: Job, interactive: bool) -> ShResult<()> {
|
|||||||
write_jobs(|j| j.fg_to_bg(*status))?;
|
write_jobs(|j| j.fg_to_bg(*status))?;
|
||||||
}
|
}
|
||||||
WtStat::Signaled(_, sig, _) => {
|
WtStat::Signaled(_, sig, _) => {
|
||||||
if *sig == Signal::SIGINT {
|
if *sig == Signal::SIGINT {
|
||||||
// interrupt propagates to the shell
|
// interrupt propagates to the shell
|
||||||
// necessary for interrupting stuff like
|
// necessary for interrupting stuff like
|
||||||
// while/for loops
|
// while/for loops
|
||||||
kill(getpid(), Signal::SIGINT)?;
|
kill(getpid(), Signal::SIGINT)?;
|
||||||
} else if *sig == Signal::SIGTSTP {
|
} else if *sig == Signal::SIGTSTP {
|
||||||
was_stopped = true;
|
was_stopped = true;
|
||||||
write_jobs(|j| j.fg_to_bg(*status))?;
|
write_jobs(|j| j.fg_to_bg(*status))?;
|
||||||
}
|
}
|
||||||
|
|||||||
24
src/main.rs
24
src/main.rs
@@ -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,
|
||||||
@@ -285,16 +287,16 @@ fn shed_interactive(args: ShedArgs) -> ShResult<()> {
|
|||||||
readline.prompt_mut().refresh();
|
readline.prompt_mut().refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
if GOT_SIGUSR1.swap(false, Ordering::SeqCst) {
|
if GOT_SIGUSR1.swap(false, Ordering::SeqCst) {
|
||||||
log::info!("SIGUSR1 received: refreshing readline state");
|
log::info!("SIGUSR1 received: refreshing readline state");
|
||||||
readline.mark_dirty();
|
readline.mark_dirty();
|
||||||
readline.prompt_mut().refresh();
|
readline.prompt_mut().refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
readline.print_line(false)?;
|
readline.print_line(false)?;
|
||||||
|
|
||||||
// Poll for
|
// Poll for
|
||||||
// stdin input
|
// stdin input
|
||||||
let mut fds = [PollFd::new(
|
let mut fds = [PollFd::new(
|
||||||
unsafe { BorrowedFd::borrow_raw(*TTY_FILENO) },
|
unsafe { BorrowedFd::borrow_raw(*TTY_FILENO) },
|
||||||
PollFlags::POLLIN,
|
PollFlags::POLLIN,
|
||||||
@@ -442,10 +444,10 @@ fn handle_readline_event(readline: &mut ShedVi, event: ShResult<ReadlineEvent>)
|
|||||||
}) {
|
}) {
|
||||||
// CleanExit signals an intentional shell exit; any other error is printed.
|
// CleanExit signals an intentional shell exit; any other error is printed.
|
||||||
match e.kind() {
|
match e.kind() {
|
||||||
ShErrKind::Interrupt => {
|
ShErrKind::Interrupt => {
|
||||||
// We got Ctrl+C during command execution
|
// We got Ctrl+C during command execution
|
||||||
// Just fall through here
|
// Just fall through here
|
||||||
}
|
}
|
||||||
ShErrKind::CleanExit(code) => {
|
ShErrKind::CleanExit(code) => {
|
||||||
QUIT_CODE.store(*code, Ordering::SeqCst);
|
QUIT_CODE.store(*code, Ordering::SeqCst);
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
|
|||||||
@@ -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::{
|
||||||
@@ -268,10 +275,10 @@ impl Dispatcher {
|
|||||||
}
|
}
|
||||||
pub fn dispatch_node(&mut self, node: Node) -> ShResult<()> {
|
pub fn dispatch_node(&mut self, node: Node) -> ShResult<()> {
|
||||||
while signals_pending() {
|
while signals_pending() {
|
||||||
// If we have received SIGINT,
|
// If we have received SIGINT,
|
||||||
// this will stop the execution here
|
// this will stop the execution here
|
||||||
// and propagate back to the functions in main.rs
|
// and propagate back to the functions in main.rs
|
||||||
check_signals()?;
|
check_signals()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
match node.class {
|
match node.class {
|
||||||
|
|||||||
@@ -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;
|
|
||||||
};
|
|
||||||
if self.grapheme_at(idx) == Some("\t") {
|
|
||||||
self.remove(idx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for idx in to_remove.into_iter().rev() {
|
||||||
|
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,54 +3405,51 @@ 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) |
|
let Ok(mut file) = (if matches!(dest, WriteDest::File(_)) {
|
||||||
WriteDest::File(ref path_buf) => {
|
OpenOptions::new()
|
||||||
let Ok(mut file) = (if matches!(dest, WriteDest::File(_)) {
|
.create(true)
|
||||||
OpenOptions::new()
|
.truncate(true)
|
||||||
.create(true)
|
.write(true)
|
||||||
.truncate(true)
|
.open(path_buf)
|
||||||
.write(true)
|
} else {
|
||||||
.open(path_buf)
|
OpenOptions::new().create(true).append(true).open(path_buf)
|
||||||
} else {
|
}) else {
|
||||||
OpenOptions::new()
|
write_meta(|m| {
|
||||||
.create(true)
|
m.post_system_message(format!("Failed to open file {}", path_buf.display()))
|
||||||
.append(true)
|
});
|
||||||
.open(path_buf)
|
return Ok(());
|
||||||
}) else {
|
};
|
||||||
write_meta(|m| {
|
if let Err(e) = file.write_all(self.as_str().as_bytes()) {
|
||||||
m.post_system_message(format!("Failed to open file {}", path_buf.display()))
|
write_meta(|m| {
|
||||||
});
|
m.post_system_message(format!(
|
||||||
return Ok(());
|
"Failed to write to file {}: {e}",
|
||||||
};
|
path_buf.display()
|
||||||
if let Err(e) = file.write_all(self.as_str().as_bytes()) {
|
))
|
||||||
write_meta(|m| {
|
});
|
||||||
m.post_system_message(format!("Failed to write to file {}: {e}", path_buf.display()))
|
}
|
||||||
});
|
return Ok(());
|
||||||
}
|
}
|
||||||
return Ok(());
|
WriteDest::Cmd(cmd) => {
|
||||||
}
|
let buf = self.as_str().to_string();
|
||||||
WriteDest::Cmd(cmd) => {
|
let io_mode = IoMode::Buffer {
|
||||||
let buf = self.as_str().to_string();
|
tgt_fd: STDIN_FILENO,
|
||||||
let io_mode = IoMode::Buffer {
|
buf,
|
||||||
tgt_fd: STDIN_FILENO,
|
flags: TkFlags::IS_HEREDOC | TkFlags::LIT_HEREDOC,
|
||||||
buf,
|
};
|
||||||
flags: TkFlags::IS_HEREDOC | TkFlags::LIT_HEREDOC,
|
let redir = Redir::new(io_mode, RedirType::Input);
|
||||||
};
|
let mut frame = IoFrame::new();
|
||||||
let redir = Redir::new(io_mode, RedirType::Input);
|
frame.push(redir);
|
||||||
let mut frame = IoFrame::new();
|
let mut stack = IoStack::new();
|
||||||
frame.push(redir);
|
stack.push_frame(frame);
|
||||||
let mut stack = IoStack::new();
|
exec_input(cmd, Some(stack), false, Some("ex write".into()))?;
|
||||||
stack.push_frame(frame);
|
}
|
||||||
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()))?;
|
||||||
}
|
}
|
||||||
Verb::Normal(_) | Verb::Substitute(..) | Verb::RepeatSubstitute | Verb::RepeatGlobal => {}
|
Verb::Normal(_) | Verb::Substitute(..) | Verb::RepeatSubstitute | Verb::RepeatGlobal => {}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -346,17 +346,17 @@ impl ShedVi {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mutable reference to the currently focused editor
|
/// A mutable reference to the currently focused editor
|
||||||
/// This includes the main LineBuf, and sub-editors for modes like Ex mode.
|
/// This includes the main LineBuf, and sub-editors for modes like Ex mode.
|
||||||
pub fn focused_editor(&mut self) -> &mut LineBuf {
|
pub fn focused_editor(&mut self) -> &mut LineBuf {
|
||||||
self.mode.editor().unwrap_or(&mut self.editor)
|
self.mode.editor().unwrap_or(&mut self.editor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A mutable reference to the currently focused history, if any.
|
/// A mutable reference to the currently focused history, if any.
|
||||||
/// This includes the main history struct, and history for sub-editors like Ex mode.
|
/// This includes the main history struct, and history for sub-editors like Ex mode.
|
||||||
pub fn focused_history(&mut self) -> &mut History {
|
pub fn focused_history(&mut self) -> &mut History {
|
||||||
self.mode.history().unwrap_or(&mut self.history)
|
self.mode.history().unwrap_or(&mut self.history)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Feed raw bytes from stdin into the reader's buffer
|
/// Feed raw bytes from stdin into the reader's buffer
|
||||||
pub fn feed_bytes(&mut self, bytes: &[u8]) {
|
pub fn feed_bytes(&mut self, bytes: &[u8]) {
|
||||||
@@ -475,21 +475,21 @@ impl ShedVi {
|
|||||||
SelectorResponse::Accept(cmd) => {
|
SelectorResponse::Accept(cmd) => {
|
||||||
let post_cmds = read_logic(|l| l.get_autocmds(AutoCmdKind::OnHistorySelect));
|
let post_cmds = read_logic(|l| l.get_autocmds(AutoCmdKind::OnHistorySelect));
|
||||||
|
|
||||||
{
|
{
|
||||||
let editor = self.focused_editor();
|
let editor = self.focused_editor();
|
||||||
editor.set_buffer(cmd.to_string());
|
editor.set_buffer(cmd.to_string());
|
||||||
editor.move_cursor_to_end();
|
editor.move_cursor_to_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
.history
|
.history
|
||||||
.update_pending_cmd((self.editor.as_str(), self.editor.cursor.get()));
|
.update_pending_cmd((self.editor.as_str(), self.editor.cursor.get()));
|
||||||
self.editor.set_hint(None);
|
self.editor.set_hint(None);
|
||||||
{
|
{
|
||||||
let mut writer = std::mem::take(&mut self.writer);
|
let mut writer = std::mem::take(&mut self.writer);
|
||||||
self.focused_history().fuzzy_finder.clear(&mut writer)?;
|
self.focused_history().fuzzy_finder.clear(&mut writer)?;
|
||||||
self.writer = writer;
|
self.writer = writer;
|
||||||
}
|
}
|
||||||
self.focused_history().fuzzy_finder.reset();
|
self.focused_history().fuzzy_finder.reset();
|
||||||
|
|
||||||
with_vars([("_HIST_ENTRY".into(), cmd.clone())], || {
|
with_vars([("_HIST_ENTRY".into(), cmd.clone())], || {
|
||||||
@@ -513,11 +513,11 @@ impl ShedVi {
|
|||||||
post_cmds.exec();
|
post_cmds.exec();
|
||||||
|
|
||||||
self.editor.set_hint(None);
|
self.editor.set_hint(None);
|
||||||
{
|
{
|
||||||
let mut writer = std::mem::take(&mut self.writer);
|
let mut writer = std::mem::take(&mut self.writer);
|
||||||
self.focused_history().fuzzy_finder.clear(&mut writer)?;
|
self.focused_history().fuzzy_finder.clear(&mut writer)?;
|
||||||
self.writer = writer;
|
self.writer = writer;
|
||||||
}
|
}
|
||||||
write_vars(|v| {
|
write_vars(|v| {
|
||||||
v.set_var(
|
v.set_var(
|
||||||
"SHED_VI_MODE",
|
"SHED_VI_MODE",
|
||||||
@@ -674,8 +674,9 @@ 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();
|
||||||
@@ -1032,11 +1031,11 @@ impl ShedVi {
|
|||||||
let one_line = new_layout.end.row == 0;
|
let one_line = new_layout.end.row == 0;
|
||||||
|
|
||||||
self.completer.clear(&mut self.writer)?;
|
self.completer.clear(&mut self.writer)?;
|
||||||
{
|
{
|
||||||
let mut writer = std::mem::take(&mut self.writer);
|
let mut writer = std::mem::take(&mut self.writer);
|
||||||
self.focused_history().fuzzy_finder.clear(&mut writer)?;
|
self.focused_history().fuzzy_finder.clear(&mut writer)?;
|
||||||
self.writer = writer;
|
self.writer = writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(layout) = self.old_layout.as_ref() {
|
if let Some(layout) = self.old_layout.as_ref() {
|
||||||
self.writer.clear_rows(layout)?;
|
self.writer.clear_rows(layout)?;
|
||||||
@@ -1133,11 +1132,11 @@ impl ShedVi {
|
|||||||
.fuzzy_finder
|
.fuzzy_finder
|
||||||
.set_prompt_line_context(preceding_width, new_layout.cursor.col);
|
.set_prompt_line_context(preceding_width, new_layout.cursor.col);
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut writer = std::mem::take(&mut self.writer);
|
let mut writer = std::mem::take(&mut self.writer);
|
||||||
self.focused_history().fuzzy_finder.draw(&mut writer)?;
|
self.focused_history().fuzzy_finder.draw(&mut writer)?;
|
||||||
self.writer = writer;
|
self.writer = writer;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.old_layout = Some(new_layout);
|
self.old_layout = Some(new_layout);
|
||||||
self.needs_redraw = false;
|
self.needs_redraw = false;
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,13 +154,13 @@ impl ViMode for ViEx {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn editor(&mut self) -> Option<&mut LineBuf> {
|
fn editor(&mut self) -> Option<&mut LineBuf> {
|
||||||
Some(&mut self.pending_cmd.buf)
|
Some(&mut self.pending_cmd.buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn history(&mut self) -> Option<&mut History> {
|
fn history(&mut self) -> Option<&mut History> {
|
||||||
Some(&mut self.pending_cmd.history)
|
Some(&mut self.pending_cmd.history)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cursor_style(&self) -> String {
|
fn cursor_style(&self) -> String {
|
||||||
"\x1b[3 q".to_string()
|
"\x1b[3 q".to_string()
|
||||||
@@ -338,13 +338,13 @@ fn parse_read(chars: &mut Peekable<Chars<'_>>) -> Result<Option<Verb>, Option<St
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_path(path: &str) -> Result<PathBuf, Option<String>> {
|
fn get_path(path: &str) -> Result<PathBuf, Option<String>> {
|
||||||
log::debug!("Expanding path: {}", path);
|
log::debug!("Expanding path: {}", path);
|
||||||
let expanded = Expander::from_raw(path, TkFlags::empty())
|
let expanded = Expander::from_raw(path, TkFlags::empty())
|
||||||
.map_err(|e| Some(format!("Error expanding path: {}", e)))?
|
.map_err(|e| Some(format!("Error expanding path: {}", e)))?
|
||||||
.expand()
|
.expand()
|
||||||
.map_err(|e| Some(format!("Error expanding path: {}", e)))?
|
.map_err(|e| Some(format!("Error expanding path: {}", e)))?
|
||||||
.join(" ");
|
.join(" ");
|
||||||
log::debug!("Expanded path: {}", expanded);
|
log::debug!("Expanded path: {}", expanded);
|
||||||
Ok(PathBuf::from(&expanded))
|
Ok(PathBuf::from(&expanded))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>;
|
||||||
|
|||||||
@@ -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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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,
|
||||||
@@ -98,10 +101,10 @@ pub fn check_signals() -> ShResult<()> {
|
|||||||
GOT_SIGWINCH.store(true, Ordering::SeqCst);
|
GOT_SIGWINCH.store(true, Ordering::SeqCst);
|
||||||
run_trap(Signal::SIGWINCH)?;
|
run_trap(Signal::SIGWINCH)?;
|
||||||
}
|
}
|
||||||
if got_signal(Signal::SIGUSR1) {
|
if got_signal(Signal::SIGUSR1) {
|
||||||
GOT_SIGUSR1.store(true, Ordering::SeqCst);
|
GOT_SIGUSR1.store(true, Ordering::SeqCst);
|
||||||
run_trap(Signal::SIGUSR1)?;
|
run_trap(Signal::SIGUSR1)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for sig in MISC_SIGNALS {
|
for sig in MISC_SIGNALS {
|
||||||
if got_signal(sig) {
|
if got_signal(sig) {
|
||||||
@@ -333,13 +336,14 @@ pub fn child_exited(pid: Pid, status: WtStat) -> ShResult<()> {
|
|||||||
let job_complete_msg = job.display(&job_order, JobCmdFlags::PIDS).to_string();
|
let job_complete_msg = job.display(&job_order, JobCmdFlags::PIDS).to_string();
|
||||||
let statuses = job.get_stats();
|
let statuses = job.get_stats();
|
||||||
|
|
||||||
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
|
{
|
||||||
kill(getpid(), Signal::SIGINT).ok();
|
// Necessary to interrupt stuff like shell loops
|
||||||
}
|
kill(getpid(), Signal::SIGINT).ok();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(pipe_status) = Job::pipe_status(&statuses) {
|
if let Some(pipe_status) = Job::pipe_status(&statuses) {
|
||||||
let pipe_status = pipe_status
|
let pipe_status = pipe_status
|
||||||
|
|||||||
Reference in New Issue
Block a user