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}