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:
Sebastian Nickels 2024-12-03 16:24:30 +01:00 committed by GitHub
parent a76cd778c4
commit 1270ef3ea5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 441 additions and 307 deletions

View file

@ -1,8 +1,9 @@
use crate::Project; use crate::Project;
use anyhow::Context as _; use anyhow::{Context as _, Result};
use collections::HashMap; 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 itertools::Itertools;
use language::LanguageName;
use settings::{Settings, SettingsLocation}; use settings::{Settings, SettingsLocation};
use smol::channel::bounded; use smol::channel::bounded;
use std::{ use std::{
@ -10,10 +11,11 @@ use std::{
env::{self}, env::{self},
iter, iter,
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::Arc,
}; };
use task::{Shell, SpawnInTerminal}; use task::{Shell, SpawnInTerminal};
use terminal::{ use terminal::{
terminal_settings::{self, TerminalSettings}, terminal_settings::{self, TerminalSettings, VenvSettings},
TaskState, TaskStatus, Terminal, TerminalBuilder, TaskState, TaskStatus, Terminal, TerminalBuilder,
}; };
use util::ResultExt; use util::ResultExt;
@ -42,7 +44,7 @@ pub struct SshCommand {
} }
impl Project { 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 let worktree = self
.active_entry() .active_entry()
.and_then(|entry_id| self.worktree_for_entry(entry_id, cx)) .and_then(|entry_id| self.worktree_for_entry(entry_id, cx))
@ -53,7 +55,7 @@ impl Project {
worktree worktree
.root_entry() .root_entry()
.filter(|entry| entry.is_dir()) .filter(|entry| entry.is_dir())
.map(|_| worktree.abs_path().to_path_buf()) .map(|_| worktree.abs_path().clone())
}); });
worktree worktree
} }
@ -87,12 +89,12 @@ impl Project {
kind: TerminalKind, kind: TerminalKind,
window: AnyWindowHandle, window: AnyWindowHandle,
cx: &mut ModelContext<Self>, cx: &mut ModelContext<Self>,
) -> anyhow::Result<Model<Terminal>> { ) -> Task<Result<Model<Terminal>>> {
let path = match &kind { let path: Option<Arc<Path>> = match &kind {
TerminalKind::Shell(path) => path.as_ref().map(|path| path.to_path_buf()), TerminalKind::Shell(path) => path.as_ref().map(|path| Arc::from(path.as_ref())),
TerminalKind::Task(spawn_task) => { TerminalKind::Task(spawn_task) => {
if let Some(cwd) = &spawn_task.cwd { if let Some(cwd) = &spawn_task.cwd {
Some(cwd.clone()) Some(Arc::from(cwd.as_ref()))
} else { } else {
self.active_project_directory(cx) 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); let (completion_tx, completion_rx) = bounded(1);
@ -128,16 +130,30 @@ impl Project {
} else { } else {
None None
}; };
let python_venv_directory = path
.as_ref() cx.spawn(move |this, mut cx| async move {
.and_then(|path| self.python_venv_directory(path, settings, cx)); 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; let mut python_venv_activate_command = None;
let (spawn_task, shell) = match kind { let (spawn_task, shell) = match kind {
TerminalKind::Shell(_) => { TerminalKind::Shell(_) => {
if let Some(python_venv_directory) = python_venv_directory { if let Some(python_venv_directory) = python_venv_directory {
python_venv_activate_command = python_venv_activate_command = this
self.python_activate_command(&python_venv_directory, settings); .update(&mut cx, |this, _| {
this.python_activate_command(
&python_venv_directory,
&settings.detect_venv,
)
})
.ok()
.flatten();
} }
match &ssh_details { match &ssh_details {
@ -155,7 +171,7 @@ impl Project {
wrap_for_ssh(ssh_command, None, path.as_deref(), env, None); wrap_for_ssh(ssh_command, None, path.as_deref(), env, None);
env = HashMap::default(); env = HashMap::default();
( (
None, Option::<TaskState>::None,
Shell::WithArguments { Shell::WithArguments {
program, program,
args, args,
@ -227,9 +243,9 @@ impl Project {
} }
} }
}; };
let terminal = this.update(&mut cx, |this, cx| {
let terminal = TerminalBuilder::new( TerminalBuilder::new(
local_path, local_path.map(|path| path.to_path_buf()),
spawn_task, spawn_task,
shell, shell,
env, env,
@ -244,7 +260,7 @@ impl Project {
.map(|builder| { .map(|builder| {
let terminal_handle = cx.new_model(|cx| builder.subscribe(cx)); let terminal_handle = cx.new_model(|cx| builder.subscribe(cx));
self.terminals this.terminals
.local_handles .local_handles
.push(terminal_handle.downgrade()); .push(terminal_handle.downgrade());
@ -263,25 +279,57 @@ impl Project {
.detach(); .detach();
if let Some(activate_command) = python_venv_activate_command { if let Some(activate_command) = python_venv_activate_command {
self.activate_python_virtual_environment(activate_command, &terminal_handle, cx); this.activate_python_virtual_environment(
activate_command,
&terminal_handle,
cx,
);
} }
terminal_handle terminal_handle
}); })
})?;
terminal terminal
})
} }
pub fn python_venv_directory( fn python_venv_directory(
&self, &self,
abs_path: &Path, abs_path: Arc<Path>,
settings: &TerminalSettings, venv_settings: VenvSettings,
cx: &AppContext, cx: &ModelContext<Project>,
) -> Option<PathBuf> { ) -> Task<Option<PathBuf>> {
let venv_settings = settings.detect_venv.as_option()?; cx.spawn(move |this, mut cx| async move {
if let Some(path) = self.find_venv_in_worktree(abs_path, &venv_settings, cx) { 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); return Some(path);
} }
self.find_venv_on_filesystem(abs_path, &venv_settings, cx) this.find_venv_on_filesystem(&abs_path, &venv_settings, cx)
})
.ok()
.flatten()
})
} }
fn find_venv_in_worktree( fn find_venv_in_worktree(
@ -337,9 +385,9 @@ impl Project {
fn python_activate_command( fn python_activate_command(
&self, &self,
venv_base_directory: &Path, venv_base_directory: &Path,
settings: &TerminalSettings, venv_settings: &VenvSettings,
) -> Option<String> { ) -> 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 { let activate_keyword = match venv_settings.activate_script {
terminal_settings::ActivateScript::Default => match std::env::consts::OS { terminal_settings::ActivateScript::Default => match std::env::consts::OS {
"windows" => ".", "windows" => ".",
@ -441,7 +489,7 @@ pub fn wrap_for_ssh(
(program, args) (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()]; let mut env_paths = vec![new_path.to_path_buf()];
if let Some(path) = env.get("PATH").or(env::var("PATH").ok().as_ref()) { if let Some(path) = env.get("PATH").or(env::var("PATH").ok().as_ref()) {
let mut paths = std::env::split_paths(&path).collect::<Vec<_>>(); let mut paths = std::env::split_paths(&path).collect::<Vec<_>>();

View file

@ -24,7 +24,7 @@ pub struct Toolbar {
pub breadcrumbs: bool, pub breadcrumbs: bool,
} }
#[derive(Debug, Deserialize)] #[derive(Clone, Debug, Deserialize)]
pub struct TerminalSettings { pub struct TerminalSettings {
pub shell: Shell, pub shell: Shell,
pub working_directory: WorkingDirectory, pub working_directory: WorkingDirectory,

View file

@ -5,7 +5,7 @@ use futures::{stream::FuturesUnordered, StreamExt as _};
use gpui::{AsyncWindowContext, Axis, Model, Task, View, WeakView}; use gpui::{AsyncWindowContext, Axis, Model, Task, View, WeakView};
use project::{terminals::TerminalKind, Project}; use project::{terminals::TerminalKind, Project};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::{Path, PathBuf};
use ui::{Pixels, ViewContext, VisualContext as _, WindowContext}; use ui::{Pixels, ViewContext, VisualContext as _, WindowContext};
use util::ResultExt as _; use util::ResultExt as _;
@ -219,7 +219,9 @@ async fn deserialize_pane_group(
}) })
.log_err()?; .log_err()?;
let active_item = serialized_pane.active_item; let active_item = serialized_pane.active_item;
pane.update(cx, |pane, cx| {
let terminal = pane
.update(cx, |pane, cx| {
populate_pane_items(pane, new_items, active_item, cx); populate_pane_items(pane, new_items, active_item, cx);
// Avoid blank panes in splits // Avoid blank panes in splits
if pane.items_len() == 0 { if pane.items_len() == 0 {
@ -227,25 +229,29 @@ async fn deserialize_pane_group(
.update(cx, |workspace, cx| default_working_directory(workspace, cx)) .update(cx, |workspace, cx| default_working_directory(workspace, cx))
.ok() .ok()
.flatten(); .flatten();
let kind = TerminalKind::Shell(working_directory); let kind = TerminalKind::Shell(
working_directory.as_deref().map(Path::to_path_buf),
);
let window = cx.window_handle(); let window = cx.window_handle();
let terminal = project let terminal = project
.update(cx, |project, cx| project.create_terminal(kind, window, cx)) .update(cx, |project, cx| project.create_terminal(kind, window, cx));
.log_err()?; Some(Some(terminal))
let terminal_view = Box::new(cx.new_view(|cx| { } else {
TerminalView::new( Some(None)
terminal.clone(),
workspace.clone(),
Some(workspace_id),
cx,
)
}));
pane.add_item(terminal_view, true, false, None, cx);
} }
Some(())
}) })
.ok() .ok()
.flatten()?; .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, workspace.clone(), Some(workspace_id), cx)
}));
pane.add_item(terminal_view, true, false, None, cx);
})
.ok()?;
}
Some((Member::Pane(pane.clone()), active.then_some(pane))) Some((Member::Pane(pane.clone()), active.then_some(pane)))
} }
} }

View file

@ -318,10 +318,19 @@ impl TerminalPanel {
} }
} }
pane::Event::Split(direction) => { pane::Event::Split(direction) => {
let Some(new_pane) = self.new_pane_with_cloned_active_terminal(cx) else { 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; return;
}; };
self.center.split(&pane, &new_pane, *direction).log_err(); this.update(&mut cx, |this, _| {
this.center.split(&pane, &new_pane, direction).log_err();
})
.ok();
})
.detach();
} }
pane::Event::Focus => { pane::Event::Focus => {
self.active_pane = pane.clone(); self.active_pane = pane.clone();
@ -334,8 +343,12 @@ impl TerminalPanel {
fn new_pane_with_cloned_active_terminal( fn new_pane_with_cloned_active_terminal(
&mut self, &mut self,
cx: &mut ViewContext<Self>, cx: &mut ViewContext<Self>,
) -> Option<View<Pane>> { ) -> Task<Option<View<Pane>>> {
let workspace = self.workspace.clone().upgrade()?; 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 project = workspace.read(cx).project().clone();
let working_directory = self let working_directory = self
.active_pane .active_pane
@ -352,21 +365,37 @@ impl TerminalPanel {
.or_else(|| default_working_directory(workspace.read(cx), cx)); .or_else(|| default_working_directory(workspace.read(cx), cx));
let kind = TerminalKind::Shell(working_directory); let kind = TerminalKind::Shell(working_directory);
let window = cx.window_handle(); let window = cx.window_handle();
cx.spawn(move |this, mut cx| async move {
let terminal = project let terminal = project
.update(cx, |project, cx| project.create_terminal(kind, window, cx)) .update(&mut cx, |project, cx| {
project.create_terminal(kind, window, cx)
})
.log_err()?
.await
.log_err()?; .log_err()?;
let database_id = workspace.read(cx).database_id();
let terminal_view = Box::new(cx.new_view(|cx| { let terminal_view = Box::new(
TerminalView::new(terminal.clone(), self.workspace.clone(), database_id, cx) cx.new_view(|cx| {
})); TerminalView::new(terminal.clone(), weak_workspace.clone(), database_id, cx)
let pane = new_terminal_pane(self.workspace.clone(), project, cx); })
self.apply_tab_bar_buttons(&pane, cx); .ok()?,
pane.update(cx, |pane, cx| { );
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); pane.add_item(terminal_view, true, true, None, cx);
}); })
cx.focus_view(&pane); .ok()?;
cx.focus_view(&pane).ok()?;
Some(pane) Some(pane)
})
} }
pub fn open_terminal( pub fn open_terminal(
@ -489,43 +518,58 @@ impl TerminalPanel {
.last() .last()
.expect("covered no terminals case above") .expect("covered no terminals case above")
.clone(); .clone();
let id = spawn_in_terminal.id.clone();
cx.spawn(move |this, mut cx| async move {
if allow_concurrent_runs { if allow_concurrent_runs {
debug_assert!( debug_assert!(
!use_new_terminal, !use_new_terminal,
"Should have handled 'allow_concurrent_runs && use_new_terminal' case above" "Should have handled 'allow_concurrent_runs && use_new_terminal' case above"
); );
self.replace_terminal( this.update(&mut cx, |this, cx| {
this.replace_terminal(
spawn_task, spawn_task,
task_pane, task_pane,
existing_item_index, existing_item_index,
existing_terminal, existing_terminal,
cx, cx,
); )
})?
.await;
} else { } else {
self.deferred_tasks.insert( this.update(&mut cx, |this, cx| {
spawn_in_terminal.id.clone(), this.deferred_tasks.insert(
id,
cx.spawn(|terminal_panel, mut cx| async move { cx.spawn(|terminal_panel, mut cx| async move {
wait_for_terminals_tasks(terminals_for_task, &mut cx).await; wait_for_terminals_tasks(terminals_for_task, &mut cx).await;
terminal_panel let Ok(Some(new_terminal_task)) =
.update(&mut cx, |terminal_panel, cx| { terminal_panel.update(&mut cx, |terminal_panel, cx| {
if use_new_terminal { if use_new_terminal {
terminal_panel terminal_panel
.spawn_in_new_terminal(spawn_task, cx) .spawn_in_new_terminal(spawn_task, cx)
.detach_and_log_err(cx); .detach_and_log_err(cx);
None
} else { } else {
terminal_panel.replace_terminal( Some(terminal_panel.replace_terminal(
spawn_task, spawn_task,
task_pane, task_pane,
existing_item_index, existing_item_index,
existing_terminal, existing_terminal,
cx, cx,
); ))
} }
}) })
.ok(); else {
return;
};
new_terminal_task.await;
}), }),
); );
})
.ok();
} }
anyhow::Result::<_, anyhow::Error>::Ok(())
})
.detach()
} }
pub fn spawn_in_new_terminal( pub fn spawn_in_new_terminal(
@ -611,11 +655,14 @@ impl TerminalPanel {
cx.spawn(|terminal_panel, mut cx| async move { cx.spawn(|terminal_panel, mut cx| async move {
let pane = terminal_panel.update(&mut cx, |this, _| this.active_pane.clone())?; let pane = terminal_panel.update(&mut cx, |this, _| this.active_pane.clone())?;
let result = workspace.update(&mut cx, |workspace, cx| { let project = workspace.update(&mut cx, |workspace, _| workspace.project().clone())?;
let window = cx.window_handle(); let window = cx.window_handle();
let terminal = workspace let terminal = project
.project() .update(&mut cx, |project, cx| {
.update(cx, |project, cx| project.create_terminal(kind, window, cx))?; project.create_terminal(kind, window, cx)
})?
.await?;
let result = workspace.update(&mut cx, |workspace, cx| {
let terminal_view = Box::new(cx.new_view(|cx| { let terminal_view = Box::new(cx.new_view(|cx| {
TerminalView::new( TerminalView::new(
terminal.clone(), terminal.clone(),
@ -695,28 +742,40 @@ impl TerminalPanel {
terminal_item_index: usize, terminal_item_index: usize,
terminal_to_replace: View<TerminalView>, terminal_to_replace: View<TerminalView>,
cx: &mut ViewContext<'_, Self>, cx: &mut ViewContext<'_, Self>,
) -> Option<()> { ) -> Task<Option<()>> {
let project = self
.workspace
.update(cx, |workspace, _| workspace.project().clone())
.ok()?;
let reveal = spawn_task.reveal; let reveal = spawn_task.reveal;
let window = cx.window_handle(); let window = cx.window_handle();
let new_terminal = project.update(cx, |project, cx| { let task_workspace = self.workspace.clone();
project cx.spawn(move |this, mut cx| async move {
.create_terminal(TerminalKind::Task(spawn_task), window, cx) let project = this
.log_err() .update(&mut cx, |this, cx| {
})?; this.workspace
terminal_to_replace.update(cx, |terminal_to_replace, cx| { .update(cx, |workspace, _| workspace.project().clone())
.ok()
})
.ok()
.flatten()?;
let new_terminal = project
.update(&mut cx, |project, cx| {
project.create_terminal(TerminalKind::Task(spawn_task), window, cx)
})
.ok()?
.await
.log_err()?;
terminal_to_replace
.update(&mut cx, |terminal_to_replace, cx| {
terminal_to_replace.set_terminal(new_terminal, cx); terminal_to_replace.set_terminal(new_terminal, cx);
}); })
.ok()?;
match reveal { match reveal {
RevealStrategy::Always => { RevealStrategy::Always => {
self.activate_terminal_view(&task_pane, terminal_item_index, true, cx); this.update(&mut cx, |this, cx| {
let task_workspace = self.workspace.clone(); this.activate_terminal_view(&task_pane, terminal_item_index, true, cx)
cx.spawn(|_, mut cx| async move { })
.ok()?;
cx.spawn(|mut cx| async move {
task_workspace task_workspace
.update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx)) .update(&mut cx, |workspace, cx| workspace.focus_panel::<Self>(cx))
.ok() .ok()
@ -724,9 +783,12 @@ impl TerminalPanel {
.detach(); .detach();
} }
RevealStrategy::NoFocus => { RevealStrategy::NoFocus => {
self.activate_terminal_view(&task_pane, terminal_item_index, false, cx); this.update(&mut cx, |this, cx| {
let task_workspace = self.workspace.clone(); this.activate_terminal_view(&task_pane, terminal_item_index, false, cx)
cx.spawn(|_, mut cx| async move { })
.ok()?;
cx.spawn(|mut cx| async move {
task_workspace task_workspace
.update(&mut cx, |workspace, cx| workspace.open_panel::<Self>(cx)) .update(&mut cx, |workspace, cx| workspace.open_panel::<Self>(cx))
.ok() .ok()
@ -737,6 +799,7 @@ impl TerminalPanel {
} }
Some(()) Some(())
})
} }
fn has_no_terminals(&self, cx: &WindowContext) -> bool { 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()) { if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) {
cx.focus_view(&pane); cx.focus_view(&pane);
} else { } else {
if let Some(new_pane) = let new_pane = terminal_panel.new_pane_with_cloned_active_terminal(cx);
terminal_panel.new_pane_with_cloned_active_terminal(cx) cx.spawn(|this, mut cx| async move {
{ if let Some(new_pane) = new_pane.await {
terminal_panel this.update(&mut cx, |this, _| {
.center this.center
.split( .split(&this.active_pane, &new_pane, SplitDirection::Right)
&terminal_panel.active_pane,
&new_pane,
SplitDirection::Right,
)
.log_err(); .log_err();
})
.ok();
} }
})
.detach();
} }
})) }))
.on_action(cx.listener( .on_action(cx.listener(

View file

@ -136,14 +136,21 @@ impl TerminalView {
let working_directory = default_working_directory(workspace, cx); let working_directory = default_working_directory(workspace, cx);
let window = cx.window_handle(); let window = cx.window_handle();
let terminal = workspace let project = workspace.project().downgrade();
.project() cx.spawn(move |workspace, mut cx| async move {
.update(cx, |project, cx| { let terminal = project
.update(&mut cx, |project, cx| {
project.create_terminal(TerminalKind::Shell(working_directory), window, cx) project.create_terminal(TerminalKind::Shell(working_directory), window, cx)
}) })
.notify_err(workspace, cx); .ok()?
.await;
let terminal = workspace
.update(&mut cx, |workspace, cx| terminal.notify_err(workspace, cx))
.ok()
.flatten()?;
if let Some(terminal) = terminal { workspace
.update(&mut cx, |workspace, cx| {
let view = cx.new_view(|cx| { let view = cx.new_view(|cx| {
TerminalView::new( TerminalView::new(
terminal, terminal,
@ -153,7 +160,12 @@ impl TerminalView {
) )
}); });
workspace.add_item_to_active_pane(Box::new(view), None, true, cx); workspace.add_item_to_active_pane(Box::new(view), None, true, cx);
} })
.ok();
Some(())
})
.detach()
} }
pub fn new( pub fn new(
@ -1231,9 +1243,11 @@ impl SerializableItem for TerminalView {
.ok() .ok()
.flatten(); .flatten();
let terminal = project.update(&mut cx, |project, cx| { let terminal = project
.update(&mut cx, |project, cx| {
project.create_terminal(TerminalKind::Shell(cwd), window, cx) project.create_terminal(TerminalKind::Shell(cwd), window, cx)
})??; })?
.await?;
cx.update(|cx| { cx.update(|cx| {
cx.new_view(|cx| TerminalView::new(terminal, workspace, Some(workspace_id), 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. ///Gets the working directory for the given workspace, respecting the user's settings.
/// None implies "~" on whichever machine we end up on. /// 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 { match &TerminalSettings::get_global(cx).working_directory {
WorkingDirectory::CurrentProjectDirectory => { WorkingDirectory::CurrentProjectDirectory => workspace
workspace.project().read(cx).active_project_directory(cx) .project()
} .read(cx)
.active_project_directory(cx)
.as_deref()
.map(Path::to_path_buf),
WorkingDirectory::FirstProjectDirectory => first_project_directory(workspace, cx), WorkingDirectory::FirstProjectDirectory => first_project_directory(workspace, cx),
WorkingDirectory::AlwaysHome => None, WorkingDirectory::AlwaysHome => None,
WorkingDirectory::Always { directory } => { WorkingDirectory::Always { directory } => {