terminal.rs

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