mirror of
https://github.com/zed-industries/zed.git
synced 2024-11-08 07:35:01 +03:00
Show task summary in its terminal after it stops running (#10615)
Based on https://github.com/alacritty/alacritty/issues/7795 Unknown error code commands (now includes the interrupted ones): ![image](https://github.com/zed-industries/zed/assets/2690773/801868bc-081c-453c-a353-233d4397bda9) Successful command: ![image](https://github.com/zed-industries/zed/assets/2690773/874377c7-c967-4a6f-8a89-ec7bf398a8b3) Unsuccessful command: ![image](https://github.com/zed-industries/zed/assets/2690773/6c99dc5d-d324-41e9-a71b-5d0bf705de27) The "design", including wordings and special characters, is not final, suggestions are welcome. The main idea was to somehow distinguish the appended lines without occupying extra vertical space. Release Notes: - Added task summary output into corresponding terminal tabs
This commit is contained in:
parent
4eb1e65fbb
commit
be2bf98529
@ -55,6 +55,14 @@ impl Project {
|
||||
id: spawn_task.id,
|
||||
full_label: spawn_task.full_label,
|
||||
label: spawn_task.label,
|
||||
command_label: spawn_task.args.iter().fold(
|
||||
spawn_task.command.clone(),
|
||||
|mut command_label, new_arg| {
|
||||
command_label.push(' ');
|
||||
command_label.push_str(new_arg);
|
||||
command_label
|
||||
},
|
||||
),
|
||||
status: TaskStatus::Running,
|
||||
completion_rx,
|
||||
}),
|
||||
|
@ -598,6 +598,7 @@ pub struct TaskState {
|
||||
pub id: TaskId,
|
||||
pub full_label: String,
|
||||
pub label: String,
|
||||
pub command_label: String,
|
||||
pub status: TaskStatus,
|
||||
pub completion_rx: Receiver<()>,
|
||||
}
|
||||
@ -657,13 +658,7 @@ impl Terminal {
|
||||
AlacTermEvent::Bell => {
|
||||
cx.emit(Event::Bell);
|
||||
}
|
||||
AlacTermEvent::Exit => match &mut self.task {
|
||||
Some(task) => {
|
||||
task.status.register_terminal_exit();
|
||||
self.completion_tx.try_send(()).ok();
|
||||
}
|
||||
None => cx.emit(Event::CloseTerminal),
|
||||
},
|
||||
AlacTermEvent::Exit => self.register_task_finished(None, cx),
|
||||
AlacTermEvent::MouseCursorDirty => {
|
||||
//NOOP, Handled in render
|
||||
}
|
||||
@ -679,10 +674,7 @@ impl Terminal {
|
||||
.push_back(InternalEvent::ColorRequest(*idx, fun_ptr.clone()));
|
||||
}
|
||||
AlacTermEvent::ChildExit(error_code) => {
|
||||
if let Some(task) = &mut self.task {
|
||||
task.status.register_task_exit(*error_code);
|
||||
self.completion_tx.try_send(()).ok();
|
||||
}
|
||||
self.register_task_finished(Some(*error_code), cx);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1425,6 +1417,97 @@ impl Terminal {
|
||||
}
|
||||
Task::ready(())
|
||||
}
|
||||
|
||||
fn register_task_finished(
|
||||
&mut self,
|
||||
error_code: Option<i32>,
|
||||
cx: &mut ModelContext<'_, Terminal>,
|
||||
) {
|
||||
self.completion_tx.try_send(()).ok();
|
||||
let task = match &mut self.task {
|
||||
Some(task) => task,
|
||||
None => {
|
||||
if error_code.is_none() {
|
||||
cx.emit(Event::CloseTerminal);
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
if task.status != TaskStatus::Running {
|
||||
return;
|
||||
}
|
||||
match error_code {
|
||||
Some(error_code) => {
|
||||
task.status.register_task_exit(error_code);
|
||||
}
|
||||
None => {
|
||||
task.status.register_terminal_exit();
|
||||
}
|
||||
};
|
||||
|
||||
let (task_line, command_line) = task_summary(task, error_code);
|
||||
// SAFETY: the invocation happens on non `TaskStatus::Running` tasks, once,
|
||||
// after either `AlacTermEvent::Exit` or `AlacTermEvent::ChildExit` events that are spawned
|
||||
// when Zed task finishes and no more output is made.
|
||||
// After the task summary is output once, no more text is appended to the terminal.
|
||||
unsafe { append_text_to_term(&mut self.term.lock(), &[&task_line, &command_line]) };
|
||||
}
|
||||
}
|
||||
|
||||
const TASK_DELIMITER: &str = "⏵ ";
|
||||
fn task_summary(task: &TaskState, error_code: Option<i32>) -> (String, String) {
|
||||
let escaped_full_label = task.full_label.replace("\r\n", "\r").replace('\n', "\r");
|
||||
let task_line = match error_code {
|
||||
Some(0) => {
|
||||
format!("{TASK_DELIMITER}Task `{escaped_full_label}` finished successfully")
|
||||
}
|
||||
Some(error_code) => {
|
||||
format!("{TASK_DELIMITER}Task `{escaped_full_label}` finished with non-zero error code: {error_code}")
|
||||
}
|
||||
None => {
|
||||
format!("{TASK_DELIMITER}Task `{escaped_full_label}` finished")
|
||||
}
|
||||
};
|
||||
let escaped_command_label = task.command_label.replace("\r\n", "\r").replace('\n', "\r");
|
||||
let command_line = format!("{TASK_DELIMITER}Command: '{escaped_command_label}'");
|
||||
(task_line, command_line)
|
||||
}
|
||||
|
||||
/// Appends a stringified task summary to the terminal, after its output.
|
||||
///
|
||||
/// SAFETY: This function should only be called after terminal's PTY is no longer alive.
|
||||
/// New text being added to the terminal here, uses "less public" APIs,
|
||||
/// which are not maintaining the entire terminal state intact.
|
||||
///
|
||||
///
|
||||
/// The library
|
||||
///
|
||||
/// * does not increment inner grid cursor's _lines_ on `input` calls
|
||||
/// (but displaying the lines correctly and incrementing cursor's columns)
|
||||
///
|
||||
/// * ignores `\n` and \r` character input, requiring the `newline` call instead
|
||||
///
|
||||
/// * does not alter grid state after `newline` call
|
||||
/// so its `bottommost_line` is always the the same additions, and
|
||||
/// the cursor's `point` is not updated to the new line and column values
|
||||
///
|
||||
/// * ??? there could be more consequences, and any further "proper" streaming from the PTY might bug and/or panic.
|
||||
/// Still, concequent `append_text_to_term` invocations are possible and display the contents correctly.
|
||||
///
|
||||
/// Despite the quirks, this is the simplest approach to appending text to the terminal: its alternative, `grid_mut` manipulations,
|
||||
/// do not properly set the scrolling state and display odd text after appending; also those manipulations are more tedious and error-prone.
|
||||
/// The function achieves proper display and scrolling capabilities, at a cost of grid state not properly synchronized.
|
||||
/// This is enough for printing moderately-sized texts like task summaries, but might break or perform poorly for larger texts.
|
||||
unsafe fn append_text_to_term(term: &mut Term<ZedListener>, text_lines: &[&str]) {
|
||||
term.newline();
|
||||
term.grid_mut().cursor.point.column = Column(0);
|
||||
for line in text_lines {
|
||||
for c in line.chars() {
|
||||
term.input(c);
|
||||
}
|
||||
term.newline();
|
||||
term.grid_mut().cursor.point.column = Column(0);
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Terminal {
|
||||
|
Loading…
Reference in New Issue
Block a user