terminal_settings.rs

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