Detailed changes
@@ -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"
}
]
@@ -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))
@@ -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,
}),
@@ -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 {
@@ -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,
)
@@ -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"))
}
@@ -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>,
+ },
+}
@@ -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(),
}),
})
}
@@ -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.
@@ -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
- }
-}
@@ -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,
+}
@@ -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)));
}
}
@@ -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,
@@ -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();
});
@@ -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"
}
]
```