Add the ability for tasks to target the center pane (#22004)

Closes #20060
Closes #20720
Closes #19873
Closes #9445

Release Notes:

- Fixed a bug where tasks would be spawned with their working directory
set to a file in some cases
- Added the ability to spawn tasks in the center pane, when spawning
from a keybinding:

```json5
[
  {
    // Assuming you have a task labeled "echo hello"
    "ctrl--": [
      "task::Spawn",
      { "task_name": "echo hello", "target": "center" }
    ]
  }
]
```
This commit is contained in:
Mikayla Maki 2024-12-13 19:39:46 -08:00 committed by GitHub
parent 85c3aec6e7
commit 4f96706161
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 263 additions and 106 deletions

View file

@ -255,7 +255,10 @@ impl TerminalPanel {
terminal_panel
.update(&mut cx, |_, cx| {
cx.subscribe(&workspace, |terminal_panel, _, e, cx| {
if let workspace::Event::SpawnTask(spawn_in_terminal) = e {
if let workspace::Event::SpawnTask {
action: spawn_in_terminal,
} = e
{
terminal_panel.spawn_task(spawn_in_terminal, cx);
};
})
@ -450,83 +453,17 @@ 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 spawn_in_terminal.shell.clone() {
Shell::System => {
match self
.workspace
.update(cx, |workspace, cx| workspace.project().read(cx).is_local())
{
Ok(local) => {
if local {
retrieve_system_shell().map(|shell| (shell, Vec::new()))
} else {
Some(("\"${SHELL:-sh}\"".to_string(), Vec::new()))
}
}
Err(_no_window_e) => return,
}
}
Shell::Program(shell) => Some((shell, Vec::new())),
Shell::WithArguments { program, args, .. } => Some((program, args)),
}) else {
let Ok(is_local) = self
.workspace
.update(cx, |workspace, cx| workspace.project().read(cx).is_local())
else {
return;
};
#[cfg(target_os = "windows")]
let windows_shell_type = to_windows_shell_type(&shell);
#[cfg(not(target_os = "windows"))]
if let ControlFlow::Break(_) =
Self::fill_command(is_local, spawn_in_terminal, &mut spawn_task)
{
spawn_task.command_label = format!("{shell} -i -c '{}'", spawn_task.command_label);
return;
}
#[cfg(target_os = "windows")]
{
use crate::terminal_panel::WindowsShellType;
match windows_shell_type {
WindowsShellType::Powershell => {
spawn_task.command_label = format!("{shell} -C '{}'", spawn_task.command_label)
}
WindowsShellType::Cmd => {
spawn_task.command_label = format!("{shell} /C '{}'", spawn_task.command_label)
}
WindowsShellType::Other => {
spawn_task.command_label =
format!("{shell} -i -c '{}'", spawn_task.command_label)
}
}
}
let task_command = std::mem::replace(&mut spawn_task.command, shell);
let task_args = std::mem::take(&mut spawn_task.args);
let combined_command = task_args
.into_iter()
.fold(task_command, |mut command, arg| {
command.push(' ');
#[cfg(not(target_os = "windows"))]
command.push_str(&arg);
#[cfg(target_os = "windows")]
command.push_str(&to_windows_shell_variable(windows_shell_type, arg));
command
});
#[cfg(not(target_os = "windows"))]
user_args.extend(["-i".to_owned(), "-c".to_owned(), combined_command]);
#[cfg(target_os = "windows")]
{
use crate::terminal_panel::WindowsShellType;
match windows_shell_type {
WindowsShellType::Powershell => {
user_args.extend(["-C".to_owned(), combined_command])
}
WindowsShellType::Cmd => user_args.extend(["/C".to_owned(), combined_command]),
WindowsShellType::Other => {
user_args.extend(["-i".to_owned(), "-c".to_owned(), combined_command])
}
}
}
spawn_task.args = user_args;
let spawn_task = spawn_task;
let allow_concurrent_runs = spawn_in_terminal.allow_concurrent_runs;
@ -602,6 +539,81 @@ impl TerminalPanel {
.detach()
}
pub fn fill_command(
is_local: bool,
spawn_in_terminal: &SpawnInTerminal,
spawn_task: &mut SpawnInTerminal,
) -> ControlFlow<()> {
let Some((shell, mut user_args)) = (match spawn_in_terminal.shell.clone() {
Shell::System => {
if is_local {
retrieve_system_shell().map(|shell| (shell, Vec::new()))
} else {
Some(("\"${SHELL:-sh}\"".to_string(), Vec::new()))
}
}
Shell::Program(shell) => Some((shell, Vec::new())),
Shell::WithArguments { program, args, .. } => Some((program, args)),
}) else {
return ControlFlow::Break(());
};
#[cfg(target_os = "windows")]
let windows_shell_type = to_windows_shell_type(&shell);
#[cfg(not(target_os = "windows"))]
{
spawn_task.command_label = format!("{shell} -i -c '{}'", spawn_task.command_label);
}
#[cfg(target_os = "windows")]
{
use crate::terminal_panel::WindowsShellType;
match windows_shell_type {
WindowsShellType::Powershell => {
spawn_task.command_label = format!("{shell} -C '{}'", spawn_task.command_label)
}
WindowsShellType::Cmd => {
spawn_task.command_label = format!("{shell} /C '{}'", spawn_task.command_label)
}
WindowsShellType::Other => {
spawn_task.command_label =
format!("{shell} -i -c '{}'", spawn_task.command_label)
}
}
}
let task_command = std::mem::replace(&mut spawn_task.command, shell);
let task_args = std::mem::take(&mut spawn_task.args);
let combined_command = task_args
.into_iter()
.fold(task_command, |mut command, arg| {
command.push(' ');
#[cfg(not(target_os = "windows"))]
command.push_str(&arg);
#[cfg(target_os = "windows")]
command.push_str(&to_windows_shell_variable(windows_shell_type, arg));
command
});
#[cfg(not(target_os = "windows"))]
user_args.extend(["-i".to_owned(), "-c".to_owned(), combined_command]);
#[cfg(target_os = "windows")]
{
use crate::terminal_panel::WindowsShellType;
match windows_shell_type {
WindowsShellType::Powershell => {
user_args.extend(["-C".to_owned(), combined_command])
}
WindowsShellType::Cmd => user_args.extend(["/C".to_owned(), combined_command]),
WindowsShellType::Other => {
user_args.extend(["-i".to_owned(), "-c".to_owned(), combined_command])
}
}
}
spawn_task.args = user_args;
// Set up shell args unconditionally, as tasks are always spawned inside of a shell.
ControlFlow::Continue(())
}
pub fn spawn_in_new_terminal(
&mut self,
spawn_task: SpawnInTerminal,