1//! Provides `language`-related settings.
2
3use crate::{File, Language, LanguageServerName};
4use anyhow::Result;
5use collections::{HashMap, HashSet};
6use globset::{Glob, GlobMatcher, GlobSet, GlobSetBuilder};
7use gpui::AppContext;
8use itertools::{Either, Itertools};
9use schemars::{
10 schema::{InstanceType, ObjectValidation, Schema, SchemaObject},
11 JsonSchema,
12};
13use serde::{Deserialize, Serialize};
14use settings::{Settings, SettingsLocation, SettingsSources};
15use std::{num::NonZeroU32, path::Path, sync::Arc};
16use util::serde::default_true;
17
18impl<'a> Into<SettingsLocation<'a>> for &'a dyn File {
19 fn into(self) -> SettingsLocation<'a> {
20 SettingsLocation {
21 worktree_id: self.worktree_id(),
22 path: self.path().as_ref(),
23 }
24 }
25}
26
27/// Initializes the language settings.
28pub fn init(cx: &mut AppContext) {
29 AllLanguageSettings::register(cx);
30}
31
32/// Returns the settings for the specified language from the provided file.
33pub fn language_settings<'a>(
34 language: Option<&Arc<Language>>,
35 file: Option<&Arc<dyn File>>,
36 cx: &'a AppContext,
37) -> &'a LanguageSettings {
38 let language_name = language.map(|l| l.name());
39 all_language_settings(file, cx).language(language_name.as_deref())
40}
41
42/// Returns the settings for all languages from the provided file.
43pub fn all_language_settings<'a>(
44 file: Option<&Arc<dyn File>>,
45 cx: &'a AppContext,
46) -> &'a AllLanguageSettings {
47 let location = file.map(|f| f.as_ref().into());
48 AllLanguageSettings::get(location, cx)
49}
50
51/// The settings for all languages.
52#[derive(Debug, Clone)]
53pub struct AllLanguageSettings {
54 /// The inline completion settings.
55 pub inline_completions: InlineCompletionSettings,
56 defaults: LanguageSettings,
57 languages: HashMap<Arc<str>, LanguageSettings>,
58 pub(crate) file_types: HashMap<Arc<str>, GlobSet>,
59}
60
61/// The settings for a particular language.
62#[derive(Debug, Clone, Deserialize)]
63pub struct LanguageSettings {
64 /// How many columns a tab should occupy.
65 pub tab_size: NonZeroU32,
66 /// Whether to indent lines using tab characters, as opposed to multiple
67 /// spaces.
68 pub hard_tabs: bool,
69 /// How to soft-wrap long lines of text.
70 pub soft_wrap: SoftWrap,
71 /// The column at which to soft-wrap lines, for buffers where soft-wrap
72 /// is enabled.
73 pub preferred_line_length: u32,
74 // Whether to show wrap guides (vertical rulers) in the editor.
75 // Setting this to true will show a guide at the 'preferred_line_length' value
76 // if softwrap is set to 'preferred_line_length', and will show any
77 // additional guides as specified by the 'wrap_guides' setting.
78 pub show_wrap_guides: bool,
79 /// Character counts at which to show wrap guides (vertical rulers) in the editor.
80 pub wrap_guides: Vec<usize>,
81 /// Indent guide related settings.
82 pub indent_guides: IndentGuideSettings,
83 /// Whether or not to perform a buffer format before saving.
84 pub format_on_save: FormatOnSave,
85 /// Whether or not to remove any trailing whitespace from lines of a buffer
86 /// before saving it.
87 pub remove_trailing_whitespace_on_save: bool,
88 /// Whether or not to ensure there's a single newline at the end of a buffer
89 /// when saving it.
90 pub ensure_final_newline_on_save: bool,
91 /// How to perform a buffer format.
92 pub formatter: Formatter,
93 /// Zed's Prettier integration settings.
94 pub prettier: PrettierSettings,
95 /// Whether to use language servers to provide code intelligence.
96 pub enable_language_server: bool,
97 /// The list of language servers to use (or disable) for this language.
98 ///
99 /// This array should consist of language server IDs, as well as the following
100 /// special tokens:
101 /// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
102 /// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
103 pub language_servers: Vec<Arc<str>>,
104 /// Controls whether inline completions are shown immediately (true)
105 /// or manually by triggering `editor::ShowInlineCompletion` (false).
106 pub show_inline_completions: bool,
107 /// Whether to show tabs and spaces in the editor.
108 pub show_whitespaces: ShowWhitespaceSetting,
109 /// Whether to start a new line with a comment when a previous line is a comment as well.
110 pub extend_comment_on_newline: bool,
111 /// Inlay hint related settings.
112 pub inlay_hints: InlayHintSettings,
113 /// Whether to automatically close brackets.
114 pub use_autoclose: bool,
115 /// Whether to automatically surround text with brackets.
116 pub use_auto_surround: bool,
117 // Controls how the editor handles the autoclosed characters.
118 pub always_treat_brackets_as_autoclosed: bool,
119 /// Which code actions to run on save
120 pub code_actions_on_format: HashMap<String, bool>,
121 /// Whether to perform linked edits
122 pub linked_edits: bool,
123 /// Task configuration for this language.
124 pub tasks: LanguageTaskConfig,
125}
126
127impl LanguageSettings {
128 /// A token representing the rest of the available language servers.
129 const REST_OF_LANGUAGE_SERVERS: &'static str = "...";
130
131 /// Returns the customized list of language servers from the list of
132 /// available language servers.
133 pub fn customized_language_servers(
134 &self,
135 available_language_servers: &[LanguageServerName],
136 ) -> Vec<LanguageServerName> {
137 Self::resolve_language_servers(&self.language_servers, available_language_servers)
138 }
139
140 pub(crate) fn resolve_language_servers(
141 configured_language_servers: &[Arc<str>],
142 available_language_servers: &[LanguageServerName],
143 ) -> Vec<LanguageServerName> {
144 let (disabled_language_servers, enabled_language_servers): (Vec<Arc<str>>, Vec<Arc<str>>) =
145 configured_language_servers.iter().partition_map(
146 |language_server| match language_server.strip_prefix('!') {
147 Some(disabled) => Either::Left(disabled.into()),
148 None => Either::Right(language_server.clone()),
149 },
150 );
151
152 let rest = available_language_servers
153 .into_iter()
154 .filter(|&available_language_server| {
155 !disabled_language_servers.contains(&&available_language_server.0)
156 && !enabled_language_servers.contains(&&available_language_server.0)
157 })
158 .cloned()
159 .collect::<Vec<_>>();
160
161 enabled_language_servers
162 .into_iter()
163 .flat_map(|language_server| {
164 if language_server.as_ref() == Self::REST_OF_LANGUAGE_SERVERS {
165 rest.clone()
166 } else {
167 vec![LanguageServerName(language_server.clone())]
168 }
169 })
170 .collect::<Vec<_>>()
171 }
172}
173
174/// The provider that supplies inline completions.
175#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize, JsonSchema)]
176#[serde(rename_all = "snake_case")]
177pub enum InlineCompletionProvider {
178 None,
179 #[default]
180 Copilot,
181 Supermaven,
182}
183
184/// The settings for inline completions, such as [GitHub Copilot](https://github.com/features/copilot)
185/// or [Supermaven](https://supermaven.com).
186#[derive(Clone, Debug, Default)]
187pub struct InlineCompletionSettings {
188 /// The provider that supplies inline completions.
189 pub provider: InlineCompletionProvider,
190 /// A list of globs representing files that inline completions should be disabled for.
191 pub disabled_globs: Vec<GlobMatcher>,
192}
193
194/// The settings for all languages.
195#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
196pub struct AllLanguageSettingsContent {
197 /// The settings for enabling/disabling features.
198 #[serde(default)]
199 pub features: Option<FeaturesContent>,
200 /// The inline completion settings.
201 #[serde(default)]
202 pub inline_completions: Option<InlineCompletionSettingsContent>,
203 /// The default language settings.
204 #[serde(flatten)]
205 pub defaults: LanguageSettingsContent,
206 /// The settings for individual languages.
207 #[serde(default)]
208 pub languages: HashMap<Arc<str>, LanguageSettingsContent>,
209 /// Settings for associating file extensions and filenames
210 /// with languages.
211 #[serde(default)]
212 pub file_types: HashMap<Arc<str>, Vec<String>>,
213}
214
215/// The settings for a particular language.
216#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
217pub struct LanguageSettingsContent {
218 /// How many columns a tab should occupy.
219 ///
220 /// Default: 4
221 #[serde(default)]
222 pub tab_size: Option<NonZeroU32>,
223 /// Whether to indent lines using tab characters, as opposed to multiple
224 /// spaces.
225 ///
226 /// Default: false
227 #[serde(default)]
228 pub hard_tabs: Option<bool>,
229 /// How to soft-wrap long lines of text.
230 ///
231 /// Default: none
232 #[serde(default)]
233 pub soft_wrap: Option<SoftWrap>,
234 /// The column at which to soft-wrap lines, for buffers where soft-wrap
235 /// is enabled.
236 ///
237 /// Default: 80
238 #[serde(default)]
239 pub preferred_line_length: Option<u32>,
240 /// Whether to show wrap guides in the editor. Setting this to true will
241 /// show a guide at the 'preferred_line_length' value if softwrap is set to
242 /// 'preferred_line_length', and will show any additional guides as specified
243 /// by the 'wrap_guides' setting.
244 ///
245 /// Default: true
246 #[serde(default)]
247 pub show_wrap_guides: Option<bool>,
248 /// Character counts at which to show wrap guides in the editor.
249 ///
250 /// Default: []
251 #[serde(default)]
252 pub wrap_guides: Option<Vec<usize>>,
253 /// Indent guide related settings.
254 #[serde(default)]
255 pub indent_guides: Option<IndentGuideSettings>,
256 /// Whether or not to perform a buffer format before saving.
257 ///
258 /// Default: on
259 #[serde(default)]
260 pub format_on_save: Option<FormatOnSave>,
261 /// Whether or not to remove any trailing whitespace from lines of a buffer
262 /// before saving it.
263 ///
264 /// Default: true
265 #[serde(default)]
266 pub remove_trailing_whitespace_on_save: Option<bool>,
267 /// Whether or not to ensure there's a single newline at the end of a buffer
268 /// when saving it.
269 ///
270 /// Default: true
271 #[serde(default)]
272 pub ensure_final_newline_on_save: Option<bool>,
273 /// How to perform a buffer format.
274 ///
275 /// Default: auto
276 #[serde(default)]
277 pub formatter: Option<Formatter>,
278 /// Zed's Prettier integration settings.
279 /// Allows to enable/disable formatting with Prettier
280 /// and configure default Prettier, used when no project-level Prettier installation is found.
281 ///
282 /// Default: off
283 #[serde(default)]
284 pub prettier: Option<PrettierSettings>,
285 /// Whether to use language servers to provide code intelligence.
286 ///
287 /// Default: true
288 #[serde(default)]
289 pub enable_language_server: Option<bool>,
290 /// The list of language servers to use (or disable) for this language.
291 ///
292 /// This array should consist of language server IDs, as well as the following
293 /// special tokens:
294 /// - `"!<language_server_id>"` - A language server ID prefixed with a `!` will be disabled.
295 /// - `"..."` - A placeholder to refer to the **rest** of the registered language servers for this language.
296 ///
297 /// Default: ["..."]
298 #[serde(default)]
299 pub language_servers: Option<Vec<Arc<str>>>,
300 /// Controls whether inline completions are shown immediately (true)
301 /// or manually by triggering `editor::ShowInlineCompletion` (false).
302 ///
303 /// Default: true
304 #[serde(default)]
305 pub show_inline_completions: Option<bool>,
306 /// Whether to show tabs and spaces in the editor.
307 #[serde(default)]
308 pub show_whitespaces: Option<ShowWhitespaceSetting>,
309 /// Whether to start a new line with a comment when a previous line is a comment as well.
310 ///
311 /// Default: true
312 #[serde(default)]
313 pub extend_comment_on_newline: Option<bool>,
314 /// Inlay hint related settings.
315 #[serde(default)]
316 pub inlay_hints: Option<InlayHintSettings>,
317 /// Whether to automatically type closing characters for you. For example,
318 /// when you type (, Zed will automatically add a closing ) at the correct position.
319 ///
320 /// Default: true
321 pub use_autoclose: Option<bool>,
322 /// Whether to automatically surround text with characters for you. For example,
323 /// when you select text and type (, Zed will automatically surround text with ().
324 ///
325 /// Default: true
326 pub use_auto_surround: Option<bool>,
327 // Controls how the editor handles the autoclosed characters.
328 // When set to `false`(default), skipping over and auto-removing of the closing characters
329 // happen only for auto-inserted characters.
330 // Otherwise(when `true`), the closing characters are always skipped over and auto-removed
331 // no matter how they were inserted.
332 ///
333 /// Default: false
334 pub always_treat_brackets_as_autoclosed: Option<bool>,
335 /// Which code actions to run on save after the formatter.
336 /// These are not run if formatting is off.
337 ///
338 /// Default: {} (or {"source.organizeImports": true} for Go).
339 pub code_actions_on_format: Option<HashMap<String, bool>>,
340 /// Whether to perform linked edits of associated ranges, if the language server supports it.
341 /// For example, when editing opening <html> tag, the contents of the closing </html> tag will be edited as well.
342 ///
343 /// Default: true
344 pub linked_edits: Option<bool>,
345 /// Task configuration for this language.
346 ///
347 /// Default: {}
348 pub tasks: Option<LanguageTaskConfig>,
349}
350
351/// The contents of the inline completion settings.
352#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema, PartialEq)]
353pub struct InlineCompletionSettingsContent {
354 /// A list of globs representing files that inline completions should be disabled for.
355 #[serde(default)]
356 pub disabled_globs: Option<Vec<String>>,
357}
358
359/// The settings for enabling/disabling features.
360#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema)]
361#[serde(rename_all = "snake_case")]
362pub struct FeaturesContent {
363 /// Whether the GitHub Copilot feature is enabled.
364 pub copilot: Option<bool>,
365 /// Determines which inline completion provider to use.
366 pub inline_completion_provider: Option<InlineCompletionProvider>,
367}
368
369/// Controls the soft-wrapping behavior in the editor.
370#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
371#[serde(rename_all = "snake_case")]
372pub enum SoftWrap {
373 /// Do not soft wrap.
374 None,
375 /// Prefer a single line generally, unless an overly long line is encountered.
376 PreferLine,
377 /// Soft wrap lines that overflow the editor
378 EditorWidth,
379 /// Soft wrap lines at the preferred line length
380 PreferredLineLength,
381}
382
383/// Controls the behavior of formatting files when they are saved.
384#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
385#[serde(rename_all = "snake_case")]
386pub enum FormatOnSave {
387 /// Files should be formatted on save.
388 On,
389 /// Files should not be formatted on save.
390 Off,
391 /// Files should be formatted using the current language server.
392 LanguageServer,
393 /// The external program to use to format the files on save.
394 External {
395 /// The external program to run.
396 command: Arc<str>,
397 /// The arguments to pass to the program.
398 arguments: Arc<[String]>,
399 },
400 /// Files should be formatted using code actions executed by language servers.
401 CodeActions(HashMap<String, bool>),
402}
403
404/// Controls how whitespace should be displayedin the editor.
405#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
406#[serde(rename_all = "snake_case")]
407pub enum ShowWhitespaceSetting {
408 /// Draw whitespace only for the selected text.
409 Selection,
410 /// Do not draw any tabs or spaces.
411 None,
412 /// Draw all invisible symbols.
413 All,
414 /// Draw whitespaces at boundaries only.
415 ///
416 /// For a whitespace to be on a boundary, any of the following conditions need to be met:
417 /// - It is a tab
418 /// - It is adjacent to an edge (start or end)
419 /// - It is adjacent to a whitespace (left or right)
420 Boundary,
421}
422
423/// Controls which formatter should be used when formatting code.
424#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
425#[serde(rename_all = "snake_case")]
426pub enum Formatter {
427 /// Format files using Zed's Prettier integration (if applicable),
428 /// or falling back to formatting via language server.
429 #[default]
430 Auto,
431 /// Format code using the current language server.
432 LanguageServer,
433 /// Format code using Zed's Prettier integration.
434 Prettier,
435 /// Format code using an external command.
436 External {
437 /// The external program to run.
438 command: Arc<str>,
439 /// The arguments to pass to the program.
440 arguments: Arc<[String]>,
441 },
442 /// Files should be formatted using code actions executed by language servers.
443 CodeActions(HashMap<String, bool>),
444}
445
446/// The settings for indent guides.
447#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
448pub struct IndentGuideSettings {
449 /// Whether to display indent guides in the editor.
450 ///
451 /// Default: true
452 #[serde(default = "default_true")]
453 pub enabled: bool,
454 /// The width of the indent guides in pixels, between 1 and 10.
455 ///
456 /// Default: 1
457 #[serde(default = "line_width")]
458 pub line_width: u32,
459 /// The width of the active indent guide in pixels, between 1 and 10.
460 ///
461 /// Default: 1
462 #[serde(default = "active_line_width")]
463 pub active_line_width: u32,
464 /// Determines how indent guides are colored.
465 ///
466 /// Default: Fixed
467 #[serde(default)]
468 pub coloring: IndentGuideColoring,
469 /// Determines how indent guide backgrounds are colored.
470 ///
471 /// Default: Disabled
472 #[serde(default)]
473 pub background_coloring: IndentGuideBackgroundColoring,
474}
475
476fn line_width() -> u32 {
477 1
478}
479
480fn active_line_width() -> u32 {
481 line_width()
482}
483
484/// Determines how indent guides are colored.
485#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
486#[serde(rename_all = "snake_case")]
487pub enum IndentGuideColoring {
488 /// Do not render any lines for indent guides.
489 Disabled,
490 /// Use the same color for all indentation levels.
491 #[default]
492 Fixed,
493 /// Use a different color for each indentation level.
494 IndentAware,
495}
496
497/// Determines how indent guide backgrounds are colored.
498#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
499#[serde(rename_all = "snake_case")]
500pub enum IndentGuideBackgroundColoring {
501 /// Do not render any background for indent guides.
502 #[default]
503 Disabled,
504 /// Use a different color for each indentation level.
505 IndentAware,
506}
507
508/// The settings for inlay hints.
509#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)]
510pub struct InlayHintSettings {
511 /// Global switch to toggle hints on and off.
512 ///
513 /// Default: false
514 #[serde(default)]
515 pub enabled: bool,
516 /// Whether type hints should be shown.
517 ///
518 /// Default: true
519 #[serde(default = "default_true")]
520 pub show_type_hints: bool,
521 /// Whether parameter hints should be shown.
522 ///
523 /// Default: true
524 #[serde(default = "default_true")]
525 pub show_parameter_hints: bool,
526 /// Whether other hints should be shown.
527 ///
528 /// Default: true
529 #[serde(default = "default_true")]
530 pub show_other_hints: bool,
531 /// Whether or not to debounce inlay hints updates after buffer edits.
532 ///
533 /// Set to 0 to disable debouncing.
534 ///
535 /// Default: 700
536 #[serde(default = "edit_debounce_ms")]
537 pub edit_debounce_ms: u64,
538 /// Whether or not to debounce inlay hints updates after buffer scrolls.
539 ///
540 /// Set to 0 to disable debouncing.
541 ///
542 /// Default: 50
543 #[serde(default = "scroll_debounce_ms")]
544 pub scroll_debounce_ms: u64,
545}
546
547fn edit_debounce_ms() -> u64 {
548 700
549}
550
551fn scroll_debounce_ms() -> u64 {
552 50
553}
554
555/// The task settings for a particular language.
556#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, JsonSchema)]
557pub struct LanguageTaskConfig {
558 /// Extra task variables to set for a particular language.
559 pub variables: HashMap<String, String>,
560}
561
562impl InlayHintSettings {
563 /// Returns the kinds of inlay hints that are enabled based on the settings.
564 pub fn enabled_inlay_hint_kinds(&self) -> HashSet<Option<InlayHintKind>> {
565 let mut kinds = HashSet::default();
566 if self.show_type_hints {
567 kinds.insert(Some(InlayHintKind::Type));
568 }
569 if self.show_parameter_hints {
570 kinds.insert(Some(InlayHintKind::Parameter));
571 }
572 if self.show_other_hints {
573 kinds.insert(None);
574 }
575 kinds
576 }
577}
578
579impl AllLanguageSettings {
580 /// Returns the [`LanguageSettings`] for the language with the specified name.
581 pub fn language<'a>(&'a self, language_name: Option<&str>) -> &'a LanguageSettings {
582 if let Some(name) = language_name {
583 if let Some(overrides) = self.languages.get(name) {
584 return overrides;
585 }
586 }
587 &self.defaults
588 }
589
590 /// Returns whether inline completions are enabled for the given path.
591 pub fn inline_completions_enabled_for_path(&self, path: &Path) -> bool {
592 !self
593 .inline_completions
594 .disabled_globs
595 .iter()
596 .any(|glob| glob.is_match(path))
597 }
598
599 /// Returns whether inline completions are enabled for the given language and path.
600 pub fn inline_completions_enabled(
601 &self,
602 language: Option<&Arc<Language>>,
603 path: Option<&Path>,
604 ) -> bool {
605 if let Some(path) = path {
606 if !self.inline_completions_enabled_for_path(path) {
607 return false;
608 }
609 }
610
611 self.language(language.map(|l| l.name()).as_deref())
612 .show_inline_completions
613 }
614}
615
616/// The kind of an inlay hint.
617#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
618pub enum InlayHintKind {
619 /// An inlay hint for a type.
620 Type,
621 /// An inlay hint for a parameter.
622 Parameter,
623}
624
625impl InlayHintKind {
626 /// Returns the [`InlayHintKind`] from the given name.
627 ///
628 /// Returns `None` if `name` does not match any of the expected
629 /// string representations.
630 pub fn from_name(name: &str) -> Option<Self> {
631 match name {
632 "type" => Some(InlayHintKind::Type),
633 "parameter" => Some(InlayHintKind::Parameter),
634 _ => None,
635 }
636 }
637
638 /// Returns the name of this [`InlayHintKind`].
639 pub fn name(&self) -> &'static str {
640 match self {
641 InlayHintKind::Type => "type",
642 InlayHintKind::Parameter => "parameter",
643 }
644 }
645}
646
647impl settings::Settings for AllLanguageSettings {
648 const KEY: Option<&'static str> = None;
649
650 type FileContent = AllLanguageSettingsContent;
651
652 fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
653 let default_value = sources.default;
654
655 // A default is provided for all settings.
656 let mut defaults: LanguageSettings =
657 serde_json::from_value(serde_json::to_value(&default_value.defaults)?)?;
658
659 let mut languages = HashMap::default();
660 for (language_name, settings) in &default_value.languages {
661 let mut language_settings = defaults.clone();
662 merge_settings(&mut language_settings, settings);
663 languages.insert(language_name.clone(), language_settings);
664 }
665
666 let mut copilot_enabled = default_value.features.as_ref().and_then(|f| f.copilot);
667 let mut inline_completion_provider = default_value
668 .features
669 .as_ref()
670 .and_then(|f| f.inline_completion_provider);
671 let mut completion_globs = default_value
672 .inline_completions
673 .as_ref()
674 .and_then(|c| c.disabled_globs.as_ref())
675 .ok_or_else(Self::missing_default)?;
676
677 let mut file_types: HashMap<Arc<str>, GlobSet> = HashMap::default();
678
679 for (language, suffixes) in &default_value.file_types {
680 let mut builder = GlobSetBuilder::new();
681
682 for suffix in suffixes {
683 builder.add(Glob::new(suffix)?);
684 }
685
686 file_types.insert(language.clone(), builder.build()?);
687 }
688
689 for user_settings in sources.customizations() {
690 if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) {
691 copilot_enabled = Some(copilot);
692 }
693 if let Some(provider) = user_settings
694 .features
695 .as_ref()
696 .and_then(|f| f.inline_completion_provider)
697 {
698 inline_completion_provider = Some(provider);
699 }
700 if let Some(globs) = user_settings
701 .inline_completions
702 .as_ref()
703 .and_then(|f| f.disabled_globs.as_ref())
704 {
705 completion_globs = globs;
706 }
707
708 // A user's global settings override the default global settings and
709 // all default language-specific settings.
710 merge_settings(&mut defaults, &user_settings.defaults);
711 for language_settings in languages.values_mut() {
712 merge_settings(language_settings, &user_settings.defaults);
713 }
714
715 // A user's language-specific settings override default language-specific settings.
716 for (language_name, user_language_settings) in &user_settings.languages {
717 merge_settings(
718 languages
719 .entry(language_name.clone())
720 .or_insert_with(|| defaults.clone()),
721 user_language_settings,
722 );
723 }
724
725 for (language, suffixes) in &user_settings.file_types {
726 let mut builder = GlobSetBuilder::new();
727
728 let default_value = default_value.file_types.get(&language.clone());
729
730 // Merge the default value with the user's value.
731 if let Some(suffixes) = default_value {
732 for suffix in suffixes {
733 builder.add(Glob::new(suffix)?);
734 }
735 }
736
737 for suffix in suffixes {
738 builder.add(Glob::new(suffix)?);
739 }
740
741 file_types.insert(language.clone(), builder.build()?);
742 }
743 }
744
745 Ok(Self {
746 inline_completions: InlineCompletionSettings {
747 provider: if let Some(provider) = inline_completion_provider {
748 provider
749 } else if copilot_enabled.unwrap_or(true) {
750 InlineCompletionProvider::Copilot
751 } else {
752 InlineCompletionProvider::None
753 },
754 disabled_globs: completion_globs
755 .iter()
756 .filter_map(|g| Some(globset::Glob::new(g).ok()?.compile_matcher()))
757 .collect(),
758 },
759 defaults,
760 languages,
761 file_types,
762 })
763 }
764
765 fn json_schema(
766 generator: &mut schemars::gen::SchemaGenerator,
767 params: &settings::SettingsJsonSchemaParams,
768 _: &AppContext,
769 ) -> schemars::schema::RootSchema {
770 let mut root_schema = generator.root_schema_for::<Self::FileContent>();
771
772 // Create a schema for a 'languages overrides' object, associating editor
773 // settings with specific languages.
774 assert!(root_schema
775 .definitions
776 .contains_key("LanguageSettingsContent"));
777
778 let languages_object_schema = SchemaObject {
779 instance_type: Some(InstanceType::Object.into()),
780 object: Some(Box::new(ObjectValidation {
781 properties: params
782 .language_names
783 .iter()
784 .map(|name| {
785 (
786 name.clone(),
787 Schema::new_ref("#/definitions/LanguageSettingsContent".into()),
788 )
789 })
790 .collect(),
791 ..Default::default()
792 })),
793 ..Default::default()
794 };
795
796 root_schema
797 .definitions
798 .extend([("Languages".into(), languages_object_schema.into())]);
799
800 root_schema
801 .schema
802 .object
803 .as_mut()
804 .unwrap()
805 .properties
806 .extend([(
807 "languages".to_owned(),
808 Schema::new_ref("#/definitions/Languages".into()),
809 )]);
810
811 root_schema
812 }
813}
814
815fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) {
816 fn merge<T>(target: &mut T, value: Option<T>) {
817 if let Some(value) = value {
818 *target = value;
819 }
820 }
821
822 merge(&mut settings.tab_size, src.tab_size);
823 merge(&mut settings.hard_tabs, src.hard_tabs);
824 merge(&mut settings.soft_wrap, src.soft_wrap);
825 merge(&mut settings.use_autoclose, src.use_autoclose);
826 merge(&mut settings.use_auto_surround, src.use_auto_surround);
827 merge(
828 &mut settings.always_treat_brackets_as_autoclosed,
829 src.always_treat_brackets_as_autoclosed,
830 );
831 merge(&mut settings.show_wrap_guides, src.show_wrap_guides);
832 merge(&mut settings.wrap_guides, src.wrap_guides.clone());
833 merge(&mut settings.indent_guides, src.indent_guides);
834 merge(
835 &mut settings.code_actions_on_format,
836 src.code_actions_on_format.clone(),
837 );
838 merge(&mut settings.linked_edits, src.linked_edits);
839 merge(&mut settings.tasks, src.tasks.clone());
840
841 merge(
842 &mut settings.preferred_line_length,
843 src.preferred_line_length,
844 );
845 merge(&mut settings.formatter, src.formatter.clone());
846 merge(&mut settings.prettier, src.prettier.clone());
847 merge(&mut settings.format_on_save, src.format_on_save.clone());
848 merge(
849 &mut settings.remove_trailing_whitespace_on_save,
850 src.remove_trailing_whitespace_on_save,
851 );
852 merge(
853 &mut settings.ensure_final_newline_on_save,
854 src.ensure_final_newline_on_save,
855 );
856 merge(
857 &mut settings.enable_language_server,
858 src.enable_language_server,
859 );
860 merge(&mut settings.language_servers, src.language_servers.clone());
861 merge(
862 &mut settings.show_inline_completions,
863 src.show_inline_completions,
864 );
865 merge(&mut settings.show_whitespaces, src.show_whitespaces);
866 merge(
867 &mut settings.extend_comment_on_newline,
868 src.extend_comment_on_newline,
869 );
870 merge(&mut settings.inlay_hints, src.inlay_hints);
871}
872
873/// Allows to enable/disable formatting with Prettier
874/// and configure default Prettier, used when no project-level Prettier installation is found.
875/// Prettier formatting is disabled by default.
876#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
877pub struct PrettierSettings {
878 /// Enables or disables formatting with Prettier for a given language.
879 #[serde(default)]
880 pub allowed: bool,
881
882 /// Forces Prettier integration to use a specific parser name when formatting files with the language.
883 #[serde(default)]
884 pub parser: Option<String>,
885
886 /// Forces Prettier integration to use specific plugins when formatting files with the language.
887 /// The default Prettier will be installed with these plugins.
888 #[serde(default)]
889 pub plugins: HashSet<String>,
890
891 /// Default Prettier options, in the format as in package.json section for Prettier.
892 /// If project installs Prettier via its package.json, these options will be ignored.
893 #[serde(flatten)]
894 pub options: HashMap<String, serde_json::Value>,
895}
896
897#[cfg(test)]
898mod tests {
899 use super::*;
900
901 #[test]
902 pub fn test_resolve_language_servers() {
903 fn language_server_names(names: &[&str]) -> Vec<LanguageServerName> {
904 names
905 .into_iter()
906 .copied()
907 .map(|name| LanguageServerName(name.into()))
908 .collect::<Vec<_>>()
909 }
910
911 let available_language_servers = language_server_names(&[
912 "typescript-language-server",
913 "biome",
914 "deno",
915 "eslint",
916 "tailwind",
917 ]);
918
919 // A value of just `["..."]` is the same as taking all of the available language servers.
920 assert_eq!(
921 LanguageSettings::resolve_language_servers(
922 &[LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()],
923 &available_language_servers,
924 ),
925 available_language_servers
926 );
927
928 // Referencing one of the available language servers will change its order.
929 assert_eq!(
930 LanguageSettings::resolve_language_servers(
931 &[
932 "biome".into(),
933 LanguageSettings::REST_OF_LANGUAGE_SERVERS.into(),
934 "deno".into()
935 ],
936 &available_language_servers
937 ),
938 language_server_names(&[
939 "biome",
940 "typescript-language-server",
941 "eslint",
942 "tailwind",
943 "deno",
944 ])
945 );
946
947 // Negating an available language server removes it from the list.
948 assert_eq!(
949 LanguageSettings::resolve_language_servers(
950 &[
951 "deno".into(),
952 "!typescript-language-server".into(),
953 "!biome".into(),
954 LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()
955 ],
956 &available_language_servers
957 ),
958 language_server_names(&["deno", "eslint", "tailwind"])
959 );
960
961 // Adding a language server not in the list of available language servers adds it to the list.
962 assert_eq!(
963 LanguageSettings::resolve_language_servers(
964 &[
965 "my-cool-language-server".into(),
966 LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()
967 ],
968 &available_language_servers
969 ),
970 language_server_names(&[
971 "my-cool-language-server",
972 "typescript-language-server",
973 "biome",
974 "deno",
975 "eslint",
976 "tailwind",
977 ])
978 );
979 }
980}