Extend task templates with shell and hide fields to use custom shells and custom close behavior (#15031)

This commit is contained in:
Kirill Bulatov 2024-07-23 22:58:36 +03:00 committed by GitHub
parent 4a43084cb7
commit b2b9d4ccb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 302 additions and 148 deletions

View File

@ -17,6 +17,27 @@
// What to do with the terminal pane and tab, after the command was started:
// * `always` always show the terminal pane, add and focus the corresponding task's tab in it (default)
// * `never` avoid changing current terminal pane focus, but still add/reuse the task's tab there
"reveal": "always"
"reveal": "always",
// What to do with the terminal pane and tab, after the command had finished:
// * `never` Do nothing when the command finishes (default)
// * `always` always hide the terminal tab, hide the pane also if it was the last tab in it
// * `on_success` hide the terminal tab on task success only, otherwise behaves similar to `always`
"hide": "never",
// Which shell to use when running a task inside the terminal.
// May take 3 values:
// 1. (default) Use the system's default terminal configuration in /etc/passwd
// "shell": "system"
// 2. A program:
// "shell": {
// "program": "sh"
// }
// 3. A program with arguments:
// "shell": {
// "with_arguments": {
// "program": "/bin/bash",
// "arguments": ["--login"]
// }
// }
"shell": "system"
}
]

View File

@ -110,7 +110,7 @@ use std::{
};
use task::{
static_source::{StaticSource, TrackedFile},
RevealStrategy, TaskContext, TaskTemplate, TaskVariables, VariableName,
HideStrategy, RevealStrategy, Shell, TaskContext, TaskTemplate, TaskVariables, VariableName,
};
use terminals::Terminals;
use text::{Anchor, BufferId, LineEnding};
@ -9587,9 +9587,25 @@ impl Project {
use_new_terminal: template.use_new_terminal,
allow_concurrent_runs: template.allow_concurrent_runs,
reveal: match template.reveal {
RevealStrategy::Always => proto::RevealStrategy::Always as i32,
RevealStrategy::Never => proto::RevealStrategy::Never as i32,
RevealStrategy::Always => proto::RevealStrategy::RevealAlways as i32,
RevealStrategy::Never => proto::RevealStrategy::RevealNever as i32,
},
hide: match template.hide {
HideStrategy::Always => proto::HideStrategy::HideAlways as i32,
HideStrategy::Never => proto::HideStrategy::HideNever as i32,
HideStrategy::OnSuccess => proto::HideStrategy::HideOnSuccess as i32,
},
shell: Some(proto::Shell {
shell_type: Some(match template.shell {
Shell::System => proto::shell::ShellType::System(proto::System {}),
Shell::Program(program) => proto::shell::ShellType::Program(program),
Shell::WithArguments { program, args } => {
proto::shell::ShellType::WithArguments(
proto::shell::WithArguments { program, args },
)
}
}),
}),
tags: template.tags,
});
proto::TemplatePair { kind, template }
@ -10628,10 +10644,31 @@ impl Project {
let proto_template = template_pair.template?;
let reveal = match proto::RevealStrategy::from_i32(proto_template.reveal)
.unwrap_or(proto::RevealStrategy::Always)
.unwrap_or(proto::RevealStrategy::RevealAlways)
{
proto::RevealStrategy::Always => RevealStrategy::Always,
proto::RevealStrategy::Never => RevealStrategy::Never,
proto::RevealStrategy::RevealAlways => RevealStrategy::Always,
proto::RevealStrategy::RevealNever => RevealStrategy::Never,
};
let hide = match proto::HideStrategy::from_i32(proto_template.hide)
.unwrap_or(proto::HideStrategy::HideNever)
{
proto::HideStrategy::HideAlways => HideStrategy::Always,
proto::HideStrategy::HideNever => HideStrategy::Never,
proto::HideStrategy::HideOnSuccess => HideStrategy::OnSuccess,
};
let shell = match proto_template
.shell
.and_then(|shell| shell.shell_type)
.unwrap_or(proto::shell::ShellType::System(proto::System {}))
{
proto::shell::ShellType::System(_) => Shell::System,
proto::shell::ShellType::Program(program) => Shell::Program(program),
proto::shell::ShellType::WithArguments(with_arguments) => {
Shell::WithArguments {
program: with_arguments.program,
args: with_arguments.args,
}
}
};
let task_template = TaskTemplate {
label: proto_template.label,
@ -10642,6 +10679,8 @@ impl Project {
use_new_terminal: proto_template.use_new_terminal,
allow_concurrent_runs: proto_template.allow_concurrent_runs,
reveal,
hide,
shell,
tags: proto_template.tags,
};
Some((task_source_kind, task_template))

View File

@ -13,9 +13,9 @@ use std::{
io::Write,
path::{Path, PathBuf},
};
use task::{SpawnInTerminal, TerminalWorkDir};
use task::{Shell, SpawnInTerminal, TerminalWorkDir};
use terminal::{
terminal_settings::{self, Shell, TerminalSettings, VenvSettingsContent},
terminal_settings::{self, TerminalSettings, VenvSettingsContent},
TaskState, TaskStatus, Terminal, TerminalBuilder,
};
use util::ResultExt;
@ -131,6 +131,7 @@ impl Project {
full_label: spawn_task.full_label,
label: spawn_task.label,
command_label: spawn_task.command_label,
hide: spawn_task.hide,
status: TaskStatus::Running,
completion_rx,
}),
@ -155,6 +156,7 @@ impl Project {
full_label: spawn_task.full_label,
label: spawn_task.label,
command_label: spawn_task.command_label,
hide: spawn_task.hide,
status: TaskStatus::Running,
completion_rx,
}),

