terminal_settings.rs

  1use alacritty_terminal::vte::ansi::{
  2    CursorShape as AlacCursorShape, CursorStyle as AlacCursorStyle,
  3};
  4use collections::HashMap;
  5use gpui::{
  6    px, AbsoluteLength, AppContext, FontFallbacks, FontFeatures, FontWeight, Pixels, SharedString,
  7};
  8use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
  9use serde_derive::{Deserialize, Serialize};
 10use settings::{add_references_to_properties, 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 breadcrumbs: bool,
 25}
 26
 27#[derive(Clone, Debug, Deserialize)]
 28pub struct TerminalSettings {
 29    pub shell: Shell,
 30    pub working_directory: WorkingDirectory,
 31    pub font_size: Option<Pixels>,
 32    pub font_family: Option<SharedString>,
 33    pub font_fallbacks: Option<FontFallbacks>,
 34    pub font_features: Option<FontFeatures>,
 35    pub font_weight: Option<FontWeight>,
 36    pub line_height: TerminalLineHeight,
 37    pub env: HashMap<String, String>,
 38    pub cursor_shape: Option<CursorShape>,
 39    pub blinking: TerminalBlink,
 40    pub alternate_scroll: AlternateScroll,
 41    pub option_as_meta: bool,
 42    pub copy_on_select: bool,
 43    pub button: bool,
 44    pub dock: TerminalDockPosition,
 45    pub default_width: Pixels,
 46    pub default_height: Pixels,
 47    pub detect_venv: VenvSettings,
 48    pub max_scroll_history_lines: Option<usize>,
 49    pub toolbar: Toolbar,
 50}
 51
 52#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 53#[serde(rename_all = "snake_case")]
 54pub enum VenvSettings {
 55    #[default]
 56    Off,
 57    On {
 58        /// Default directories to search for virtual environments, relative
 59        /// to the current working directory. We recommend overriding this
 60        /// in your project's settings, rather than globally.
 61        activate_script: Option<ActivateScript>,
 62        directories: Option<Vec<PathBuf>>,
 63    },
 64}
 65
 66pub struct VenvSettingsContent<'a> {
 67    pub activate_script: ActivateScript,
 68    pub directories: &'a [PathBuf],
 69}
 70
 71impl VenvSettings {
 72    pub fn as_option(&self) -> Option<VenvSettingsContent> {
 73        match self {
 74            VenvSettings::Off => None,
 75            VenvSettings::On {
 76                activate_script,
 77                directories,
 78            } => Some(VenvSettingsContent {
 79                activate_script: activate_script.unwrap_or(ActivateScript::Default),
 80                directories: directories.as_deref().unwrap_or(&[]),
 81            }),
 82        }
 83    }
 84}
 85
 86#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
 87#[serde(rename_all = "snake_case")]
 88pub enum ActivateScript {
 89    #[default]
 90    Default,
 91    Csh,
 92    Fish,
 93    Nushell,
 94    PowerShell,
 95}
 96
 97#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 98pub struct TerminalSettingsContent {
 99    /// What shell to use when opening a terminal.
