mail/compose.rs: add ability to kill embed process
If embed editor process is unresponsive, there was no way to kill it. Add force kill option by pressing Ctrl+C.pull/144/head
parent
9cb66ef818
commit
f4e0970d46
|
@ -57,6 +57,13 @@ enum EmbedStatus {
|
||||||
Running(Arc<Mutex<EmbedTerminal>>, File),
|
Running(Arc<Mutex<EmbedTerminal>>, File),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl EmbedStatus {
|
||||||
|
#[inline(always)]
|
||||||
|
fn is_stopped(&self) -> bool {
|
||||||
|
matches!(self, Self::Stopped(_, _))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::ops::Deref for EmbedStatus {
|
impl std::ops::Deref for EmbedStatus {
|
||||||
type Target = Arc<Mutex<EmbedTerminal>>;
|
type Target = Arc<Mutex<EmbedTerminal>>;
|
||||||
fn deref(&self) -> &Arc<Mutex<EmbedTerminal>> {
|
fn deref(&self) -> &Arc<Mutex<EmbedTerminal>> {
|
||||||
|
@ -696,6 +703,33 @@ To: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn update_from_file(&mut self, file: File, context: &mut Context) -> bool {
|
||||||
|
let result = file.read_to_string();
|
||||||
|
match Draft::from_str(result.as_str()) {
|
||||||
|
Ok(mut new_draft) => {
|
||||||
|
std::mem::swap(self.draft.attachments_mut(), new_draft.attachments_mut());
|
||||||
|
if self.draft != new_draft {
|
||||||
|
self.has_changes = true;
|
||||||
|
}
|
||||||
|
self.draft = new_draft;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
context.replies.push_back(UIEvent::Notification(
|
||||||
|
Some("Could not parse draft headers correctly.".to_string()),
|
||||||
|
format!(
|
||||||
|
"{}\nThe invalid text has been set as the body of your draft",
|
||||||
|
&err
|
||||||
|
),
|
||||||
|
Some(NotificationType::Error(melib::error::ErrorKind::None)),
|
||||||
|
));
|
||||||
|
self.draft.set_body(result);
|
||||||
|
self.has_changes = true;
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Composer {
|
impl Component for Composer {
|
||||||
|
@ -854,7 +888,23 @@ impl Component for Composer {
|
||||||
((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))),
|
((0, 0), pos_dec(guard.grid.terminal_size, (1, 1))),
|
||||||
);
|
);
|
||||||
change_colors(grid, embed_area, Color::Byte(8), theme_default.bg);
|
change_colors(grid, embed_area, Color::Byte(8), theme_default.bg);
|
||||||
const STOPPED_MESSAGE: &str = "process has stopped, press 'e' to re-activate";
|
let our_map: ShortcutMap =
|
||||||
|
account_settings!(context[self.account_hash].shortcuts.composing)
|
||||||
|
.key_values();
|
||||||
|
let mut shortcuts: ShortcutMaps = Default::default();
|
||||||
|
shortcuts.insert(Composer::DESCRIPTION, our_map);
|
||||||
|
let stopped_message: String =
|
||||||
|
format!("Process with PID {} has stopped.", guard.child_pid);
|
||||||
|
let stopped_message_2: String = format!(
|
||||||
|
"-press '{}' (edit_mail shortcut) to re-activate.",
|
||||||
|
shortcuts[Self::DESCRIPTION]["edit_mail"]
|
||||||
|
);
|
||||||
|
const STOPPED_MESSAGE_3: &str =
|
||||||
|
"-press Ctrl-C to forcefully kill it and return to editor.";
|
||||||
|
let max_len = std::cmp::max(
|
||||||
|
stopped_message.len(),
|
||||||
|
std::cmp::max(stopped_message_2.len(), STOPPED_MESSAGE_3.len()),
|
||||||
|
);
|
||||||
let inner_area = create_box(
|
let inner_area = create_box(
|
||||||
grid,
|
grid,
|
||||||
(
|
(
|
||||||
|
@ -862,22 +912,34 @@ impl Component for Composer {
|
||||||
pos_inc(
|
pos_inc(
|
||||||
upper_left!(body_area),
|
upper_left!(body_area),
|
||||||
(
|
(
|
||||||
std::cmp::min(STOPPED_MESSAGE.len() + 5, width!(body_area)),
|
std::cmp::min(max_len + 5, width!(body_area)),
|
||||||
std::cmp::min(5, height!(body_area)),
|
std::cmp::min(5, height!(body_area)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
clear_area(grid, inner_area, theme_default);
|
clear_area(grid, inner_area, theme_default);
|
||||||
write_string_to_grid(
|
for (i, l) in [
|
||||||
STOPPED_MESSAGE,
|
stopped_message.as_str(),
|
||||||
grid,
|
stopped_message_2.as_str(),
|
||||||
theme_default.fg,
|
STOPPED_MESSAGE_3,
|
||||||
theme_default.bg,
|
]
|
||||||
theme_default.attrs,
|
.iter()
|
||||||
inner_area,
|
.enumerate()
|
||||||
Some(get_x(upper_left!(inner_area))),
|
{
|
||||||
);
|
write_string_to_grid(
|
||||||
|
l,
|
||||||
|
grid,
|
||||||
|
theme_default.fg,
|
||||||
|
theme_default.bg,
|
||||||
|
theme_default.attrs,
|
||||||
|
(
|
||||||
|
pos_inc((0, i), upper_left!(inner_area)),
|
||||||
|
bottom_right!(inner_area),
|
||||||
|
),
|
||||||
|
Some(get_x(upper_left!(inner_area))),
|
||||||
|
);
|
||||||
|
}
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
self.dirty = false;
|
self.dirty = false;
|
||||||
return;
|
return;
|
||||||
|
@ -1356,6 +1418,7 @@ impl Component for Composer {
|
||||||
match embed_guard.is_active() {
|
match embed_guard.is_active() {
|
||||||
Ok(WaitStatus::Exited(_, exit_code)) => {
|
Ok(WaitStatus::Exited(_, exit_code)) => {
|
||||||
drop(embed_guard);
|
drop(embed_guard);
|
||||||
|
let embed = self.embed.take();
|
||||||
if exit_code != 0 {
|
if exit_code != 0 {
|
||||||
context.replies.push_back(UIEvent::Notification(
|
context.replies.push_back(UIEvent::Notification(
|
||||||
None,
|
None,
|
||||||
|
@ -1367,31 +1430,9 @@ impl Component for Composer {
|
||||||
melib::error::ErrorKind::External,
|
melib::error::ErrorKind::External,
|
||||||
)),
|
)),
|
||||||
));
|
));
|
||||||
} else if let EmbedStatus::Running(_, f) = embed {
|
} else if let Some(EmbedStatus::Running(_, file)) = embed {
|
||||||
let result = f.read_to_string();
|
self.update_from_file(file, context);
|
||||||
match Draft::from_str(result.as_str()) {
|
|
||||||
Ok(mut new_draft) => {
|
|
||||||
std::mem::swap(
|
|
||||||
self.draft.attachments_mut(),
|
|
||||||
new_draft.attachments_mut(),
|
|
||||||
);
|
|
||||||
if self.draft != new_draft {
|
|
||||||
self.has_changes = true;
|
|
||||||
}
|
|
||||||
self.draft = new_draft;
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
context.replies.push_back(UIEvent::Notification(
|
|
||||||
Some("Could not parse draft headers correctly.".to_string()),
|
|
||||||
format!("{}\nThe invalid text has been set as the body of your draft", &err),
|
|
||||||
Some(NotificationType::Error(melib::error::ErrorKind::None)),
|
|
||||||
));
|
|
||||||
self.draft.set_body(result);
|
|
||||||
self.has_changes = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self.embed = None;
|
|
||||||
self.initialized = false;
|
self.initialized = false;
|
||||||
self.mode = ViewMode::Edit;
|
self.mode = ViewMode::Edit;
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
|
@ -1587,6 +1628,32 @@ impl Component for Composer {
|
||||||
self.set_dirty(true);
|
self.set_dirty(true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
UIEvent::Input(Key::Ctrl('c'))
|
||||||
|
if self.embed.is_some() && self.embed.as_ref().unwrap().is_stopped() =>
|
||||||
|
{
|
||||||
|
match self.embed.take() {
|
||||||
|
Some(EmbedStatus::Running(embed, file))
|
||||||
|
| Some(EmbedStatus::Stopped(embed, file)) => {
|
||||||
|
let guard = embed.lock().unwrap();
|
||||||
|
guard.wake_up();
|
||||||
|
guard.terminate();
|
||||||
|
self.update_from_file(file, context);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
context.replies.push_back(UIEvent::Notification(
|
||||||
|
None,
|
||||||
|
"Subprocess was killed by SIGTERM signal".to_string(),
|
||||||
|
Some(NotificationType::Error(melib::error::ErrorKind::External)),
|
||||||
|
));
|
||||||
|
self.initialized = false;
|
||||||
|
self.mode = ViewMode::Edit;
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::ChangeMode(UIMode::Normal));
|
||||||
|
self.set_dirty(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
UIEvent::Input(ref key)
|
UIEvent::Input(ref key)
|
||||||
if self.mode.is_edit()
|
if self.mode.is_edit()
|
||||||
&& shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
|
&& shortcut!(key == shortcuts[Self::DESCRIPTION]["edit_mail"]) =>
|
||||||
|
|
|
@ -109,6 +109,12 @@ impl EmbedTerminal {
|
||||||
let _ = nix::sys::signal::kill(debug!(self.child_pid), nix::sys::signal::SIGSTOP);
|
let _ = nix::sys::signal::kill(debug!(self.child_pid), nix::sys::signal::SIGSTOP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn terminate(&self) {
|
||||||
|
let _ = nix::sys::signal::kill(self.child_pid, nix::sys::signal::SIGTERM);
|
||||||
|
std::thread::sleep(std::time::Duration::from_millis(150));
|
||||||
|
let _ = waitpid(self.child_pid, Some(WaitPidFlag::WNOHANG));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_active(&self) -> Result<WaitStatus> {
|
pub fn is_active(&self) -> Result<WaitStatus> {
|
||||||
debug!(waitpid(self.child_pid, Some(WaitPidFlag::WNOHANG),))
|
debug!(waitpid(self.child_pid, Some(WaitPidFlag::WNOHANG),))
|
||||||
.map_err(|e| MeliError::new(e.to_string()))
|
.map_err(|e| MeliError::new(e.to_string()))
|
||||||
|
|
Loading…
Reference in New Issue