workspace_settings.rs

  1use std::num::NonZeroUsize;
  2
  3use crate::DockPosition;
  4use collections::HashMap;
  5use gpui::App;
  6use serde::Deserialize;
  7pub use settings::AutosaveSetting;
  8use settings::Settings;
  9pub use settings::{
 10    BottomDockLayout, PaneSplitDirectionHorizontal, PaneSplitDirectionVertical,
 11    RestoreOnStartupBehavior,
 12};
 13use util::MergeFrom as _;
 14
 15pub struct WorkspaceSettings {
 16    pub active_pane_modifiers: ActivePanelModifiers,
 17    pub bottom_dock_layout: settings::BottomDockLayout,
 18    pub pane_split_direction_horizontal: settings::PaneSplitDirectionHorizontal,
 19    pub pane_split_direction_vertical: settings::PaneSplitDirectionVertical,
 20    pub centered_layout: settings::CenteredLayoutSettings,
 21    pub confirm_quit: bool,
 22    pub show_call_status_icon: bool,
 23    pub autosave: AutosaveSetting,
 24    pub restore_on_startup: settings::RestoreOnStartupBehavior,
 25    pub restore_on_file_reopen: bool,
 26    pub drop_target_size: f32,
 27    pub use_system_path_prompts: bool,
 28    pub use_system_prompts: bool,
 29    pub command_aliases: HashMap<String, String>,
 30    pub max_tabs: Option<NonZeroUsize>,
 31    pub when_closing_with_no_tabs: settings::CloseWindowWhenNoItems,
 32    pub on_last_window_closed: settings::OnLastWindowClosed,
 33    pub resize_all_panels_in_dock: Vec<DockPosition>,
 34    pub close_on_file_delete: bool,
 35    pub use_system_window_tabs: bool,
 36    pub zoomed_padding: bool,
 37}
 38
 39#[derive(Copy, Clone, PartialEq, Debug, Default)]
 40pub struct ActivePanelModifiers {
 41    /// Size of the border surrounding the active pane.
 42    /// When set to 0, the active pane doesn't have any border.
 43    /// The border is drawn inset.
 44    ///
 45    /// Default: `0.0`
 46    // TODO: make this not an option, it is never None
 47    pub border_size: Option<f32>,
 48    /// Opacity of inactive panels.
 49    /// When set to 1.0, the inactive panes have the same opacity as the active one.
 50    /// If set to 0, the inactive panes content will not be visible at all.
 51    /// Values are clamped to the [0.0, 1.0] range.
 52    ///
 53    /// Default: `1.0`
 54    // TODO: make this not an option, it is never None
 55    pub inactive_opacity: Option<f32>,
 56}
 57
 58#[derive(Deserialize)]
 59pub struct TabBarSettings {
 60    pub show: bool,
 61    pub show_nav_history_buttons: bool,
 62    pub show_tab_bar_buttons: bool,
 63}
 64
 65impl Settings for WorkspaceSettings {
 66    fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
 67        let workspace = &content.workspace;
 68        Self {
 69            active_pane_modifiers: ActivePanelModifiers {
 70                border_size: Some(
 71                    workspace
 72                        .active_pane_modifiers
 73                        .unwrap()
 74                        .border_size
 75                        .unwrap(),
 76                ),
 77                inactive_opacity: Some(
 78                    workspace
 79                        .active_pane_modifiers
 80                        .unwrap()
 81                        .inactive_opacity
 82                        .unwrap(),
 83                ),
 84            },
 85            bottom_dock_layout: workspace.bottom_dock_layout.clone().unwrap(),
 86            pane_split_direction_horizontal: workspace
 87                .pane_split_direction_horizontal
 88                .clone()
 89                .unwrap(),
 90            pane_split_direction_vertical: workspace.pane_split_direction_vertical.clone().unwrap(),
 91            centered_layout: workspace.centered_layout.clone().unwrap(),
 92            confirm_quit: workspace.confirm_quit.clone().unwrap(),
 93            show_call_status_icon: workspace.show_call_status_icon.clone().unwrap(),
 94            autosave: workspace.autosave.clone().unwrap(),
 95            restore_on_startup: workspace.restore_on_startup.clone().unwrap(),
 96            restore_on_file_reopen: workspace.restore_on_file_reopen.clone().unwrap(),
 97            drop_target_size: workspace.drop_target_size.clone().unwrap(),
 98            use_system_path_prompts: workspace.use_system_path_prompts.clone().unwrap(),
 99            use_system_prompts: workspace.use_system_prompts.clone().unwrap(),
100            command_aliases: workspace.command_aliases.clone(),
101            max_tabs: workspace.max_tabs.clone(),
102            when_closing_with_no_tabs: workspace.when_closing_with_no_tabs.clone().unwrap(),
103            on_last_window_closed: workspace.on_last_window_closed.clone().unwrap(),
104            resize_all_panels_in_dock: workspace
105                .resize_all_panels_in_dock
106                .clone()
107                .unwrap()
108                .into_iter()
109                .map(Into::into)
110                .collect(),
111            close_on_file_delete: workspace.close_on_file_delete.clone().unwrap(),
112            use_system_window_tabs: workspace.use_system_window_tabs.clone().unwrap(),
113            zoomed_padding: workspace.zoomed_padding.clone().unwrap(),
114        }
115    }
116
117    fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
118        let workspace = &content.workspace;
119        if let Some(border_size) = *&workspace
120            .active_pane_modifiers
121            .and_then(|modifier| modifier.border_size)
122        {
123            self.active_pane_modifiers.border_size = Some(border_size);
124        }
125
126        if let Some(inactive_opacity) = *&workspace
127            .active_pane_modifiers
128            .and_then(|modifier| modifier.inactive_opacity)
129        {
130            self.active_pane_modifiers.inactive_opacity = Some(inactive_opacity);
131        }
132
133        self.bottom_dock_layout
134            .merge_from(&workspace.bottom_dock_layout);
135        self.pane_split_direction_horizontal
136            .merge_from(&workspace.pane_split_direction_horizontal);
137        self.pane_split_direction_vertical
138            .merge_from(&workspace.pane_split_direction_vertical);
139        self.centered_layout.merge_from(&workspace.centered_layout);
140        self.confirm_quit.merge_from(&workspace.confirm_quit);
141        self.show_call_status_icon
142            .merge_from(&workspace.show_call_status_icon);
143        self.autosave.merge_from(&workspace.autosave);
144        self.restore_on_startup
145            .merge_from(&workspace.restore_on_startup);
146        self.restore_on_file_reopen
147            .merge_from(&workspace.restore_on_file_reopen);
148        self.drop_target_size
149            .merge_from(&workspace.drop_target_size);
150        self.use_system_path_prompts
151            .merge_from(&workspace.use_system_path_prompts);
152        self.use_system_prompts
153            .merge_from(&workspace.use_system_prompts);
154        self.command_aliases
155            .extend(workspace.command_aliases.clone());
156        if let Some(max_tabs) = workspace.max_tabs {
157            self.max_tabs = Some(max_tabs);
158        }
159        self.when_closing_with_no_tabs
160            .merge_from(&workspace.when_closing_with_no_tabs);
161        self.on_last_window_closed
162            .merge_from(&workspace.on_last_window_closed);
163        self.resize_all_panels_in_dock.merge_from(
164            &workspace
165                .resize_all_panels_in_dock
166                .as_ref()
167                .map(|resize| resize.clone().into_iter().map(Into::into).collect()),
168        );
169        self.close_on_file_delete
170            .merge_from(&workspace.close_on_file_delete);
171        self.use_system_window_tabs
172            .merge_from(&workspace.use_system_window_tabs);
173        self.zoomed_padding.merge_from(&workspace.zoomed_padding);
174    }
175
176    fn import_from_vscode(
177        vscode: &settings::VsCodeSettings,
178        current: &mut settings::SettingsContent,
179    ) {
180        if vscode
181            .read_bool("accessibility.dimUnfocused.enabled")
182            .unwrap_or_default()
183            && let Some(opacity) = vscode
184                .read_value("accessibility.dimUnfocused.opacity")
185                .and_then(|v| v.as_f64())
186        {
187            current
188                .workspace
189                .active_pane_modifiers
190                .get_or_insert_default()
191                .inactive_opacity = Some(opacity as f32);
192        }
193
194        vscode.enum_setting(
195            "window.confirmBeforeClose",
196            &mut current.workspace.confirm_quit,
197            |s| match s {
198                "always" | "keyboardOnly" => Some(true),
199                "never" => Some(false),
200                _ => None,
201            },
202        );
203
204        vscode.bool_setting(
205            "workbench.editor.restoreViewState",
206            &mut current.workspace.restore_on_file_reopen,
207        );
208
209        if let Some(b) = vscode.read_bool("window.closeWhenEmpty") {
210            current.workspace.when_closing_with_no_tabs = Some(if b {
211                settings::CloseWindowWhenNoItems::CloseWindow
212            } else {
213                settings::CloseWindowWhenNoItems::KeepWindowOpen
214            });
215        }
216
217        if let Some(b) = vscode.read_bool("files.simpleDialog.enable") {
218            current.workspace.use_system_path_prompts = Some(!b);
219        }
220
221        if let Some(v) = vscode.read_enum("files.autoSave", |s| match s {
222            "off" => Some(AutosaveSetting::Off),
223            "afterDelay" => Some(AutosaveSetting::AfterDelay {
224                milliseconds: vscode
225                    .read_value("files.autoSaveDelay")
226                    .and_then(|v| v.as_u64())
227                    .unwrap_or(1000),
228            }),
229            "onFocusChange" => Some(AutosaveSetting::OnFocusChange),
230            "onWindowChange" => Some(AutosaveSetting::OnWindowChange),
231            _ => None,
232        }) {
233            current.workspace.autosave = Some(v);
234        }
235
236        // workbench.editor.limit contains "enabled", "value", and "perEditorGroup"
237        // our semantics match if those are set to true, some N, and true respectively.
238        // we'll ignore "perEditorGroup" for now since we only support a global max
239        if let Some(n) = vscode
240            .read_value("workbench.editor.limit.value")
241            .and_then(|v| v.as_u64())
242            .and_then(|n| NonZeroUsize::new(n as usize))
243            && vscode
244                .read_bool("workbench.editor.limit.enabled")
245                .unwrap_or_default()
246        {
247            current.workspace.max_tabs = Some(n)
248        }
249
250        if let Some(b) = vscode.read_bool("window.nativeTabs") {
251            current.workspace.use_system_window_tabs = Some(b);
252        }
253
254        // some combination of "window.restoreWindows" and "workbench.startupEditor" might
255        // map to our "restore_on_startup"
256
257        // there doesn't seem to be a way to read whether the bottom dock's "justified"
258        // setting is enabled in vscode. that'd be our equivalent to "bottom_dock_layout"
259    }
260}
261
262impl Settings for TabBarSettings {
263    fn from_defaults(content: &settings::SettingsContent, _cx: &mut App) -> Self {
264        let tab_bar = content.tab_bar.clone().unwrap();
265        TabBarSettings {
266            show: tab_bar.show.unwrap(),
267            show_nav_history_buttons: tab_bar.show_nav_history_buttons.unwrap(),
268            show_tab_bar_buttons: tab_bar.show_tab_bar_buttons.unwrap(),
269        }
270    }
271
272    fn refine(&mut self, content: &settings::SettingsContent, _cx: &mut App) {
273        let Some(tab_bar) = &content.tab_bar else {
274            return;
275        };
276        self.show.merge_from(&tab_bar.show);
277        self.show_nav_history_buttons
278            .merge_from(&tab_bar.show_nav_history_buttons);
279        self.show_tab_bar_buttons
280            .merge_from(&tab_bar.show_tab_bar_buttons);
281    }
282
283    fn import_from_vscode(
284        vscode: &settings::VsCodeSettings,
285        current: &mut settings::SettingsContent,
286    ) {
287        if let Some(b) = vscode.read_enum("workbench.editor.showTabs", |s| match s {
288            "multiple" => Some(true),
289            "single" | "none" => Some(false),
290            _ => None,
291        }) {
292            current.tab_bar.get_or_insert_default().show = Some(b);
293        }
294        if Some("hidden") == vscode.read_string("workbench.editor.editorActionsLocation") {
295            current.tab_bar.get_or_insert_default().show_tab_bar_buttons = Some(false)
296        }
297    }
298}