Add setting to automatically enable virtual environment (#2882)
This isn't ready to go - I'm opening a PR to ask for some advice. When activating a python virtual environment, the typical command used is `source path_to_venv/bin/activate`. The problem is, the activatate script isn't portable to all shells, so some additional scripts are bundled in the env, for example, `activate.fish`. We don't have a good way of knowing what shell we are in, in order to know what script to run. Julia gave the alternative of simply activating the virtual environment while in the zsh context, before the user's custom shell is launched, which I think does work, but because we activate the virtual environment before we launch the custom shell, the shell isn't really aware that we are in the virtual environment and it fails to display the information in the prompt that is typically shown after activating. Is there a clean way for us to know for a fact what shell is being ran, so we know what script to run? Check out the code comments below for more context. --- https://github.com/zed-industries/zed/assets/19867440/ddb76aaa-152b-4c93-a513-3cd580b7c40f I've used Zed to write Python scripts, but working on an actual project has really magnified where Python dev is falling short. A huge quality-of-life thing we can do is provide a setting to automaticaly search for and activate virtual environments when found, when terminals are created. Manually starting these up in every terminal instance is such a drag. A few quirks: - We don't have a way of knowing if the prompt is ready before we try run the command, which means we see the text inserted at the top of the terminal and on the prompt - I dont think this should be a blocker though. - If a user has multiple python projects with mutliple virtual environments, we only detect and activate the first one, since can't really make any assumptions about which one to activate. I dont think this should be a blocker either, as I think most users will have a single project open in Zed. Release Notes: - Added a `detect_venv` setting for the terminal. When configured, the Zed terminal will automatically activate Python virtual environments on terminal creation.
This commit is contained in:
commit
c1fd648390
7 changed files with 268 additions and 131 deletions
|
@ -1,7 +1,13 @@
|
|||
use crate::Project;
|
||||
use gpui::{AnyWindowHandle, ModelContext, ModelHandle, WeakModelHandle};
|
||||
use std::path::PathBuf;
|
||||
use terminal::{Terminal, TerminalBuilder, TerminalSettings};
|
||||
use std::path::{Path, PathBuf};
|
||||
use terminal::{
|
||||
terminal_settings::{self, TerminalSettings, VenvSettingsContent},
|
||||
Terminal, TerminalBuilder,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
pub struct Terminals {
|
||||
pub(crate) local_handles: Vec<WeakModelHandle<terminal::Terminal>>,
|
||||
|
@ -20,10 +26,12 @@ impl Project {
|
|||
));
|
||||
} else {
|
||||
let settings = settings::get::<TerminalSettings>(cx);
|
||||
let python_settings = settings.detect_venv.clone();
|
||||
let shell = settings.shell.clone();
|
||||
|
||||
let terminal = TerminalBuilder::new(
|
||||
working_directory.clone(),
|
||||
settings.shell.clone(),
|
||||
shell.clone(),
|
||||
settings.env.clone(),
|
||||
Some(settings.blinking.clone()),
|
||||
settings.alternate_scroll,
|
||||
|
@ -47,6 +55,15 @@ impl Project {
|
|||
})
|
||||
.detach();
|
||||
|
||||
if let Some(python_settings) = &python_settings.as_option() {
|
||||
let activate_script_path =
|
||||
self.find_activate_script_path(&python_settings, working_directory);
|
||||
self.activate_python_virtual_environment(
|
||||
activate_script_path,
|
||||
&terminal_handle,
|
||||
cx,
|
||||
);
|
||||
}
|
||||
terminal_handle
|
||||
});
|
||||
|
||||
|
@ -54,6 +71,50 @@ impl Project {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn find_activate_script_path(
|
||||
&mut self,
|
||||
settings: &VenvSettingsContent,
|
||||
working_directory: Option<PathBuf>,
|
||||
) -> Option<PathBuf> {
|
||||
// When we are unable to resolve the working directory, the terminal builder
|
||||
// defaults to '/'. We should probably encode this directly somewhere, but for
|
||||
// now, let's just hard code it here.
|
||||
let working_directory = working_directory.unwrap_or_else(|| Path::new("/").to_path_buf());
|
||||
let activate_script_name = match settings.activate_script {
|
||||
terminal_settings::ActivateScript::Default => "activate",
|
||||
terminal_settings::ActivateScript::Csh => "activate.csh",
|
||||
terminal_settings::ActivateScript::Fish => "activate.fish",
|
||||
};
|
||||
|
||||
for virtual_environment_name in settings.directories {
|
||||
let mut path = working_directory.join(virtual_environment_name);
|
||||
path.push("bin/");
|
||||
path.push(activate_script_name);
|
||||
|
||||
if path.exists() {
|
||||
return Some(path);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn activate_python_virtual_environment(
|
||||
&mut self,
|
||||
activate_script: Option<PathBuf>,
|
||||
terminal_handle: &ModelHandle<Terminal>,
|
||||
cx: &mut ModelContext<Project>,
|
||||
) {
|
||||
if let Some(activate_script) = activate_script {
|
||||
// Paths are not strings so we need to jump through some hoops to format the command without `format!`
|
||||
let mut command = Vec::from("source ".as_bytes());
|
||||
command.extend_from_slice(activate_script.as_os_str().as_bytes());
|
||||
command.push(b'\n');
|
||||
|
||||
terminal_handle.update(cx, |this, _| this.input_bytes(command));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_terminal_handles(&self) -> &Vec<WeakModelHandle<terminal::Terminal>> {
|
||||
&self.terminals.local_handles
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue