1//! Provides `language`-related settings.
2
3use crate::{File, Language, 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_deref())
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<Arc<str>, 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<Arc<str>, 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 or not to debounce inlay hints updates after buffer edits.
745 ///
746 /// Set to 0 to disable debouncing.
747 ///
748 /// Default: 700
749 #[serde(default = "edit_debounce_ms")]
750 pub edit_debounce_ms: u64,
751 /// Whether or not to debounce inlay hints updates after buffer scrolls.
752 ///
753 /// Set to 0 to disable debouncing.
754 ///
755 /// Default: 50
756 #[serde(default = "scroll_debounce_ms")]
757 pub scroll_debounce_ms: u64,
758}
759
760fn edit_debounce_ms() -> u64 {
761 700
762}
763
764fn scroll_debounce_ms() -> u64 {
765 50
766}
767
768/// The task settings for a particular language.
769#[derive(Debug, Clone, Deserialize, PartialEq, Serialize, JsonSchema)]
770pub struct LanguageTaskConfig {
771 /// Extra task variables to set for a particular language.
772 pub variables: HashMap<String, String>,
773}
774
775impl InlayHintSettings {
776 /// Returns the kinds of inlay hints that are enabled based on the settings.
777 pub fn enabled_inlay_hint_kinds(&self) -> HashSet<Option<InlayHintKind>> {
778 let mut kinds = HashSet::default();
779 if self.show_type_hints {
780 kinds.insert(Some(InlayHintKind::Type));
781 }
782 if self.show_parameter_hints {
783 kinds.insert(Some(InlayHintKind::Parameter));
784 }
785 if self.show_other_hints {
786 kinds.insert(None);
787 }
788 kinds
789 }
790}
791
792impl AllLanguageSettings {
793 /// Returns the [`LanguageSettings`] for the language with the specified name.
794 pub fn language<'a>(&'a self, language_name: Option<&str>) -> &'a LanguageSettings {
795 if let Some(name) = language_name {
796 if let Some(overrides) = self.languages.get(name) {
797 return overrides;
798 }
799 }
800 &self.defaults
801 }
802
803 /// Returns whether inline completions are enabled for the given path.
804 pub fn inline_completions_enabled_for_path(&self, path: &Path) -> bool {
805 !self
806 .inline_completions
807 .disabled_globs
808 .iter()
809 .any(|glob| glob.is_match(path))
810 }
811
812 /// Returns whether inline completions are enabled for the given language and path.
813 pub fn inline_completions_enabled(
814 &self,
815 language: Option<&Arc<Language>>,
816 path: Option<&Path>,
817 ) -> bool {
818 if let Some(path) = path {
819 if !self.inline_completions_enabled_for_path(path) {
820 return false;
821 }
822 }
823
824 self.language(language.map(|l| l.name()).as_deref())
825 .show_inline_completions
826 }
827}
828
829/// The kind of an inlay hint.
830#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
831pub enum InlayHintKind {
832 /// An inlay hint for a type.
833 Type,
834 /// An inlay hint for a parameter.
835 Parameter,
836}
837
838impl InlayHintKind {
839 /// Returns the [`InlayHintKind`] from the given name.
840 ///
841 /// Returns `None` if `name` does not match any of the expected
842 /// string representations.
843 pub fn from_name(name: &str) -> Option<Self> {
844 match name {
845 "type" => Some(InlayHintKind::Type),
846 "parameter" => Some(InlayHintKind::Parameter),
847 _ => None,
848 }
849 }
850
851 /// Returns the name of this [`InlayHintKind`].
852 pub fn name(&self) -> &'static str {
853 match self {
854 InlayHintKind::Type => "type",
855 InlayHintKind::Parameter => "parameter",
856 }
857 }
858}
859
860impl settings::Settings for AllLanguageSettings {
861 const KEY: Option<&'static str> = None;
862
863 type FileContent = AllLanguageSettingsContent;
864
865 fn load(sources: SettingsSources<Self::FileContent>, _: &mut AppContext) -> Result<Self> {
866 let default_value = sources.default;
867
868 // A default is provided for all settings.
869 let mut defaults: LanguageSettings =
870 serde_json::from_value(serde_json::to_value(&default_value.defaults)?)?;
871
872 let mut languages = HashMap::default();
873 for (language_name, settings) in &default_value.languages {
874 let mut language_settings = defaults.clone();
875 merge_settings(&mut language_settings, settings);
876 languages.insert(language_name.clone(), language_settings);
877 }
878
879 let mut copilot_enabled = default_value.features.as_ref().and_then(|f| f.copilot);
880 let mut inline_completion_provider = default_value
881 .features
882 .as_ref()
883 .and_then(|f| f.inline_completion_provider);
884 let mut completion_globs = default_value
885 .inline_completions
886 .as_ref()
887 .and_then(|c| c.disabled_globs.as_ref())
888 .ok_or_else(Self::missing_default)?;
889
890 let mut file_types: HashMap<Arc<str>, GlobSet> = HashMap::default();
891
892 for (language, suffixes) in &default_value.file_types {
893 let mut builder = GlobSetBuilder::new();
894
895 for suffix in suffixes {
896 builder.add(Glob::new(suffix)?);
897 }
898
899 file_types.insert(language.clone(), builder.build()?);
900 }
901
902 for user_settings in sources.customizations() {
903 if let Some(copilot) = user_settings.features.as_ref().and_then(|f| f.copilot) {
904 copilot_enabled = Some(copilot);
905 }
906 if let Some(provider) = user_settings
907 .features
908 .as_ref()
909 .and_then(|f| f.inline_completion_provider)
910 {
911 inline_completion_provider = Some(provider);
912 }
913 if let Some(globs) = user_settings
914 .inline_completions
915 .as_ref()
916 .and_then(|f| f.disabled_globs.as_ref())
917 {
918 completion_globs = globs;
919 }
920
921 // A user's global settings override the default global settings and
922 // all default language-specific settings.
923 merge_settings(&mut defaults, &user_settings.defaults);
924 for language_settings in languages.values_mut() {
925 merge_settings(language_settings, &user_settings.defaults);
926 }
927
928 // A user's language-specific settings override default language-specific settings.
929 for (language_name, user_language_settings) in &user_settings.languages {
930 merge_settings(
931 languages
932 .entry(language_name.clone())
933 .or_insert_with(|| defaults.clone()),
934 user_language_settings,
935 );
936 }
937
938 for (language, suffixes) in &user_settings.file_types {
939 let mut builder = GlobSetBuilder::new();
940
941 let default_value = default_value.file_types.get(&language.clone());
942
943 // Merge the default value with the user's value.
944 if let Some(suffixes) = default_value {
945 for suffix in suffixes {
946 builder.add(Glob::new(suffix)?);
947 }
948 }
949
950 for suffix in suffixes {
951 builder.add(Glob::new(suffix)?);
952 }
953
954 file_types.insert(language.clone(), builder.build()?);
955 }
956 }
957
958 Ok(Self {
959 inline_completions: InlineCompletionSettings {
960 provider: if let Some(provider) = inline_completion_provider {
961 provider
962 } else if copilot_enabled.unwrap_or(true) {
963 InlineCompletionProvider::Copilot
964 } else {
965 InlineCompletionProvider::None
966 },
967 disabled_globs: completion_globs
968 .iter()
969 .filter_map(|g| Some(globset::Glob::new(g).ok()?.compile_matcher()))
970 .collect(),
971 },
972 defaults,
973 languages,
974 file_types,
975 })
976 }
977
978 fn json_schema(
979 generator: &mut schemars::gen::SchemaGenerator,
980 params: &settings::SettingsJsonSchemaParams,
981 _: &AppContext,
982 ) -> schemars::schema::RootSchema {
983 let mut root_schema = generator.root_schema_for::<Self::FileContent>();
984
985 // Create a schema for a 'languages overrides' object, associating editor
986 // settings with specific languages.
987 assert!(root_schema
988 .definitions
989 .contains_key("LanguageSettingsContent"));
990
991 let languages_object_schema = SchemaObject {
992 instance_type: Some(InstanceType::Object.into()),
993 object: Some(Box::new(ObjectValidation {
994 properties: params
995 .language_names
996 .iter()
997 .map(|name| {
998 (
999 name.clone(),
1000 Schema::new_ref("#/definitions/LanguageSettingsContent".into()),
1001 )
1002 })
1003 .collect(),
1004 ..Default::default()
1005 })),
1006 ..Default::default()
1007 };
1008
1009 root_schema
1010 .definitions
1011 .extend([("Languages".into(), languages_object_schema.into())]);
1012
1013 add_references_to_properties(
1014 &mut root_schema,
1015 &[("languages", "#/definitions/Languages")],
1016 );
1017
1018 root_schema
1019 }
1020}
1021
1022fn merge_settings(settings: &mut LanguageSettings, src: &LanguageSettingsContent) {
1023 fn merge<T>(target: &mut T, value: Option<T>) {
1024 if let Some(value) = value {
1025 *target = value;
1026 }
1027 }
1028
1029 merge(&mut settings.tab_size, src.tab_size);
1030 merge(&mut settings.hard_tabs, src.hard_tabs);
1031 merge(&mut settings.soft_wrap, src.soft_wrap);
1032 merge(&mut settings.use_autoclose, src.use_autoclose);
1033 merge(&mut settings.use_auto_surround, src.use_auto_surround);
1034 merge(
1035 &mut settings.always_treat_brackets_as_autoclosed,
1036 src.always_treat_brackets_as_autoclosed,
1037 );
1038 merge(&mut settings.show_wrap_guides, src.show_wrap_guides);
1039 merge(&mut settings.wrap_guides, src.wrap_guides.clone());
1040 merge(&mut settings.indent_guides, src.indent_guides);
1041 merge(
1042 &mut settings.code_actions_on_format,
1043 src.code_actions_on_format.clone(),
1044 );
1045 merge(&mut settings.linked_edits, src.linked_edits);
1046 merge(&mut settings.tasks, src.tasks.clone());
1047
1048 merge(
1049 &mut settings.preferred_line_length,
1050 src.preferred_line_length,
1051 );
1052 merge(&mut settings.formatter, src.formatter.clone());
1053 merge(&mut settings.prettier, src.prettier.clone());
1054 merge(&mut settings.format_on_save, src.format_on_save.clone());
1055 merge(
1056 &mut settings.remove_trailing_whitespace_on_save,
1057 src.remove_trailing_whitespace_on_save,
1058 );
1059 merge(
1060 &mut settings.ensure_final_newline_on_save,
1061 src.ensure_final_newline_on_save,
1062 );
1063 merge(
1064 &mut settings.enable_language_server,
1065 src.enable_language_server,
1066 );
1067 merge(&mut settings.language_servers, src.language_servers.clone());
1068 merge(
1069 &mut settings.show_inline_completions,
1070 src.show_inline_completions,
1071 );
1072 merge(&mut settings.show_whitespaces, src.show_whitespaces);
1073 merge(
1074 &mut settings.extend_comment_on_newline,
1075 src.extend_comment_on_newline,
1076 );
1077 merge(&mut settings.inlay_hints, src.inlay_hints);
1078}
1079
1080/// Allows to enable/disable formatting with Prettier
1081/// and configure default Prettier, used when no project-level Prettier installation is found.
1082/// Prettier formatting is disabled by default.
1083#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
1084pub struct PrettierSettings {
1085 /// Enables or disables formatting with Prettier for a given language.
1086 #[serde(default)]
1087 pub allowed: bool,
1088
1089 /// Forces Prettier integration to use a specific parser name when formatting files with the language.
1090 #[serde(default)]
1091 pub parser: Option<String>,
1092
1093 /// Forces Prettier integration to use specific plugins when formatting files with the language.
1094 /// The default Prettier will be installed with these plugins.
1095 #[serde(default)]
1096 pub plugins: HashSet<String>,
1097
1098 /// Default Prettier options, in the format as in package.json section for Prettier.
1099 /// If project installs Prettier via its package.json, these options will be ignored.
1100 #[serde(flatten)]
1101 pub options: HashMap<String, serde_json::Value>,
1102}
1103
1104#[cfg(test)]
1105mod tests {
1106 use super::*;
1107
1108 #[test]
1109 fn test_formatter_deserialization() {
1110 let raw_auto = "{\"formatter\": \"auto\"}";
1111 let settings: LanguageSettingsContent = serde_json::from_str(raw_auto).unwrap();
1112 assert_eq!(settings.formatter, Some(SelectedFormatter::Auto));
1113 let raw = "{\"formatter\": \"language_server\"}";
1114 let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
1115 assert_eq!(
1116 settings.formatter,
1117 Some(SelectedFormatter::List(FormatterList(
1118 Formatter::LanguageServer { name: None }.into()
1119 )))
1120 );
1121 let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}]}";
1122 let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
1123 assert_eq!(
1124 settings.formatter,
1125 Some(SelectedFormatter::List(FormatterList(
1126 vec![Formatter::LanguageServer { name: None }].into()
1127 )))
1128 );
1129 let raw = "{\"formatter\": [{\"language_server\": {\"name\": null}}, \"prettier\"]}";
1130 let settings: LanguageSettingsContent = serde_json::from_str(raw).unwrap();
1131 assert_eq!(
1132 settings.formatter,
1133 Some(SelectedFormatter::List(FormatterList(
1134 vec![
1135 Formatter::LanguageServer { name: None },
1136 Formatter::Prettier
1137 ]
1138 .into()
1139 )))
1140 );
1141 }
1142
1143 #[test]
1144 pub fn test_resolve_language_servers() {
1145 fn language_server_names(names: &[&str]) -> Vec<LanguageServerName> {
1146 names
1147 .iter()
1148 .copied()
1149 .map(|name| LanguageServerName(name.into()))
1150 .collect::<Vec<_>>()
1151 }
1152
1153 let available_language_servers = language_server_names(&[
1154 "typescript-language-server",
1155 "biome",
1156 "deno",
1157 "eslint",
1158 "tailwind",
1159 ]);
1160
1161 // A value of just `["..."]` is the same as taking all of the available language servers.
1162 assert_eq!(
1163 LanguageSettings::resolve_language_servers(
1164 &[LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()],
1165 &available_language_servers,
1166 ),
1167 available_language_servers
1168 );
1169
1170 // Referencing one of the available language servers will change its order.
1171 assert_eq!(
1172 LanguageSettings::resolve_language_servers(
1173 &[
1174 "biome".into(),
1175 LanguageSettings::REST_OF_LANGUAGE_SERVERS.into(),
1176 "deno".into()
1177 ],
1178 &available_language_servers
1179 ),
1180 language_server_names(&[
1181 "biome",
1182 "typescript-language-server",
1183 "eslint",
1184 "tailwind",
1185 "deno",
1186 ])
1187 );
1188
1189 // Negating an available language server removes it from the list.
1190 assert_eq!(
1191 LanguageSettings::resolve_language_servers(
1192 &[
1193 "deno".into(),
1194 "!typescript-language-server".into(),
1195 "!biome".into(),
1196 LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()
1197 ],
1198 &available_language_servers
1199 ),
1200 language_server_names(&["deno", "eslint", "tailwind"])
1201 );
1202
1203 // Adding a language server not in the list of available language servers adds it to the list.
1204 assert_eq!(
1205 LanguageSettings::resolve_language_servers(
1206 &[
1207 "my-cool-language-server".into(),
1208 LanguageSettings::REST_OF_LANGUAGE_SERVERS.into()
1209 ],
1210 &available_language_servers
1211 ),
1212 language_server_names(&[
1213 "my-cool-language-server",
1214 "typescript-language-server",
1215 "biome",
1216 "deno",
1217 "eslint",
1218 "tailwind",
1219 ])
1220 );
1221 }
1222}