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}