100    ///
101    /// Default: system
102    pub shell: Option<Shell>,
103    /// What working directory to use when launching the terminal
104    ///
105    /// Default: current_project_directory
106    pub working_directory: Option<WorkingDirectory>,
107    /// Sets the terminal's font size.
108    ///
109    /// If this option is not included,
110    /// the terminal will default to matching the buffer's font size.
111    pub font_size: Option<f32>,
112    /// Sets the terminal's font family.
113    ///
114    /// If this option is not included,
115    /// the terminal will default to matching the buffer's font family.
116    pub font_family: Option<String>,
117
118    /// Sets the terminal's font fallbacks.
119    ///
120    /// If this option is not included,
121    /// the terminal will default to matching the buffer's font fallbacks.
122    pub font_fallbacks: Option<Vec<String>>,
123
124    /// Sets the terminal's line height.
125    ///
126    /// Default: comfortable
127    pub line_height: Option<TerminalLineHeight>,
128    pub font_features: Option<FontFeatures>,
129    /// Sets the terminal's font weight in CSS weight units 0-900.
130    pub font_weight: Option<f32>,
131    /// Any key-value pairs added to this list will be added to the terminal's
132    /// environment. Use `:` to separate multiple values.
133    ///
134    /// Default: {}
135    pub env: Option<HashMap<String, String>>,
136    /// Default cursor shape for the terminal.
137    /// Can be "bar", "block", "underline", or "hollow".
138    ///
139    /// Default: None
140    pub cursor_shape: Option<CursorShape>,
141    /// Sets the cursor blinking behavior in the terminal.
142    ///
143    /// Default: terminal_controlled
144    pub blinking: Option<TerminalBlink>,
145    /// Sets whether Alternate Scroll mode (code: ?1007) is active by default.
146    /// Alternate Scroll mode converts mouse scroll events into up / down key
147    /// presses when in the alternate screen (e.g. when running applications
148    /// like vim or  less). The terminal can still set and unset this mode.
149    ///
150    /// Default: off
151    pub alternate_scroll: Option<AlternateScroll>,
152    /// Sets whether the option key behaves as the meta key.
153    ///
154    /// Default: false
155    pub option_as_meta: Option<bool>,
156    /// Whether or not selecting text in the terminal will automatically
157    /// copy to the system clipboard.
158    ///
159    /// Default: false
160    pub copy_on_select: Option<bool>,
161    /// Whether to show the terminal button in the status bar.
162    ///
163    /// Default: true
164    pub button: Option<bool>,
165    pub dock: Option<TerminalDockPosition>,
166    /// Default width when the terminal is docked to the left or right.
167    ///
168    /// Default: 640
169    pub default_width: Option<f32>,
170    /// Default height when the terminal is docked to the bottom.
171    ///
172    /// Default: 320
173    pub default_height: Option<f32>,
174    /// Activates the python virtual environment, if one is found, in the
175    /// terminal's working directory (as resolved by the working_directory
176    /// setting). Set this to "off" to disable this behavior.
177    ///
178    /// Default: on
179    pub detect_venv: Option<VenvSettings>,
180    /// The maximum number of lines to keep in the scrollback history.
181    /// Maximum allowed value is 100_000, all values above that will be treated as 100_000.
182    /// 0 disables the scrolling.
183    /// Existing terminals will not pick up this change until they are recreated.
184    /// 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.
185    ///
186    /// Default: 10_000
187    pub max_scroll_history_lines: Option<usize>,
188    /// Toolbar related settings
189    pub toolbar: Option<ToolbarContent>,
190}
191
192impl settings::Settings for TerminalSettings {
193    const KEY: Option<&'static str> = Some("terminal");
194
195    type FileContent = TerminalSettingsContent;
196
197    fn load(
198        sources: SettingsSources<Self::FileContent>,
199        _: &mut AppContext,
200    ) -> anyhow::Result<Self> {
201        sources.json_merge()
202    }
203
204    fn json_schema(
205        generator: &mut SchemaGenerator,
206        params: &SettingsJsonSchemaParams,
207        _: &AppContext,
208    ) -> RootSchema {
209        let mut root_schema = generator.root_schema_for::<Self::FileContent>();
210        root_schema.definitions.extend([
211            ("FontFamilies".into(), params.font_family_schema()),
212            ("FontFallbacks".into(), params.font_fallback_schema()),
213        ]);
214
215        add_references_to_properties(
216            &mut root_schema,
217            &[
218                ("font_family", "#/definitions/FontFamilies"),
219                ("font_fallbacks", "#/definitions/FontFallbacks"),
220            ],
221        );
222
223        root_schema
224    }
225}
226
227#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
228#[serde(rename_all = "snake_case")]
229pub enum TerminalLineHeight {
230    /// Use a line height that's comfortable for reading, 1.618
231    #[default]
232    Comfortable,
233    /// Use a standard line height, 1.3. This option is useful for TUIs,
234    /// particularly if they use box characters
235    Standard,
236    /// Use a custom line height.
237    Custom(f32),
238}
239
240impl TerminalLineHeight {
241    pub fn value(&self) -> AbsoluteLength {
242        let value = match self {
243            TerminalLineHeight::Comfortable => 1.618,
244            TerminalLineHeight::Standard => 1.3,
245            TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.),
246        };
247        px(value).into()
248    }
249}
250
251#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
252#[serde(rename_all = "snake_case")]
253pub enum TerminalBlink {
254    /// Never blink the cursor, ignoring the terminal mode.
255    Off,
256    /// Default the cursor blink to off, but allow the terminal to
257    /// set blinking.
258    TerminalControlled,
259    /// Always blink the cursor, ignoring the terminal mode.
260    On,
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 breadcrumbs inside the terminal pane.
290    /// Only shown if the terminal title is not empty.
291    ///
292    /// The shell running in the terminal needs to be configured to emit the title.
293    /// Example: `echo -e "\e]2;New Title\007";`
294    ///
295    /// Default: true
296    pub breadcrumbs: Option<bool>,
297}
298
299#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
300#[serde(rename_all = "snake_case")]
301pub enum CursorShape {
302    /// Cursor is a block like `█`.
303    #[default]
304    Block,
305    /// Cursor is an underscore like `_`.
306    Underline,
307    /// Cursor is a vertical bar like `⎸`.
308    Bar,
309    /// Cursor is a hollow box like `▯`.
310    Hollow,
311}
312
313impl From<CursorShape> for AlacCursorShape {
314    fn from(value: CursorShape) -> Self {
315        match value {
316            CursorShape::Block => AlacCursorShape::Block,
317            CursorShape::Underline => AlacCursorShape::Underline,
318            CursorShape::Bar => AlacCursorShape::Beam,
319            CursorShape::Hollow => AlacCursorShape::HollowBlock,
320        }
321    }
322}
323
324impl From<CursorShape> for AlacCursorStyle {
325    fn from(value: CursorShape) -> Self {
326        AlacCursorStyle {
327            shape: value.into(),
328            blinking: false,
329        }
330    }
331}