Detailed changes
@@ -284,8 +284,6 @@
// "directory": "~/zed/projects/"
// }
// }
- //
- //
"working_directory": "current_project_directory",
// Set the cursor blinking behavior in the terminal.
// May take 4 values:
@@ -334,13 +332,23 @@
// "line_height": {
// "custom": 2
// },
- "line_height": "comfortable"
+ "line_height": "comfortable",
// Set the terminal's font size. If this option is not included,
// the terminal will default to matching the buffer's font size.
- // "font_size": "15"
+ // "font_size": "15",
// Set the terminal's font family. If this option is not included,
// the terminal will default to matching the buffer's font family.
- // "font_family": "Zed Mono"
+ // "font_family": "Zed Mono",
+ // ---
+ // Whether or not to automatically search for, and activate, Python virtual
+ // environments.
+ // Current limitations:
+ // - Only ".env", "env", ".venv", and "venv" are searched for at the
+ // root of the project
+ // - Only works with Posix-complaint shells
+ // - Only activates the first virtual environment it finds, regardless
+ // of the nunber of projects in the workspace.
+ "automatically_activate_python_virtual_environment": false
},
// Difference settings for semantic_index
"semantic_index": {
@@ -3,6 +3,9 @@ use gpui::{AnyWindowHandle, ModelContext, ModelHandle, WeakModelHandle};
use std::path::PathBuf;
use terminal::{Terminal, TerminalBuilder, TerminalSettings};
+#[cfg(target_os = "macos")]
+use std::os::unix::ffi::OsStrExt;
+
pub struct Terminals {
pub(crate) local_handles: Vec<WeakModelHandle<terminal::Terminal>>,
}
@@ -47,6 +50,12 @@ impl Project {
})
.detach();
+ let setting = settings::get::<TerminalSettings>(cx);
+
+ if setting.automatically_activate_python_virtual_environment {
+ self.set_up_python_virtual_environment(&terminal_handle, cx);
+ }
+
terminal_handle
});
@@ -54,6 +63,46 @@ impl Project {
}
}
+ fn set_up_python_virtual_environment(
+ &mut self,
+ terminal_handle: &ModelHandle<Terminal>,
+ cx: &mut ModelContext<Project>,
+ ) {
+ let virtual_environment = self.find_python_virtual_environment(cx);
+ if let Some(virtual_environment) = virtual_environment {
+ // 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(virtual_environment.as_os_str().as_bytes());
+ command.push(b'\n');
+
+ terminal_handle.update(cx, |this, _| this.input_bytes(command));
+ }
+ }
+
+ pub fn find_python_virtual_environment(
+ &mut self,
+ cx: &mut ModelContext<Project>,
+ ) -> Option<PathBuf> {
+ const VIRTUAL_ENVIRONMENT_NAMES: [&str; 4] = [".env", "env", ".venv", "venv"];
+
+ let worktree_paths = self
+ .worktrees(cx)
+ .map(|worktree| worktree.read(cx).abs_path());
+
+ for worktree_path in worktree_paths {
+ for virtual_environment_name in VIRTUAL_ENVIRONMENT_NAMES {
+ let mut path = worktree_path.join(virtual_environment_name);
+ path.push("bin/activate");
+
+ if path.exists() {
+ return Some(path);
+ }
+ }
+ }
+
+ None
+ }
+
pub fn local_terminal_handles(&self) -> &Vec<WeakModelHandle<terminal::Terminal>> {
&self.terminals.local_handles
}
@@ -158,6 +158,7 @@ pub struct TerminalSettings {
pub dock: TerminalDockPosition,
pub default_width: f32,
pub default_height: f32,
+ pub automatically_activate_python_virtual_environment: bool,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
@@ -176,6 +177,7 @@ pub struct TerminalSettingsContent {
pub dock: Option<TerminalDockPosition>,
pub default_width: Option<f32>,
pub default_height: Option<f32>,
+ pub automatically_activate_python_virtual_environment: Option<bool>,
}
impl TerminalSettings {
@@ -1018,6 +1020,10 @@ impl Terminal {
self.pty_tx.notify(input.into_bytes());
}
+ fn write_bytes_to_pty(&self, input: Vec<u8>) {
+ self.pty_tx.notify(input);
+ }
+
pub fn input(&mut self, input: String) {
self.events
.push_back(InternalEvent::Scroll(AlacScroll::Bottom));
@@ -1026,6 +1032,14 @@ impl Terminal {
self.write_to_pty(input);
}
+ pub fn input_bytes(&mut self, input: Vec<u8>) {
+ self.events
+ .push_back(InternalEvent::Scroll(AlacScroll::Bottom));
+ self.events.push_back(InternalEvent::SetSelection(None));
+
+ self.write_bytes_to_pty(input);
+ }
+
pub fn try_keystroke(&mut self, keystroke: &Keystroke, alt_is_meta: bool) -> bool {
let esc = to_esc_str(keystroke, &self.last_content.mode, alt_is_meta);
if let Some(esc) = esc {