Small improvements of the task terminal spawn behavior (#9399)

* Add a `reveal: always|never` field in task definitions from tasks.json
, allowing to customize task terminal behavior on spawn
* Ensure reveal: always reveals the terminal even if the old task is
already running


Release Notes:

- Added a `reveal: always|never` (`always` is a default) field in task
definitions from tasks.json , allowing to customize task terminal
behavior on spawn

---------

Co-authored-by: Piotr Osiewicz <piotr@zed.dev>
This commit is contained in:
Kirill Bulatov 2024-03-15 18:32:59 +02:00 committed by GitHub
parent 276139f792
commit dcdd1ece1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 79 additions and 19 deletions

View File

@ -5,6 +5,7 @@
{ {
"label": "Example task", "label": "Example task",
"command": "for i in {1..5}; do echo \"Hello $i/5\"; sleep 1; done", "command": "for i in {1..5}; do echo \"Hello $i/5\"; sleep 1; done",
//"args": [],
// Env overrides for the command, will be appended to the terminal's environment from the settings. // Env overrides for the command, will be appended to the terminal's environment from the settings.
"env": { "foo": "bar" }, "env": { "foo": "bar" },
// Current working directory to spawn the command into, defaults to current project root. // Current working directory to spawn the command into, defaults to current project root.
@ -12,6 +13,10 @@
// Whether to use a new terminal tab or reuse the existing one to spawn the process, defaults to `false`. // Whether to use a new terminal tab or reuse the existing one to spawn the process, defaults to `false`.
"use_new_terminal": false, "use_new_terminal": false,
// Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish, defaults to `false`. // Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish, defaults to `false`.
"allow_concurrent_runs": false "allow_concurrent_runs": false,
// 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"
} }
] ]

View File

@ -6,6 +6,7 @@ pub mod static_source;
use collections::HashMap; use collections::HashMap;
use gpui::ModelContext; use gpui::ModelContext;
use static_source::RevealStrategy;
use std::any::Any; use std::any::Any;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
@ -34,6 +35,8 @@ pub struct SpawnInTerminal {
pub use_new_terminal: bool, pub use_new_terminal: bool,
/// Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish. /// Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish.
pub allow_concurrent_runs: bool, pub allow_concurrent_runs: bool,
/// What to do with the terminal pane and tab, after the command was started.
pub reveal: RevealStrategy,
} }
/// Keeps track of the file associated with a task and context of tasks execution (i.e. current file or current function) /// Keeps track of the file associated with a task and context of tasks execution (i.e. current file or current function)

View File

@ -2,7 +2,9 @@
use std::sync::Arc; use std::sync::Arc;
use crate::{SpawnInTerminal, Task, TaskContext, TaskId, TaskSource}; use crate::{
static_source::RevealStrategy, SpawnInTerminal, Task, TaskContext, TaskId, TaskSource,
};
use gpui::{AppContext, Context, Model}; use gpui::{AppContext, Context, Model};
/// A storage and source of tasks generated out of user command prompt inputs. /// A storage and source of tasks generated out of user command prompt inputs.
@ -48,6 +50,7 @@ impl Task for OneshotTask {
env, env,
use_new_terminal: Default::default(), use_new_terminal: Default::default(),
allow_concurrent_runs: Default::default(), allow_concurrent_runs: Default::default(),
reveal: RevealStrategy::default(),
}) })
} }
} }

View File

@ -38,6 +38,7 @@ impl Task for StaticTask {
label: self.definition.label.clone(), label: self.definition.label.clone(),
command: self.definition.command.clone(), command: self.definition.command.clone(),
args: self.definition.args.clone(), args: self.definition.args.clone(),
reveal: self.definition.reveal,
env: definition_env, env: definition_env,
}) })
} }
@ -64,6 +65,7 @@ pub struct StaticSource {
/// Static task definition from the tasks config file. /// Static task definition from the tasks config file.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub(crate) struct Definition { pub(crate) struct Definition {
/// Human readable name of the task to display in the UI. /// Human readable name of the task to display in the UI.
pub label: String, pub label: String,
@ -84,6 +86,22 @@ pub(crate) struct Definition {
/// Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish. /// Whether to allow multiple instances of the same task to be run, or rather wait for the existing ones to finish.
#[serde(default)] #[serde(default)]
pub allow_concurrent_runs: bool, pub allow_concurrent_runs: bool,
/// 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
#[serde(default)]
pub reveal: RevealStrategy,
}
/// What to do with the terminal pane and tab, after the command was started.
#[derive(Default, Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum RevealStrategy {
/// Always show the terminal pane, add and focus the corresponding task's tab in it.
#[default]
Always,
/// Do not change terminal pane focus, but still add/reuse the task's tab there.
Never,
} }
/// A group of Tasks defined in a JSON file. /// A group of Tasks defined in a JSON file.

View File

