use collections::HashMap; use gpui::{px, AbsoluteLength, AppContext, FontFeatures, Pixels}; use schemars::{ gen::SchemaGenerator, schema::{InstanceType, RootSchema, Schema, SchemaObject}, JsonSchema, }; use serde_derive::{Deserialize, Serialize}; use serde_json::Value; use settings::{SettingsJsonSchemaParams, SettingsSources}; use std::path::PathBuf; #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] #[serde(rename_all = "snake_case")] pub enum TerminalDockPosition { Left, Bottom, Right, } #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] pub struct Toolbar { pub title: bool, } #[derive(Deserialize)] pub struct TerminalSettings { pub shell: Shell, pub working_directory: WorkingDirectory, pub font_size: Option, pub font_family: Option, pub line_height: TerminalLineHeight, pub font_features: Option, pub env: HashMap, pub blinking: TerminalBlink, pub alternate_scroll: AlternateScroll, pub option_as_meta: bool, pub copy_on_select: bool, pub button: bool, pub dock: TerminalDockPosition, pub default_width: Pixels, pub default_height: Pixels, pub detect_venv: VenvSettings, pub max_scroll_history_lines: Option, pub toolbar: Toolbar, } #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum VenvSettings { #[default] Off, On { /// Default directories to search for virtual environments, relative /// to the current working directory. We recommend overriding this /// in your project's settings, rather than globally. activate_script: Option, directories: Option>, }, } pub struct VenvSettingsContent<'a> { pub activate_script: ActivateScript, pub directories: &'a [PathBuf], } impl VenvSettings { pub fn as_option(&self) -> Option { match self { VenvSettings::Off => None, VenvSettings::On { activate_script, directories, } => Some(VenvSettingsContent { activate_script: activate_script.unwrap_or(ActivateScript::Default), directories: directories.as_deref().unwrap_or(&[]), }), } } } #[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum ActivateScript { #[default] Default, Csh, Fish, Nushell, } #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)] pub struct TerminalSettingsContent { /// What shell to use when opening a terminal. /// /// Default: system pub shell: Option, /// What working directory to use when launching the terminal /// /// Default: current_project_directory pub working_directory: Option, /// Sets the terminal's font size. /// /// If this option is not included, /// the terminal will default to matching the buffer's font size. pub font_size: Option, /// Sets the terminal's font family. /// /// If this option is not included, /// the terminal will default to matching the buffer's font family. pub font_family: Option, /// Sets the terminal's line height. /// /// Default: comfortable pub line_height: Option, pub font_features: Option, /// Any key-value pairs added to this list will be added to the terminal's /// environment. Use `:` to separate multiple values. /// /// Default: {} pub env: Option>, /// Sets the cursor blinking behavior in the terminal. /// /// Default: terminal_controlled pub blinking: Option, /// Sets whether Alternate Scroll mode (code: ?1007) is active by default. /// Alternate Scroll mode converts mouse scroll events into up / down key /// presses when in the alternate screen (e.g. when running applications /// like vim or less). The terminal can still set and unset this mode. /// /// Default: off pub alternate_scroll: Option, /// Sets whether the option key behaves as the meta key. /// /// Default: false pub option_as_meta: Option, /// Whether or not selecting text in the terminal will automatically /// copy to the system clipboard. /// /// Default: false pub copy_on_select: Option, /// Whether to show the terminal button in the status bar. /// /// Default: true pub button: Option, pub dock: Option, /// Default width when the terminal is docked to the left or right. /// /// Default: 640 pub default_width: Option, /// Default height when the terminal is docked to the bottom. /// /// Default: 320 pub default_height: Option, /// Activates the python virtual environment, if one is found, in the /// terminal's working directory (as resolved by the working_directory /// setting). Set this to "off" to disable this behavior. /// /// Default: on pub detect_venv: Option, /// The maximum number of lines to keep in the scrollback history. /// Maximum allowed value is 100_000, all values above that will be treated as 100_000. /// 0 disables the scrolling. /// Existing terminals will not pick up this change until they are recreated. /// See Alacritty documentation for more information. /// /// Default: 10_000 pub max_scroll_history_lines: Option, /// Toolbar related settings pub toolbar: Option, } impl settings::Settings for TerminalSettings { const KEY: Option<&'static str> = Some("terminal"); type FileContent = TerminalSettingsContent; fn load( sources: SettingsSources, _: &mut AppContext, ) -> anyhow::Result { sources.json_merge() } fn json_schema( generator: &mut SchemaGenerator, params: &SettingsJsonSchemaParams, _: &AppContext, ) -> RootSchema { let mut root_schema = generator.root_schema_for::(); let available_fonts = params .font_names .iter() .cloned() .map(Value::String) .collect(); let fonts_schema = SchemaObject { instance_type: Some(InstanceType::String.into()), enum_values: Some(available_fonts), ..Default::default() }; root_schema .definitions .extend([("FontFamilies".into(), fonts_schema.into())]); root_schema .schema .object .as_mut() .unwrap() .properties .extend([( "font_family".to_owned(), Schema::new_ref("#/definitions/FontFamilies".into()), )]); root_schema } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)] #[serde(rename_all = "snake_case")] pub enum TerminalLineHeight { /// Use a line height that's comfortable for reading, 1.618 #[default] Comfortable, /// Use a standard line height, 1.3. This option is useful for TUIs, /// particularly if they use box characters Standard, /// Use a custom line height. Custom(f32), } impl TerminalLineHeight { pub fn value(&self) -> AbsoluteLength { let value = match self { TerminalLineHeight::Comfortable => 1.618, TerminalLineHeight::Standard => 1.3, TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.), }; px(value).into() } } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum TerminalBlink { /// Never blink the cursor, ignoring the terminal mode. Off, /// Default the cursor blink to off, but allow the terminal to /// set blinking. TerminalControlled, /// Always blink the cursor, ignoring the terminal mode. On, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum Shell { /// Use the system's default terminal configuration in /etc/passwd System, Program(String), WithArguments { program: String, args: Vec, }, } #[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum AlternateScroll { On, Off, } #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)] #[serde(rename_all = "snake_case")] pub enum WorkingDirectory { /// Use the current file's project directory. Will Fallback to the /// first project directory strategy if unsuccessful. CurrentProjectDirectory, /// Use the first project in this workspace's directory. FirstProjectDirectory, /// Always use this platform's home directory (if it can be found). AlwaysHome, /// Always use a specific directory. This value will be shell expanded. /// If this path is not a valid directory the terminal will default to /// this platform's home directory (if it can be found). Always { directory: String }, } // Toolbar related settings #[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] pub struct ToolbarContent { /// Whether to display the terminal title in its toolbar. /// /// Default: true pub title: Option, }