Add basic PyEnv and pixi support for python environments (#37156)
cc https://github.com/zed-industries/zed/issues/29807 Release Notes: - Fixed terminals and tasks not respecting python pyenv and pixi environments
This commit is contained in:
@@ -14,6 +14,7 @@ use collections::HashMap;
|
|||||||
use fs::Fs;
|
use fs::Fs;
|
||||||
use gpui::{AsyncApp, SharedString};
|
use gpui::{AsyncApp, SharedString};
|
||||||
use settings::WorktreeId;
|
use settings::WorktreeId;
|
||||||
|
use task::ShellKind;
|
||||||
|
|
||||||
use crate::{LanguageName, ManifestName};
|
use crate::{LanguageName, ManifestName};
|
||||||
|
|
||||||
@@ -68,7 +69,12 @@ pub trait ToolchainLister: Send + Sync {
|
|||||||
fn term(&self) -> SharedString;
|
fn term(&self) -> SharedString;
|
||||||
/// Returns the name of the manifest file for this toolchain.
|
/// Returns the name of the manifest file for this toolchain.
|
||||||
fn manifest_name(&self) -> ManifestName;
|
fn manifest_name(&self) -> ManifestName;
|
||||||
async fn activation_script(&self, toolchain: &Toolchain, fs: &dyn Fs) -> Option<String>;
|
async fn activation_script(
|
||||||
|
&self,
|
||||||
|
toolchain: &Toolchain,
|
||||||
|
shell: ShellKind,
|
||||||
|
fs: &dyn Fs,
|
||||||
|
) -> Vec<String>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use task::{TaskTemplate, TaskTemplates, VariableName};
|
use task::{ShellKind, TaskTemplate, TaskTemplates, VariableName};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
pub(crate) struct PyprojectTomlManifestProvider;
|
pub(crate) struct PyprojectTomlManifestProvider;
|
||||||
@@ -894,20 +894,65 @@ impl ToolchainLister for PythonToolchainProvider {
|
|||||||
fn term(&self) -> SharedString {
|
fn term(&self) -> SharedString {
|
||||||
self.term.clone()
|
self.term.clone()
|
||||||
}
|
}
|
||||||
async fn activation_script(&self, toolchain: &Toolchain, fs: &dyn Fs) -> Option<String> {
|
async fn activation_script(
|
||||||
let toolchain = serde_json::from_value::<pet_core::python_environment::PythonEnvironment>(
|
&self,
|
||||||
|
toolchain: &Toolchain,
|
||||||
|
shell: ShellKind,
|
||||||
|
fs: &dyn Fs,
|
||||||
|
) -> Vec<String> {
|
||||||
|
let Ok(toolchain) = serde_json::from_value::<pet_core::python_environment::PythonEnvironment>(
|
||||||
toolchain.as_json.clone(),
|
toolchain.as_json.clone(),
|
||||||
)
|
) else {
|
||||||
.ok()?;
|
return vec![];
|
||||||
let mut activation_script = None;
|
};
|
||||||
if let Some(prefix) = &toolchain.prefix {
|
let mut activation_script = vec![];
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
let path = prefix.join(BINARY_DIR).join("activate");
|
match toolchain.kind {
|
||||||
#[cfg(target_os = "windows")]
|
Some(PythonEnvironmentKind::Pixi) => {
|
||||||
let path = prefix.join(BINARY_DIR).join("activate.ps1");
|
let env = toolchain.name.as_deref().unwrap_or("default");
|
||||||
if fs.is_file(&path).await {
|
activation_script.push(format!("pixi shell -e {env}"))
|
||||||
activation_script = Some(format!(". {}", path.display()));
|
|
||||||
}
|
}
|
||||||
|
Some(PythonEnvironmentKind::Venv | PythonEnvironmentKind::VirtualEnv) => {
|
||||||
|
if let Some(prefix) = &toolchain.prefix {
|
||||||
|
let activate_keyword = match shell {
|
||||||
|
ShellKind::Cmd => ".",
|
||||||
|
ShellKind::Nushell => "overlay use",
|
||||||
|
ShellKind::Powershell => ".",
|
||||||
|
ShellKind::Fish => "source",
|
||||||
|
ShellKind::Csh => "source",
|
||||||
|
ShellKind::Posix => "source",
|
||||||
|
};
|
||||||
|
let activate_script_name = match shell {
|
||||||
|
ShellKind::Posix => "activate",
|
||||||
|
ShellKind::Csh => "activate.csh",
|
||||||
|
ShellKind::Fish => "activate.fish",
|
||||||
|
ShellKind::Nushell => "activate.nu",
|
||||||
|
ShellKind::Powershell => "activate.ps1",
|
||||||
|
ShellKind::Cmd => "activate.bat",
|
||||||
|
};
|
||||||
|
let path = prefix.join(BINARY_DIR).join(activate_script_name);
|
||||||
|
if fs.is_file(&path).await {
|
||||||
|
activation_script.push(format!("{activate_keyword} {}", path.display()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(PythonEnvironmentKind::Pyenv) => {
|
||||||
|
let Some(manager) = toolchain.manager else {
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
let version = toolchain.version.as_deref().unwrap_or("system");
|
||||||
|
let pyenv = manager.executable;
|
||||||
|
let pyenv = pyenv.display();
|
||||||
|
activation_script.extend(match shell {
|
||||||
|
ShellKind::Fish => Some(format!("{pyenv} shell - fish {version}")),
|
||||||
|
ShellKind::Posix => Some(format!("{pyenv} shell - sh {version}")),
|
||||||
|
ShellKind::Nushell => Some(format!("{pyenv} shell - nu {version}")),
|
||||||
|
ShellKind::Powershell => None,
|
||||||
|
ShellKind::Csh => None,
|
||||||
|
ShellKind::Cmd => None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
activation_script
|
activation_script
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -276,7 +276,6 @@ impl DapStore {
|
|||||||
&binary.arguments,
|
&binary.arguments,
|
||||||
&binary.envs,
|
&binary.envs,
|
||||||
binary.cwd.map(|path| path.display().to_string()),
|
binary.cwd.map(|path| path.display().to_string()),
|
||||||
None,
|
|
||||||
port_forwarding,
|
port_forwarding,
|
||||||
)
|
)
|
||||||
})??;
|
})??;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ use serde_json::json;
|
|||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
use std::os;
|
use std::os;
|
||||||
use std::{env, mem, num::NonZeroU32, ops::Range, str::FromStr, sync::OnceLock, task::Poll};
|
use std::{env, mem, num::NonZeroU32, ops::Range, str::FromStr, sync::OnceLock, task::Poll};
|
||||||
use task::{ResolvedTask, TaskContext};
|
use task::{ResolvedTask, ShellKind, TaskContext};
|
||||||
use unindent::Unindent as _;
|
use unindent::Unindent as _;
|
||||||
use util::{
|
use util::{
|
||||||
TryFutureExt as _, assert_set_eq, maybe, path,
|
TryFutureExt as _, assert_set_eq, maybe, path,
|
||||||
@@ -9222,8 +9222,8 @@ fn python_lang(fs: Arc<FakeFs>) -> Arc<Language> {
|
|||||||
fn manifest_name(&self) -> ManifestName {
|
fn manifest_name(&self) -> ManifestName {
|
||||||
SharedString::new_static("pyproject.toml").into()
|
SharedString::new_static("pyproject.toml").into()
|
||||||
}
|
}
|
||||||
async fn activation_script(&self, _: &Toolchain, _: &dyn Fs) -> Option<String> {
|
async fn activation_script(&self, _: &Toolchain, _: ShellKind, _: &dyn Fs) -> Vec<String> {
|
||||||
None
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Arc::new(
|
Arc::new(
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use gpui::{App, AppContext as _, Context, Entity, Task, WeakEntity};
|
use gpui::{App, AppContext as _, Context, Entity, Task, WeakEntity};
|
||||||
use itertools::Itertools;
|
|
||||||
|
use itertools::Itertools as _;
|
||||||
use language::LanguageName;
|
use language::LanguageName;
|
||||||
use remote::RemoteClient;
|
use remote::RemoteClient;
|
||||||
use settings::{Settings, SettingsLocation};
|
use settings::{Settings, SettingsLocation};
|
||||||
@@ -11,7 +12,7 @@ use std::{
|
|||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use task::{Shell, ShellBuilder, SpawnInTerminal};
|
use task::{Shell, ShellBuilder, ShellKind, SpawnInTerminal};
|
||||||
use terminal::{
|
use terminal::{
|
||||||
TaskState, TaskStatus, Terminal, TerminalBuilder, terminal_settings::TerminalSettings,
|
TaskState, TaskStatus, Terminal, TerminalBuilder, terminal_settings::TerminalSettings,
|
||||||
};
|
};
|
||||||
@@ -131,33 +132,62 @@ impl Project {
|
|||||||
cx.spawn(async move |project, cx| {
|
cx.spawn(async move |project, cx| {
|
||||||
let activation_script = maybe!(async {
|
let activation_script = maybe!(async {
|
||||||
let toolchain = toolchain?.await?;
|
let toolchain = toolchain?.await?;
|
||||||
lang_registry
|
Some(
|
||||||
.language_for_name(&toolchain.language_name.0)
|
lang_registry
|
||||||
.await
|
.language_for_name(&toolchain.language_name.0)
|
||||||
.ok()?
|
.await
|
||||||
.toolchain_lister()?
|
.ok()?
|
||||||
.activation_script(&toolchain, fs.as_ref())
|
.toolchain_lister()?
|
||||||
.await
|
.activation_script(&toolchain, ShellKind::new(&shell), fs.as_ref())
|
||||||
|
.await,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.await;
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
project.update(cx, move |this, cx| {
|
project.update(cx, move |this, cx| {
|
||||||
let shell = {
|
let shell = {
|
||||||
env.extend(spawn_task.env);
|
env.extend(spawn_task.env);
|
||||||
match remote_client {
|
match remote_client {
|
||||||
Some(remote_client) => create_remote_shell(
|
Some(remote_client) => match activation_script.clone() {
|
||||||
spawn_task
|
activation_script if !activation_script.is_empty() => {
|
||||||
.command
|
let activation_script = activation_script.join("; ");
|
||||||
.as_ref()
|
let to_run = if let Some(command) = spawn_task.command {
|
||||||
.map(|command| (command, &spawn_task.args)),
|
let command: Option<Cow<str>> = shlex::try_quote(&command).ok();
|
||||||
&mut env,
|
let args = spawn_task
|
||||||
path,
|
.args
|
||||||
remote_client,
|
.iter()
|
||||||
activation_script.clone(),
|
.filter_map(|arg| shlex::try_quote(arg).ok());
|
||||||
cx,
|
command.into_iter().chain(args).join(" ")
|
||||||
)?,
|
} else {
|
||||||
|
format!("exec {shell} -l")
|
||||||
|
};
|
||||||
|
let args = vec![
|
||||||
|
"-c".to_owned(),
|
||||||
|
format!("{activation_script}; {to_run}",),
|
||||||
|
];
|
||||||
|
create_remote_shell(
|
||||||
|
Some((&shell, &args)),
|
||||||
|
&mut env,
|
||||||
|
path,
|
||||||
|
remote_client,
|
||||||
|
cx,
|
||||||
|
)?
|
||||||
|
}
|
||||||
|
_ => create_remote_shell(
|
||||||
|
spawn_task
|
||||||
|
.command
|
||||||
|
.as_ref()
|
||||||
|
.map(|command| (command, &spawn_task.args)),
|
||||||
|
&mut env,
|
||||||
|
path,
|
||||||
|
remote_client,
|
||||||
|
cx,
|
||||||
|
)?,
|
||||||
|
},
|
||||||
None => match activation_script.clone() {
|
None => match activation_script.clone() {
|
||||||
Some(activation_script) => {
|
activation_script if !activation_script.is_empty() => {
|
||||||
|
let activation_script = activation_script.join("; ");
|
||||||
let to_run = if let Some(command) = spawn_task.command {
|
let to_run = if let Some(command) = spawn_task.command {
|
||||||
let command: Option<Cow<str>> = shlex::try_quote(&command).ok();
|
let command: Option<Cow<str>> = shlex::try_quote(&command).ok();
|
||||||
let args = spawn_task
|
let args = spawn_task
|
||||||
@@ -169,7 +199,7 @@ impl Project {
|
|||||||
format!("exec {shell} -l")
|
format!("exec {shell} -l")
|
||||||
};
|
};
|
||||||
Shell::WithArguments {
|
Shell::WithArguments {
|
||||||
program: get_default_system_shell(),
|
program: shell,
|
||||||
args: vec![
|
args: vec![
|
||||||
"-c".to_owned(),
|
"-c".to_owned(),
|
||||||
format!("{activation_script}; {to_run}",),
|
format!("{activation_script}; {to_run}",),
|
||||||
@@ -177,7 +207,7 @@ impl Project {
|
|||||||
title_override: None,
|
title_override: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
_ => {
|
||||||
if let Some(program) = spawn_task.command {
|
if let Some(program) = spawn_task.command {
|
||||||
Shell::WithArguments {
|
Shell::WithArguments {
|
||||||
program,
|
program,
|
||||||
@@ -302,31 +332,21 @@ impl Project {
|
|||||||
.await
|
.await
|
||||||
.ok();
|
.ok();
|
||||||
let lister = language?.toolchain_lister();
|
let lister = language?.toolchain_lister();
|
||||||
lister?.activation_script(&toolchain, fs.as_ref()).await
|
Some(
|
||||||
|
lister?
|
||||||
|
.activation_script(&toolchain, ShellKind::new(&shell), fs.as_ref())
|
||||||
|
.await,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.await;
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
project.update(cx, move |this, cx| {
|
project.update(cx, move |this, cx| {
|
||||||
let shell = {
|
let shell = {
|
||||||
match remote_client {
|
match remote_client {
|
||||||
Some(remote_client) => create_remote_shell(
|
Some(remote_client) => {
|
||||||
None,
|
create_remote_shell(None, &mut env, path, remote_client, cx)?
|
||||||
&mut env,
|
}
|
||||||
path,
|
None => settings.shell,
|
||||||
remote_client,
|
|
||||||
activation_script.clone(),
|
|
||||||
cx,
|
|
||||||
)?,
|
|
||||||
None => match activation_script.clone() {
|
|
||||||
Some(activation_script) => Shell::WithArguments {
|
|
||||||
program: get_default_system_shell(),
|
|
||||||
args: vec![
|
|
||||||
"-c".to_owned(),
|
|
||||||
format!("{activation_script}; exec {shell} -l",),
|
|
||||||
],
|
|
||||||
title_override: Some(shell.into()),
|
|
||||||
},
|
|
||||||
None => settings.shell,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
TerminalBuilder::new(
|
TerminalBuilder::new(
|
||||||
@@ -437,15 +457,10 @@ impl Project {
|
|||||||
|
|
||||||
match remote_client {
|
match remote_client {
|
||||||
Some(remote_client) => {
|
Some(remote_client) => {
|
||||||
let command_template = remote_client.read(cx).build_command(
|
let command_template =
|
||||||
Some(command),
|
remote_client
|
||||||
&args,
|
.read(cx)
|
||||||
&env,
|
.build_command(Some(command), &args, &env, None, None)?;
|
||||||
None,
|
|
||||||
// todo
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
let mut command = std::process::Command::new(command_template.program);
|
let mut command = std::process::Command::new(command_template.program);
|
||||||
command.args(command_template.args);
|
command.args(command_template.args);
|
||||||
command.envs(command_template.env);
|
command.envs(command_template.env);
|
||||||
@@ -473,7 +488,6 @@ fn create_remote_shell(
|
|||||||
env: &mut HashMap<String, String>,
|
env: &mut HashMap<String, String>,
|
||||||
working_directory: Option<Arc<Path>>,
|
working_directory: Option<Arc<Path>>,
|
||||||
remote_client: Entity<RemoteClient>,
|
remote_client: Entity<RemoteClient>,
|
||||||
activation_script: Option<String>,
|
|
||||||
cx: &mut App,
|
cx: &mut App,
|
||||||
) -> Result<Shell> {
|
) -> Result<Shell> {
|
||||||
// Alacritty sets its terminfo to `alacritty`, this requiring hosts to have it installed
|
// Alacritty sets its terminfo to `alacritty`, this requiring hosts to have it installed
|
||||||
@@ -493,7 +507,6 @@ fn create_remote_shell(
|
|||||||
args.as_slice(),
|
args.as_slice(),
|
||||||
env,
|
env,
|
||||||
working_directory.map(|path| path.display().to_string()),
|
working_directory.map(|path| path.display().to_string()),
|
||||||
activation_script,
|
|
||||||
None,
|
None,
|
||||||
)?;
|
)?;
|
||||||
*env = command.env;
|
*env = command.env;
|
||||||
|
|||||||
@@ -757,7 +757,6 @@ impl RemoteClient {
|
|||||||
args: &[String],
|
args: &[String],
|
||||||
env: &HashMap<String, String>,
|
env: &HashMap<String, String>,
|
||||||
working_dir: Option<String>,
|
working_dir: Option<String>,
|
||||||
activation_script: Option<String>,
|
|
||||||
port_forward: Option<(u16, String, u16)>,
|
port_forward: Option<(u16, String, u16)>,
|
||||||
) -> Result<CommandTemplate> {
|
) -> Result<CommandTemplate> {
|
||||||
let Some(connection) = self
|
let Some(connection) = self
|
||||||
@@ -767,14 +766,7 @@ impl RemoteClient {
|
|||||||
else {
|
else {
|
||||||
return Err(anyhow!("no connection"));
|
return Err(anyhow!("no connection"));
|
||||||
};
|
};
|
||||||
connection.build_command(
|
connection.build_command(program, args, env, working_dir, port_forward)
|
||||||
program,
|
|
||||||
args,
|
|
||||||
env,
|
|
||||||
working_dir,
|
|
||||||
activation_script,
|
|
||||||
port_forward,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upload_directory(
|
pub fn upload_directory(
|
||||||
@@ -1006,7 +998,6 @@ pub(crate) trait RemoteConnection: Send + Sync {
|
|||||||
args: &[String],
|
args: &[String],
|
||||||
env: &HashMap<String, String>,
|
env: &HashMap<String, String>,
|
||||||
working_dir: Option<String>,
|
working_dir: Option<String>,
|
||||||
activation_script: Option<String>,
|
|
||||||
port_forward: Option<(u16, String, u16)>,
|
port_forward: Option<(u16, String, u16)>,
|
||||||
) -> Result<CommandTemplate>;
|
) -> Result<CommandTemplate>;
|
||||||
fn connection_options(&self) -> SshConnectionOptions;
|
fn connection_options(&self) -> SshConnectionOptions;
|
||||||
@@ -1373,7 +1364,6 @@ mod fake {
|
|||||||
args: &[String],
|
args: &[String],
|
||||||
env: &HashMap<String, String>,
|
env: &HashMap<String, String>,
|
||||||
_: Option<String>,
|
_: Option<String>,
|
||||||
_: Option<String>,
|
|
||||||
_: Option<(u16, String, u16)>,
|
_: Option<(u16, String, u16)>,
|
||||||
) -> Result<CommandTemplate> {
|
) -> Result<CommandTemplate> {
|
||||||
let ssh_program = program.unwrap_or_else(|| "sh".to_string());
|
let ssh_program = program.unwrap_or_else(|| "sh".to_string());
|
||||||
|
|||||||
@@ -30,10 +30,7 @@ use std::{
|
|||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
use util::{
|
use util::paths::{PathStyle, RemotePathBuf};
|
||||||
get_default_system_shell,
|
|
||||||
paths::{PathStyle, RemotePathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) struct SshRemoteConnection {
|
pub(crate) struct SshRemoteConnection {
|
||||||
socket: SshSocket,
|
socket: SshSocket,
|
||||||
@@ -116,7 +113,6 @@ impl RemoteConnection for SshRemoteConnection {
|
|||||||
input_args: &[String],
|
input_args: &[String],
|
||||||
input_env: &HashMap<String, String>,
|
input_env: &HashMap<String, String>,
|
||||||
working_dir: Option<String>,
|
working_dir: Option<String>,
|
||||||
activation_script: Option<String>,
|
|
||||||
port_forward: Option<(u16, String, u16)>,
|
port_forward: Option<(u16, String, u16)>,
|
||||||
) -> Result<CommandTemplate> {
|
) -> Result<CommandTemplate> {
|
||||||
use std::fmt::Write as _;
|
use std::fmt::Write as _;
|
||||||
@@ -138,9 +134,6 @@ impl RemoteConnection for SshRemoteConnection {
|
|||||||
} else {
|
} else {
|
||||||
write!(&mut script, "cd; ").unwrap();
|
write!(&mut script, "cd; ").unwrap();
|
||||||
};
|
};
|
||||||
if let Some(activation_script) = activation_script {
|
|
||||||
write!(&mut script, " {activation_script};").unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (k, v) in input_env.iter() {
|
for (k, v) in input_env.iter() {
|
||||||
if let Some((k, v)) = shlex::try_quote(k).ok().zip(shlex::try_quote(v).ok()) {
|
if let Some((k, v)) = shlex::try_quote(k).ok().zip(shlex::try_quote(v).ok()) {
|
||||||
@@ -162,8 +155,7 @@ impl RemoteConnection for SshRemoteConnection {
|
|||||||
write!(&mut script, "exec {shell} -l").unwrap();
|
write!(&mut script, "exec {shell} -l").unwrap();
|
||||||
};
|
};
|
||||||
|
|
||||||
let sys_shell = get_default_system_shell();
|
let shell_invocation = format!("{shell} -c {}", shlex::try_quote(&script).unwrap());
|
||||||
let shell_invocation = format!("{sys_shell} -c {}", shlex::try_quote(&script).unwrap());
|
|
||||||
|
|
||||||
let mut args = Vec::new();
|
let mut args = Vec::new();
|
||||||
args.extend(self.socket.ssh_args());
|
args.extend(self.socket.ssh_args());
|
||||||
|
|||||||
@@ -354,7 +354,7 @@ impl TerminalBuilder {
|
|||||||
window_id: u64,
|
window_id: u64,
|
||||||
completion_tx: Option<Sender<Option<ExitStatus>>>,
|
completion_tx: Option<Sender<Option<ExitStatus>>>,
|
||||||
cx: &App,
|
cx: &App,
|
||||||
activation_script: Option<String>,
|
activation_script: Vec<String>,
|
||||||
) -> Result<TerminalBuilder> {
|
) -> Result<TerminalBuilder> {
|
||||||
// If the parent environment doesn't have a locale set
|
// If the parent environment doesn't have a locale set
|
||||||
// (As is the case when launched from a .app on MacOS),
|
// (As is the case when launched from a .app on MacOS),
|
||||||
@@ -493,7 +493,9 @@ impl TerminalBuilder {
|
|||||||
let pty_tx = event_loop.channel();
|
let pty_tx = event_loop.channel();
|
||||||
let _io_thread = event_loop.spawn(); // DANGER
|
let _io_thread = event_loop.spawn(); // DANGER
|
||||||
|
|
||||||
let terminal = Terminal {
|
let no_task = task.is_none();
|
||||||
|
|
||||||
|
let mut terminal = Terminal {
|
||||||
task,
|
task,
|
||||||
pty_tx: Notifier(pty_tx),
|
pty_tx: Notifier(pty_tx),
|
||||||
completion_tx,
|
completion_tx,
|
||||||
@@ -518,7 +520,7 @@ impl TerminalBuilder {
|
|||||||
last_hyperlink_search_position: None,
|
last_hyperlink_search_position: None,
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
shell_program,
|
shell_program,
|
||||||
activation_script,
|
activation_script: activation_script.clone(),
|
||||||
template: CopyTemplate {
|
template: CopyTemplate {
|
||||||
shell,
|
shell,
|
||||||
env,
|
env,
|
||||||
@@ -529,6 +531,14 @@ impl TerminalBuilder {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !activation_script.is_empty() && no_task {
|
||||||
|
for activation_script in activation_script {
|
||||||
|
terminal.input(activation_script.into_bytes());
|
||||||
|
terminal.write_to_pty(b"\n");
|
||||||
|
}
|
||||||
|
terminal.clear();
|
||||||
|
}
|
||||||
|
|
||||||
Ok(TerminalBuilder {
|
Ok(TerminalBuilder {
|
||||||
terminal,
|
terminal,
|
||||||
events_rx,
|
events_rx,
|
||||||
@@ -712,7 +722,7 @@ pub struct Terminal {
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
shell_program: Option<String>,
|
shell_program: Option<String>,
|
||||||
template: CopyTemplate,
|
template: CopyTemplate,
|
||||||
activation_script: Option<String>,
|
activation_script: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CopyTemplate {
|
struct CopyTemplate {
|
||||||
@@ -2218,7 +2228,7 @@ mod tests {
|
|||||||
0,
|
0,
|
||||||
Some(completion_tx),
|
Some(completion_tx),
|
||||||
cx,
|
cx,
|
||||||
None,
|
vec![],
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.subscribe(cx)
|
.subscribe(cx)
|
||||||
|
|||||||
Reference in New Issue
Block a user