@ -39,7 +39,7 @@ use pty_info::PtyProcessInfo;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::Settings; use settings::Settings;
use smol::channel::{Receiver, Sender}; use smol::channel::{Receiver, Sender};
use task::TaskId; use task::{static_source::RevealStrategy, TaskId};
use terminal_settings::{AlternateScroll, Shell, TerminalBlink, TerminalSettings}; use terminal_settings::{AlternateScroll, Shell, TerminalBlink, TerminalSettings};
use theme::{ActiveTheme, Theme}; use theme::{ActiveTheme, Theme};
use util::truncate_and_trailoff; use util::truncate_and_trailoff;
@ -289,6 +289,7 @@ pub struct SpawnTask {
pub command: String, pub command: String,
pub args: Vec<String>, pub args: Vec<String>,
pub env: HashMap<String, String>, pub env: HashMap<String, String>,
pub reveal: RevealStrategy,
} }
// https://github.com/alacritty/alacritty/blob/cb3a79dbf6472740daca8440d5166c1d4af5029e/extra/man/alacritty.5.scd?plain=1#L207-L213 // https://github.com/alacritty/alacritty/blob/cb3a79dbf6472740daca8440d5166c1d4af5029e/extra/man/alacritty.5.scd?plain=1#L207-L213

View File

@ -14,7 +14,7 @@ use project::{Fs, ProjectEntryId};
use search::{buffer_search::DivRegistrar, BufferSearchBar}; use search::{buffer_search::DivRegistrar, BufferSearchBar};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use settings::Settings; use settings::Settings;
use task::{SpawnInTerminal, TaskId}; use task::{static_source::RevealStrategy, SpawnInTerminal, TaskId};
use terminal::{ use terminal::{
terminal_settings::{Shell, TerminalDockPosition, TerminalSettings}, terminal_settings::{Shell, TerminalDockPosition, TerminalSettings},
SpawnTask, SpawnTask,
@ -303,6 +303,7 @@ impl TerminalPanel {
command: spawn_in_terminal.command.clone(), command: spawn_in_terminal.command.clone(),
args: spawn_in_terminal.args.clone(), args: spawn_in_terminal.args.clone(),
env: spawn_in_terminal.env.clone(), env: spawn_in_terminal.env.clone(),
reveal: spawn_in_terminal.reveal,
}; };
// Set up shell args unconditionally, as tasks are always spawned inside of a shell. // 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() { let Some((shell, mut user_args)) = (match TerminalSettings::get_global(cx).shell.clone() {
@ -322,6 +323,7 @@ impl TerminalPanel {
spawn_task.command = shell; spawn_task.command = shell;
user_args.extend(["-i".to_owned(), "-c".to_owned(), command]); user_args.extend(["-i".to_owned(), "-c".to_owned(), command]);
spawn_task.args = user_args; spawn_task.args = user_args;
let reveal = spawn_task.reveal;
let working_directory = spawn_in_terminal.cwd.clone(); let working_directory = spawn_in_terminal.cwd.clone();
let allow_concurrent_runs = spawn_in_terminal.allow_concurrent_runs; let allow_concurrent_runs = spawn_in_terminal.allow_concurrent_runs;
@ -379,6 +381,20 @@ impl TerminalPanel {
.ok(); .ok();
}), }),
); );
match reveal {
RevealStrategy::Always => {
self.activate_terminal_view(existing_item_index, cx);
let task_workspace = self.workspace.clone();
cx.spawn(|_, mut cx| async move {
task_workspace
.update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx))
.ok()
})
.detach();
}
RevealStrategy::Never => {}
}
} }
} }
@ -388,14 +404,20 @@ impl TerminalPanel {
working_directory: Option<PathBuf>, working_directory: Option<PathBuf>,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) { ) {
let reveal = spawn_task.reveal;
self.add_terminal(working_directory, Some(spawn_task), cx); self.add_terminal(working_directory, Some(spawn_task), cx);
let task_workspace = self.workspace.clone(); match reveal {
cx.spawn(|_, mut cx| async move { RevealStrategy::Always => {
task_workspace let task_workspace = self.workspace.clone();
.update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx)) cx.spawn(|_, mut cx| async move {
.ok() task_workspace
}) .update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx))
.detach(); .ok()
})
.detach();
}
RevealStrategy::Never => {}
}
} }
///Create a new Terminal in the current working directory or the user's home directory ///Create a new Terminal in the current working directory or the user's home directory
@ -542,6 +564,7 @@ impl TerminalPanel {
.workspace .workspace
.update(cx, |workspace, _| workspace.project().clone()) .update(cx, |workspace, _| workspace.project().clone())
.ok()?; .ok()?;
let reveal = spawn_task.reveal;
let window = cx.window_handle(); let window = cx.window_handle();
let new_terminal = project.update(cx, |project, cx| { let new_terminal = project.update(cx, |project, cx| {
project project
@ -551,14 +574,21 @@ impl TerminalPanel {
terminal_to_replace.update(cx, |terminal_to_replace, cx| { terminal_to_replace.update(cx, |terminal_to_replace, cx| {
terminal_to_replace.set_terminal(new_terminal, cx); terminal_to_replace.set_terminal(new_terminal, cx);
}); });
self.activate_terminal_view(terminal_item_index, cx);
let task_workspace = self.workspace.clone(); match reveal {
cx.spawn(|_, mut cx| async move { RevealStrategy::Always => {
task_workspace self.activate_terminal_view(terminal_item_index, cx);
.update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx)) let task_workspace = self.workspace.clone();
.ok() cx.spawn(|_, mut cx| async move {
}) task_workspace
.detach(); .update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx))
.ok()
})
.detach();
}
RevealStrategy::Never => {}
}
Some(()) Some(())
} }
} }