View File

@ -2284,12 +2284,35 @@ message TaskTemplate {
bool use_new_terminal = 6;
bool allow_concurrent_runs = 7;
RevealStrategy reveal = 8;
HideStrategy hide = 10;
repeated string tags = 9;
Shell shell = 11;
}
message Shell {
message WithArguments {
string program = 1;
repeated string args = 2;
}
oneof shell_type {
System system = 1;
string program = 2;
WithArguments with_arguments = 3;
}
}
message System {}
enum RevealStrategy {
Always = 0;
Never = 1;
RevealAlways = 0;
RevealNever = 1;
}
enum HideStrategy {
HideAlways = 0;
HideNever = 1;
HideOnSuccess = 2;
}
message TaskSourceKind {

View File

@ -20,6 +20,7 @@ use rpc::{
proto::{CreateDevServerResponse, DevServerStatus},
ErrorCode, ErrorExt,
};
use task::HideStrategy;
use task::RevealStrategy;
use task::SpawnInTerminal;
use task::TerminalWorkDir;
@ -1191,10 +1192,12 @@ pub async fn spawn_ssh_task(
ssh_command: ssh_connection_string,
path: None,
}),
env: Default::default(),
use_new_terminal: true,
allow_concurrent_runs: false,
reveal: RevealStrategy::Always,
hide: HideStrategy::Never,
env: Default::default(),
shell: Default::default(),
},
cx,
)

View File

