terminal_settings.rs

  1use gpui::{px, AbsoluteLength, AppContext, FontFeatures, Pixels};
  2use schemars::JsonSchema;
  3use serde_derive::{Deserialize, Serialize};
  4use std::{collections::HashMap, path::PathBuf};
  5
  6#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
  7#[serde(rename_all = "snake_case")]
  8pub enum TerminalDockPosition {
  9    Left,
 10    Bottom,
 11    Right,
 12}
 13
 14#[derive(Deserialize)]
 15pub struct TerminalSettings {
 16    pub shell: Shell,
 17    pub working_directory: WorkingDirectory,
 18    pub font_size: Option<Pixels>,
 19    pub font_family: Option<String>,
 20    pub line_height: TerminalLineHeight,
 21    pub font_features: Option<FontFeatures>,
 22    pub env: HashMap<String, String>,
 23    pub blinking: TerminalBlink,
 24    pub alternate_scroll: AlternateScroll,
 25    pub option_as_meta: bool,
 26    pub copy_on_select: bool,
 27    pub dock: TerminalDockPosition,
 28    pub default_width: Pixels,
 29    pub default_height: Pixels,
 30    pub detect_venv: VenvSettings,
 31}
 32
 33#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 34#[serde(rename_all = "snake_case")]
 35pub enum VenvSettings {
 36    #[default]
 37    Off,
 38    On {
 39        /// Default directories to search for virtual environments, relative
 40        /// to the current working directory. We recommend overriding this
 41        /// in your project's settings, rather than globally.
 42        activate_script: Option<ActivateScript>,
 43        directories: Option<Vec<PathBuf>>,
 44    },
 45}
 46
 47pub struct VenvSettingsContent<'a> {
 48    pub activate_script: ActivateScript,
 49    pub directories: &'a [PathBuf],
 50}
 51
 52impl VenvSettings {
 53    pub fn as_option(&self) -> Option<VenvSettingsContent> {
 54        match self {
 55            VenvSettings::Off => None,
 56            VenvSettings::On {
 57                activate_script,
 58                directories,
 59            } => Some(VenvSettingsContent {
 60                activate_script: activate_script.unwrap_or(ActivateScript::Default),
 61                directories: directories.as_deref().unwrap_or(&[]),
 62            }),
 63        }
 64    }
 65}
 66
 67#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
 68#[serde(rename_all = "snake_case")]
 69pub enum ActivateScript {
 70    #[default]
 71    Default,
 72    Csh,
 73    Fish,
 74    Nushell,
 75}
 76
 77#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 78pub struct TerminalSettingsContent {
 79    /// What shell to use when opening a terminal.
 80    ///
 81    /// Default: system
 82    pub shell: Option<Shell>,
 83    /// What working directory to use when launching the terminal
 84    ///
 85    /// Default: current_project_directory
 86    pub working_directory: Option<WorkingDirectory>,
 87    /// Set the terminal's font size.
 88    ///
 89    /// If this option is not included,
 90    /// the terminal will default to matching the buffer's font size.
 91    pub font_size: Option<f32>,
 92    /// Set the terminal's font family.
 93    ///
 94    /// If this option is not included,
 95    /// the terminal will default to matching the buffer's font family.
 96    pub font_family: Option<String>,
 97    /// Set the terminal's line height.
 98    ///
 99    /// Default: comfortable
100    pub line_height: Option<TerminalLineHeight>,
101    pub font_features: Option<FontFeatures>,
102    /// Any key-value pairs added to this list will be added to the terminal's
103    /// environment. Use `:` to separate multiple values.
104    ///
105    /// Default: {}
106    pub env: Option<HashMap<String, String>>,
107    /// Set the cursor blinking behavior in the terminal.
108    ///
109    /// Default: terminal_controlled
110    pub blinking: Option<TerminalBlink>,
111    /// Set whether Alternate Scroll mode (code: ?1007) is active by default.
112    /// Alternate Scroll mode converts mouse scroll events into up / down key
113    /// presses when in the alternate screen (e.g. when running applications
114    /// like vim or  less). The terminal can still set and unset this mode.
115    ///
116    /// Default: off
117    pub alternate_scroll: Option<AlternateScroll>,
118    /// Set whether the option key behaves as the meta key.
119    ///
120    /// Default: false
121    pub option_as_meta: Option<bool>,
122    /// Whether or not selecting text in the terminal will automatically
123    /// copy to the system clipboard.
124    ///
125    /// Default: false
126    pub copy_on_select: Option<bool>,
127    pub dock: Option<TerminalDockPosition>,
128    /// Default width when the terminal is docked to the left or right.
129    ///
130    /// Default: 640
131    pub default_width: Option<f32>,
132    /// Default height when the terminal is docked to the bottom.
133    ///
134    /// Default: 320
135    pub default_height: Option<f32>,
136    /// Activate the python virtual environment, if one is found, in the
137    /// terminal's working directory (as resolved by the working_directory
138    /// setting). Set this to "off" to disable this behavior.
139    ///
140    /// Default: on
141    pub detect_venv: Option<VenvSettings>,
142}
143
144impl settings::Settings for TerminalSettings {
145    const KEY: Option<&'static str> = Some("terminal");
146
147    type FileContent = TerminalSettingsContent;
148
149    fn load(
150        default_value: &Self::FileContent,
151        user_values: &[&Self::FileContent],
152        _: &mut AppContext,
153    ) -> anyhow::Result<Self> {
154        Self::load_via_json_merge(default_value, user_values)
155    }
156}
157
158#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
159#[serde(rename_all = "snake_case")]
160pub enum TerminalLineHeight {
161    /// Use a line height that's comfortable for reading, 1.618
162    #[default]
163    Comfortable,
164    /// Use a standard line height, 1.3. This option is useful for TUIs,
165    /// particularly if they use box characters
166    Standard,
167    /// Use a custom line height.
168    Custom(f32),
169}
170
171impl TerminalLineHeight {
172    pub fn value(&self) -> AbsoluteLength {
173        let value = match self {
174            TerminalLineHeight::Comfortable => 1.618,
175            TerminalLineHeight::Standard => 1.3,
176            TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.),
177        };
178        px(value).into()
179    }
180}
181
182#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
183#[serde(rename_all = "snake_case")]
184pub enum TerminalBlink {
185    /// Never blink the cursor, ignoring the terminal mode.
186    Off,
187    /// Default the cursor blink to off, but allow the terminal to
188    /// set blinking.
189    TerminalControlled,
190    /// Always blink the cursor, ignoring the terminal mode.
191    On,
192}
193
194#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
195#[serde(rename_all = "snake_case")]
196pub enum Shell {
197    /// Use the system's default terminal configuration in /etc/passwd
198    System,
199    Program(String),
200    WithArguments {
201        program: String,
202        args: Vec<String>,
203    },
204}
205
206#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
207#[serde(rename_all = "snake_case")]
208pub enum AlternateScroll {
209    On,
210    Off,
211}
212
213#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
214#[serde(rename_all = "snake_case")]
215pub enum WorkingDirectory {
216    /// Use the current file's project directory.  Will Fallback to the
217    /// first project directory strategy if unsuccessful.
218    CurrentProjectDirectory,
219    /// Use the first project in this workspace's directory.
220    FirstProjectDirectory,
221    /// Always use this platform's home directory (if it can be found).
222    AlwaysHome,
223    /// Slways use a specific directory. This value will be shell expanded.
224    /// If this path is not a valid directory the terminal will default to
225    /// this platform's home directory  (if it can be found).
226    Always { directory: String },
227}