1use collections::HashMap;
2use gpui::{
3 px, AbsoluteLength, AppContext, FontFallbacks, FontFeatures, FontWeight, Pixels, SharedString,
4};
5use schemars::{gen::SchemaGenerator, schema::RootSchema, JsonSchema};
6use serde_derive::{Deserialize, Serialize};
7use settings::{add_references_to_properties, SettingsJsonSchemaParams, SettingsSources};
8use std::path::PathBuf;
9use task::Shell;
10
11#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
12#[serde(rename_all = "snake_case")]
13pub enum TerminalDockPosition {
14 Left,
15 Bottom,
16 Right,
17}
18
19#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
20pub struct Toolbar {
21 pub title: bool,
22}
23
24#[derive(Debug, Deserialize)]
25pub struct TerminalSettings {
26 pub shell: Shell,
27 pub working_directory: WorkingDirectory,
28 pub font_size: Option<Pixels>,
29 pub font_family: Option<SharedString>,
30 pub font_fallbacks: Option<FontFallbacks>,
31 pub font_features: Option<FontFeatures>,
32 pub font_weight: Option<FontWeight>,
33 pub line_height: TerminalLineHeight,
34 pub env: HashMap<String, String>,
35 pub blinking: TerminalBlink,
36 pub alternate_scroll: AlternateScroll,
37 pub option_as_meta: bool,
38 pub copy_on_select: bool,
39 pub button: bool,
40 pub dock: TerminalDockPosition,
41 pub default_width: Pixels,
42 pub default_height: Pixels,
43 pub detect_venv: VenvSettings,
44 pub max_scroll_history_lines: Option<usize>,
45 pub toolbar: Toolbar,
46}
47
48#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
49#[serde(rename_all = "snake_case")]
50pub enum VenvSettings {
51 #[default]
52 Off,
53 On {
54 /// Default directories to search for virtual environments, relative
55 /// to the current working directory. We recommend overriding this
56 /// in your project's settings, rather than globally.
57 activate_script: Option<ActivateScript>,
58 directories: Option<Vec<PathBuf>>,
59 },
60}
61
62pub struct VenvSettingsContent<'a> {
63 pub activate_script: ActivateScript,
64 pub directories: &'a [PathBuf],
65}
66
67impl VenvSettings {
68 pub fn as_option(&self) -> Option<VenvSettingsContent> {
69 match self {
70 VenvSettings::Off => None,
71 VenvSettings::On {
72 activate_script,
73 directories,
74 } => Some(VenvSettingsContent {
75 activate_script: activate_script.unwrap_or(ActivateScript::Default),
76 directories: directories.as_deref().unwrap_or(&[]),
77 }),
78 }
79 }
80}
81
82#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
83#[serde(rename_all = "snake_case")]
84pub enum ActivateScript {
85 #[default]
86 Default,
87 Csh,
88 Fish,
89 Nushell,
90 PowerShell,
91}
92
93#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
94pub struct TerminalSettingsContent {
95 /// What shell to use when opening a terminal.
96 ///
97 /// Default: system
98 pub shell: Option<Shell>,
99 /// What working directory to use when launching the terminal
100 ///
101 /// Default: current_project_directory
102 pub working_directory: Option<WorkingDirectory>,
103 /// Sets the terminal's font size.
104 ///
105 /// If this option is not included,
106 /// the terminal will default to matching the buffer's font size.
107 pub font_size: Option<f32>,
108 /// Sets the terminal's font family.
109 ///
110 /// If this option is not included,
111 /// the terminal will default to matching the buffer's font family.
112 pub font_family: Option<String>,
113
114 /// Sets the terminal's font fallbacks.
115 ///
116 /// If this option is not included,
117 /// the terminal will default to matching the buffer's font fallbacks.
118 pub font_fallbacks: Option<Vec<String>>,
119
120 /// Sets the terminal's line height.
121 ///
122 /// Default: comfortable
123 pub line_height: Option<TerminalLineHeight>,
124 pub font_features: Option<FontFeatures>,
125 /// Sets the terminal's font weight in CSS weight units 0-900.
126 pub font_weight: Option<f32>,
127 /// Any key-value pairs added to this list will be added to the terminal's
128 /// environment. Use `:` to separate multiple values.
129 ///
130 /// Default: {}
131 pub env: Option<HashMap<String, String>>,
132 /// Sets the cursor blinking behavior in the terminal.
133 ///
134 /// Default: terminal_controlled
135 pub blinking: Option<TerminalBlink>,
136 /// Sets whether Alternate Scroll mode (code: ?1007) is active by default.
137 /// Alternate Scroll mode converts mouse scroll events into up / down key
138 /// presses when in the alternate screen (e.g. when running applications
139 /// like vim or less). The terminal can still set and unset this mode.
140 ///
141 /// Default: off
142 pub alternate_scroll: Option<AlternateScroll>,
143 /// Sets whether the option key behaves as the meta key.
144 ///
145 /// Default: true
146 pub option_as_meta: Option<bool>,
147 /// Whether or not selecting text in the terminal will automatically
148 /// copy to the system clipboard.
149 ///
150 /// Default: false
151 pub copy_on_select: Option<bool>,
152 /// Whether to show the terminal button in the status bar.
153 ///
154 /// Default: true
155 pub button: Option<bool>,
156 pub dock: Option<TerminalDockPosition>,
157 /// Default width when the terminal is docked to the left or right.
158 ///
159 /// Default: 640
160 pub default_width: Option<f32>,
161 /// Default height when the terminal is docked to the bottom.
162 ///
163 /// Default: 320
164 pub default_height: Option<f32>,
165 /// Activates the python virtual environment, if one is found, in the
166 /// terminal's working directory (as resolved by the working_directory
167 /// setting). Set this to "off" to disable this behavior.
168 ///
169 /// Default: on
170 pub detect_venv: Option<VenvSettings>,
171 /// The maximum number of lines to keep in the scrollback history.
172 /// Maximum allowed value is 100_000, all values above that will be treated as 100_000.
173 /// 0 disables the scrolling.
174 /// Existing terminals will not pick up this change until they are recreated.
175 /// 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.
176 ///
177 /// Default: 10_000
178 pub max_scroll_history_lines: Option<usize>,
179 /// Toolbar related settings
180 pub toolbar: Option<ToolbarContent>,
181}
182
183impl settings::Settings for TerminalSettings {
184 const KEY: Option<&'static str> = Some("terminal");
185
186 type FileContent = TerminalSettingsContent;
187
188 fn load(
189 sources: SettingsSources<Self::FileContent>,
190 _: &mut AppContext,
191 ) -> anyhow::Result<Self> {
192 sources.json_merge()
193 }
194
195 fn json_schema(
196 generator: &mut SchemaGenerator,
197 params: &SettingsJsonSchemaParams,
198 _: &AppContext,
199 ) -> RootSchema {
200 let mut root_schema = generator.root_schema_for::<Self::FileContent>();
201 root_schema.definitions.extend([
202 ("FontFamilies".into(), params.font_family_schema()),
203 ("FontFallbacks".into(), params.font_fallback_schema()),
204 ]);
205
206 add_references_to_properties(
207 &mut root_schema,
208 &[
209 ("font_family", "#/definitions/FontFamilies"),
210 ("font_fallbacks", "#/definitions/FontFallbacks"),
211 ],
212 );
213
214 root_schema
215 }
216}
217
218#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, JsonSchema, Default)]
219#[serde(rename_all = "snake_case")]
220pub enum TerminalLineHeight {
221 /// Use a line height that's comfortable for reading, 1.618
222 #[default]
223 Comfortable,
224 /// Use a standard line height, 1.3. This option is useful for TUIs,
225 /// particularly if they use box characters
226 Standard,
227 /// Use a custom line height.
228 Custom(f32),
229}
230
231impl TerminalLineHeight {
232 pub fn value(&self) -> AbsoluteLength {
233 let value = match self {
234 TerminalLineHeight::Comfortable => 1.618,
235 TerminalLineHeight::Standard => 1.3,
236 TerminalLineHeight::Custom(line_height) => f32::max(*line_height, 1.),
237 };
238 px(value).into()
239 }
240}
241
242#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
243#[serde(rename_all = "snake_case")]
244pub enum TerminalBlink {
245 /// Never blink the cursor, ignoring the terminal mode.
246 Off,
247 /// Default the cursor blink to off, but allow the terminal to
248 /// set blinking.
249 TerminalControlled,
250 /// Always blink the cursor, ignoring the terminal mode.
251 On,
252}
253
254#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
255#[serde(rename_all = "snake_case")]
256pub enum AlternateScroll {
257 On,
258 Off,
259}
260
261#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
262#[serde(rename_all = "snake_case")]
263pub enum WorkingDirectory {
264 /// Use the current file's project directory. Will Fallback to the
265 /// first project directory strategy if unsuccessful.
266 CurrentProjectDirectory,
267 /// Use the first project in this workspace's directory.
268 FirstProjectDirectory,
269 /// Always use this platform's home directory (if it can be found).
270 AlwaysHome,
271 /// Always use a specific directory. This value will be shell expanded.
272 /// If this path is not a valid directory the terminal will default to
273 /// this platform's home directory (if it can be found).
274 Always { directory: String },
275}
276
277// Toolbar related settings
278#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
279pub struct ToolbarContent {
280 /// Whether to display the terminal title in its toolbar.
281 ///
282 /// Default: true
283 pub title: Option<bool>,
284}