terminal.rs

  1use std::path::PathBuf;
  2
  3use collections::HashMap;
  4use gpui::{AbsoluteLength, FontFeatures, SharedString, px};
  5use schemars::JsonSchema;
  6use serde::{Deserialize, Serialize};
  7
  8use crate::FontFamilyName;
  9
 10#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
 11pub struct TerminalSettingsContent {
 12    /// What shell to use when opening a terminal.
 13    ///
 14    /// Default: system
 15    pub shell: Option<Shell>,
 16    /// What working directory to use when launching the terminal
 17    ///
 18    /// Default: current_project_directory
 19    pub working_directory: Option<WorkingDirectory>,
 20    /// Sets the terminal's font size.
 21    ///
 22    /// If this option is not included,
 23    /// the terminal will default to matching the buffer's font size.
 24    pub font_size: Option<f32>,
 25    /// Sets the terminal's font family.
 26    ///
 27    /// If this option is not included,
 28    /// the terminal will default to matching the buffer's font family.
 29    pub font_family: Option<FontFamilyName>,
 30
 31    /// Sets the terminal's font fallbacks.
 32    ///
 33    /// If this option is not included,
 34    /// the terminal will default to matching the buffer's font fallbacks.
 35    #[schemars(extend("uniqueItems" = true))]
 36    pub font_fallbacks: Option<Vec<FontFamilyName>>,
 37
 38    /// Sets the terminal's line height.
 39    ///
 40    /// Default: comfortable
 41    pub line_height: Option<TerminalLineHeight>,
 42    pub font_features: Option<FontFeatures>,
 43    /// Sets the terminal's font weight in CSS weight units 0-900.
 44    pub font_weight: Option<f32>,
 45    /// Any key-value pairs added to this list will be added to the terminal's
 46    /// environment. Use `:` to separate multiple values.
 47    ///
 48    /// Default: {}
 49    pub env: Option<HashMap<String, String>>,
 50    /// Default cursor shape for the terminal.
 51    /// Can be "bar", "block", "underline", or "hollow".
 52    ///
 53    /// Default: None
 54    pub cursor_shape: Option<CursorShapeContent>,
 55    /// Sets the cursor blinking behavior in the terminal.
 56    ///
 57    /// Default: terminal_controlled
 58    pub blinking: Option<TerminalBlink>,
 59    /// Sets whether Alternate Scroll mode (code: ?1007) is active by default.
 60    /// Alternate Scroll mode converts mouse scroll events into up / down key
 61    /// presses when in the alternate screen (e.g. when running applications
 62    /// like vim or  less). The terminal can still set and unset this mode.
 63    ///
 64    /// Default: on
 65    pub alternate_scroll: Option<AlternateScroll>,
 66    /// Sets whether the option key behaves as the meta key.
 67    ///
 68    /// Default: false
 69    pub option_as_meta: Option<bool>,
 70    /// Whether or not selecting text in the terminal will automatically
 71    /// copy to the system clipboard.
 72    ///
 73    /// Default: false
 74    pub copy_on_select: Option<bool>,
 75    /// Whether to keep the text selection after copying it to the clipboard.
 76    ///
 77    /// Default: false
 78    pub keep_selection_on_copy: Option<bool>,
 79    /// Whether to show the terminal button in the status bar.
 80    ///
 81    /// Default: true
 82    pub button: Option<bool>,
 83    pub dock: Option<TerminalDockPosition>,
 84    /// Default width when the terminal is docked to the left or right.
 85    ///
 86    /// Default: 640
 87    pub default_width: Option<f32>,
 88    /// Default height when the terminal is docked to the bottom.
 89    ///
 90    /// Default: 320
 91    pub default_height: Option<f32>,
 92    /// Activates the python virtual environment, if one is found, in the
 93    /// terminal's working directory (as resolved by the working_directory
 94    /// setting). Set this to "off" to disable this behavior.
 95    ///
 96    /// Default: on
 97    pub detect_venv: Option<VenvSettings>,
 98    /// The maximum number of lines to keep in the scrollback history.
 99    /// Maximum allowed value is 100_000, all values above that will be treated as 100_000.
100    /// 0 disables the scrolling.
101    /// Existing terminals will not pick up this change until they are recreated.
102    /// 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.
103    ///
104    /// Default: 10_000
105    pub max_scroll_history_lines: Option<usize>,
106    /// Toolbar related settings
107    pub toolbar: Option<TerminalToolbarContent>,
108    /// Scrollbar-related settings
109    pub scrollbar: Option<ScrollbarSettingsContent>,
110    /// The minimum APCA perceptual contrast between foreground and background colors.
111    ///
112    /// APCA (Accessible Perceptual Contrast Algorithm) is more accurate than WCAG 2.x,
113    /// especially for dark mode. Values range from 0 to 106.
114    ///
115    /// Based on APCA Readability Criterion (ARC) Bronze Simple Mode:
116    /// https://readtech.org/ARC/tests/bronze-simple-mode/
117    /// - 0: No contrast adjustment
118    /// - 45: Minimum for large fluent text (36px+)
119    /// - 60: Minimum for other content text
120    /// - 75: Minimum for body text
121    /// - 90: Preferred for body text
122    ///
123    /// Default: 45
124    pub minimum_contrast: Option<f32>,
125}
126
127/// Shell configuration to open the terminal with.
128#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
129#[serde(rename_all = "snake_case")]
130pub enum Shell {
131    /// Use the system's default terminal configuration in /etc/passwd
132    #[default]
133    System,
134    /// Use a specific program with no arguments.
135    Program(String),
136    /// Use a specific program with arguments.
137    WithArguments {
138        /// The program to run.
139        program: String,
140        /// The arguments to pass to the program.
141        args: Vec<String>,
142        /// An optional string to override the title of the terminal tab
143        title_override: Option<SharedString>,
144    },
145}
146
147#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
148#[serde(rename_all = "snake_case")]
149pub enum WorkingDirectory {
150    /// Use the current file's project directory.  Will Fallback to the
151    /// first project directory strategy if unsuccessful.
152    CurrentProjectDirectory,
153    /// Use the first project in this workspace's directory.
154    FirstProjectDirectory,
155    /// Always use this platform's home directory (if it can be found).
156    AlwaysHome,
157    /// Always use a specific directory. This value will be shell expanded.
158    /// If this path is not a valid directory the terminal will default to
159    /// this platform's home directory  (if it can be found).
160    Always { directory: String },
161}
162
163#[derive(Clone, Copy, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
164pub struct ScrollbarSettingsContent {
165    /// When to show the scrollbar in the terminal.
166    ///
167    /// Default: inherits editor scrollbar settings
168    pub show: Option<Option<ShowScrollbar>>,
169}
170
171#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
172#[serde(rename_all = "snake_case")]
173pub enum TerminalLineHeight {
174    /// Use a line height that's comfortable for reading, 1.618
175    #[default]
176    Comfortable,
177    /// Use a standard line height, 1.3. This option is useful for TUIs,
178    /// particularly if they use box characters
179    Standard,
180    /// Use a custom line height.
181    Custom(f32),
182}
183
184impl TerminalLineHeight {
185    pub fn value(&self) -> AbsoluteLength {
186        let value = match self {
187            TerminalLineHeight::Comfortable => 1.618,
188            TerminalLineHeight::Standard => 1.3,
189            TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.),
190        };
191        px(value).into()
192    }
193}
194
195/// When to show the scrollbar.
196///
197/// Default: auto
198#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
199#[serde(rename_all = "snake_case")]
200pub enum ShowScrollbar {
201    /// Show the scrollbar if there's important information or
202    /// follow the system's configured behavior.
203    Auto,
204    /// Match the system's configured behavior.
205    System,
206    /// Always show the scrollbar.
207    Always,
208    /// Never show the scrollbar.
209    Never,
210}
211
212#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
213#[serde(rename_all = "snake_case")]
214pub enum CursorShapeContent {
215    /// Cursor is a block like `█`.
216    #[default]
217    Block,
218    /// Cursor is an underscore like `_`.
219    Underline,
220    /// Cursor is a vertical bar like `⎸`.
221    Bar,
222    /// Cursor is a hollow box like `▯`.
223    Hollow,
224}
225
226#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
227#[serde(rename_all = "snake_case")]
228pub enum TerminalBlink {
229    /// Never blink the cursor, ignoring the terminal mode.
230    Off,
231    /// Default the cursor blink to off, but allow the terminal to
232    /// set blinking.
233    TerminalControlled,
234    /// Always blink the cursor, ignoring the terminal mode.
235    On,
236}
237
238#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
239#[serde(rename_all = "snake_case")]
240pub enum AlternateScroll {
241    On,
242    Off,
243}
244
245// Toolbar related settings
246#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
247pub struct TerminalToolbarContent {
248    /// Whether to display the terminal title in breadcrumbs inside the terminal pane.
249    /// Only shown if the terminal title is not empty.
250    ///
251    /// The shell running in the terminal needs to be configured to emit the title.
252    /// Example: `echo -e "\e]2;New Title\007";`
253    ///
254    /// Default: true
255    pub breadcrumbs: Option<bool>,
256}
257
258#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
259#[serde(rename_all = "snake_case")]
260pub enum VenvSettings {
261    #[default]
262    Off,
263    On {
264        /// Default directories to search for virtual environments, relative
265        /// to the current working directory. We recommend overriding this
266        /// in your project's settings, rather than globally.
267        activate_script: Option<ActivateScript>,
268        venv_name: Option<String>,
269        directories: Option<Vec<PathBuf>>,
270    },
271}
272
273pub struct VenvSettingsContent<'a> {
274    pub activate_script: ActivateScript,
275    pub venv_name: &'a str,
276    pub directories: &'a [PathBuf],
277}
278
279impl VenvSettings {
280    pub fn as_option(&self) -> Option<VenvSettingsContent<'_>> {
281        match self {
282            VenvSettings::Off => None,
283            VenvSettings::On {
284                activate_script,
285                venv_name,
286                directories,
287            } => Some(VenvSettingsContent {
288                activate_script: activate_script.unwrap_or(ActivateScript::Default),
289                venv_name: venv_name.as_deref().unwrap_or(""),
290                directories: directories.as_deref().unwrap_or(&[]),
291            }),
292        }
293    }
294}
295
296#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
297#[serde(rename_all = "snake_case")]
298pub enum TerminalDockPosition {
299    Left,
300    Bottom,
301    Right,
302}
303
304#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
305#[serde(rename_all = "snake_case")]
306pub enum ActivateScript {
307    #[default]
308    Default,
309    Csh,
310    Fish,
311    Nushell,
312    PowerShell,
313    Pyenv,
314}