1use std::num::NonZeroUsize;
2
3use crate::DockPosition;
4use anyhow::Result;
5use collections::HashMap;
6use gpui::App;
7use schemars::JsonSchema;
8use serde::{Deserialize, Serialize};
9use settings::{Settings, SettingsKey, SettingsSources, SettingsUi};
10
11#[derive(Deserialize)]
12pub struct WorkspaceSettings {
13 pub active_pane_modifiers: ActivePanelModifiers,
14 pub bottom_dock_layout: BottomDockLayout,
15 pub pane_split_direction_horizontal: PaneSplitDirectionHorizontal,
16 pub pane_split_direction_vertical: PaneSplitDirectionVertical,
17 pub centered_layout: CenteredLayoutSettings,
18 pub confirm_quit: bool,
19 pub show_call_status_icon: bool,
20 pub autosave: AutosaveSetting,
21 pub restore_on_startup: RestoreOnStartupBehavior,
22 pub restore_on_file_reopen: bool,
23 pub drop_target_size: f32,
24 pub use_system_path_prompts: bool,
25 pub use_system_prompts: bool,
26 pub command_aliases: HashMap<String, String>,
27 pub max_tabs: Option<NonZeroUsize>,
28 pub when_closing_with_no_tabs: CloseWindowWhenNoItems,
29 pub on_last_window_closed: OnLastWindowClosed,
30 pub resize_all_panels_in_dock: Vec<DockPosition>,
31 pub close_on_file_delete: bool,
32 pub use_system_window_tabs: bool,
33 pub zoomed_padding: bool,
34}
35
36#[derive(Copy, Clone, Default, Serialize, Deserialize, JsonSchema)]
37#[serde(rename_all = "snake_case")]
38pub enum OnLastWindowClosed {
39 /// Match platform conventions by default, so don't quit on macOS, and quit on other platforms
40 #[default]
41 PlatformDefault,
42 /// Quit the application the last window is closed
43 QuitApp,
44}
45
46impl OnLastWindowClosed {
47 pub fn is_quit_app(&self) -> bool {
48 match self {
49 OnLastWindowClosed::PlatformDefault => false,
50 OnLastWindowClosed::QuitApp => true,
51 }
52 }
53}
54
55#[derive(Deserialize)]
56pub struct TabBarSettings {
57 pub show: bool,
58 pub show_nav_history_buttons: bool,
59 pub show_tab_bar_buttons: bool,
60}
61impl Settings for WorkspaceSettings {
62 type FileContent = WorkspaceSettingsContent;
63
64 fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
65 sources.json_merge()
66 }
67
68 fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
69 if vscode
70 .read_bool("accessibility.dimUnfocused.enabled")
71 .unwrap_or_default()
72 && let Some(opacity) = vscode
73 .read_value("accessibility.dimUnfocused.opacity")
74 .and_then(|v| v.as_f64())
75 {
76 if let Some(settings) = current.active_pane_modifiers.as_mut() {
77 settings.inactive_opacity = Some(opacity as f32)
78 } else {
79 current.active_pane_modifiers = Some(ActivePanelModifiers {
80 inactive_opacity: Some(opacity as f32),
81 ..Default::default()
82 })
83 }
84 }
85
86 vscode.enum_setting(
87 "window.confirmBeforeClose",
88 &mut current.confirm_quit,
89 |s| match s {
90 "always" | "keyboardOnly" => Some(true),
91 "never" => Some(false),
92 _ => None,
93 },
94 );
95
96 vscode.bool_setting(
97 "workbench.editor.restoreViewState",
98 &mut current.restore_on_file_reopen,
99 );
100
101 if let Some(b) = vscode.read_bool("window.closeWhenEmpty") {
102 current.when_closing_with_no_tabs = Some(if b {
103 CloseWindowWhenNoItems::CloseWindow
104 } else {
105 CloseWindowWhenNoItems::KeepWindowOpen
106 })
107 }
108
109 if let Some(b) = vscode.read_bool("files.simpleDialog.enable") {
110 current.use_system_path_prompts = Some(!b);
111 }
112
113 vscode.enum_setting("files.autoSave", &mut current.autosave, |s| match s {
114 "off" => Some(AutosaveSetting::Off),
115 "afterDelay" => Some(AutosaveSetting::AfterDelay {
116 milliseconds: vscode
117 .read_value("files.autoSaveDelay")
118 .and_then(|v| v.as_u64())
119 .unwrap_or(1000),
120 }),
121 "onFocusChange" => Some(AutosaveSetting::OnFocusChange),
122 "onWindowChange" => Some(AutosaveSetting::OnWindowChange),
123 _ => None,
124 });
125
126 // workbench.editor.limit contains "enabled", "value", and "perEditorGroup"
127 // our semantics match if those are set to true, some N, and true respectively.
128 // we'll ignore "perEditorGroup" for now since we only support a global max
129 if let Some(n) = vscode
130 .read_value("workbench.editor.limit.value")
131 .and_then(|v| v.as_u64())
132 .and_then(|n| NonZeroUsize::new(n as usize))
133 && vscode
134 .read_bool("workbench.editor.limit.enabled")
135 .unwrap_or_default()
136 {
137 current.max_tabs = Some(n)
138 }
139
140 vscode.bool_setting("window.nativeTabs", &mut current.use_system_window_tabs);
141
142 // some combination of "window.restoreWindows" and "workbench.startupEditor" might
143 // map to our "restore_on_startup"
144
145 // there doesn't seem to be a way to read whether the bottom dock's "justified"
146 // setting is enabled in vscode. that'd be our equivalent to "bottom_dock_layout"
147 }
148}
149
150impl Settings for TabBarSettings {
151 type FileContent = TabBarSettingsContent;
152
153 fn load(sources: SettingsSources<Self::FileContent>, _: &mut App) -> Result<Self> {
154 sources.json_merge()
155 }
156
157 fn import_from_vscode(vscode: &settings::VsCodeSettings, current: &mut Self::FileContent) {
158 vscode.enum_setting(
159 "workbench.editor.showTabs",
160 &mut current.show,
161 |s| match s {
162 "multiple" => Some(true),
163 "single" | "none" => Some(false),
164 _ => None,
165 },
166 );
167 if Some("hidden") == vscode.read_string("workbench.editor.editorActionsLocation") {
168 current.show_tab_bar_buttons = Some(false)
169 }
170 }
171}