terminal_settings.rs

  1use collections::HashMap;
  2use gpui::{
  3    px, AbsoluteLength, AppContext, FontFallbacks, FontFeatures, FontWeight, Pixels, SharedString,
  4};
  5use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
  6use serde_derive::{Deserialize, Serialize};
  7use settings::{add_references_to_properties, SettingsJsonSchemaParams, SettingsSources};
  8use std::path::PathBuf;
  9use task::Shell;
 10
 11#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
 12#[serde(rename_all = "snake_case")]
 13pub enum TerminalDockPosition {
 14    Left,
 15    Bottom,
 16    Right,
 17}
 18
 19#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
 20pub struct Toolbar {
 21    pub title: bool,
 22}
 23
 24#[derive(Debug, Deserialize)]
 25pub struct TerminalSettings {
 26    pub shell: Shell,
 27    pub working_directory: WorkingDirectory,
 28    pub font_size: Option<Pixels>,
 29    pub font_family: Option<SharedString>,
 30    pub font_fallbacks: Option<FontFallbacks>,
 31    pub font_features: Option<FontFeatures>,
 32    pub font_weight: Option<FontWeight>,
 33    pub line_height: TerminalLineHeight,
 34    pub env: HashMap<String, String>,
 35    pub blinking: TerminalBlink,
 36    pub alternate_scroll: AlternateScroll,
 37    pub option_as_meta: bool,
 38    pub copy_on_select: bool,
 39    pub button: bool,
 40    pub dock: TerminalDockPosition,
 41    pub default_width: Pixels,
 42    pub default_height: Pixels,
 43    pub detect_venv: VenvSettings,
 44    pub max_scroll_history_lines: Option<usize>,
 45    pub toolbar: Toolbar,
 46}
 47
 48#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 49#[serde(rename_all = "snake_case")]
 50pub enum VenvSettings {
 51    #[default]
 52    Off,
 53    On {
 54        /// Default directories to search for virtual environments, relative
 55        /// to the current working directory. We recommend overriding this
 56        /// in your project's settings, rather than globally.
 57        activate_script: Option<ActivateScript>,
 58        directories: Option<Vec<PathBuf>>,
 59    },
 60}
 61
 62pub struct VenvSettingsContent<'a> {
 63    pub activate_script: ActivateScript,
 64    pub directories: &'a [PathBuf],
 65}
 66
 67impl VenvSettings {
 68    pub fn as_option(&self) -> Option<VenvSettingsContent> {
 69        match self {
 70            VenvSettings::Off => None,
 71            VenvSettings::On {
 72                activate_script,
 73                directories,
 74            } => Some(VenvSettingsContent {
 75                activate_script: activate_script.unwrap_or(ActivateScript::Default),
 76                directories: directories.as_deref().unwrap_or(&[]),
 77            }),
 78        }
 79    }
 80}
 81
 82#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
 83#[serde(rename_all = "snake_case")]
 84pub enum ActivateScript {
 85    #[default]
 86    Default,
 87    Csh,
 88    Fish,
 89    Nushell,
 90    PowerShell,
 91}
 92
 93#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 94pub struct TerminalSettingsContent {
 95    /// What shell to use when opening a terminal.
 96    ///
 97    /// Default: system
 98    pub shell: Option<Shell>,
 99    /// What working directory to use when launching the terminal
