editor_settings.rs

  1use core::num;
  2use std::num::NonZeroU32;
  3
  4use gpui::App;
  5use language::CursorShape;
  6use project::project_settings::DiagnosticSeverity;
  7pub use settings::{
  8    CurrentLineHighlight, DisplayIn, DocumentColorsRenderMode, DoubleClickInMultibuffer,
  9    GoToDefinitionFallback, HideMouseMode, MinimapThumb, MinimapThumbBorder, MultiCursorModifier,
 10    ScrollBeyondLastLine, ScrollbarDiagnostics, SeedQuerySetting, ShowMinimap, SnippetSortOrder,
 11    VsCodeSettings,
 12};
 13use settings::{Settings, SettingsContent};
 14use ui::scrollbars::{ScrollbarVisibility, ShowScrollbar};
 15
 16/// Imports from the VSCode settings at
 17/// https://code.visualstudio.com/docs/reference/default-settings
 18#[derive(Clone)]
 19pub struct EditorSettings {
 20    pub cursor_blink: bool,
 21    pub cursor_shape: Option<CursorShape>,
 22    pub current_line_highlight: CurrentLineHighlight,
 23    pub selection_highlight: bool,
 24    pub rounded_selection: bool,
 25    pub lsp_highlight_debounce: u64,
 26    pub hover_popover_enabled: bool,
 27    pub hover_popover_delay: u64,
 28    pub status_bar: StatusBar,
 29    pub toolbar: Toolbar,
 30    pub scrollbar: Scrollbar,
 31    pub minimap: Minimap,
 32    pub gutter: Gutter,
 33    pub scroll_beyond_last_line: ScrollBeyondLastLine,
 34    pub vertical_scroll_margin: f32,
 35    pub autoscroll_on_clicks: bool,
 36    pub horizontal_scroll_margin: f32,
 37    pub scroll_sensitivity: f32,
 38    pub fast_scroll_sensitivity: f32,
 39    pub relative_line_numbers: bool,
 40    pub seed_search_query_from_cursor: SeedQuerySetting,
 41    pub use_smartcase_search: bool,
 42    pub multi_cursor_modifier: MultiCursorModifier,
 43    pub redact_private_values: bool,
 44    pub expand_excerpt_lines: u32,
 45    pub excerpt_context_lines: u32,
 46    pub middle_click_paste: bool,
 47    pub double_click_in_multibuffer: DoubleClickInMultibuffer,
 48    pub search_wrap: bool,
 49    pub search: SearchSettings,
 50    pub auto_signature_help: bool,
 51    pub show_signature_help_after_edits: bool,
 52    pub go_to_definition_fallback: GoToDefinitionFallback,
 53    pub jupyter: Jupyter,
 54    pub hide_mouse: Option<HideMouseMode>,
 55    pub snippet_sort_order: SnippetSortOrder,
 56    pub diagnostics_max_severity: Option<DiagnosticSeverity>,
 57    pub inline_code_actions: bool,
 58    pub drag_and_drop_selection: DragAndDropSelection,
 59    pub lsp_document_colors: DocumentColorsRenderMode,
 60    pub minimum_contrast_for_highlights: f32,
 61}
 62#[derive(Debug, Clone)]
 63pub struct Jupyter {
 64    /// Whether the Jupyter feature is enabled.
 65    ///
 66    /// Default: true
 67    pub enabled: bool,
 68}
 69
 70#[derive(Clone, Debug, PartialEq, Eq)]
 71pub struct StatusBar {
 72    /// Whether to display the active language button in the status bar.
 73    ///
 74    /// Default: true
 75    pub active_language_button: bool,
 76    /// Whether to show the cursor position button in the status bar.
 77    ///
 78    /// Default: true
 79    pub cursor_position_button: bool,
 80}
 81
 82#[derive(Clone, Debug, PartialEq, Eq)]
 83pub struct Toolbar {
 84    pub breadcrumbs: bool,
 85    pub quick_actions: bool,
 86    pub selections_menu: bool,
 87    pub agent_review: bool,
 88    pub code_actions: bool,
 89}
 90
 91#[derive(Copy, Clone, Debug, PartialEq, Eq)]
 92pub struct Scrollbar {
 93    pub show: ShowScrollbar,
 94    pub git_diff: bool,
 95    pub selected_text: bool,
 96    pub selected_symbol: bool,
 97    pub search_results: bool,
 98    pub diagnostics: ScrollbarDiagnostics,
 99    pub cursors: bool,
100    pub axes: ScrollbarAxes,
101}
102
103#[derive(Copy, Clone, Debug, PartialEq)]
104pub struct Minimap {
105    pub show: ShowMinimap,
106    pub display_in: DisplayIn,
107    pub thumb: MinimapThumb,
108    pub thumb_border: MinimapThumbBorder,
109    pub current_line_highlight: Option<CurrentLineHighlight>,
110    pub max_width_columns: num::NonZeroU32,
111}
112
113impl Minimap {
114    pub fn minimap_enabled(&self) -> bool {
115        self.show != ShowMinimap::Never
116    }
117
118    #[inline]
119    pub fn on_active_editor(&self) -> bool {
120        self.display_in == DisplayIn::ActiveEditor
121    }
122
123    pub fn with_show_override(self) -> Self {
124        Self {
125            show: ShowMinimap::Always,
126            ..self
127        }
128    }
129}
130
131#[derive(Copy, Clone, Debug, PartialEq, Eq)]
132pub struct Gutter {
133    pub min_line_number_digits: usize,
134    pub line_numbers: bool,
135    pub runnables: bool,
136    pub breakpoints: bool,
137    pub folds: bool,
138}
139
140/// Forcefully enable or disable the scrollbar for each axis
141#[derive(Copy, Clone, Debug, PartialEq, Eq)]
142pub struct ScrollbarAxes {
143    /// When false, forcefully disables the horizontal scrollbar. Otherwise, obey other settings.
144    ///
145    /// Default: true
146    pub horizontal: bool,
147
148    /// When false, forcefully disables the vertical scrollbar. Otherwise, obey other settings.
149    ///
150    /// Default: true
151    pub vertical: bool,
152}
153
154/// Whether to allow drag and drop text selection in buffer.
155#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
156pub struct DragAndDropSelection {
157    /// When true, enables drag and drop text selection in buffer.
158    ///
159    /// Default: true
160    pub enabled: bool,
161
162    /// The delay in milliseconds that must elapse before drag and drop is allowed. Otherwise, a new text selection is created.
163    ///
164    /// Default: 300
165    pub delay: u64,
166}
167
168/// Default options for buffer and project search items.
169#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
170pub struct SearchSettings {
171    /// Whether to show the project search button in the status bar.
172    pub button: bool,
173    pub whole_word: bool,
174    pub case_sensitive: bool,
175    pub include_ignored: bool,
176    pub regex: bool,
177}
178
179impl EditorSettings {
180    pub fn jupyter_enabled(cx: &App) -> bool {
181        EditorSettings::get_global(cx).jupyter.enabled
182    }
183}
184
185impl ScrollbarVisibility for EditorSettings {
186    fn visibility(&self, _cx: &App) -> ShowScrollbar {
187        self.scrollbar.show
188    }
189}
190
191impl Settings for EditorSettings {
192    fn from_settings(content: &settings::SettingsContent, _cx: &mut App) -> Self {
193        let editor = content.editor.clone();
194        let scrollbar = editor.scrollbar.unwrap();
195        let minimap = editor.minimap.unwrap();
196        let gutter = editor.gutter.unwrap();
197        let axes = scrollbar.axes.unwrap();
198        let status_bar = editor.status_bar.unwrap();
199        let toolbar = editor.toolbar.unwrap();
200        let search = editor.search.unwrap();
201        let drag_and_drop_selection = editor.drag_and_drop_selection.unwrap();
202        Self {
203            cursor_blink: editor.cursor_blink.unwrap(),
204            cursor_shape: editor.cursor_shape.map(Into::into),
205            current_line_highlight: editor.current_line_highlight.unwrap(),
206            selection_highlight: editor.selection_highlight.unwrap(),
207            rounded_selection: editor.rounded_selection.unwrap(),
208            lsp_highlight_debounce: editor.lsp_highlight_debounce.unwrap(),
209            hover_popover_enabled: editor.hover_popover_enabled.unwrap(),
210            hover_popover_delay: editor.hover_popover_delay.unwrap(),
211            status_bar: StatusBar {
212                active_language_button: status_bar.active_language_button.unwrap(),
213                cursor_position_button: status_bar.cursor_position_button.unwrap(),
214            },
215            toolbar: Toolbar {
216                breadcrumbs: toolbar.breadcrumbs.unwrap(),
217                quick_actions: toolbar.quick_actions.unwrap(),
218                selections_menu: toolbar.selections_menu.unwrap(),
219                agent_review: toolbar.agent_review.unwrap(),
220                code_actions: toolbar.code_actions.unwrap(),
221            },
222            scrollbar: Scrollbar {
223                show: scrollbar.show.map(Into::into).unwrap(),
224                git_diff: scrollbar.git_diff.unwrap(),
225                selected_text: scrollbar.selected_text.unwrap(),
226                selected_symbol: scrollbar.selected_symbol.unwrap(),
227                search_results: scrollbar.search_results.unwrap(),
228                diagnostics: scrollbar.diagnostics.unwrap(),
229                cursors: scrollbar.cursors.unwrap(),
230                axes: ScrollbarAxes {
231                    horizontal: axes.horizontal.unwrap(),
232                    vertical: axes.vertical.unwrap(),
233                },
234            },
235            minimap: Minimap {
236                show: minimap.show.unwrap(),
237                display_in: minimap.display_in.unwrap(),
238                thumb: minimap.thumb.unwrap(),
239                thumb_border: minimap.thumb_border.unwrap(),
240                current_line_highlight: minimap.current_line_highlight,
241                max_width_columns: minimap.max_width_columns.unwrap(),
242            },
243            gutter: Gutter {
244                min_line_number_digits: gutter.min_line_number_digits.unwrap(),
245                line_numbers: gutter.line_numbers.unwrap(),
246                runnables: gutter.runnables.unwrap(),
247                breakpoints: gutter.breakpoints.unwrap(),
248                folds: gutter.folds.unwrap(),
249            },
250            scroll_beyond_last_line: editor.scroll_beyond_last_line.unwrap(),
251            vertical_scroll_margin: editor.vertical_scroll_margin.unwrap(),
252            autoscroll_on_clicks: editor.autoscroll_on_clicks.unwrap(),
253            horizontal_scroll_margin: editor.horizontal_scroll_margin.unwrap(),
254            scroll_sensitivity: editor.scroll_sensitivity.unwrap(),
255            fast_scroll_sensitivity: editor.fast_scroll_sensitivity.unwrap(),
256            relative_line_numbers: editor.relative_line_numbers.unwrap(),
257            seed_search_query_from_cursor: editor.seed_search_query_from_cursor.unwrap(),
258            use_smartcase_search: editor.use_smartcase_search.unwrap(),
259            multi_cursor_modifier: editor.multi_cursor_modifier.unwrap(),
260            redact_private_values: editor.redact_private_values.unwrap(),
261            expand_excerpt_lines: editor.expand_excerpt_lines.unwrap(),
262            excerpt_context_lines: editor.excerpt_context_lines.unwrap(),
263            middle_click_paste: editor.middle_click_paste.unwrap(),
264            double_click_in_multibuffer: editor.double_click_in_multibuffer.unwrap(),
265            search_wrap: editor.search_wrap.unwrap(),
266            search: SearchSettings {
267                button: search.button.unwrap(),
268                whole_word: search.whole_word.unwrap(),
269                case_sensitive: search.case_sensitive.unwrap(),
270                include_ignored: search.include_ignored.unwrap(),
271                regex: search.regex.unwrap(),
272            },
273            auto_signature_help: editor.auto_signature_help.unwrap(),
274            show_signature_help_after_edits: editor.show_signature_help_after_edits.unwrap(),
275            go_to_definition_fallback: editor.go_to_definition_fallback.unwrap(),
276            jupyter: Jupyter {
277                enabled: editor.jupyter.unwrap().enabled.unwrap(),
278            },
279            hide_mouse: editor.hide_mouse,
280            snippet_sort_order: editor.snippet_sort_order.unwrap(),
281            diagnostics_max_severity: editor.diagnostics_max_severity.map(Into::into),
282            inline_code_actions: editor.inline_code_actions.unwrap(),
283            drag_and_drop_selection: DragAndDropSelection {
284                enabled: drag_and_drop_selection.enabled.unwrap(),
285                delay: drag_and_drop_selection.delay.unwrap(),
286            },
287            lsp_document_colors: editor.lsp_document_colors.unwrap(),
288            minimum_contrast_for_highlights: editor.minimum_contrast_for_highlights.unwrap(),
289        }
290    }
291
292    fn import_from_vscode(vscode: &VsCodeSettings, current: &mut SettingsContent) {
293        vscode.enum_setting(
294            "editor.cursorBlinking",
295            &mut current.editor.cursor_blink,
296            |s| match s {
297                "blink" | "phase" | "expand" | "smooth" => Some(true),
298                "solid" => Some(false),
299                _ => None,
300            },
301        );
302        vscode.enum_setting(
303            "editor.cursorStyle",
304            &mut current.editor.cursor_shape,
305            |s| match s {
306                "block" => Some(settings::CursorShape::Block),
307                "block-outline" => Some(settings::CursorShape::Hollow),
308                "line" | "line-thin" => Some(settings::CursorShape::Bar),
309                "underline" | "underline-thin" => Some(settings::CursorShape::Underline),
310                _ => None,
311            },
312        );
313
314        vscode.enum_setting(
315            "editor.renderLineHighlight",
316            &mut current.editor.current_line_highlight,
317            |s| match s {
318                "gutter" => Some(CurrentLineHighlight::Gutter),
319                "line" => Some(CurrentLineHighlight::Line),
320                "all" => Some(CurrentLineHighlight::All),
321                _ => None,
322            },
323        );
324
325        vscode.bool_setting(
326            "editor.selectionHighlight",
327            &mut current.editor.selection_highlight,
328        );
329        vscode.bool_setting(
330            "editor.roundedSelection",
331            &mut current.editor.rounded_selection,
332        );
333        vscode.bool_setting(
334            "editor.hover.enabled",
335            &mut current.editor.hover_popover_enabled,
336        );
337        vscode.u64_setting(
338            "editor.hover.delay",
339            &mut current.editor.hover_popover_delay,
340        );
341
342        let mut gutter = settings::GutterContent::default();
343        vscode.enum_setting(
344            "editor.showFoldingControls",
345            &mut gutter.folds,
346            |s| match s {
347                "always" | "mouseover" => Some(true),
348                "never" => Some(false),
349                _ => None,
350            },
351        );
352        vscode.enum_setting(
353            "editor.lineNumbers",
354            &mut gutter.line_numbers,
355            |s| match s {
356                "on" | "relative" => Some(true),
357                "off" => Some(false),
358                _ => None,
359            },
360        );
361        if let Some(old_gutter) = current.editor.gutter.as_mut() {
362            if gutter.folds.is_some() {
363                old_gutter.folds = gutter.folds
364            }
365            if gutter.line_numbers.is_some() {
366                old_gutter.line_numbers = gutter.line_numbers
367            }
368        } else if gutter != settings::GutterContent::default() {
369            current.editor.gutter = Some(gutter)
370        }
371        if let Some(b) = vscode.read_bool("editor.scrollBeyondLastLine") {
372            current.editor.scroll_beyond_last_line = Some(if b {
373                ScrollBeyondLastLine::OnePage
374            } else {
375                ScrollBeyondLastLine::Off
376            })
377        }
378
379        let mut scrollbar_axes = settings::ScrollbarAxesContent::default();
380        vscode.enum_setting(
381            "editor.scrollbar.horizontal",
382            &mut scrollbar_axes.horizontal,
383            |s| match s {
384                "auto" | "visible" => Some(true),
385                "hidden" => Some(false),
386                _ => None,
387            },
388        );
389        vscode.enum_setting(
390            "editor.scrollbar.vertical",
391            &mut scrollbar_axes.horizontal,
392            |s| match s {
393                "auto" | "visible" => Some(true),
394                "hidden" => Some(false),
395                _ => None,
396            },
397        );
398
399        if scrollbar_axes != settings::ScrollbarAxesContent::default() {
400            let scrollbar_settings = current.editor.scrollbar.get_or_insert_default();
401            let axes_settings = scrollbar_settings.axes.get_or_insert_default();
402
403            if let Some(vertical) = scrollbar_axes.vertical {
404                axes_settings.vertical = Some(vertical);
405            }
406            if let Some(horizontal) = scrollbar_axes.horizontal {
407                axes_settings.horizontal = Some(horizontal);
408            }
409        }
410
411        // TODO: check if this does the int->float conversion?
412        vscode.f32_setting(
413            "editor.cursorSurroundingLines",
414            &mut current.editor.vertical_scroll_margin,
415        );
416        vscode.f32_setting(
417            "editor.mouseWheelScrollSensitivity",
418            &mut current.editor.scroll_sensitivity,
419        );
420        vscode.f32_setting(
421            "editor.fastScrollSensitivity",
422            &mut current.editor.fast_scroll_sensitivity,
423        );
424        if Some("relative") == vscode.read_string("editor.lineNumbers") {
425            current.editor.relative_line_numbers = Some(true);
426        }
427
428        vscode.enum_setting(
429            "editor.find.seedSearchStringFromSelection",
430            &mut current.editor.seed_search_query_from_cursor,
431            |s| match s {
432                "always" => Some(SeedQuerySetting::Always),
433                "selection" => Some(SeedQuerySetting::Selection),
434                "never" => Some(SeedQuerySetting::Never),
435                _ => None,
436            },
437        );
438        vscode.bool_setting("search.smartCase", &mut current.editor.use_smartcase_search);
439        vscode.enum_setting(
440            "editor.multiCursorModifier",
441            &mut current.editor.multi_cursor_modifier,
442            |s| match s {
443                "ctrlCmd" => Some(MultiCursorModifier::CmdOrCtrl),
444                "alt" => Some(MultiCursorModifier::Alt),
445                _ => None,
446            },
447        );
448
449        vscode.bool_setting(
450            "editor.parameterHints.enabled",
451            &mut current.editor.auto_signature_help,
452        );
453        vscode.bool_setting(
454            "editor.parameterHints.enabled",
455            &mut current.editor.show_signature_help_after_edits,
456        );
457
458        if let Some(use_ignored) = vscode.read_bool("search.useIgnoreFiles") {
459            let search = current.editor.search.get_or_insert_default();
460            search.include_ignored = Some(use_ignored);
461        }
462
463        let mut minimap = settings::MinimapContent::default();
464        let minimap_enabled = vscode.read_bool("editor.minimap.enabled").unwrap_or(true);
465        let autohide = vscode.read_bool("editor.minimap.autohide");
466        let mut max_width_columns: Option<u32> = None;
467        vscode.u32_setting("editor.minimap.maxColumn", &mut max_width_columns);
468        if minimap_enabled {
469            if let Some(false) = autohide {
470                minimap.show = Some(ShowMinimap::Always);
471            } else {
472                minimap.show = Some(ShowMinimap::Auto);
473            }
474        } else {
475            minimap.show = Some(ShowMinimap::Never);
476        }
477        if let Some(max_width_columns) = max_width_columns {
478            minimap.max_width_columns = NonZeroU32::new(max_width_columns);
479        }
480
481        vscode.enum_setting(
482            "editor.minimap.showSlider",
483            &mut minimap.thumb,
484            |s| match s {
485                "always" => Some(MinimapThumb::Always),
486                "mouseover" => Some(MinimapThumb::Hover),
487                _ => None,
488            },
489        );
490
491        if minimap != settings::MinimapContent::default() {
492            current.editor.minimap = Some(minimap)
493        }
494    }
495}