Enable toolchain venv in new terminals (#21388)
Fixes part of issue #7808 > This venv should be the one we automatically activate when opening new terminals, if the detect_venv setting is on. Release Notes: - Selected Python toolchains (virtual environments) are now automatically activated in new terminals. --------- Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
This commit is contained in:
parent
a76cd778c4
commit
1270ef3ea5
5 changed files with 441 additions and 307 deletions
|
@ -1,8 +1,9 @@
|
|||
use crate::Project;
|
||||
use anyhow::Context as _;
|
||||
use anyhow::{Context as _, Result};
|
||||
use collections::HashMap;
|
||||
use gpui::{AnyWindowHandle, AppContext, Context, Entity, Model, ModelContext, WeakModel};
|
||||
use gpui::{AnyWindowHandle, AppContext, Context, Entity, Model, ModelContext, Task, WeakModel};
|
||||
use itertools::Itertools;
|
||||
use language::LanguageName;
|
||||
use settings::{Settings, SettingsLocation};
|
||||
use smol::channel::bounded;
|
||||
use std::{
|
||||
|
@ -10,10 +11,11 @@ use std::{
|
|||
env::{self},
|
||||
iter,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use task::{Shell, SpawnInTerminal};
|
||||
use terminal::{
|
||||
terminal_settings::{self, TerminalSettings},
|
||||
terminal_settings::{self, TerminalSettings, VenvSettings},
|
||||
TaskState, TaskStatus, Terminal, TerminalBuilder,
|
||||
};
|
||||
use util::ResultExt;
|
||||
|
@ -42,7 +44,7 @@ pub struct SshCommand {
|
|||
}
|
||||
|
||||
impl Project {
|
||||
pub fn active_project_directory(&self, cx: &AppContext) -> Option<PathBuf> {
|
||||
pub fn active_project_directory(&self, cx: &AppContext) -> Option<Arc<Path>> {
|
||||
let worktree = self
|
||||
.active_entry()
|
||||
.and_then(|entry_id| self.worktree_for_entry(entry_id, cx))
|
||||
|
@ -53,7 +55,7 @@ impl Project {
|
|||
worktree
|
||||
.root_entry()
|
||||
.filter(|entry| entry.is_dir())
|
||||
.map(|_| worktree.abs_path().to_path_buf())
|
||||
.map(|_| worktree.abs_path().clone())
|
||||
});
|
||||
worktree
|
||||
}
|
||||
|
@ -87,12 +89,12 @@ impl Project {
|
|||
kind: TerminalKind,
|
||||
window: AnyWindowHandle,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> anyhow::Result<Model<Terminal>> {
|
||||
let path = match &kind {
|
||||
TerminalKind::Shell(path) => path.as_ref().map(|path| path.to_path_buf()),
|
||||
) -> Task<Result<Model<Terminal>>> {
|
||||
let path: Option<Arc<Path>> = match &kind {
|
||||
TerminalKind::Shell(path) => path.as_ref().map(|path| Arc::from(path.as_ref())),
|
||||
TerminalKind::Task(spawn_task) => {
|
||||
if let Some(cwd) = &spawn_task.cwd {
|
||||
Some(cwd.clone())
|
||||
Some(Arc::from(cwd.as_ref()))
|
||||
} else {
|
||||
self.active_project_directory(cx)
|
||||
}
|
||||
|
@ -109,7 +111,7 @@ impl Project {
|
|||
});
|
||||
}
|
||||
}
|
||||
let settings = TerminalSettings::get(settings_location, cx);
|
||||
let settings = TerminalSettings::get(settings_location, cx).clone();
|
||||
|
||||
let (completion_tx, completion_rx) = bounded(1);
|
||||
|
||||
|
@ -128,160 +130,206 @@ impl Project {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let python_venv_directory = path
|
||||
.as_ref()
|
||||
.and_then(|path| self.python_venv_directory(path, settings, cx));
|
||||
let mut python_venv_activate_command = None;
|
||||
|
||||
let (spawn_task, shell) = match kind {
|
||||
TerminalKind::Shell(_) => {
|
||||
if let Some(python_venv_directory) = python_venv_directory {
|
||||
python_venv_activate_command =
|
||||
self.python_activate_command(&python_venv_directory, settings);
|
||||
}
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let python_venv_directory = if let Some(path) = path.clone() {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.python_venv_directory(path, settings.detect_venv.clone(), cx)
|
||||
})?
|
||||
.await
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut python_venv_activate_command = None;
|
||||
|
||||
match &ssh_details {
|
||||
Some((host, ssh_command)) => {
|
||||
log::debug!("Connecting to a remote server: {ssh_command:?}");
|
||||
|
||||
// Alacritty sets its terminfo to `alacritty`, this requiring hosts to have it installed
|
||||
// to properly display colors.
|
||||
// We do not have the luxury of assuming the host has it installed,
|
||||
// so we set it to a default that does not break the highlighting via ssh.
|
||||
env.entry("TERM".to_string())
|
||||
.or_insert_with(|| "xterm-256color".to_string());
|
||||
|
||||
let (program, args) =
|
||||
wrap_for_ssh(ssh_command, None, path.as_deref(), env, None);
|
||||
env = HashMap::default();
|
||||
(
|
||||
None,
|
||||
Shell::WithArguments {
|
||||
program,
|
||||
args,
|
||||
title_override: Some(format!("{} — Terminal", host).into()),
|
||||
},
|
||||
)
|
||||
let (spawn_task, shell) = match kind {
|
||||
TerminalKind::Shell(_) => {
|
||||
if let Some(python_venv_directory) = python_venv_directory {
|
||||
python_venv_activate_command = this
|
||||
.update(&mut cx, |this, _| {
|
||||
this.python_activate_command(
|
||||
&python_venv_directory,
|
||||
&settings.detect_venv,
|
||||
)
|
||||
})
|
||||
.ok()
|
||||
.flatten();
|
||||
}
|
||||
None => (None, settings.shell.clone()),
|
||||
}
|
||||
}
|
||||
TerminalKind::Task(spawn_task) => {
|
||||
let task_state = Some(TaskState {
|
||||
id: spawn_task.id,
|
||||
full_label: spawn_task.full_label,
|
||||
label: spawn_task.label,
|
||||
command_label: spawn_task.command_label,
|
||||
hide: spawn_task.hide,
|
||||
status: TaskStatus::Running,
|
||||
show_summary: spawn_task.show_summary,
|
||||
show_command: spawn_task.show_command,
|
||||
completion_rx,
|
||||
});
|
||||
|
||||
env.extend(spawn_task.env);
|
||||
match &ssh_details {
|
||||
Some((host, ssh_command)) => {
|
||||
log::debug!("Connecting to a remote server: {ssh_command:?}");
|
||||
|
||||
if let Some(venv_path) = &python_venv_directory {
|
||||
env.insert(
|
||||
"VIRTUAL_ENV".to_string(),
|
||||
venv_path.to_string_lossy().to_string(),
|
||||
);
|
||||
}
|
||||
// Alacritty sets its terminfo to `alacritty`, this requiring hosts to have it installed
|
||||
// to properly display colors.
|
||||
// We do not have the luxury of assuming the host has it installed,
|
||||
// so we set it to a default that does not break the highlighting via ssh.
|
||||
env.entry("TERM".to_string())
|
||||
.or_insert_with(|| "xterm-256color".to_string());
|
||||
|
||||
match &ssh_details {
|
||||
Some((host, ssh_command)) => {
|
||||
log::debug!("Connecting to a remote server: {ssh_command:?}");
|
||||
env.entry("TERM".to_string())
|
||||
.or_insert_with(|| "xterm-256color".to_string());
|
||||
let (program, args) = wrap_for_ssh(
|
||||
ssh_command,
|
||||
Some((&spawn_task.command, &spawn_task.args)),
|
||||
path.as_deref(),
|
||||
env,
|
||||
python_venv_directory,
|
||||
);
|
||||
env = HashMap::default();
|
||||
(
|
||||
task_state,
|
||||
Shell::WithArguments {
|
||||
program,
|
||||
args,
|
||||
title_override: Some(format!("{} — Terminal", host).into()),
|
||||
},
|
||||
)
|
||||
}
|
||||
None => {
|
||||
if let Some(venv_path) = &python_venv_directory {
|
||||
add_environment_path(&mut env, &venv_path.join("bin")).log_err();
|
||||
let (program, args) =
|
||||
wrap_for_ssh(ssh_command, None, path.as_deref(), env, None);
|
||||
env = HashMap::default();
|
||||
(
|
||||
Option::<TaskState>::None,
|
||||
Shell::WithArguments {
|
||||
program,
|
||||
args,
|
||||
title_override: Some(format!("{} — Terminal", host).into()),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
(
|
||||
task_state,
|
||||
Shell::WithArguments {
|
||||
program: spawn_task.command,
|
||||
args: spawn_task.args,
|
||||
title_override: None,
|
||||
},
|
||||
)
|
||||
None => (None, settings.shell.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
TerminalKind::Task(spawn_task) => {
|
||||
let task_state = Some(TaskState {
|
||||
id: spawn_task.id,
|
||||
full_label: spawn_task.full_label,
|
||||
label: spawn_task.label,
|
||||
command_label: spawn_task.command_label,
|
||||
hide: spawn_task.hide,
|
||||
status: TaskStatus::Running,
|
||||
show_summary: spawn_task.show_summary,
|
||||
show_command: spawn_task.show_command,
|
||||
completion_rx,
|
||||
});
|
||||
|
||||
let terminal = TerminalBuilder::new(
|
||||
local_path,
|
||||
spawn_task,
|
||||
shell,
|
||||
env,
|
||||
settings.cursor_shape.unwrap_or_default(),
|
||||
settings.alternate_scroll,
|
||||
settings.max_scroll_history_lines,
|
||||
ssh_details.is_some(),
|
||||
window,
|
||||
completion_tx,
|
||||
cx,
|
||||
)
|
||||
.map(|builder| {
|
||||
let terminal_handle = cx.new_model(|cx| builder.subscribe(cx));
|
||||
env.extend(spawn_task.env);
|
||||
|
||||
self.terminals
|
||||
.local_handles
|
||||
.push(terminal_handle.downgrade());
|
||||
if let Some(venv_path) = &python_venv_directory {
|
||||
env.insert(
|
||||
"VIRTUAL_ENV".to_string(),
|
||||
venv_path.to_string_lossy().to_string(),
|
||||
);
|
||||
}
|
||||
|
||||
let id = terminal_handle.entity_id();
|
||||
cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
|
||||
let handles = &mut project.terminals.local_handles;
|
||||
match &ssh_details {
|
||||
Some((host, ssh_command)) => {
|
||||
log::debug!("Connecting to a remote server: {ssh_command:?}");
|
||||
env.entry("TERM".to_string())
|
||||
.or_insert_with(|| "xterm-256color".to_string());
|
||||
let (program, args) = wrap_for_ssh(
|
||||
ssh_command,
|
||||
Some((&spawn_task.command, &spawn_task.args)),
|
||||
path.as_deref(),
|
||||
env,
|
||||
python_venv_directory,
|
||||
);
|
||||
env = HashMap::default();
|
||||
(
|
||||
task_state,
|
||||
Shell::WithArguments {
|
||||
program,
|
||||
args,
|
||||
title_override: Some(format!("{} — Terminal", host).into()),
|
||||
},
|
||||
)
|
||||
}
|
||||
None => {
|
||||
if let Some(venv_path) = &python_venv_directory {
|
||||
add_environment_path(&mut env, &venv_path.join("bin")).log_err();
|
||||
}
|
||||
|
||||
if let Some(index) = handles
|
||||
.iter()
|
||||
.position(|terminal| terminal.entity_id() == id)
|
||||
{
|
||||
handles.remove(index);
|
||||
cx.notify();
|
||||
(
|
||||
task_state,
|
||||
Shell::WithArguments {
|
||||
program: spawn_task.command,
|
||||
args: spawn_task.args,
|
||||
title_override: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
};
|
||||
let terminal = this.update(&mut cx, |this, cx| {
|
||||
TerminalBuilder::new(
|
||||
local_path.map(|path| path.to_path_buf()),
|
||||
spawn_task,
|
||||
shell,
|
||||
env,
|
||||
settings.cursor_shape.unwrap_or_default(),
|
||||
settings.alternate_scroll,
|
||||
settings.max_scroll_history_lines,
|
||||
ssh_details.is_some(),
|
||||
window,
|
||||
completion_tx,
|
||||
cx,
|
||||
)
|
||||
.map(|builder| {
|
||||
let terminal_handle = cx.new_model(|cx| builder.subscribe(cx));
|
||||
|
||||
if let Some(activate_command) = python_venv_activate_command {
|
||||
self.activate_python_virtual_environment(activate_command, &terminal_handle, cx);
|
||||
}
|
||||
terminal_handle
|
||||
});
|
||||
this.terminals
|
||||
.local_handles
|
||||
.push(terminal_handle.downgrade());
|
||||
|
||||
terminal
|
||||
let id = terminal_handle.entity_id();
|
||||
cx.observe_release(&terminal_handle, move |project, _terminal, cx| {
|
||||
let handles = &mut project.terminals.local_handles;
|
||||
|
||||
if let Some(index) = handles
|
||||
.iter()
|
||||
.position(|terminal| terminal.entity_id() == id)
|
||||
{
|
||||
handles.remove(index);
|
||||
cx.notify();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
if let Some(activate_command) = python_venv_activate_command {
|
||||
this.activate_python_virtual_environment(
|
||||
activate_command,
|
||||
&terminal_handle,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
terminal_handle
|
||||
})
|
||||
})?;
|
||||
|
||||
terminal
|
||||
})
|
||||
}
|
||||
|
||||
pub fn python_venv_directory(
|
||||
fn python_venv_directory(
|
||||
&self,
|
||||
abs_path: &Path,
|
||||
settings: &TerminalSettings,
|
||||
cx: &AppContext,
|
||||
) -> Option<PathBuf> {
|
||||
let venv_settings = settings.detect_venv.as_option()?;
|
||||
if let Some(path) = self.find_venv_in_worktree(abs_path, &venv_settings, cx) {
|
||||
return Some(path);
|
||||
}
|
||||
self.find_venv_on_filesystem(abs_path, &venv_settings, cx)
|
||||
abs_path: Arc<Path>,
|
||||
venv_settings: VenvSettings,
|
||||
cx: &ModelContext<Project>,
|
||||
) -> Task<Option<PathBuf>> {
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
if let Some((worktree, _)) = this
|
||||
.update(&mut cx, |this, cx| this.find_worktree(&abs_path, cx))
|
||||
.ok()?
|
||||
{
|
||||
let toolchain = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.active_toolchain(
|
||||
worktree.read(cx).id(),
|
||||
LanguageName::new("Python"),
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.ok()?
|
||||
.await;
|
||||
|
||||
if let Some(toolchain) = toolchain {
|
||||
let toolchain_path = Path::new(toolchain.path.as_ref());
|
||||
return Some(toolchain_path.parent()?.parent()?.to_path_buf());
|
||||
}
|
||||
}
|
||||
let venv_settings = venv_settings.as_option()?;
|
||||
this.update(&mut cx, move |this, cx| {
|
||||
if let Some(path) = this.find_venv_in_worktree(&abs_path, &venv_settings, cx) {
|
||||
return Some(path);
|
||||
}
|
||||
this.find_venv_on_filesystem(&abs_path, &venv_settings, cx)
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
})
|
||||
}
|
||||
|
||||
fn find_venv_in_worktree(
|
||||
|
@ -337,9 +385,9 @@ impl Project {
|
|||
fn python_activate_command(
|
||||
&self,
|
||||
venv_base_directory: &Path,
|
||||
settings: &TerminalSettings,
|
||||
venv_settings: &VenvSettings,
|
||||
) -> Option<String> {
|
||||
let venv_settings = settings.detect_venv.as_option()?;
|
||||
let venv_settings = venv_settings.as_option()?;
|
||||
let activate_keyword = match venv_settings.activate_script {
|
||||
terminal_settings::ActivateScript::Default => match std::env::consts::OS {
|
||||
"windows" => ".",
|
||||
|
@ -441,7 +489,7 @@ pub fn wrap_for_ssh(
|
|||
(program, args)
|
||||
}
|
||||
|
||||
fn add_environment_path(env: &mut HashMap<String, String>, new_path: &Path) -> anyhow::Result<()> {
|
||||
fn add_environment_path(env: &mut HashMap<String, String>, new_path: &Path) -> Result<()> {
|
||||
let mut env_paths = vec![new_path.to_path_buf()];
|
||||
if let Some(path) = env.get("PATH").or(env::var("PATH").ok().as_ref()) {
|
||||
let mut paths = std::env::split_paths(&path).collect::<Vec<_>>();
|
||||
|
|
|
@ -24,7 +24,7 @@ pub struct Toolbar {
|
|||
pub breadcrumbs: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct TerminalSettings {
|
||||
pub shell: Shell,
|
||||
pub working_directory: WorkingDirectory,
|
||||
|
|
|
@ -5,7 +5,7 @@ use futures::{stream::FuturesUnordered, StreamExt as _};
|
|||
use gpui::{AsyncWindowContext, Axis, Model, Task, View, WeakView};
|
||||
use project::{terminals::TerminalKind, Project};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use ui::{Pixels, ViewContext, VisualContext as _, WindowContext};
|
||||
use util::ResultExt as _;
|
||||
|
||||
|
@ -219,33 +219,39 @@ async fn deserialize_pane_group(
|
|||
})
|
||||
.log_err()?;
|
||||
let active_item = serialized_pane.active_item;
|
||||
pane.update(cx, |pane, cx| {
|
||||
populate_pane_items(pane, new_items, active_item, cx);
|
||||
// Avoid blank panes in splits
|
||||
if pane.items_len() == 0 {
|
||||
let working_directory = workspace
|
||||
.update(cx, |workspace, cx| default_working_directory(workspace, cx))
|
||||
.ok()
|
||||
.flatten();
|
||||
let kind = TerminalKind::Shell(working_directory);
|
||||
let window = cx.window_handle();
|
||||
let terminal = project
|
||||
.update(cx, |project, cx| project.create_terminal(kind, window, cx))
|
||||
.log_err()?;
|
||||
|
||||
let terminal = pane
|
||||
.update(cx, |pane, cx| {
|
||||
populate_pane_items(pane, new_items, active_item, cx);
|
||||
// Avoid blank panes in splits
|
||||
if pane.items_len() == 0 {
|
||||
let working_directory = workspace
|
||||
.update(cx, |workspace, cx| default_working_directory(workspace, cx))
|
||||
.ok()
|
||||
.flatten();
|
||||
let kind = TerminalKind::Shell(
|
||||
working_directory.as_deref().map(Path::to_path_buf),
|
||||
);
|
||||
let window = cx.window_handle();
|
||||
let terminal = project
|
||||
.update(cx, |project, cx| project.create_terminal(kind, window, cx));
|
||||
Some(Some(terminal))
|
||||
} else {
|
||||
Some(None)
|
||||
}
|
||||
})
|
||||
.ok()
|
||||
.flatten()?;
|
||||
if let Some(terminal) = terminal {
|
||||
let terminal = terminal.await.ok()?;
|
||||
pane.update(cx, |pane, cx| {
|
||||
let terminal_view = Box::new(cx.new_view(|cx| {
|
||||
TerminalView::new(
|
||||
terminal.clone(),
|
||||
workspace.clone(),
|
||||
Some(workspace_id),
|
||||
cx,
|
||||
)
|
||||
TerminalView::new(terminal, workspace.clone(), Some(workspace_id), cx)
|
||||
}));
|
||||
pane.add_item(terminal_view, true, false, None, cx);
|
||||
}
|
||||
Some(())
|
||||
})
|
||||
.ok()
|
||||
.flatten()?;
|
||||
})
|
||||
.ok()?;
|
||||
}
|
||||
Some((Member::Pane(pane.clone()), active.then_some(pane)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -318,10 +318,19 @@ impl TerminalPanel {
|
|||
}
|
||||
}
|
||||
pane::Event::Split(direction) => {
|
||||
let Some(new_pane) = self.new_pane_with_cloned_active_terminal(cx) else {
|
||||
return;
|
||||
};
|
||||
self.center.split(&pane, &new_pane, *direction).log_err();
|
||||
let new_pane = self.new_pane_with_cloned_active_terminal(cx);
|
||||
let pane = pane.clone();
|
||||
let direction = *direction;
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let Some(new_pane) = new_pane.await else {
|
||||
return;
|
||||
};
|
||||
this.update(&mut cx, |this, _| {
|
||||
this.center.split(&pane, &new_pane, direction).log_err();
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
pane::Event::Focus => {
|
||||
self.active_pane = pane.clone();
|
||||
|
@ -334,8 +343,12 @@ impl TerminalPanel {
|
|||
fn new_pane_with_cloned_active_terminal(
|
||||
&mut self,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> Option<View<Pane>> {
|
||||
let workspace = self.workspace.clone().upgrade()?;
|
||||
) -> Task<Option<View<Pane>>> {
|
||||
let Some(workspace) = self.workspace.clone().upgrade() else {
|
||||
return Task::ready(None);
|
||||
};
|
||||
let database_id = workspace.read(cx).database_id();
|
||||
let weak_workspace = self.workspace.clone();
|
||||
let project = workspace.read(cx).project().clone();
|
||||
let working_directory = self
|
||||
.active_pane
|
||||
|
@ -352,21 +365,37 @@ impl TerminalPanel {
|
|||
.or_else(|| default_working_directory(workspace.read(cx), cx));
|
||||
let kind = TerminalKind::Shell(working_directory);
|
||||
let window = cx.window_handle();
|
||||
let terminal = project
|
||||
.update(cx, |project, cx| project.create_terminal(kind, window, cx))
|
||||
.log_err()?;
|
||||
let database_id = workspace.read(cx).database_id();
|
||||
let terminal_view = Box::new(cx.new_view(|cx| {
|
||||
TerminalView::new(terminal.clone(), self.workspace.clone(), database_id, cx)
|
||||
}));
|
||||
let pane = new_terminal_pane(self.workspace.clone(), project, cx);
|
||||
self.apply_tab_bar_buttons(&pane, cx);
|
||||
pane.update(cx, |pane, cx| {
|
||||
pane.add_item(terminal_view, true, true, None, cx);
|
||||
});
|
||||
cx.focus_view(&pane);
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let terminal = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.create_terminal(kind, window, cx)
|
||||
})
|
||||
.log_err()?
|
||||
.await
|
||||
.log_err()?;
|
||||
|
||||
Some(pane)
|
||||
let terminal_view = Box::new(
|
||||
cx.new_view(|cx| {
|
||||
TerminalView::new(terminal.clone(), weak_workspace.clone(), database_id, cx)
|
||||
})
|
||||
.ok()?,
|
||||
);
|
||||
let pane = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
let pane = new_terminal_pane(weak_workspace, project, cx);
|
||||
this.apply_tab_bar_buttons(&pane, cx);
|
||||
pane
|
||||
})
|
||||
.ok()?;
|
||||
|
||||
pane.update(&mut cx, |pane, cx| {
|
||||
pane.add_item(terminal_view, true, true, None, cx);
|
||||
})
|
||||
.ok()?;
|
||||
cx.focus_view(&pane).ok()?;
|
||||
|
||||
Some(pane)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn open_terminal(
|
||||
|
@ -489,43 +518,58 @@ impl TerminalPanel {
|
|||
.last()
|
||||
.expect("covered no terminals case above")
|
||||
.clone();
|
||||
if allow_concurrent_runs {
|
||||
debug_assert!(
|
||||
!use_new_terminal,
|
||||
"Should have handled 'allow_concurrent_runs && use_new_terminal' case above"
|
||||
);
|
||||
self.replace_terminal(
|
||||
spawn_task,
|
||||
task_pane,
|
||||
existing_item_index,
|
||||
existing_terminal,
|
||||
cx,
|
||||
);
|
||||
} else {
|
||||
self.deferred_tasks.insert(
|
||||
spawn_in_terminal.id.clone(),
|
||||
cx.spawn(|terminal_panel, mut cx| async move {
|
||||
wait_for_terminals_tasks(terminals_for_task, &mut cx).await;
|
||||
terminal_panel
|
||||
.update(&mut cx, |terminal_panel, cx| {
|
||||
if use_new_terminal {
|
||||
terminal_panel
|
||||
.spawn_in_new_terminal(spawn_task, cx)
|
||||
.detach_and_log_err(cx);
|
||||
} else {
|
||||
terminal_panel.replace_terminal(
|
||||
spawn_task,
|
||||
task_pane,
|
||||
existing_item_index,
|
||||
existing_terminal,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
}),
|
||||
);
|
||||
}
|
||||
let id = spawn_in_terminal.id.clone();
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
if allow_concurrent_runs {
|
||||
debug_assert!(
|
||||
!use_new_terminal,
|
||||
"Should have handled 'allow_concurrent_runs && use_new_terminal' case above"
|
||||
);
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.replace_terminal(
|
||||
spawn_task,
|
||||
task_pane,
|
||||
existing_item_index,
|
||||
existing_terminal,
|
||||
cx,
|
||||
)
|
||||
})?
|
||||
.await;
|
||||
} else {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.deferred_tasks.insert(
|
||||
id,
|
||||
cx.spawn(|terminal_panel, mut cx| async move {
|
||||
wait_for_terminals_tasks(terminals_for_task, &mut cx).await;
|
||||
let Ok(Some(new_terminal_task)) =
|
||||
terminal_panel.update(&mut cx, |terminal_panel, cx| {
|
||||
if use_new_terminal {
|
||||
terminal_panel
|
||||
.spawn_in_new_terminal(spawn_task, cx)
|
||||
.detach_and_log_err(cx);
|
||||
None
|
||||
} else {
|
||||
Some(terminal_panel.replace_terminal(
|
||||
spawn_task,
|
||||
task_pane,
|
||||
existing_item_index,
|
||||
existing_terminal,
|
||||
cx,
|
||||
))
|
||||
}
|
||||
})
|
||||
else {
|
||||
return;
|
||||
};
|
||||
new_terminal_task.await;
|
||||
}),
|
||||
);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
anyhow::Result::<_, anyhow::Error>::Ok(())
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
|
||||
pub fn spawn_in_new_terminal(
|
||||
|
@ -611,11 +655,14 @@ impl TerminalPanel {
|
|||
|
||||
cx.spawn(|terminal_panel, mut cx| async move {
|
||||
let pane = terminal_panel.update(&mut cx, |this, _| this.active_pane.clone())?;
|
||||
let project = workspace.update(&mut cx, |workspace, _| workspace.project().clone())?;
|
||||
let window = cx.window_handle();
|
||||
let terminal = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.create_terminal(kind, window, cx)
|
||||
})?
|
||||
.await?;
|
||||
let result = workspace.update(&mut cx, |workspace, cx| {
|
||||
let window = cx.window_handle();
|
||||
let terminal = workspace
|
||||
.project()
|
||||
.update(cx, |project, cx| project.create_terminal(kind, window, cx))?;
|
||||
let terminal_view = Box::new(cx.new_view(|cx| {
|
||||
TerminalView::new(
|
||||
terminal.clone(),
|
||||
|
@ -695,48 +742,64 @@ impl TerminalPanel {
|
|||
terminal_item_index: usize,
|
||||
terminal_to_replace: View<TerminalView>,
|
||||
cx: &mut ViewContext<'_, Self>,
|
||||
) -> Option<()> {
|
||||
let project = self
|
||||
.workspace
|
||||
.update(cx, |workspace, _| workspace.project().clone())
|
||||
.ok()?;
|
||||
|
||||
) -> Task<Option<()>> {
|
||||
let reveal = spawn_task.reveal;
|
||||
let window = cx.window_handle();
|
||||
let new_terminal = project.update(cx, |project, cx| {
|
||||
project
|
||||
.create_terminal(TerminalKind::Task(spawn_task), window, cx)
|
||||
.log_err()
|
||||
})?;
|
||||
terminal_to_replace.update(cx, |terminal_to_replace, cx| {
|
||||
terminal_to_replace.set_terminal(new_terminal, cx);
|
||||
});
|
||||
|
||||
match reveal {
|
||||
RevealStrategy::Always => {
|
||||
self.activate_terminal_view(&task_pane, terminal_item_index, true, 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))
|
||||
let task_workspace = self.workspace.clone();
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let project = this
|
||||
.update(&mut cx, |this, cx| {
|
||||
this.workspace
|
||||
.update(cx, |workspace, _| workspace.project().clone())
|
||||
.ok()
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
RevealStrategy::NoFocus => {
|
||||
self.activate_terminal_view(&task_pane, terminal_item_index, false, cx);
|
||||
let task_workspace = self.workspace.clone();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
task_workspace
|
||||
.update(&mut cx, |workspace, cx| workspace.open_panel::<Self>(cx))
|
||||
.ok()
|
||||
.ok()
|
||||
.flatten()?;
|
||||
let new_terminal = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.create_terminal(TerminalKind::Task(spawn_task), window, cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
RevealStrategy::Never => {}
|
||||
}
|
||||
.ok()?
|
||||
.await
|
||||
.log_err()?;
|
||||
terminal_to_replace
|
||||
.update(&mut cx, |terminal_to_replace, cx| {
|
||||
terminal_to_replace.set_terminal(new_terminal, cx);
|
||||
})
|
||||
.ok()?;
|
||||
|
||||
Some(())
|
||||
match reveal {
|
||||
RevealStrategy::Always => {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.activate_terminal_view(&task_pane, terminal_item_index, true, cx)
|
||||
})
|
||||
.ok()?;
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
task_workspace
|
||||
.update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx))
|
||||
.ok()
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
RevealStrategy::NoFocus => {
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.activate_terminal_view(&task_pane, terminal_item_index, false, cx)
|
||||
})
|
||||
.ok()?;
|
||||
|
||||
cx.spawn(|mut cx| async move {
|
||||
task_workspace
|
||||
.update(&mut cx, |workspace, cx| workspace.open_panel::<Self>(cx))
|
||||
.ok()
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
RevealStrategy::Never => {}
|
||||
}
|
||||
|
||||
Some(())
|
||||
})
|
||||
}
|
||||
|
||||
fn has_no_terminals(&self, cx: &WindowContext) -> bool {
|
||||
|
@ -998,18 +1061,18 @@ impl Render for TerminalPanel {
|
|||
if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) {
|
||||
cx.focus_view(&pane);
|
||||
} else {
|
||||
if let Some(new_pane) =
|
||||
terminal_panel.new_pane_with_cloned_active_terminal(cx)
|
||||
{
|
||||
terminal_panel
|
||||
.center
|
||||
.split(
|
||||
&terminal_panel.active_pane,
|
||||
&new_pane,
|
||||
SplitDirection::Right,
|
||||
)
|
||||
.log_err();
|
||||
}
|
||||
let new_pane = terminal_panel.new_pane_with_cloned_active_terminal(cx);
|
||||
cx.spawn(|this, mut cx| async move {
|
||||
if let Some(new_pane) = new_pane.await {
|
||||
this.update(&mut cx, |this, _| {
|
||||
this.center
|
||||
.split(&this.active_pane, &new_pane, SplitDirection::Right)
|
||||
.log_err();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}))
|
||||
.on_action(cx.listener(
|
||||
|
|
|
@ -136,24 +136,36 @@ impl TerminalView {
|
|||
let working_directory = default_working_directory(workspace, cx);
|
||||
|
||||
let window = cx.window_handle();
|
||||
let terminal = workspace
|
||||
.project()
|
||||
.update(cx, |project, cx| {
|
||||
project.create_terminal(TerminalKind::Shell(working_directory), window, cx)
|
||||
})
|
||||
.notify_err(workspace, cx);
|
||||
let project = workspace.project().downgrade();
|
||||
cx.spawn(move |workspace, mut cx| async move {
|
||||
let terminal = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.create_terminal(TerminalKind::Shell(working_directory), window, cx)
|
||||
})
|
||||
.ok()?
|
||||
.await;
|
||||
let terminal = workspace
|
||||
.update(&mut cx, |workspace, cx| terminal.notify_err(workspace, cx))
|
||||
.ok()
|
||||
.flatten()?;
|
||||
|
||||
if let Some(terminal) = terminal {
|
||||
let view = cx.new_view(|cx| {
|
||||
TerminalView::new(
|
||||
terminal,
|
||||
workspace.weak_handle(),
|
||||
workspace.database_id(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
workspace.add_item_to_active_pane(Box::new(view), None, true, cx);
|
||||
}
|
||||
workspace
|
||||
.update(&mut cx, |workspace, cx| {
|
||||
let view = cx.new_view(|cx| {
|
||||
TerminalView::new(
|
||||
terminal,
|
||||
workspace.weak_handle(),
|
||||
workspace.database_id(),
|
||||
cx,
|
||||
)
|
||||
});
|
||||
workspace.add_item_to_active_pane(Box::new(view), None, true, cx);
|
||||
})
|
||||
.ok();
|
||||
|
||||
Some(())
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
|
@ -1231,9 +1243,11 @@ impl SerializableItem for TerminalView {
|
|||
.ok()
|
||||
.flatten();
|
||||
|
||||
let terminal = project.update(&mut cx, |project, cx| {
|
||||
project.create_terminal(TerminalKind::Shell(cwd), window, cx)
|
||||
})??;
|
||||
let terminal = project
|
||||
.update(&mut cx, |project, cx| {
|
||||
project.create_terminal(TerminalKind::Shell(cwd), window, cx)
|
||||
})?
|
||||
.await?;
|
||||
cx.update(|cx| {
|
||||
cx.new_view(|cx| TerminalView::new(terminal, workspace, Some(workspace_id), cx))
|
||||
})
|
||||
|
@ -1362,11 +1376,14 @@ impl SearchableItem for TerminalView {
|
|||
|
||||
///Gets the working directory for the given workspace, respecting the user's settings.
|
||||
/// None implies "~" on whichever machine we end up on.
|
||||
pub fn default_working_directory(workspace: &Workspace, cx: &AppContext) -> Option<PathBuf> {
|
||||
pub(crate) fn default_working_directory(workspace: &Workspace, cx: &AppContext) -> Option<PathBuf> {
|
||||
match &TerminalSettings::get_global(cx).working_directory {
|
||||
WorkingDirectory::CurrentProjectDirectory => {
|
||||
workspace.project().read(cx).active_project_directory(cx)
|
||||
}
|
||||
WorkingDirectory::CurrentProjectDirectory => workspace
|
||||
.project()
|
||||
.read(cx)
|
||||
.active_project_directory(cx)
|
||||
.as_deref()
|
||||
.map(Path::to_path_buf),
|
||||
WorkingDirectory::FirstProjectDirectory => first_project_directory(workspace, cx),
|
||||
WorkingDirectory::AlwaysHome => None,
|
||||
WorkingDirectory::Always { directory } => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue