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}
 91
 92#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 93pub struct TerminalSettingsContent {
 94    /// What shell to use when opening a terminal.
 95    ///
 96    /// Default: system
 97    pub shell: Option<Shell>,
 98    /// What working directory to use when launching the terminal
 99    ///
100    /// Default: current_project_directory
101    pub working_directory: Option<WorkingDirectory>,
102    /// Sets the terminal's font size.
103    ///
104    /// If this option is not included,
105    /// the terminal will default to matching the buffer's font size.
106    pub font_size: Option<f32>,
107    /// Sets the terminal's font family.
108    ///
109    /// If this option is not included,
110    /// the terminal will default to matching the buffer's font family.
111    pub font_family: Option<String>,
112
113    /// Sets the terminal's font fallbacks.
114    ///
115    /// If this option is not included,
116    /// the terminal will default to matching the buffer's font fallbacks.
117    pub font_fallbacks: Option<Vec<String>>,
118
119    /// Sets the terminal's line height.
120    ///
121    /// Default: comfortable
122    pub line_height: Option<TerminalLineHeight>,
123    pub font_features: Option<FontFeatures>,
124    /// Sets the terminal's font weight in CSS weight units 0-900.
125    pub font_weight: Option<f32>,
126    /// Any key-value pairs added to this list will be added to the terminal's
127    /// environment. Use `:` to separate multiple values.
128    ///
129    /// Default: {}
130    pub env: Option<HashMap<String, String>>,
131    /// Sets the cursor blinking behavior in the terminal.
132    ///
133    /// Default: terminal_controlled
134    pub blinking: Option<TerminalBlink>,
135    /// Sets whether Alternate Scroll mode (code: ?1007) is active by default.
136    /// Alternate Scroll mode converts mouse scroll events into up / down key
137    /// presses when in the alternate screen (e.g. when running applications
138    /// like vim or  less). The terminal can still set and unset this mode.
139    ///
140    /// Default: off
141    pub alternate_scroll: Option<AlternateScroll>,
142    /// Sets whether the option key behaves as the meta key.
143    ///
144    /// Default: true
145    pub option_as_meta: Option<bool>,
146    /// Whether or not selecting text in the terminal will automatically
147    /// copy to the system clipboard.
148    ///
149    /// Default: false
150    pub copy_on_select: Option<bool>,
151    /// Whether to show the terminal button in the status bar.
152    ///
153    /// Default: true
154    pub button: Option<bool>,
155    pub dock: Option<TerminalDockPosition>,
156    /// Default width when the terminal is docked to the left or right.
157    ///
158    /// Default: 640
159    pub default_width: Option<f32>,
160    /// Default height when the terminal is docked to the bottom.
161    ///
162    /// Default: 320
163    pub default_height: Option<f32>,
164    /// Activates the python virtual environment, if one is found, in the
165    /// terminal's working directory (as resolved by the working_directory
166    /// setting). Set this to "off" to disable this behavior.
167    ///
168    /// Default: on
169    pub detect_venv: Option<VenvSettings>,
170    /// The maximum number of lines to keep in the scrollback history.
171    /// Maximum allowed value is 100_000, all values above that will be treated as 100_000.
172    /// 0 disables the scrolling.
173    /// Existing terminals will not pick up this change until they are recreated.
174    /// 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.
175    ///
176    /// Default: 10_000
177    pub max_scroll_history_lines: Option<usize>,
178    /// Toolbar related settings
179    pub toolbar: Option<ToolbarContent>,
180}
181
182impl settings::Settings for TerminalSettings {
183    const KEY: Option<&'static str> = Some("terminal");
184
185    type FileContent = TerminalSettingsContent;
186
187    fn load(
188        sources: SettingsSources<Self::FileContent>,
189        _: &mut AppContext,
190    ) -> anyhow::Result<Self> {
191        sources.json_merge()
192    }
193
194    fn json_schema(
195        generator: &mut SchemaGenerator,
196        params: &SettingsJsonSchemaParams,
197        _: &AppContext,
198    ) -> RootSchema {
199        let mut root_schema = generator.root_schema_for::<Self::FileContent>();
200        root_schema.definitions.extend([
201            ("FontFamilies".into(), params.font_family_schema()),
202            ("FontFallbacks".into(), params.font_fallback_schema()),
203        ]);
204
205        add_references_to_properties(
206            &mut root_schema,
207            &[
208                ("font_family", "#/definitions/FontFamilies"),
209                ("font_fallbacks", "#/definitions/FontFallbacks"),
210            ],
211        );
212
213        root_schema
214    }
215}
216
217#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
218#[serde(rename_all = "snake_case")]
219pub enum TerminalLineHeight {
220    /// Use a line height that's comfortable for reading, 1.618
221    #[default]
222    Comfortable,
223    /// Use a standard line height, 1.3. This option is useful for TUIs,
224    /// particularly if they use box characters
225    Standard,
226    /// Use a custom line height.
227    Custom(f32),
228}
229
230impl TerminalLineHeight {
231    pub fn value(&self) -> AbsoluteLength {
232        let value = match self {
233            TerminalLineHeight::Comfortable => 1.618,
234            TerminalLineHeight::Standard => 1.3,
235            TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.),
236        };
237        px(value).into()
238    }
239}
240
241#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
242#[serde(rename_all = "snake_case")]
243pub enum TerminalBlink {
244    /// Never blink the cursor, ignoring the terminal mode.
245    Off,
246    /// Default the cursor blink to off, but allow the terminal to
247    /// set blinking.
248    TerminalControlled,
249    /// Always blink the cursor, ignoring the terminal mode.
250    On,
251}
252
253#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
254#[serde(rename_all = "snake_case")]
255pub enum AlternateScroll {
256    On,
257    Off,
258}
259
260#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
261#[serde(rename_all = "snake_case")]
262pub enum WorkingDirectory {
263    /// Use the current file's project directory.  Will Fallback to the
264    /// first project directory strategy if unsuccessful.
265    CurrentProjectDirectory,
266    /// Use the first project in this workspace's directory.
267    FirstProjectDirectory,
268    /// Always use this platform's home directory (if it can be found).
269    AlwaysHome,
270    /// Always use a specific directory. This value will be shell expanded.
271    /// If this path is not a valid directory the terminal will default to
272    /// this platform's home directory  (if it can be found).
273    Always { directory: String },
274}
275
276// Toolbar related settings
277#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
278pub struct ToolbarContent {
279    /// Whether to display the terminal title in its toolbar.
280    ///
281    /// Default: true
282    pub title: Option<bool>,
283}