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