@ -412,11 +412,11 @@ impl ProtoClient for SshSession {
impl SshClientState {
#[cfg(not(unix))]
async fn new(
user: String,
host: String,
port: u16,
delegate: Arc<dyn SshClientDelegate>,
cx: &mut AsyncAppContext,
_user: String,
_host: String,
_port: u16,
_delegate: Arc<dyn SshClientDelegate>,
_cx: &mut AsyncAppContext,
) -> Result<Self> {
Err(anyhow!("ssh is not supported on this platform"))
}

View File

@ -7,12 +7,13 @@ mod vscode_format;
use collections::{hash_map, HashMap, HashSet};
use gpui::SharedString;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
use std::str::FromStr;
use std::{borrow::Cow, path::Path};
pub use task_template::{RevealStrategy, TaskTemplate, TaskTemplates};
pub use task_template::{HideStrategy, RevealStrategy, TaskTemplate, TaskTemplates};
pub use vscode_format::VsCodeTaskFile;
/// Task identifier, unique within the application.
@ -78,6 +79,10 @@ pub struct SpawnInTerminal {
pub allow_concurrent_runs: bool,
/// What to do with the terminal pane and tab, after the command was started.
pub reveal: RevealStrategy,
/// What to do with the terminal pane and tab, after the command had finished.
pub hide: HideStrategy,
/// Which shell to use when spawning the task.
pub shell: Shell,
}
/// A final form of the [`TaskTemplate`], that got resolved with a particualar [`TaskContext`] and now is ready to spawn the actual task.
@ -271,3 +276,21 @@ pub struct TaskContext {
/// This is a new type representing a 'tag' on a 'runnable symbol', typically a test of main() function, found via treesitter.
#[derive(Clone, Debug)]
pub struct RunnableTag(pub SharedString);
/// Shell configuration to open the terminal with.
#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Shell {
/// Use the system's default terminal configuration in /etc/passwd
#[default]
System,
/// Use a specific program with no arguments.
Program(String),
/// Use a specific program with arguments.
WithArguments {
/// The program to run.
program: String,
/// The arguments to pass to the program.
args: Vec<String>,
},
}

View File

@ -8,7 +8,7 @@ use sha2::{Digest, Sha256};
use util::{truncate_and_remove_front, ResultExt};
use crate::{
ResolvedTask, SpawnInTerminal, TaskContext, TaskId, TerminalWorkDir, VariableName,
ResolvedTask, Shell, SpawnInTerminal, TaskContext, TaskId, TerminalWorkDir, VariableName,
ZED_VARIABLE_NAME_PREFIX,
};
@ -45,10 +45,18 @@ pub struct TaskTemplate {
/// * `never` — avoid changing current terminal pane focus, but still add/reuse the task's tab there
#[serde(default)]
pub reveal: RevealStrategy,
/// What to do with the terminal pane and tab, after the command had finished:
/// * `never` — do nothing when the command finishes (default)
/// * `always` — always hide the terminal tab, hide the pane also if it was the last tab in it
/// * `on_success` — hide the terminal tab on task success only, otherwise behaves similar to `always`.
#[serde(default)]
pub hide: HideStrategy,
/// Represents the tags which this template attaches to. Adding this removes this task from other UI.
#[serde(default)]
pub tags: Vec<String>,
/// Which shell to use when spawning the task.
#[serde(default)]
pub shell: Shell,
}
/// What to do with the terminal pane and tab, after the command was started.
@ -62,6 +70,19 @@ pub enum RevealStrategy {
Never,
}
/// What to do with the terminal pane and tab, after the command has finished.
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum HideStrategy {
/// Do nothing when the command finishes.
#[default]
Never,
/// Always hide the terminal tab, hide the pane also if it was the last tab in it.
Always,
/// Hide the terminal tab on task success only, otherwise behaves similar to `Always`.
OnSuccess,
}
/// A group of Tasks defined in a JSON file.
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct TaskTemplates(pub Vec<TaskTemplate>);
@ -194,6 +215,8 @@ impl TaskTemplate {
use_new_terminal: self.use_new_terminal,
allow_concurrent_runs: self.allow_concurrent_runs,
reveal: self.reveal,
hide: self.hide,
shell: self.shell.clone(),
}),
})
}

View File

