Detailed changes
@@ -333,6 +333,24 @@
// "custom": 2
// },
"line_height": "comfortable",
+ // Activate the python virtual environment, if one is found, in the
+ // terminal's working directory (as resolved by the working_directory
+ // setting). Set this to "off" to disable this behavior.
+ "detect_venv": {
+ "on": {
+ // Default directories to search for virtual environments, relative
+ // to the current working directory. We recommend overriding this
+ // in your project's settings, rather than globally.
+ "directories": [
+ ".env",
+ "env",
+ ".venv",
+ "venv"
+ ],
+ // Can also be 'csh' and 'fish'
+ "activate_script": "default"
+ }
+ }
// 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",
@@ -340,15 +358,6 @@
// the terminal will default to matching the buffer's font family.
// "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.
- "activate_python_virtual_environment": false
},
// Difference settings for semantic_index
"semantic_index": {
@@ -1,7 +1,10 @@
use crate::Project;
use gpui::{AnyWindowHandle, ModelContext, ModelHandle, WeakModelHandle};
-use std::path::PathBuf;
-use terminal::{Shell, 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;
@@ -23,8 +26,7 @@ impl Project {
));
} else {
let settings = settings::get::<TerminalSettings>(cx);
- let activate_python_virtual_environment =
- settings.activate_python_virtual_environment.clone();
+ let python_settings = settings.detect_venv.clone();
let shell = settings.shell.clone();
let terminal = TerminalBuilder::new(
@@ -53,15 +55,15 @@ impl Project {
})
.detach();
- if activate_python_virtual_environment {
- let activate_script_path = self.find_activate_script_path(&shell, cx);
+ 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
});
@@ -71,37 +73,26 @@ impl Project {
pub fn find_activate_script_path(
&mut self,
- shell: &Shell,
- cx: &mut ModelContext<Project>,
+ settings: &VenvSettingsContent,
+ working_directory: Option<PathBuf>,
) -> Option<PathBuf> {
- let program = match shell {
- terminal::Shell::System => "Figure this out",
- terminal::Shell::Program(program) => program,
- terminal::Shell::WithArguments { program, args: _ } => program,
+ // 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",
};
- // This is so hacky - find a better way to do this
- let script_name = if program.contains("fish") {
- "activate.fish"
- } else {
- "activate"
- };
+ for virtual_environment_name in settings.directories {
+ let mut path = working_directory.join(virtual_environment_name);
+ path.push("bin/");
+ path.push(activate_script_name);
- let worktree_paths = self
- .worktrees(cx)
- .map(|worktree| worktree.read(cx).abs_path());
-
- const VIRTUAL_ENVIRONMENT_NAMES: [&str; 4] = [".env", "env", ".venv", "venv"];
-
- 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/");
- path.push(script_name);
-
- if path.exists() {
- return Some(path);
- }
+ if path.exists() {
+ return Some(path);
}
}
@@ -1,5 +1,6 @@
pub mod mappings;
pub use alacritty_terminal;
+pub mod terminal_settings;
use alacritty_terminal::{
ansi::{ClearMode, Handler},
@@ -31,8 +32,8 @@ use mappings::mouse::{
};
use procinfo::LocalProcessInfo;
-use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
+use terminal_settings::{AlternateScroll, Shell, TerminalBlink, TerminalSettings};
use util::truncate_and_trailoff;
use std::{
@@ -48,7 +49,6 @@ use std::{
use thiserror::Error;
use gpui::{
- fonts,
geometry::vector::{vec2f, Vector2F},
keymap_matcher::Keystroke,
platform::{Modifiers, MouseButton, MouseMovedEvent, TouchPhase},
@@ -134,124 +134,6 @@ pub fn init(cx: &mut AppContext) {
settings::register::<TerminalSettings>(cx);
}
-#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
-#[serde(rename_all = "snake_case")]
-pub enum TerminalDockPosition {
- Left,
- Bottom,
- Right,
-}
-
-#[derive(Deserialize)]
-pub struct TerminalSettings {
- pub shell: Shell,
- pub working_directory: WorkingDirectory,
- font_size: Option<f32>,
- pub font_family: Option<String>,
- pub line_height: TerminalLineHeight,
- pub font_features: Option<fonts::Features>,
- pub env: HashMap<String, String>,
- pub blinking: TerminalBlink,
- pub alternate_scroll: AlternateScroll,
- pub option_as_meta: bool,
- pub copy_on_select: bool,
- pub dock: TerminalDockPosition,
- pub default_width: f32,
- pub default_height: f32,
- pub activate_python_virtual_environment: bool,
-}
-
-#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
-pub struct TerminalSettingsContent {
- pub shell: Option<Shell>,
- pub working_directory: Option<WorkingDirectory>,
- pub font_size: Option<f32>,
- pub font_family: Option<String>,
- pub line_height: Option<TerminalLineHeight>,
- pub font_features: Option<fonts::Features>,
- pub env: Option<HashMap<String, String>>,
- pub blinking: Option<TerminalBlink>,
- pub alternate_scroll: Option<AlternateScroll>,
- pub option_as_meta: Option<bool>,
- pub copy_on_select: Option<bool>,
- pub dock: Option<TerminalDockPosition>,
- pub default_width: Option<f32>,
- pub default_height: Option<f32>,
- pub activate_python_virtual_environment: Option<bool>,
-}
-
-impl TerminalSettings {
- pub fn font_size(&self, cx: &AppContext) -> Option<f32> {
- self.font_size
- .map(|size| theme::adjusted_font_size(size, cx))
- }
-}
-
-impl settings::Setting for TerminalSettings {
- const KEY: Option<&'static str> = Some("terminal");
-
- type FileContent = TerminalSettingsContent;
-
- fn load(
- default_value: &Self::FileContent,
- user_values: &[&Self::FileContent],
- _: &AppContext,
- ) -> Result<Self> {
- Self::load_via_json_merge(default_value, user_values)
- }
-}
-
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
-#[serde(rename_all = "snake_case")]
-pub enum TerminalLineHeight {
- #[default]
- Comfortable,
- Standard,
- Custom(f32),
-}
-
-impl TerminalLineHeight {
- pub fn value(&self) -> f32 {
- match self {
- TerminalLineHeight::Comfortable => 1.618,
- TerminalLineHeight::Standard => 1.3,
- TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.),
- }
- }
-}
-
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum TerminalBlink {
- Off,
- TerminalControlled,
- On,
-}
-
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum Shell {
- System,
- Program(String),
- WithArguments { program: String, args: Vec<String> },
-}
-
-#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum AlternateScroll {
- On,
- Off,
-}
-
-#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
-#[serde(rename_all = "snake_case")]
-pub enum WorkingDirectory {
- CurrentProjectDirectory,
- FirstProjectDirectory,
- AlwaysHome,
- Always { directory: String },
-}
-
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct TerminalSize {
pub cell_width: f32,
@@ -0,0 +1,163 @@
+use std::{collections::HashMap, path::PathBuf};
+
+use gpui::{fonts, AppContext};
+use schemars::JsonSchema;
+use serde_derive::{Deserialize, Serialize};
+
+#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
+#[serde(rename_all = "snake_case")]
+pub enum TerminalDockPosition {
+ Left,
+ Bottom,
+ Right,
+}
+
+#[derive(Deserialize)]
+pub struct TerminalSettings {
+ pub shell: Shell,
+ pub working_directory: WorkingDirectory,
+ font_size: Option<f32>,
+ pub font_family: Option<String>,
+ pub line_height: TerminalLineHeight,
+ pub font_features: Option<fonts::Features>,
+ pub env: HashMap<String, String>,
+ pub blinking: TerminalBlink,
+ pub alternate_scroll: AlternateScroll,
+ pub option_as_meta: bool,
+ pub copy_on_select: bool,
+ pub dock: TerminalDockPosition,
+ pub default_width: f32,
+ pub default_height: f32,
+ pub detect_venv: VenvSettings,
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum VenvSettings {
+ #[default]
+ Off,
+ On {
+ activate_script: Option<ActivateScript>,
+ directories: Option<Vec<PathBuf>>,
+ },
+}
+
+pub struct VenvSettingsContent<'a> {
+ pub activate_script: ActivateScript,
+ pub directories: &'a [PathBuf],
+}
+
+impl VenvSettings {
+ pub fn as_option(&self) -> Option<VenvSettingsContent> {
+ match self {
+ VenvSettings::Off => None,
+ VenvSettings::On {
+ activate_script,
+ directories,
+ } => Some(VenvSettingsContent {
+ activate_script: activate_script.unwrap_or(ActivateScript::Default),
+ directories: directories.as_deref().unwrap_or(&[]),
+ }),
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum ActivateScript {
+ #[default]
+ Default,
+ Csh,
+ Fish,
+}
+
+#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
+pub struct TerminalSettingsContent {
+ pub shell: Option<Shell>,
+ pub working_directory: Option<WorkingDirectory>,
+ pub font_size: Option<f32>,
+ pub font_family: Option<String>,
+ pub line_height: Option<TerminalLineHeight>,
+ pub font_features: Option<fonts::Features>,
+ pub env: Option<HashMap<String, String>>,
+ pub blinking: Option<TerminalBlink>,
+ pub alternate_scroll: Option<AlternateScroll>,
+ pub option_as_meta: Option<bool>,
+ pub copy_on_select: Option<bool>,
+ pub dock: Option<TerminalDockPosition>,
+ pub default_width: Option<f32>,
+ pub default_height: Option<f32>,
+ pub detect_venv: Option<VenvSettings>,
+}
+
+impl TerminalSettings {
+ pub fn font_size(&self, cx: &AppContext) -> Option<f32> {
+ self.font_size
+ .map(|size| theme::adjusted_font_size(size, cx))
+ }
+}
+
+impl settings::Setting for TerminalSettings {
+ const KEY: Option<&'static str> = Some("terminal");
+
+ type FileContent = TerminalSettingsContent;
+
+ fn load(
+ default_value: &Self::FileContent,
+ user_values: &[&Self::FileContent],
+ _: &AppContext,
+ ) -> anyhow::Result<Self> {
+ Self::load_via_json_merge(default_value, user_values)
+ }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
+#[serde(rename_all = "snake_case")]
+pub enum TerminalLineHeight {
+ #[default]
+ Comfortable,
+ Standard,
+ Custom(f32),
+}
+
+impl TerminalLineHeight {
+ pub fn value(&self) -> f32 {
+ match self {
+ TerminalLineHeight::Comfortable => 1.618,
+ TerminalLineHeight::Standard => 1.3,
+ TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.),
+ }
+ }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum TerminalBlink {
+ Off,
+ TerminalControlled,
+ On,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum Shell {
+ System,
+ Program(String),
+ WithArguments { program: String, args: Vec<String> },
+}
+
+#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum AlternateScroll {
+ On,
+ Off,
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum WorkingDirectory {
+ CurrentProjectDirectory,
+ FirstProjectDirectory,
+ AlwaysHome,
+ Always { directory: String },
+}
@@ -25,7 +25,8 @@ use terminal::{
term::{cell::Flags, TermMode},
},
mappings::colors::convert_color,
- IndexedCell, Terminal, TerminalContent, TerminalSettings, TerminalSize,
+ terminal_settings::TerminalSettings,
+ IndexedCell, Terminal, TerminalContent, TerminalSize,
};
use theme::{TerminalStyle, ThemeSettings};
use util::ResultExt;
@@ -9,7 +9,7 @@ use gpui::{
use project::Fs;
use serde::{Deserialize, Serialize};
use settings::SettingsStore;
-use terminal::{TerminalDockPosition, TerminalSettings};
+use terminal::terminal_settings::{TerminalDockPosition, TerminalSettings};
use util::{ResultExt, TryFutureExt};
use workspace::{
dock::{DockPosition, Panel},
@@ -33,7 +33,8 @@ use terminal::{
index::Point,
term::{search::RegexSearch, TermMode},
},
- Event, MaybeNavigationTarget, Terminal, TerminalBlink, WorkingDirectory,
+ terminal_settings::{TerminalBlink, TerminalSettings, WorkingDirectory},
+ Event, MaybeNavigationTarget, Terminal,
};
use util::{paths::PathLikeWithPosition, ResultExt};
use workspace::{
@@ -44,8 +45,6 @@ use workspace::{
NewCenterTerminal, Pane, ToolbarItemLocation, Workspace, WorkspaceId,
};
-pub use terminal::TerminalSettings;
-
const CURSOR_BLINK_INTERVAL: Duration = Duration::from_millis(500);
///Event to transmit the scroll from the element to the view