terminal_settings.rs

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