@ -39,8 +39,8 @@ use pty_info::PtyProcessInfo;
use serde::{Deserialize, Serialize};
use settings::Settings;
use smol::channel::{Receiver, Sender};
use task::TaskId;
use terminal_settings::{AlternateScroll, Shell, TerminalBlink, TerminalSettings};
use task::{HideStrategy, Shell, TaskId};
use terminal_settings::{AlternateScroll, TerminalBlink, TerminalSettings};
use theme::{ActiveTheme, Theme};
use util::truncate_and_trailoff;
@ -612,6 +612,7 @@ pub struct TaskState {
pub command_label: String,
pub status: TaskStatus,
pub completion_rx: Receiver<()>,
pub hide: HideStrategy,
}
/// A status of the current terminal tab's task.
@ -1567,32 +1568,43 @@ impl Terminal {
}
};
let (task_line, command_line) = task_summary(task, error_code);
let (finished_successfully, 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]) };
match task.hide {
HideStrategy::Never => {}
HideStrategy::Always => {
cx.emit(Event::CloseTerminal);
}
HideStrategy::OnSuccess => {
if finished_successfully {
cx.emit(Event::CloseTerminal);
}
}
}
}
}
const TASK_DELIMITER: &str = "";
fn task_summary(task: &TaskState, error_code: Option<i32>) -> (String, String) {
fn task_summary(task: &TaskState, error_code: Option<i32>) -> (bool, String, String) {
let escaped_full_label = task.full_label.replace("\r\n", "\r").replace('\n', "\r");
let task_line = match error_code {
let (success, task_line) = match error_code {
Some(0) => {
format!("{TASK_DELIMITER}Task `{escaped_full_label}` finished successfully")
(true, 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}")
(false, format!("{TASK_DELIMITER}Task `{escaped_full_label}` finished with non-zero error code: {error_code}"))
}
None => {
format!("{TASK_DELIMITER}Task `{escaped_full_label}` finished")
(false, 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)
(success, task_line, command_line)
}
/// Appends a stringified task summary to the terminal, after its output.

View File

@ -9,6 +9,7 @@ use serde_derive::{Deserialize, Serialize};
use serde_json::Value;
use settings::{SettingsJsonSchemaParams, SettingsSources};
use std::path::PathBuf;
use task::Shell;
#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
@ -256,60 +257,6 @@ pub enum TerminalBlink {
On,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Shell {
/// Use the system's default terminal configuration in /etc/passwd
System,
Program(String),
WithArguments {
program: String,
args: Vec<String>,
},
}
impl Shell {
pub fn retrieve_system_shell() -> Option<String> {
#[cfg(not(target_os = "windows"))]
{
use anyhow::Context;
use util::ResultExt;
return std::env::var("SHELL")
.context("Error finding SHELL in env.")
.log_err();
}
// `alacritty_terminal` uses this as default on Windows. See:
// https://github.com/alacritty/alacritty/blob/0d4ab7bca43213d96ddfe40048fc0f922543c6f8/alacritty_terminal/src/tty/windows/mod.rs#L130
#[cfg(target_os = "windows")]
return Some("powershell".to_owned());
}
/// Convert unix-shell variable syntax to windows-shell syntax.
/// `powershell` and `cmd` are considered valid here.
#[cfg(target_os = "windows")]
pub fn to_windows_shell_variable(shell_type: WindowsShellType, input: String) -> String {
match shell_type {
WindowsShellType::Powershell => to_powershell_variable(input),
WindowsShellType::Cmd => to_cmd_variable(input),
WindowsShellType::Other => input,
}
}
#[cfg(target_os = "windows")]
pub fn to_windows_shell_type(shell: &str) -> WindowsShellType {
if shell == "powershell" || shell.ends_with("powershell.exe") {
WindowsShellType::Powershell
} else if shell == "cmd" || shell.ends_with("cmd.exe") {
WindowsShellType::Cmd
} else {
// Someother shell detected, the user might install and use a
// unix-like shell.
WindowsShellType::Other
}
}
}
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum AlternateScroll {
@ -341,55 +288,3 @@ pub struct ToolbarContent {
/// Default: true
pub title: Option<bool>,
}
#[cfg(target_os = "windows")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WindowsShellType {
Powershell,
Cmd,
Other,
}
/// Convert `${SOME_VAR}`, `$SOME_VAR` to `%SOME_VAR%`.
#[inline]
#[cfg(target_os = "windows")]
fn to_cmd_variable(input: String) -> String {
if let Some(var_str) = input.strip_prefix("${") {
if var_str.find(':').is_none() {
// If the input starts with "${", remove the trailing "}"
format!("%{}%", &var_str[..var_str.len() - 1])
} else {
// `${SOME_VAR:-SOME_DEFAULT}`, we currently do not handle this situation,
// which will result in the task failing to run in such cases.
input
}
} else if let Some(var_str) = input.strip_prefix('$') {
// If the input starts with "$", directly append to "$env:"
format!("%{}%", var_str)
} else {
// If no prefix is found, return the input as is
input
}
}
/// Convert `${SOME_VAR}`, `$SOME_VAR` to `$env:SOME_VAR`.
#[inline]
#[cfg(target_os = "windows")]
fn to_powershell_variable(input: String) -> String {
if let Some(var_str) = input.strip_prefix("${") {
if var_str.find(':').is_none() {
// If the input starts with "${", remove the trailing "}"
format!("$env:{}", &var_str[..var_str.len() - 1])
} else {
// `${SOME_VAR:-SOME_DEFAULT}`, we currently do not handle this situation,
// which will result in the task failing to run in such cases.
input
}
} else if let Some(var_str) = input.strip_prefix('$') {
// If the input starts with "$", directly append to "$env:"
format!("$env:{}", var_str)
} else {
// If no prefix is found, return the input as is
input
}
}

View File

@ -14,9 +14,9 @@ use project::{Fs, ProjectEntryId};
use search::{buffer_search::DivRegistrar, BufferSearchBar};
use serde::{Deserialize, Serialize};
use settings::Settings;
use task::{RevealStrategy, SpawnInTerminal, TaskId, TerminalWorkDir};
use task::{RevealStrategy, Shell, SpawnInTerminal, TaskId, TerminalWorkDir};
use terminal::{
terminal_settings::{Shell, TerminalDockPosition, TerminalSettings},
terminal_settings::{TerminalDockPosition, TerminalSettings},
Terminal,
};
use ui::{
@ -363,15 +363,15 @@ impl TerminalPanel {
fn spawn_task(&mut self, spawn_in_terminal: &SpawnInTerminal, cx: &mut ViewContext<Self>) {
let mut spawn_task = spawn_in_terminal.clone();
// Set up shell args unconditionally, as tasks are always spawned inside of a shell.
let Some((shell, mut user_args)) = (match TerminalSettings::get_global(cx).shell.clone() {
Shell::System => Shell::retrieve_system_shell().map(|shell| (shell, Vec::new())),
let Some((shell, mut user_args)) = (match spawn_in_terminal.shell.clone() {
Shell::System => retrieve_system_shell().map(|shell| (shell, Vec::new())),
Shell::Program(shell) => Some((shell, Vec::new())),
Shell::WithArguments { program, args } => Some((program, args)),
}) else {
return;
};
#[cfg(target_os = "windows")]
let windows_shell_type = Shell::to_windows_shell_type(&shell);
let windows_shell_type = to_windows_shell_type(&shell);
#[cfg(not(target_os = "windows"))]
{
@ -379,7 +379,7 @@ impl TerminalPanel {
}
#[cfg(target_os = "windows")]
{
use terminal::terminal_settings::WindowsShellType;
use crate::terminal_panel::WindowsShellType;
match windows_shell_type {
WindowsShellType::Powershell => {
@ -404,7 +404,7 @@ impl TerminalPanel {
#[cfg(not(target_os = "windows"))]
command.push_str(&arg);
#[cfg(target_os = "windows")]
command.push_str(&Shell::to_windows_shell_variable(windows_shell_type, arg));
command.push_str(&to_windows_shell_variable(windows_shell_type, arg));
command
});
@ -412,7 +412,7 @@ impl TerminalPanel {
user_args.extend(["-i".to_owned(), "-c".to_owned(), combined_command]);
#[cfg(target_os = "windows")]
{
use terminal::terminal_settings::WindowsShellType;
use crate::terminal_panel::WindowsShellType;
match windows_shell_type {
WindowsShellType::Powershell => {
@ -845,3 +845,93 @@ struct SerializedTerminalPanel {
width: Option<Pixels>,
height: Option<Pixels>,
}
fn retrieve_system_shell() -> Option<String> {
#[cfg(not(target_os = "windows"))]
{
use anyhow::Context;
use util::ResultExt;
return std::env::var("SHELL")
.context("Error finding SHELL in env.")
.log_err();
}
// `alacritty_terminal` uses this as default on Windows. See:
// https://github.com/alacritty/alacritty/blob/0d4ab7bca43213d96ddfe40048fc0f922543c6f8/alacritty_terminal/src/tty/windows/mod.rs#L130
#[cfg(target_os = "windows")]
return Some("powershell".to_owned());
}
#[cfg(target_os = "windows")]
fn to_windows_shell_variable(shell_type: WindowsShellType, input: String) -> String {
match shell_type {
WindowsShellType::Powershell => to_powershell_variable(input),
WindowsShellType::Cmd => to_cmd_variable(input),
WindowsShellType::Other => input,
}
}
#[cfg(target_os = "windows")]
fn to_windows_shell_type(shell: &str) -> WindowsShellType {
if shell == "powershell" || shell.ends_with("powershell.exe") {
WindowsShellType::Powershell
} else if shell == "cmd" || shell.ends_with("cmd.exe") {
WindowsShellType::Cmd
} else {
// Someother shell detected, the user might install and use a
// unix-like shell.
WindowsShellType::Other
}
}
/// Convert `${SOME_VAR}`, `$SOME_VAR` to `%SOME_VAR%`.
#[inline]
#[cfg(target_os = "windows")]
fn to_cmd_variable(input: String) -> String {
if let Some(var_str) = input.strip_prefix("${") {
if var_str.find(':').is_none() {
// If the input starts with "${", remove the trailing "}"
format!("%{}%", &var_str[..var_str.len() - 1])
} else {
// `${SOME_VAR:-SOME_DEFAULT}`, we currently do not handle this situation,
// which will result in the task failing to run in such cases.
input
}
} else if let Some(var_str) = input.strip_prefix('$') {
// If the input starts with "$", directly append to "$env:"
format!("%{}%", var_str)
} else {
// If no prefix is found, return the input as is
input
}
}
/// Convert `${SOME_VAR}`, `$SOME_VAR` to `$env:SOME_VAR`.
#[inline]
#[cfg(target_os = "windows")]
fn to_powershell_variable(input: String) -> String {
if let Some(var_str) = input.strip_prefix("${") {
if var_str.find(':').is_none() {
// If the input starts with "${", remove the trailing "}"
format!("$env:{}", &var_str[..var_str.len() - 1])
} else {
// `${SOME_VAR:-SOME_DEFAULT}`, we currently do not handle this situation,
// which will result in the task failing to run in such cases.
input
}
} else if let Some(var_str) = input.strip_prefix('$') {
// If the input starts with "$", directly append to "$env:"
format!("$env:{}", var_str)
} else {
// If no prefix is found, return the input as is
input
}
}
#[cfg(target_os = "windows")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum WindowsShellType {
Powershell,
Cmd,
Other,
}

View File

@ -41,6 +41,6 @@ pub fn schedule_resolved_task(
})
});
}
cx.emit(crate::Event::SpawnTask(spawn_in_terminal));
cx.emit(crate::Event::SpawnTask(Box::new(spawn_in_terminal)));
}
}

View File

@ -660,7 +660,7 @@ pub enum Event {
ActiveItemChanged,
ContactRequestedJoin(u64),
WorkspaceCreated(WeakView<Workspace>),
SpawnTask(SpawnInTerminal),
SpawnTask(Box<SpawnInTerminal>),
OpenBundledFile {
text: Cow<'static, str>,
title: &'static str,

View File

@ -1000,7 +1000,7 @@ mod tests {
path::{Path, PathBuf},
time::Duration,
};
use task::{RevealStrategy, SpawnInTerminal};
use task::{HideStrategy, RevealStrategy, Shell, SpawnInTerminal};
use theme::{ThemeRegistry, ThemeSettings};
use workspace::{
item::{Item, ItemHandle},
@ -3349,6 +3349,8 @@ mod tests {
use_new_terminal: false,
allow_concurrent_runs: false,
reveal: RevealStrategy::Always,
hide: HideStrategy::Never,
shell: Shell::System,
};
let project = Project::test(app_state.fs.clone(), [project_root.path()], cx).await;
let window = cx.add_window(|cx| Workspace::test_new(project, cx));
@ -3356,7 +3358,7 @@ mod tests {
cx.update(|cx| {
window
.update(cx, |_workspace, cx| {
cx.emit(workspace::Event::SpawnTask(spawn_in_terminal));
cx.emit(workspace::Event::SpawnTask(Box::new(spawn_in_terminal)));
})
.unwrap();
});

View File

@ -19,7 +19,28 @@ Zed supports ways to spawn (and rerun) commands using its integrated terminal to
// What to do with the terminal pane and tab, after the command was started:
// * `always` — always show the terminal pane, add and focus the corresponding task's tab in it (default)
// * `never` — avoid changing current terminal pane focus, but still add/reuse the task's tab there
"reveal": "always"
"reveal": "always",
// What to do with the terminal pane and tab, after the command had finished:
// * `never` — Do nothing when the command finishes (default)
// * `always` — always hide the terminal tab, hide the pane also if it was the last tab in it
// * `on_success` — hide the terminal tab on task success only, otherwise behaves similar to `always`
"hide": "never",
// Which shell to use when running a task inside the terminal.
// May take 3 values:
// 1. (default) Use the system's default terminal configuration in /etc/passwd
// "shell": "system"
// 2. A program:
// "shell": {
// "program": "sh"
// }
// 3. A program with arguments:
// "shell": {
// "with_arguments": {
// "program": "/bin/bash",
// "arguments": ["--login"]
// }
// }
"shell": "system"
}
]
```