workspace_settings.rs

  1use std::num::NonZeroUsize;
  2
  3use anyhow::Result;
  4use collections::HashMap;
  5use gpui::App;
  6use schemars::JsonSchema;
  7use serde::{Deserialize, Serialize};
  8use settings::{Settings, SettingsSources};
  9
 10#[derive(Deserialize)]
 11pub struct WorkspaceSettings {
 12    pub active_pane_modifiers: ActivePanelModifiers,
 13    pub bottom_dock_layout: BottomDockLayout,
 14    pub pane_split_direction_horizontal: PaneSplitDirectionHorizontal,
 15    pub pane_split_direction_vertical: PaneSplitDirectionVertical,
 16    pub centered_layout: CenteredLayoutSettings,
 17    pub confirm_quit: bool,
 18    pub show_call_status_icon: bool,
 19    pub autosave: AutosaveSetting,
 20    pub restore_on_startup: RestoreOnStartupBehavior,
 21    pub restore_on_file_reopen: bool,
 22    pub drop_target_size: f32,
 23    pub use_system_path_prompts: bool,
 24    pub use_system_prompts: bool,
 25    pub command_aliases: HashMap<String, String>,
 26    pub max_tabs: Option<NonZeroUsize>,
 27    pub when_closing_with_no_tabs: CloseWindowWhenNoItems,
 28    pub on_last_window_closed: OnLastWindowClosed,
 29    pub close_on_file_delete: bool,
 30}
 31
 32#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema)]
 33#[serde(rename_all = "snake_case")]
 34pub enum OnLastWindowClosed {
 35    /// Match platform conventions by default, so don't quit on macOS, and quit on other platforms
 36    #[default]
 37    PlatformDefault,
 38    /// Quit the application the last window is closed
 39    QuitApp,
 40}
 41
 42impl OnLastWindowClosed {
 43    pub fn is_quit_app(&self) -> bool {
 44        match self {
 45            OnLastWindowClosed::PlatformDefault => false,
 46            OnLastWindowClosed::QuitApp => true,
 47        }
 48    }
 49}
 50
 51#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
 52#[serde(rename_all = "snake_case")]
 53pub struct ActivePanelModifiers {
 54    /// Size of the border surrounding the active pane.
 55    /// When set to 0, the active pane doesn't have any border.
 56    /// The border is drawn inset.
 57    ///
 58    /// Default: `0.0`
 59    pub border_size: Option<f32>,
 60    /// Opacity of inactive panels.
 61    /// When set to 1.0, the inactive panes have the same opacity as the active one.
 62    /// If set to 0, the inactive panes content will not be visible at all.
 63    /// Values are clamped to the [0.0, 1.0] range.
 64    ///
 65    /// Default: `1.0`
 66    pub inactive_opacity: Option<f32>,
 67}
 68
 69#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, PartialEq, JsonSchema)]
 70#[serde(rename_all = "snake_case")]
 71pub enum BottomDockLayout {
 72    /// Contained between the left and right docks
 73    #[default]
 74    Contained,
 75    /// Takes up the full width of the window
 76    Full,
 77    /// Extends under the left dock while snapping to the right dock
 78    LeftAligned,
 79    /// Extends under the right dock while snapping to the left dock
 80    RightAligned,
 81}
 82
 83#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema)]
 84#[serde(rename_all = "snake_case")]
 85pub enum CloseWindowWhenNoItems {
 86    /// Match platform conventions by default, so "on" on macOS and "off" everywhere else
 87    #[default]
 88    PlatformDefault,
 89    /// Close the window when there are no tabs
 90    CloseWindow,
 91    /// Leave the window open when there are no tabs
 92    KeepWindowOpen,
 93}
 94
 95impl CloseWindowWhenNoItems {
 96    pub fn should_close(&self) -> bool {
 97        match self {
 98            CloseWindowWhenNoItems::PlatformDefault => cfg!(target_os = "macos"),
 99            CloseWindowWhenNoItems::CloseWindow => true,
100            CloseWindowWhenNoItems::KeepWindowOpen => false,
101        }
102    }
103}
104
105#[derive(Copy, Clone, PartialEq, Eq, Default, Serialize, Deserialize, JsonSchema)]
106#[serde(rename_all = "snake_case")]
107pub enum RestoreOnStartupBehavior {
108    /// Always start with an empty editor
109    None,
110    /// Restore the workspace that was closed last.
111    LastWorkspace,
112    /// Restore all workspaces that were open when quitting Zed.
113    #[default]
114    LastSession,
115}
116
117#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
118pub struct WorkspaceSettingsContent {
119    /// Active pane styling settings.
120    pub active_pane_modifiers: Option<ActivePanelModifiers>,
121    /// Layout mode for the bottom dock
122    ///
123    /// Default: contained
124    pub bottom_dock_layout: Option<BottomDockLayout>,
125    /// Direction to split horizontally.
126    ///
127    /// Default: "up"
128    pub pane_split_direction_horizontal: Option<PaneSplitDirectionHorizontal>,
129    /// Direction to split vertically.
130    ///
131    /// Default: "left"
132    pub pane_split_direction_vertical: Option<PaneSplitDirectionVertical>,
133    /// Centered layout related settings.
134    pub centered_layout: Option<CenteredLayoutSettings>,
135    /// Whether or not to prompt the user to confirm before closing the application.
136    ///
137    /// Default: false
138    pub confirm_quit: Option<bool>,
139    /// Whether or not to show the call status icon in the status bar.
140    ///
141    /// Default: true
142    pub show_call_status_icon: Option<bool>,
143    /// When to automatically save edited buffers.
144    ///
145    /// Default: off
146    pub autosave: Option<AutosaveSetting>,
147    /// Controls previous session restoration in freshly launched Zed instance.
148    /// Values: none, last_workspace, last_session
149    /// Default: last_session
150    pub restore_on_startup: Option<RestoreOnStartupBehavior>,
151    /// Whether to attempt to restore previous file's state when opening it again.
152    /// The state is stored per pane.
153    /// When disabled, defaults are applied instead of the state restoration.
154    ///
155    /// E.g. for editors, selections, folds and scroll positions are restored, if the same file is closed and, later, opened again in the same pane.
156    /// When disabled, a single selection in the very beginning of the file, zero scroll position and no folds state is used as a default.
157    ///
158    /// Default: true
159    pub restore_on_file_reopen: Option<bool>,
160    /// The size of the workspace split drop targets on the outer edges.
161    /// Given as a fraction that will be multiplied by the smaller dimension of the workspace.
162    ///
163    /// Default: `0.2` (20% of the smaller dimension of the workspace)
164    pub drop_target_size: Option<f32>,
165    /// Whether to close the window when using 'close active item' on a workspace with no tabs
166    ///
167    /// Default: auto ("on" on macOS, "off" otherwise)
168    pub when_closing_with_no_tabs: Option<CloseWindowWhenNoItems>,
169    /// Whether to use the system provided dialogs for Open and Save As.
170    /// When set to false, Zed will use the built-in keyboard-first pickers.
171    ///
172    /// Default: true
173    pub use_system_path_prompts: Option<bool>,
174    /// Whether to use the system provided prompts.
175    /// When set to false, Zed will use the built-in prompts.
176    /// Note that this setting has no effect on Linux, where Zed will always
177    /// use the built-in prompts.
178    ///
179    /// Default: true
180    pub use_system_prompts: Option<bool>,
181    /// Aliases for the command palette. When you type a key in this map,
182    /// it will be assumed to equal the value.
183    ///
184    /// Default: true
185    pub command_aliases: Option<HashMap<String, String>>,
186    /// Maximum open tabs in a pane. Will not close an unsaved
187    /// tab. Set to `None` for unlimited tabs.
188    ///
189    /// Default: none
190    pub max_tabs: Option<NonZeroUsize>,
191    /// What to do when the last window is closed
192    ///
193    /// Default: auto (nothing on macOS, "app quit" otherwise)
194    pub on_last_window_closed: Option<OnLastWindowClosed>,
195    /// Whether to automatically close files that have been deleted on disk.
196    ///
197    /// Default: false
198    pub close_on_file_delete: Option<bool>,
199}
200
201#[derive(Deserialize)]
202pub struct TabBarSettings {
203    pub show: bool,
204    pub show_nav_history_buttons: bool,
205    pub show_tab_bar_buttons: bool,
206}
207
208#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
209pub struct TabBarSettingsContent {
210    /// Whether or not to show the tab bar in the editor.
211    ///
212    /// Default: true
213    pub show: Option<bool>,
214    /// Whether or not to show the navigation history buttons in the tab bar.
215    ///
216    /// Default: true
217    pub show_nav_history_buttons: Option<bool>,
218    /// Whether or not to show the tab bar buttons.
219    ///
220    /// Default: true
221    pub show_tab_bar_buttons: Option<bool>,
222}
223
224#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
225#[serde(rename_all = "snake_case")]
226pub enum AutosaveSetting {
227    /// Disable autosave.
228    Off,
229    /// Save after inactivity period of `milliseconds`.
230    AfterDelay { milliseconds: u64 },
231    /// Autosave when focus changes.
232    OnFocusChange,
233    /// Autosave when the active window changes.
234    OnWindowChange,
235}
236
237#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
238#[serde(rename_all = "snake_case")]
239pub enum PaneSplitDirectionHorizontal {
240    Up,
241    Down,
242}
243
244#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
245#[serde(rename_all = "snake_case")]
246pub enum PaneSplitDirectionVertical {
247    Left,
248    Right,
249}
250
251#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
252#[serde(rename_all = "snake_case")]
253pub struct CenteredLayoutSettings {
254    /// The relative width of the left padding of the central pane from the
255    /// workspace when the centered layout is used.
256    ///
257    /// Default: 0.2
258    pub left_padding: Option<f32>,
259    // The relative width of the right padding of the central pane from the
260    // workspace when the centered layout is used.
261    ///
262    /// Default: 0.2
263    pub right_padding: Option<f32>,
264}
265
266impl Settings for WorkspaceSettings {
267    const KEY: Option<&'static str> = None;
268
269    type FileContent = WorkspaceSettingsContent;
270
271    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
272        sources.json_merge()
273    }
274
275    fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
276        if vscode
277            .read_bool("accessibility.dimUnfocused.enabled")
278            .unwrap_or_default()
279        {
280            if let Some(opacity) = vscode
281                .read_value("accessibility.dimUnfocused.opacity")
282                .and_then(|v| v.as_f64())
283            {
284                if let Some(settings) = current.active_pane_modifiers.as_mut() {
285                    settings.inactive_opacity = Some(opacity as f32)
286                } else {
287                    current.active_pane_modifiers = Some(ActivePanelModifiers {
288                        inactive_opacity: Some(opacity as f32),
289                        ..Default::default()
290                    })
291                }
292            }
293        }
294
295        vscode.enum_setting(
296            "window.confirmBeforeClose",
297            &mut current.confirm_quit,
298            |s| match s {
299                "always" | "keyboardOnly" => Some(true),
300                "never" => Some(false),
301                _ => None,
302            },
303        );
304
305        vscode.bool_setting(
306            "workbench.editor.restoreViewState",
307            &mut current.restore_on_file_reopen,
308        );
309
310        if let Some(b) = vscode.read_bool("window.closeWhenEmpty") {
311            current.when_closing_with_no_tabs = Some(if b {
312                CloseWindowWhenNoItems::CloseWindow
313            } else {
314                CloseWindowWhenNoItems::KeepWindowOpen
315            })
316        }
317
318        if let Some(b) = vscode.read_bool("files.simpleDialog.enable") {
319            current.use_system_path_prompts = Some(!b);
320        }
321
322        vscode.enum_setting("files.autoSave", &mut current.autosave, |s| match s {
323            "off" => Some(AutosaveSetting::Off),
324            "afterDelay" => Some(AutosaveSetting::AfterDelay {
325                milliseconds: vscode
326                    .read_value("files.autoSaveDelay")
327                    .and_then(|v| v.as_u64())
328                    .unwrap_or(1000),
329            }),
330            "onFocusChange" => Some(AutosaveSetting::OnFocusChange),
331            "onWindowChange" => Some(AutosaveSetting::OnWindowChange),
332            _ => None,
333        });
334
335        // workbench.editor.limit contains "enabled", "value", and "perEditorGroup"
336        // our semantics match if those are set to true, some N, and true respectively.
337        // we'll ignore "perEditorGroup" for now since we only support a global max
338        if let Some(n) = vscode
339            .read_value("workbench.editor.limit.value")
340            .and_then(|v| v.as_u64())
341            .and_then(|n| NonZeroUsize::new(n as usize))
342        {
343            if vscode
344                .read_bool("workbench.editor.limit.enabled")
345                .unwrap_or_default()
346            {
347                current.max_tabs = Some(n)
348            }
349        }
350
351        // some combination of "window.restoreWindows" and "workbench.startupEditor" might
352        // map to our "restore_on_startup"
353
354        // there doesn't seem to be a way to read whether the bottom dock's "justified"
355        // setting is enabled in vscode. that'd be our equivalent to "bottom_dock_layout"
356    }
357}
358
359impl Settings for TabBarSettings {
360    const KEY: Option<&'static str> = Some("tab_bar");
361
362    type FileContent = TabBarSettingsContent;
363
364    fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
365        sources.json_merge()
366    }
367
368    fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
369        vscode.enum_setting(
370            "workbench.editor.showTabs",
371            &mut current.show,
372            |s| match s {
373                "multiple" => Some(true),
374                "single" | "none" => Some(false),
375                _ => None,
376            },
377        );
378        if Some("hidden") == vscode.read_string("workbench.editor.editorActionsLocation") {
379            current.show_tab_bar_buttons = Some(false)
380        }
381    }
382}