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