100    ///
101    /// Default: current_project_directory
102    pub working_directory: Option<WorkingDirectory>,
103    /// Sets the terminal's font size.
104    ///
105    /// If this option is not included,
106    /// the terminal will default to matching the buffer's font size.
107    pub font_size: Option<f32>,
108    /// Sets the terminal's font family.
109    ///
110    /// If this option is not included,
111    /// the terminal will default to matching the buffer's font family.
112    pub font_family: Option<String>,
113
114    /// Sets the terminal's font fallbacks.
115    ///
116    /// If this option is not included,
117    /// the terminal will default to matching the buffer's font fallbacks.
118    pub font_fallbacks: Option<Vec<String>>,
119
120    /// Sets the terminal's line height.
121    ///
122    /// Default: comfortable
123    pub line_height: Option<TerminalLineHeight>,
124    pub font_features: Option<FontFeatures>,
125    /// Sets the terminal's font weight in CSS weight units 0-900.
126    pub font_weight: Option<f32>,
127    /// Any key-value pairs added to this list will be added to the terminal's
128    /// environment. Use `:` to separate multiple values.
129    ///
130    /// Default: {}
131    pub env: Option<HashMap<String, String>>,
132    /// Sets the cursor blinking behavior in the terminal.
133    ///
134    /// Default: terminal_controlled
135    pub blinking: Option<TerminalBlink>,
136    /// Sets whether Alternate Scroll mode (code: ?1007) is active by default.
137    /// Alternate Scroll mode converts mouse scroll events into up / down key
138    /// presses when in the alternate screen (e.g. when running applications
139    /// like vim or  less). The terminal can still set and unset this mode.
140    ///
141    /// Default: off
142    pub alternate_scroll: Option<AlternateScroll>,
143    /// Sets whether the option key behaves as the meta key.
144    ///
145    /// Default: true
146    pub option_as_meta: Option<bool>,
147    /// Whether or not selecting text in the terminal will automatically
148    /// copy to the system clipboard.
149    ///
150    /// Default: false
151    pub copy_on_select: Option<bool>,
152    /// Whether to show the terminal button in the status bar.
153    ///
154    /// Default: true
155    pub button: Option<bool>,
156    pub dock: Option<TerminalDockPosition>,
157    /// Default width when the terminal is docked to the left or right.
158    ///
159    /// Default: 640
160    pub default_width: Option<f32>,
161    /// Default height when the terminal is docked to the bottom.
162    ///
163    /// Default: 320
164    pub default_height: Option<f32>,
165    /// Activates the python virtual environment, if one is found, in the
166    /// terminal's working directory (as resolved by the working_directory
167    /// setting). Set this to "off" to disable this behavior.
168    ///
169    /// Default: on
170    pub detect_venv: Option<VenvSettings>,
171    /// The maximum number of lines to keep in the scrollback history.
172    /// Maximum allowed value is 100_000, all values above that will be treated as 100_000.
173    /// 0 disables the scrolling.
174    /// Existing terminals will not pick up this change until they are recreated.
175    /// See <a href="https://github.com/alacritty/alacritty/blob/cb3a79dbf6472740daca8440d5166c1d4af5029e/extra/man/alacritty.5.scd?plain=1#L207-L213">Alacritty documentation</a> for more information.
176    ///
177    /// Default: 10_000
178    pub max_scroll_history_lines: Option<usize>,
179    /// Toolbar related settings
180    pub toolbar: Option<ToolbarContent>,
181}
182
183impl settings::Settings for TerminalSettings {
184    const KEY: Option<&'static str> = Some("terminal");
185
186    type FileContent = TerminalSettingsContent;
187
188    fn load(
189        sources: SettingsSources<Self::FileContent>,
190        _: &mut AppContext,
191    ) -> anyhow::Result<Self> {
192        sources.json_merge()
193    }
194
195    fn json_schema(
196        generator: &mut SchemaGenerator,
197        params: &SettingsJsonSchemaParams,
198        _: &AppContext,
199    ) -> RootSchema {
200        let mut root_schema = generator.root_schema_for::<Self::FileContent>();
201        root_schema.definitions.extend([
202            ("FontFamilies".into(), params.font_family_schema()),
203            ("FontFallbacks".into(), params.font_fallback_schema()),
204        ]);
205
206        add_references_to_properties(
207            &mut root_schema,
208            &[
209                ("font_family", "#/definitions/FontFamilies"),
210                ("font_fallbacks", "#/definitions/FontFallbacks"),
211            ],
212        );
213
214        root_schema
215    }
216}
217
218#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
219#[serde(rename_all = "snake_case")]
220pub enum TerminalLineHeight {
221    /// Use a line height that's comfortable for reading, 1.618
222    #[default]
223    Comfortable,
224    /// Use a standard line height, 1.3. This option is useful for TUIs,
225    /// particularly if they use box characters
226    Standard,
227    /// Use a custom line height.
228    Custom(f32),
229}
230
231impl TerminalLineHeight {
232    pub fn value(&self) -> AbsoluteLength {
233        let value = match self {
234            TerminalLineHeight::Comfortable => 1.618,
235            TerminalLineHeight::Standard => 1.3,
236            TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.),
237        };
238        px(value).into()
239    }
240}
241
242#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
243#[serde(rename_all = "snake_case")]
244pub enum TerminalBlink {
245    /// Never blink the cursor, ignoring the terminal mode.
246    Off,
247    /// Default the cursor blink to off, but allow the terminal to
248    /// set blinking.
249    TerminalControlled,
250    /// Always blink the cursor, ignoring the terminal mode.
251    On,
252}
253
254#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
255#[serde(rename_all = "snake_case")]
256pub enum AlternateScroll {
257    On,
258    Off,
259}
260
261#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
262#[serde(rename_all = "snake_case")]
263pub enum WorkingDirectory {
264    /// Use the current file's project directory.  Will Fallback to the
265    /// first project directory strategy if unsuccessful.
266    CurrentProjectDirectory,
267    /// Use the first project in this workspace's directory.
268    FirstProjectDirectory,
269    /// Always use this platform's home directory (if it can be found).
270    AlwaysHome,
271    /// Always use a specific directory. This value will be shell expanded.
272    /// If this path is not a valid directory the terminal will default to
273    /// this platform's home directory  (if it can be found).
274    Always { directory: String },
275}
276
277// Toolbar related settings
278#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
279pub struct ToolbarContent {
280    /// Whether to display the terminal title in its toolbar.
281    ///
282    /// Default: true
283    pub title: Option<bool>,
284}