Detailed changes
@@ -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);
- }
- 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()),
- },
- )
+ 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;
+
+ 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);
- if let Some(venv_path) = &python_venv_directory {
- env.insert(
- "VIRTUAL_ENV".to_string(),
- venv_path.to_string_lossy().to_string(),
- );
+ 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();
+ (
+ Option::<TaskState>::None,
+ Shell::WithArguments {
+ program,
+ args,
+ title_override: Some(format!("{} — Terminal", host).into()),
+ },
+ )
+ }
+ None => (None, settings.shell.clone()),
+ }
}
-
- 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,
+ 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);
+
+ if let Some(venv_path) = &python_venv_directory {
+ env.insert(
+ "VIRTUAL_ENV".to_string(),
+ venv_path.to_string_lossy().to_string(),
);
- 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();
- }
- (
- task_state,
- Shell::WithArguments {
- program: spawn_task.command,
- args: spawn_task.args,
- title_override: None,
- },
- )
+ 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();
+ }
+
+ (
+ task_state,
+ Shell::WithArguments {
+ program: spawn_task.command,
+ args: spawn_task.args,
+ title_override: None,
+ },
+ )
+ }
}
}
- }
- };
-
- 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));
-
- self.terminals
- .local_handles
- .push(terminal_handle.downgrade());
-
- 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();
+ };
+ 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));
+
+ this.terminals
+ .local_handles
+ .push(terminal_handle.downgrade());
+
+ 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 {
- self.activate_python_virtual_environment(activate_command, &terminal_handle, cx);
- }
- terminal_handle
- });
+ if let Some(activate_command) = python_venv_activate_command {
+ this.activate_python_virtual_environment(
+ activate_command,
+ &terminal_handle,
+ cx,
+ );
+ }
+ terminal_handle
+ })
+ })?;
- terminal
+ 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()?;
+
+ 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)
+ 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();
+ .ok()?
+ .await
+ .log_err()?;
+ terminal_to_replace
+ .update(&mut cx, |terminal_to_replace, cx| {
+ terminal_to_replace.set_terminal(new_terminal, cx);
+ })
+ .ok()?;
+
+ 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 => {}
}
- RevealStrategy::Never => {}
- }
- Some(())
+ 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);
-
- 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);
- }
+ 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()?;
+
+ 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 } => {