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