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}