workspace_settings.rs

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