1use crate::types::{context_keys, LspFeature, LspLanguageConfig, LspServerConfig, ProcessLimits};
2
3use rust_i18n::t;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use std::borrow::Cow;
7use std::collections::HashMap;
8use std::ops::Deref;
9use std::path::Path;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(transparent)]
14pub struct ThemeName(pub String);
15
16impl ThemeName {
17 pub const BUILTIN_OPTIONS: &'static [&'static str] =
19 &["dark", "light", "high-contrast", "nostalgia"];
20}
21
22impl Deref for ThemeName {
23 type Target = str;
24 fn deref(&self) -> &Self::Target {
25 &self.0
26 }
27}
28
29impl From<String> for ThemeName {
30 fn from(s: String) -> Self {
31 Self(s)
32 }
33}
34
35impl From<&str> for ThemeName {
36 fn from(s: &str) -> Self {
37 Self(s.to_string())
38 }
39}
40
41impl PartialEq<str> for ThemeName {
42 fn eq(&self, other: &str) -> bool {
43 self.0 == other
44 }
45}
46
47impl PartialEq<ThemeName> for str {
48 fn eq(&self, other: &ThemeName) -> bool {
49 self == other.0
50 }
51}
52
53impl JsonSchema for ThemeName {
54 fn schema_name() -> Cow<'static, str> {
55 Cow::Borrowed("ThemeOptions")
56 }
57
58 fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
59 schemars::json_schema!({
60 "description": "Available color themes",
61 "type": "string",
62 "enum": Self::BUILTIN_OPTIONS
63 })
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
70#[serde(transparent)]
71pub struct LocaleName(pub Option<String>);
72
73include!(concat!(env!("OUT_DIR"), "/locale_options.rs"));
75
76impl LocaleName {
77 pub const LOCALE_OPTIONS: &'static [Option<&'static str>] = GENERATED_LOCALE_OPTIONS;
81
82 pub fn as_option(&self) -> Option<&str> {
84 self.0.as_deref()
85 }
86}
87
88impl From<Option<String>> for LocaleName {
89 fn from(s: Option<String>) -> Self {
90 Self(s)
91 }
92}
93
94impl From<Option<&str>> for LocaleName {
95 fn from(s: Option<&str>) -> Self {
96 Self(s.map(|s| s.to_string()))
97 }
98}
99
100impl JsonSchema for LocaleName {
101 fn schema_name() -> Cow<'static, str> {
102 Cow::Borrowed("LocaleOptions")
103 }
104
105 fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
106 schemars::json_schema!({
107 "description": "UI locale (language). Use null for auto-detection from environment.",
108 "enum": Self::LOCALE_OPTIONS
109 })
110 }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
115#[serde(rename_all = "snake_case")]
116pub enum CursorStyle {
117 #[default]
119 Default,
120 BlinkingBlock,
122 SteadyBlock,
124 BlinkingBar,
126 SteadyBar,
128 BlinkingUnderline,
130 SteadyUnderline,
132}
133
134impl CursorStyle {
135 pub const OPTIONS: &'static [&'static str] = &[
137 "default",
138 "blinking_block",
139 "steady_block",
140 "blinking_bar",
141 "steady_bar",
142 "blinking_underline",
143 "steady_underline",
144 ];
145
146 pub const DESCRIPTIONS: &'static [&'static str] = &[
148 "Terminal default",
149 "█ Blinking block",
150 "█ Solid block",
151 "│ Blinking bar",
152 "│ Solid bar",
153 "_ Blinking underline",
154 "_ Solid underline",
155 ];
156
157 #[cfg(feature = "runtime")]
159 pub fn to_crossterm_style(self) -> crossterm::cursor::SetCursorStyle {
160 use crossterm::cursor::SetCursorStyle;
161 match self {
162 Self::Default => SetCursorStyle::DefaultUserShape,
163 Self::BlinkingBlock => SetCursorStyle::BlinkingBlock,
164 Self::SteadyBlock => SetCursorStyle::SteadyBlock,
165 Self::BlinkingBar => SetCursorStyle::BlinkingBar,
166 Self::SteadyBar => SetCursorStyle::SteadyBar,
167 Self::BlinkingUnderline => SetCursorStyle::BlinkingUnderScore,
168 Self::SteadyUnderline => SetCursorStyle::SteadyUnderScore,
169 }
170 }
171
172 pub fn to_escape_sequence(self) -> &'static [u8] {
175 match self {
176 Self::Default => b"\x1b[0 q",
177 Self::BlinkingBlock => b"\x1b[1 q",
178 Self::SteadyBlock => b"\x1b[2 q",
179 Self::BlinkingUnderline => b"\x1b[3 q",
180 Self::SteadyUnderline => b"\x1b[4 q",
181 Self::BlinkingBar => b"\x1b[5 q",
182 Self::SteadyBar => b"\x1b[6 q",
183 }
184 }
185
186 pub fn parse(s: &str) -> Option<Self> {
188 match s {
189 "default" => Some(CursorStyle::Default),
190 "blinking_block" => Some(CursorStyle::BlinkingBlock),
191 "steady_block" => Some(CursorStyle::SteadyBlock),
192 "blinking_bar" => Some(CursorStyle::BlinkingBar),
193 "steady_bar" => Some(CursorStyle::SteadyBar),
194 "blinking_underline" => Some(CursorStyle::BlinkingUnderline),
195 "steady_underline" => Some(CursorStyle::SteadyUnderline),
196 _ => None,
197 }
198 }
199
200 pub fn as_str(self) -> &'static str {
202 match self {
203 Self::Default => "default",
204 Self::BlinkingBlock => "blinking_block",
205 Self::SteadyBlock => "steady_block",
206 Self::BlinkingBar => "blinking_bar",
207 Self::SteadyBar => "steady_bar",
208 Self::BlinkingUnderline => "blinking_underline",
209 Self::SteadyUnderline => "steady_underline",
210 }
211 }
212}
213
214impl JsonSchema for CursorStyle {
215 fn schema_name() -> Cow<'static, str> {
216 Cow::Borrowed("CursorStyle")
217 }
218
219 fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
220 schemars::json_schema!({
221 "description": "Terminal cursor style",
222 "type": "string",
223 "enum": Self::OPTIONS
224 })
225 }
226}
227
228#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
230#[serde(transparent)]
231pub struct KeybindingMapName(pub String);
232
233impl KeybindingMapName {
234 pub const BUILTIN_OPTIONS: &'static [&'static str] =
236 &["default", "emacs", "vscode", "macos", "macos-gui"];
237}
238
239impl Deref for KeybindingMapName {
240 type Target = str;
241 fn deref(&self) -> &Self::Target {
242 &self.0
243 }
244}
245
246impl From<String> for KeybindingMapName {
247 fn from(s: String) -> Self {
248 Self(s)
249 }
250}
251
252impl From<&str> for KeybindingMapName {
253 fn from(s: &str) -> Self {
254 Self(s.to_string())
255 }
256}
257
258impl PartialEq<str> for KeybindingMapName {
259 fn eq(&self, other: &str) -> bool {
260 self.0 == other
261 }
262}
263
264#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
266#[serde(rename_all = "lowercase")]
267pub enum LineEndingOption {
268 #[default]
270 Lf,
271 Crlf,
273 Cr,
275}
276
277impl LineEndingOption {
278 pub fn to_line_ending(&self) -> crate::model::buffer::LineEnding {
280 match self {
281 Self::Lf => crate::model::buffer::LineEnding::LF,
282 Self::Crlf => crate::model::buffer::LineEnding::CRLF,
283 Self::Cr => crate::model::buffer::LineEnding::CR,
284 }
285 }
286}
287
288impl JsonSchema for LineEndingOption {
289 fn schema_name() -> Cow<'static, str> {
290 Cow::Borrowed("LineEndingOption")
291 }
292
293 fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
294 schemars::json_schema!({
295 "description": "Default line ending format for new files",
296 "type": "string",
297 "enum": ["lf", "crlf", "cr"],
298 "default": "lf"
299 })
300 }
301}
302
303impl PartialEq<KeybindingMapName> for str {
304 fn eq(&self, other: &KeybindingMapName) -> bool {
305 self == other.0
306 }
307}
308
309impl JsonSchema for KeybindingMapName {
310 fn schema_name() -> Cow<'static, str> {
311 Cow::Borrowed("KeybindingMapOptions")
312 }
313
314 fn json_schema(_gen: &mut schemars::SchemaGenerator) -> schemars::Schema {
315 schemars::json_schema!({
316 "description": "Available keybinding maps",
317 "type": "string",
318 "enum": Self::BUILTIN_OPTIONS
319 })
320 }
321}
322
323#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
325pub struct Config {
326 #[serde(default)]
329 pub version: u32,
330
331 #[serde(default = "default_theme_name")]
333 pub theme: ThemeName,
334
335 #[serde(default)]
338 pub locale: LocaleName,
339
340 #[serde(default = "default_true")]
343 pub check_for_updates: bool,
344
345 #[serde(default)]
347 pub editor: EditorConfig,
348
349 #[serde(default)]
351 pub file_explorer: FileExplorerConfig,
352
353 #[serde(default)]
355 pub file_browser: FileBrowserConfig,
356
357 #[serde(default)]
359 pub clipboard: ClipboardConfig,
360
361 #[serde(default)]
363 pub terminal: TerminalConfig,
364
365 #[serde(default)]
367 pub keybindings: Vec<Keybinding>,
368
369 #[serde(default)]
372 pub keybinding_maps: HashMap<String, KeymapConfig>,
373
374 #[serde(default = "default_keybinding_map_name")]
376 pub active_keybinding_map: KeybindingMapName,
377
378 #[serde(default)]
380 pub languages: HashMap<String, LanguageConfig>,
381
382 #[serde(default)]
388 #[schemars(extend("x-enum-from" = "/languages"))]
389 pub default_language: Option<String>,
390
391 #[serde(default)]
395 pub lsp: HashMap<String, LspLanguageConfig>,
396
397 #[serde(default)]
401 pub universal_lsp: HashMap<String, LspLanguageConfig>,
402
403 #[serde(default)]
405 pub warnings: WarningsConfig,
406
407 #[serde(default)]
411 #[schemars(extend("x-standalone-category" = true, "x-no-add" = true))]
412 pub plugins: HashMap<String, PluginConfig>,
413
414 #[serde(default)]
416 pub packages: PackagesConfig,
417}
418
419fn default_keybinding_map_name() -> KeybindingMapName {
420 if cfg!(target_os = "macos") {
423 KeybindingMapName("macos".to_string())
424 } else {
425 KeybindingMapName("default".to_string())
426 }
427}
428
429fn default_theme_name() -> ThemeName {
430 ThemeName("high-contrast".to_string())
431}
432
433#[derive(Debug, Clone, Copy)]
438pub struct WhitespaceVisibility {
439 pub spaces_leading: bool,
440 pub spaces_inner: bool,
441 pub spaces_trailing: bool,
442 pub tabs_leading: bool,
443 pub tabs_inner: bool,
444 pub tabs_trailing: bool,
445}
446
447impl Default for WhitespaceVisibility {
448 fn default() -> Self {
449 Self {
451 spaces_leading: false,
452 spaces_inner: false,
453 spaces_trailing: false,
454 tabs_leading: true,
455 tabs_inner: true,
456 tabs_trailing: true,
457 }
458 }
459}
460
461impl WhitespaceVisibility {
462 pub fn from_editor_config(editor: &EditorConfig) -> Self {
464 if !editor.whitespace_show {
465 return Self {
466 spaces_leading: false,
467 spaces_inner: false,
468 spaces_trailing: false,
469 tabs_leading: false,
470 tabs_inner: false,
471 tabs_trailing: false,
472 };
473 }
474 Self {
475 spaces_leading: editor.whitespace_spaces_leading,
476 spaces_inner: editor.whitespace_spaces_inner,
477 spaces_trailing: editor.whitespace_spaces_trailing,
478 tabs_leading: editor.whitespace_tabs_leading,
479 tabs_inner: editor.whitespace_tabs_inner,
480 tabs_trailing: editor.whitespace_tabs_trailing,
481 }
482 }
483
484 pub fn with_language_tab_override(mut self, show_whitespace_tabs: bool) -> Self {
487 if !show_whitespace_tabs {
488 self.tabs_leading = false;
489 self.tabs_inner = false;
490 self.tabs_trailing = false;
491 }
492 self
493 }
494
495 pub fn any_spaces(&self) -> bool {
497 self.spaces_leading || self.spaces_inner || self.spaces_trailing
498 }
499
500 pub fn any_tabs(&self) -> bool {
502 self.tabs_leading || self.tabs_inner || self.tabs_trailing
503 }
504
505 pub fn any_visible(&self) -> bool {
507 self.any_spaces() || self.any_tabs()
508 }
509
510 pub fn toggle_all(&mut self) {
514 if self.any_visible() {
515 *self = Self {
516 spaces_leading: false,
517 spaces_inner: false,
518 spaces_trailing: false,
519 tabs_leading: false,
520 tabs_inner: false,
521 tabs_trailing: false,
522 };
523 } else {
524 *self = Self::default();
525 }
526 }
527}
528
529#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
531pub struct EditorConfig {
532 #[serde(default = "default_true")]
535 #[schemars(extend("x-section" = "Display"))]
536 pub line_numbers: bool,
537
538 #[serde(default = "default_false")]
540 #[schemars(extend("x-section" = "Display"))]
541 pub relative_line_numbers: bool,
542
543 #[serde(default = "default_true")]
545 #[schemars(extend("x-section" = "Display"))]
546 pub highlight_current_line: bool,
547
548 #[serde(default = "default_true")]
550 #[schemars(extend("x-section" = "Display"))]
551 pub line_wrap: bool,
552
553 #[serde(default = "default_true")]
555 #[schemars(extend("x-section" = "Display"))]
556 pub wrap_indent: bool,
557
558 #[serde(default)]
563 #[schemars(extend("x-section" = "Display"))]
564 pub wrap_column: Option<usize>,
565
566 #[serde(default = "default_page_width")]
570 #[schemars(extend("x-section" = "Display"))]
571 pub page_width: Option<usize>,
572
573 #[serde(default = "default_true")]
575 #[schemars(extend("x-section" = "Display"))]
576 pub syntax_highlighting: bool,
577
578 #[serde(default = "default_true")]
583 #[schemars(extend("x-section" = "Display"))]
584 pub show_menu_bar: bool,
585
586 #[serde(default = "default_true")]
591 #[schemars(extend("x-section" = "Display"))]
592 pub menu_bar_mnemonics: bool,
593
594 #[serde(default = "default_true")]
599 #[schemars(extend("x-section" = "Display"))]
600 pub show_tab_bar: bool,
601
602 #[serde(default = "default_true")]
607 #[schemars(extend("x-section" = "Display"))]
608 pub show_status_bar: bool,
609
610 #[serde(default = "default_true")]
616 #[schemars(extend("x-section" = "Display"))]
617 pub show_prompt_line: bool,
618
619 #[serde(default = "default_true")]
623 #[schemars(extend("x-section" = "Display"))]
624 pub show_vertical_scrollbar: bool,
625
626 #[serde(default = "default_false")]
631 #[schemars(extend("x-section" = "Display"))]
632 pub show_horizontal_scrollbar: bool,
633
634 #[serde(default = "default_true")]
638 #[schemars(extend("x-section" = "Display"))]
639 pub show_tilde: bool,
640
641 #[serde(default = "default_false")]
646 #[schemars(extend("x-section" = "Display"))]
647 pub use_terminal_bg: bool,
648
649 #[serde(default)]
653 #[schemars(extend("x-section" = "Display"))]
654 pub cursor_style: CursorStyle,
655
656 #[serde(default)]
661 #[schemars(extend("x-section" = "Display"))]
662 pub rulers: Vec<usize>,
663
664 #[serde(default = "default_true")]
670 #[schemars(extend("x-section" = "Whitespace"))]
671 pub whitespace_show: bool,
672
673 #[serde(default = "default_false")]
677 #[schemars(extend("x-section" = "Whitespace"))]
678 pub whitespace_spaces_leading: bool,
679
680 #[serde(default = "default_false")]
684 #[schemars(extend("x-section" = "Whitespace"))]
685 pub whitespace_spaces_inner: bool,
686
687 #[serde(default = "default_false")]
691 #[schemars(extend("x-section" = "Whitespace"))]
692 pub whitespace_spaces_trailing: bool,
693
694 #[serde(default = "default_true")]
698 #[schemars(extend("x-section" = "Whitespace"))]
699 pub whitespace_tabs_leading: bool,
700
701 #[serde(default = "default_true")]
705 #[schemars(extend("x-section" = "Whitespace"))]
706 pub whitespace_tabs_inner: bool,
707
708 #[serde(default = "default_true")]
712 #[schemars(extend("x-section" = "Whitespace"))]
713 pub whitespace_tabs_trailing: bool,
714
715 #[serde(default = "default_false")]
721 #[schemars(extend("x-section" = "Editing"))]
722 pub use_tabs: bool,
723
724 #[serde(default = "default_tab_size")]
726 #[schemars(extend("x-section" = "Editing"))]
727 pub tab_size: usize,
728
729 #[serde(default = "default_true")]
731 #[schemars(extend("x-section" = "Editing"))]
732 pub auto_indent: bool,
733
734 #[serde(default = "default_true")]
741 #[schemars(extend("x-section" = "Editing"))]
742 pub auto_close: bool,
743
744 #[serde(default = "default_true")]
749 #[schemars(extend("x-section" = "Editing"))]
750 pub auto_surround: bool,
751
752 #[serde(default = "default_scroll_offset")]
754 #[schemars(extend("x-section" = "Editing"))]
755 pub scroll_offset: usize,
756
757 #[serde(default)]
762 #[schemars(extend("x-section" = "Editing"))]
763 pub default_line_ending: LineEndingOption,
764
765 #[serde(default = "default_false")]
768 #[schemars(extend("x-section" = "Editing"))]
769 pub trim_trailing_whitespace_on_save: bool,
770
771 #[serde(default = "default_false")]
774 #[schemars(extend("x-section" = "Editing"))]
775 pub ensure_final_newline_on_save: bool,
776
777 #[serde(default = "default_true")]
781 #[schemars(extend("x-section" = "Bracket Matching"))]
782 pub highlight_matching_brackets: bool,
783
784 #[serde(default = "default_true")]
788 #[schemars(extend("x-section" = "Bracket Matching"))]
789 pub rainbow_brackets: bool,
790
791 #[serde(default = "default_false")]
798 #[schemars(extend("x-section" = "Completion"))]
799 pub completion_popup_auto_show: bool,
800
801 #[serde(default = "default_true")]
807 #[schemars(extend("x-section" = "Completion"))]
808 pub quick_suggestions: bool,
809
810 #[serde(default = "default_quick_suggestions_delay")]
816 #[schemars(extend("x-section" = "Completion"))]
817 pub quick_suggestions_delay_ms: u64,
818
819 #[serde(default = "default_true")]
823 #[schemars(extend("x-section" = "Completion"))]
824 pub suggest_on_trigger_characters: bool,
825
826 #[serde(default = "default_true")]
829 #[schemars(extend("x-section" = "LSP"))]
830 pub enable_inlay_hints: bool,
831
832 #[serde(default = "default_false")]
836 #[schemars(extend("x-section" = "LSP"))]
837 pub enable_semantic_tokens_full: bool,
838
839 #[serde(default = "default_false")]
844 #[schemars(extend("x-section" = "Diagnostics"))]
845 pub diagnostics_inline_text: bool,
846
847 #[serde(default = "default_mouse_hover_enabled")]
858 #[schemars(extend("x-section" = "Mouse"))]
859 pub mouse_hover_enabled: bool,
860
861 #[serde(default = "default_mouse_hover_delay")]
865 #[schemars(extend("x-section" = "Mouse"))]
866 pub mouse_hover_delay_ms: u64,
867
868 #[serde(default = "default_double_click_time")]
872 #[schemars(extend("x-section" = "Mouse"))]
873 pub double_click_time_ms: u64,
874
875 #[serde(default = "default_false")]
880 #[schemars(extend("x-section" = "Recovery"))]
881 pub auto_save_enabled: bool,
882
883 #[serde(default = "default_auto_save_interval")]
888 #[schemars(extend("x-section" = "Recovery"))]
889 pub auto_save_interval_secs: u32,
890
891 #[serde(default = "default_true", alias = "persist_unnamed_buffers")]
898 #[schemars(extend("x-section" = "Recovery"))]
899 pub hot_exit: bool,
900
901 #[serde(default = "default_true")]
906 #[schemars(extend("x-section" = "Recovery"))]
907 pub recovery_enabled: bool,
908
909 #[serde(default = "default_auto_recovery_save_interval")]
914 #[schemars(extend("x-section" = "Recovery"))]
915 pub auto_recovery_save_interval_secs: u32,
916
917 #[serde(default = "default_auto_revert_poll_interval")]
922 #[schemars(extend("x-section" = "Recovery"))]
923 pub auto_revert_poll_interval_ms: u64,
924
925 #[serde(default = "default_true")]
931 #[schemars(extend("x-section" = "Keyboard"))]
932 pub keyboard_disambiguate_escape_codes: bool,
933
934 #[serde(default = "default_false")]
939 #[schemars(extend("x-section" = "Keyboard"))]
940 pub keyboard_report_event_types: bool,
941
942 #[serde(default = "default_true")]
947 #[schemars(extend("x-section" = "Keyboard"))]
948 pub keyboard_report_alternate_keys: bool,
949
950 #[serde(default = "default_false")]
956 #[schemars(extend("x-section" = "Keyboard"))]
957 pub keyboard_report_all_keys_as_escape_codes: bool,
958
959 #[serde(default = "default_highlight_timeout")]
962 #[schemars(extend("x-section" = "Performance"))]
963 pub highlight_timeout_ms: u64,
964
965 #[serde(default = "default_snapshot_interval")]
967 #[schemars(extend("x-section" = "Performance"))]
968 pub snapshot_interval: usize,
969
970 #[serde(default = "default_highlight_context_bytes")]
975 #[schemars(extend("x-section" = "Performance"))]
976 pub highlight_context_bytes: usize,
977
978 #[serde(default = "default_large_file_threshold")]
985 #[schemars(extend("x-section" = "Performance"))]
986 pub large_file_threshold_bytes: u64,
987
988 #[serde(default = "default_estimated_line_length")]
992 #[schemars(extend("x-section" = "Performance"))]
993 pub estimated_line_length: usize,
994
995 #[serde(default = "default_read_concurrency")]
1000 #[schemars(extend("x-section" = "Performance"))]
1001 pub read_concurrency: usize,
1002
1003 #[serde(default = "default_file_tree_poll_interval")]
1008 #[schemars(extend("x-section" = "Performance"))]
1009 pub file_tree_poll_interval_ms: u64,
1010}
1011
1012fn default_tab_size() -> usize {
1013 4
1014}
1015
1016pub const LARGE_FILE_THRESHOLD_BYTES: u64 = 1024 * 1024; fn default_large_file_threshold() -> u64 {
1022 LARGE_FILE_THRESHOLD_BYTES
1023}
1024
1025pub const INDENT_FOLD_MAX_SCAN_LINES: usize = 10_000;
1028
1029pub const INDENT_FOLD_INDICATOR_MAX_SCAN: usize = 50;
1032
1033pub const INDENT_FOLD_MAX_UPWARD_SCAN: usize = 200;
1036
1037fn default_read_concurrency() -> usize {
1038 64
1039}
1040
1041fn default_true() -> bool {
1042 true
1043}
1044
1045fn default_false() -> bool {
1046 false
1047}
1048
1049fn default_quick_suggestions_delay() -> u64 {
1050 150 }
1052
1053fn default_scroll_offset() -> usize {
1054 3
1055}
1056
1057fn default_highlight_timeout() -> u64 {
1058 5
1059}
1060
1061fn default_snapshot_interval() -> usize {
1062 100
1063}
1064
1065fn default_estimated_line_length() -> usize {
1066 80
1067}
1068
1069fn default_auto_save_interval() -> u32 {
1070 30 }
1072
1073fn default_auto_recovery_save_interval() -> u32 {
1074 2 }
1076
1077fn default_highlight_context_bytes() -> usize {
1078 10_000 }
1080
1081fn default_mouse_hover_enabled() -> bool {
1082 !cfg!(windows)
1083}
1084
1085fn default_mouse_hover_delay() -> u64 {
1086 500 }
1088
1089fn default_double_click_time() -> u64 {
1090 500 }
1092
1093fn default_auto_revert_poll_interval() -> u64 {
1094 2000 }
1096
1097fn default_file_tree_poll_interval() -> u64 {
1098 3000 }
1100
1101impl Default for EditorConfig {
1102 fn default() -> Self {
1103 Self {
1104 use_tabs: false,
1105 tab_size: default_tab_size(),
1106 auto_indent: true,
1107 auto_close: true,
1108 auto_surround: true,
1109 line_numbers: true,
1110 relative_line_numbers: false,
1111 scroll_offset: default_scroll_offset(),
1112 syntax_highlighting: true,
1113 highlight_current_line: true,
1114 line_wrap: true,
1115 wrap_indent: true,
1116 wrap_column: None,
1117 page_width: default_page_width(),
1118 highlight_timeout_ms: default_highlight_timeout(),
1119 snapshot_interval: default_snapshot_interval(),
1120 large_file_threshold_bytes: default_large_file_threshold(),
1121 estimated_line_length: default_estimated_line_length(),
1122 enable_inlay_hints: true,
1123 enable_semantic_tokens_full: false,
1124 diagnostics_inline_text: false,
1125 auto_save_enabled: false,
1126 auto_save_interval_secs: default_auto_save_interval(),
1127 hot_exit: true,
1128 recovery_enabled: true,
1129 auto_recovery_save_interval_secs: default_auto_recovery_save_interval(),
1130 highlight_context_bytes: default_highlight_context_bytes(),
1131 mouse_hover_enabled: default_mouse_hover_enabled(),
1132 mouse_hover_delay_ms: default_mouse_hover_delay(),
1133 double_click_time_ms: default_double_click_time(),
1134 auto_revert_poll_interval_ms: default_auto_revert_poll_interval(),
1135 read_concurrency: default_read_concurrency(),
1136 file_tree_poll_interval_ms: default_file_tree_poll_interval(),
1137 default_line_ending: LineEndingOption::default(),
1138 trim_trailing_whitespace_on_save: false,
1139 ensure_final_newline_on_save: false,
1140 highlight_matching_brackets: true,
1141 rainbow_brackets: true,
1142 cursor_style: CursorStyle::default(),
1143 keyboard_disambiguate_escape_codes: true,
1144 keyboard_report_event_types: false,
1145 keyboard_report_alternate_keys: true,
1146 keyboard_report_all_keys_as_escape_codes: false,
1147 completion_popup_auto_show: false,
1148 quick_suggestions: true,
1149 quick_suggestions_delay_ms: default_quick_suggestions_delay(),
1150 suggest_on_trigger_characters: true,
1151 show_menu_bar: true,
1152 menu_bar_mnemonics: true,
1153 show_tab_bar: true,
1154 show_status_bar: true,
1155 show_prompt_line: true,
1156 show_vertical_scrollbar: true,
1157 show_horizontal_scrollbar: false,
1158 show_tilde: true,
1159 use_terminal_bg: false,
1160 rulers: Vec::new(),
1161 whitespace_show: true,
1162 whitespace_spaces_leading: false,
1163 whitespace_spaces_inner: false,
1164 whitespace_spaces_trailing: false,
1165 whitespace_tabs_leading: true,
1166 whitespace_tabs_inner: true,
1167 whitespace_tabs_trailing: true,
1168 }
1169 }
1170}
1171
1172#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1174pub struct FileExplorerConfig {
1175 #[serde(default = "default_true")]
1177 pub respect_gitignore: bool,
1178
1179 #[serde(default = "default_false")]
1181 pub show_hidden: bool,
1182
1183 #[serde(default = "default_false")]
1185 pub show_gitignored: bool,
1186
1187 #[serde(default)]
1189 pub custom_ignore_patterns: Vec<String>,
1190
1191 #[serde(default = "default_explorer_width")]
1193 pub width: f32,
1194}
1195
1196fn default_explorer_width() -> f32 {
1197 0.3 }
1199
1200#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1211pub struct ClipboardConfig {
1212 #[serde(default = "default_true")]
1215 pub use_osc52: bool,
1216
1217 #[serde(default = "default_true")]
1220 pub use_system_clipboard: bool,
1221}
1222
1223impl Default for ClipboardConfig {
1224 fn default() -> Self {
1225 Self {
1226 use_osc52: true,
1227 use_system_clipboard: true,
1228 }
1229 }
1230}
1231
1232#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1234pub struct TerminalConfig {
1235 #[serde(default = "default_true")]
1238 pub jump_to_end_on_output: bool,
1239}
1240
1241impl Default for TerminalConfig {
1242 fn default() -> Self {
1243 Self {
1244 jump_to_end_on_output: true,
1245 }
1246 }
1247}
1248
1249#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1251pub struct WarningsConfig {
1252 #[serde(default = "default_true")]
1255 pub show_status_indicator: bool,
1256}
1257
1258impl Default for WarningsConfig {
1259 fn default() -> Self {
1260 Self {
1261 show_status_indicator: true,
1262 }
1263 }
1264}
1265
1266#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1268pub struct PackagesConfig {
1269 #[serde(default = "default_package_sources")]
1272 pub sources: Vec<String>,
1273}
1274
1275fn default_package_sources() -> Vec<String> {
1276 vec!["https://github.com/sinelaw/fresh-plugins-registry".to_string()]
1277}
1278
1279impl Default for PackagesConfig {
1280 fn default() -> Self {
1281 Self {
1282 sources: default_package_sources(),
1283 }
1284 }
1285}
1286
1287pub use fresh_core::config::PluginConfig;
1289
1290impl Default for FileExplorerConfig {
1291 fn default() -> Self {
1292 Self {
1293 respect_gitignore: true,
1294 show_hidden: false,
1295 show_gitignored: false,
1296 custom_ignore_patterns: Vec::new(),
1297 width: default_explorer_width(),
1298 }
1299 }
1300}
1301
1302#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
1304pub struct FileBrowserConfig {
1305 #[serde(default = "default_false")]
1307 pub show_hidden: bool,
1308}
1309
1310#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1312pub struct KeyPress {
1313 pub key: String,
1315 #[serde(default)]
1317 pub modifiers: Vec<String>,
1318}
1319
1320#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1322#[schemars(extend("x-display-field" = "/action"))]
1323pub struct Keybinding {
1324 #[serde(default, skip_serializing_if = "String::is_empty")]
1326 pub key: String,
1327
1328 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1330 pub modifiers: Vec<String>,
1331
1332 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1335 pub keys: Vec<KeyPress>,
1336
1337 pub action: String,
1339
1340 #[serde(default)]
1342 pub args: HashMap<String, serde_json::Value>,
1343
1344 #[serde(default)]
1346 pub when: Option<String>,
1347}
1348
1349#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1351#[schemars(extend("x-display-field" = "/inherits"))]
1352pub struct KeymapConfig {
1353 #[serde(default, skip_serializing_if = "Option::is_none")]
1355 pub inherits: Option<String>,
1356
1357 #[serde(default)]
1359 pub bindings: Vec<Keybinding>,
1360}
1361
1362#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1364#[schemars(extend("x-display-field" = "/command"))]
1365pub struct FormatterConfig {
1366 pub command: String,
1368
1369 #[serde(default)]
1372 pub args: Vec<String>,
1373
1374 #[serde(default = "default_true")]
1377 pub stdin: bool,
1378
1379 #[serde(default = "default_on_save_timeout")]
1381 pub timeout_ms: u64,
1382}
1383
1384#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1386#[schemars(extend("x-display-field" = "/command"))]
1387pub struct OnSaveAction {
1388 pub command: String,
1391
1392 #[serde(default)]
1395 pub args: Vec<String>,
1396
1397 #[serde(default)]
1399 pub working_dir: Option<String>,
1400
1401 #[serde(default)]
1403 pub stdin: bool,
1404
1405 #[serde(default = "default_on_save_timeout")]
1407 pub timeout_ms: u64,
1408
1409 #[serde(default = "default_true")]
1412 pub enabled: bool,
1413}
1414
1415fn default_on_save_timeout() -> u64 {
1416 10000
1417}
1418
1419fn default_page_width() -> Option<usize> {
1420 Some(80)
1421}
1422
1423#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
1425#[schemars(extend("x-display-field" = "/grammar"))]
1426pub struct LanguageConfig {
1427 #[serde(default)]
1429 pub extensions: Vec<String>,
1430
1431 #[serde(default)]
1433 pub filenames: Vec<String>,
1434
1435 #[serde(default)]
1437 pub grammar: String,
1438
1439 #[serde(default)]
1441 pub comment_prefix: Option<String>,
1442
1443 #[serde(default = "default_true")]
1445 pub auto_indent: bool,
1446
1447 #[serde(default)]
1450 pub auto_close: Option<bool>,
1451
1452 #[serde(default)]
1455 pub auto_surround: Option<bool>,
1456
1457 #[serde(default)]
1460 pub textmate_grammar: Option<std::path::PathBuf>,
1461
1462 #[serde(default = "default_true")]
1465 pub show_whitespace_tabs: bool,
1466
1467 #[serde(default)]
1472 pub line_wrap: Option<bool>,
1473
1474 #[serde(default)]
1477 pub wrap_column: Option<usize>,
1478
1479 #[serde(default)]
1484 pub page_view: Option<bool>,
1485
1486 #[serde(default)]
1490 pub page_width: Option<usize>,
1491
1492 #[serde(default)]
1496 pub use_tabs: Option<bool>,
1497
1498 #[serde(default)]
1501 pub tab_size: Option<usize>,
1502
1503 #[serde(default)]
1505 pub formatter: Option<FormatterConfig>,
1506
1507 #[serde(default)]
1509 pub format_on_save: bool,
1510
1511 #[serde(default)]
1515 pub on_save: Vec<OnSaveAction>,
1516
1517 #[serde(default)]
1527 pub word_characters: Option<String>,
1528}
1529
1530#[derive(Debug, Clone)]
1537pub struct BufferConfig {
1538 pub tab_size: usize,
1540
1541 pub use_tabs: bool,
1543
1544 pub auto_indent: bool,
1546
1547 pub auto_close: bool,
1549
1550 pub auto_surround: bool,
1552
1553 pub line_wrap: bool,
1555
1556 pub wrap_column: Option<usize>,
1558
1559 pub whitespace: WhitespaceVisibility,
1561
1562 pub formatter: Option<FormatterConfig>,
1564
1565 pub format_on_save: bool,
1567
1568 pub on_save: Vec<OnSaveAction>,
1570
1571 pub textmate_grammar: Option<std::path::PathBuf>,
1573
1574 pub word_characters: String,
1577}
1578
1579impl BufferConfig {
1580 pub fn resolve(global_config: &Config, language_id: Option<&str>) -> Self {
1589 let editor = &global_config.editor;
1590
1591 let mut whitespace = WhitespaceVisibility::from_editor_config(editor);
1593 let mut config = BufferConfig {
1594 tab_size: editor.tab_size,
1595 use_tabs: editor.use_tabs,
1596 auto_indent: editor.auto_indent,
1597 auto_close: editor.auto_close,
1598 auto_surround: editor.auto_surround,
1599 line_wrap: editor.line_wrap,
1600 wrap_column: editor.wrap_column,
1601 whitespace,
1602 formatter: None,
1603 format_on_save: false,
1604 on_save: Vec::new(),
1605 textmate_grammar: None,
1606 word_characters: String::new(),
1607 };
1608
1609 let lang_config_ref = language_id
1613 .and_then(|id| global_config.languages.get(id))
1614 .or_else(|| {
1615 match language_id {
1617 None | Some("text") => global_config
1618 .default_language
1619 .as_deref()
1620 .and_then(|lang| global_config.languages.get(lang)),
1621 _ => None,
1622 }
1623 });
1624 if let Some(lang_config) = lang_config_ref {
1625 if let Some(ts) = lang_config.tab_size {
1627 config.tab_size = ts;
1628 }
1629
1630 if let Some(use_tabs) = lang_config.use_tabs {
1632 config.use_tabs = use_tabs;
1633 }
1634
1635 if let Some(line_wrap) = lang_config.line_wrap {
1637 config.line_wrap = line_wrap;
1638 }
1639
1640 if lang_config.wrap_column.is_some() {
1642 config.wrap_column = lang_config.wrap_column;
1643 }
1644
1645 config.auto_indent = lang_config.auto_indent;
1647
1648 if config.auto_close {
1650 if let Some(lang_auto_close) = lang_config.auto_close {
1651 config.auto_close = lang_auto_close;
1652 }
1653 }
1654
1655 if config.auto_surround {
1657 if let Some(lang_auto_surround) = lang_config.auto_surround {
1658 config.auto_surround = lang_auto_surround;
1659 }
1660 }
1661
1662 whitespace = whitespace.with_language_tab_override(lang_config.show_whitespace_tabs);
1664 config.whitespace = whitespace;
1665
1666 config.formatter = lang_config.formatter.clone();
1668
1669 config.format_on_save = lang_config.format_on_save;
1671
1672 config.on_save = lang_config.on_save.clone();
1674
1675 config.textmate_grammar = lang_config.textmate_grammar.clone();
1677
1678 if let Some(ref wc) = lang_config.word_characters {
1680 config.word_characters = wc.clone();
1681 }
1682 }
1683
1684 config
1685 }
1686
1687 pub fn indent_string(&self) -> String {
1692 if self.use_tabs {
1693 "\t".to_string()
1694 } else {
1695 " ".repeat(self.tab_size)
1696 }
1697 }
1698}
1699
1700#[derive(Debug, Clone, Default, Serialize, Deserialize, JsonSchema)]
1702pub struct MenuConfig {
1703 #[serde(default)]
1705 pub menus: Vec<Menu>,
1706}
1707
1708pub use fresh_core::menu::{Menu, MenuItem};
1710
1711pub trait MenuExt {
1713 fn match_id(&self) -> &str;
1716
1717 fn expand_dynamic_items(&mut self, themes_dir: &std::path::Path);
1720}
1721
1722impl MenuExt for Menu {
1723 fn match_id(&self) -> &str {
1724 self.id.as_deref().unwrap_or(&self.label)
1725 }
1726
1727 fn expand_dynamic_items(&mut self, themes_dir: &std::path::Path) {
1728 self.items = self
1729 .items
1730 .iter()
1731 .map(|item| item.expand_dynamic(themes_dir))
1732 .collect();
1733 }
1734}
1735
1736pub trait MenuItemExt {
1738 fn expand_dynamic(&self, themes_dir: &std::path::Path) -> MenuItem;
1741}
1742
1743impl MenuItemExt for MenuItem {
1744 fn expand_dynamic(&self, themes_dir: &std::path::Path) -> MenuItem {
1745 match self {
1746 MenuItem::DynamicSubmenu { label, source } => {
1747 let items = generate_dynamic_items(source, themes_dir);
1748 MenuItem::Submenu {
1749 label: label.clone(),
1750 items,
1751 }
1752 }
1753 other => other.clone(),
1754 }
1755 }
1756}
1757
1758#[cfg(feature = "runtime")]
1760pub fn generate_dynamic_items(source: &str, themes_dir: &std::path::Path) -> Vec<MenuItem> {
1761 match source {
1762 "copy_with_theme" => {
1763 let loader = crate::view::theme::ThemeLoader::new(themes_dir.to_path_buf());
1765 let registry = loader.load_all(&[]);
1766 registry
1767 .list()
1768 .iter()
1769 .map(|info| {
1770 let mut args = HashMap::new();
1771 args.insert("theme".to_string(), serde_json::json!(info.key));
1772 MenuItem::Action {
1773 label: info.name.clone(),
1774 action: "copy_with_theme".to_string(),
1775 args,
1776 when: Some(context_keys::HAS_SELECTION.to_string()),
1777 checkbox: None,
1778 }
1779 })
1780 .collect()
1781 }
1782 _ => vec![MenuItem::Label {
1783 info: format!("Unknown source: {}", source),
1784 }],
1785 }
1786}
1787
1788#[cfg(not(feature = "runtime"))]
1790pub fn generate_dynamic_items(_source: &str, _themes_dir: &std::path::Path) -> Vec<MenuItem> {
1791 vec![]
1793}
1794
1795impl Default for Config {
1796 fn default() -> Self {
1797 Self {
1798 version: 0,
1799 theme: default_theme_name(),
1800 locale: LocaleName::default(),
1801 check_for_updates: true,
1802 editor: EditorConfig::default(),
1803 file_explorer: FileExplorerConfig::default(),
1804 file_browser: FileBrowserConfig::default(),
1805 clipboard: ClipboardConfig::default(),
1806 terminal: TerminalConfig::default(),
1807 keybindings: vec![], keybinding_maps: HashMap::new(), active_keybinding_map: default_keybinding_map_name(),
1810 languages: Self::default_languages(),
1811 default_language: None,
1812 lsp: Self::default_lsp_config(),
1813 universal_lsp: Self::default_universal_lsp_config(),
1814 warnings: WarningsConfig::default(),
1815 plugins: HashMap::new(), packages: PackagesConfig::default(),
1817 }
1818 }
1819}
1820
1821impl MenuConfig {
1822 pub fn translated() -> Self {
1824 Self {
1825 menus: Self::translated_menus(),
1826 }
1827 }
1828
1829 pub fn translated_menus() -> Vec<Menu> {
1835 vec![
1836 Menu {
1838 id: Some("File".to_string()),
1839 label: t!("menu.file").to_string(),
1840 when: None,
1841 items: vec![
1842 MenuItem::Action {
1843 label: t!("menu.file.new_file").to_string(),
1844 action: "new".to_string(),
1845 args: HashMap::new(),
1846 when: None,
1847 checkbox: None,
1848 },
1849 MenuItem::Action {
1850 label: t!("menu.file.open_file").to_string(),
1851 action: "open".to_string(),
1852 args: HashMap::new(),
1853 when: None,
1854 checkbox: None,
1855 },
1856 MenuItem::Separator { separator: true },
1857 MenuItem::Action {
1858 label: t!("menu.file.save").to_string(),
1859 action: "save".to_string(),
1860 args: HashMap::new(),
1861 when: None,
1862 checkbox: None,
1863 },
1864 MenuItem::Action {
1865 label: t!("menu.file.save_as").to_string(),
1866 action: "save_as".to_string(),
1867 args: HashMap::new(),
1868 when: None,
1869 checkbox: None,
1870 },
1871 MenuItem::Action {
1872 label: t!("menu.file.revert").to_string(),
1873 action: "revert".to_string(),
1874 args: HashMap::new(),
1875 when: None,
1876 checkbox: None,
1877 },
1878 MenuItem::Action {
1879 label: t!("menu.file.reload_with_encoding").to_string(),
1880 action: "reload_with_encoding".to_string(),
1881 args: HashMap::new(),
1882 when: None,
1883 checkbox: None,
1884 },
1885 MenuItem::Separator { separator: true },
1886 MenuItem::Action {
1887 label: t!("menu.file.close_buffer").to_string(),
1888 action: "close".to_string(),
1889 args: HashMap::new(),
1890 when: None,
1891 checkbox: None,
1892 },
1893 MenuItem::Separator { separator: true },
1894 MenuItem::Action {
1895 label: t!("menu.file.switch_project").to_string(),
1896 action: "switch_project".to_string(),
1897 args: HashMap::new(),
1898 when: None,
1899 checkbox: None,
1900 },
1901 MenuItem::Separator { separator: true },
1902 MenuItem::Action {
1903 label: t!("menu.file.detach").to_string(),
1904 action: "detach".to_string(),
1905 args: HashMap::new(),
1906 when: Some(context_keys::SESSION_MODE.to_string()),
1907 checkbox: None,
1908 },
1909 MenuItem::Action {
1910 label: t!("menu.file.quit").to_string(),
1911 action: "quit".to_string(),
1912 args: HashMap::new(),
1913 when: None,
1914 checkbox: None,
1915 },
1916 ],
1917 },
1918 Menu {
1920 id: Some("Edit".to_string()),
1921 label: t!("menu.edit").to_string(),
1922 when: None,
1923 items: vec![
1924 MenuItem::Action {
1925 label: t!("menu.edit.undo").to_string(),
1926 action: "undo".to_string(),
1927 args: HashMap::new(),
1928 when: None,
1929 checkbox: None,
1930 },
1931 MenuItem::Action {
1932 label: t!("menu.edit.redo").to_string(),
1933 action: "redo".to_string(),
1934 args: HashMap::new(),
1935 when: None,
1936 checkbox: None,
1937 },
1938 MenuItem::Separator { separator: true },
1939 MenuItem::Action {
1940 label: t!("menu.edit.cut").to_string(),
1941 action: "cut".to_string(),
1942 args: HashMap::new(),
1943 when: Some(context_keys::HAS_SELECTION.to_string()),
1944 checkbox: None,
1945 },
1946 MenuItem::Action {
1947 label: t!("menu.edit.copy").to_string(),
1948 action: "copy".to_string(),
1949 args: HashMap::new(),
1950 when: Some(context_keys::HAS_SELECTION.to_string()),
1951 checkbox: None,
1952 },
1953 MenuItem::DynamicSubmenu {
1954 label: t!("menu.edit.copy_with_formatting").to_string(),
1955 source: "copy_with_theme".to_string(),
1956 },
1957 MenuItem::Action {
1958 label: t!("menu.edit.paste").to_string(),
1959 action: "paste".to_string(),
1960 args: HashMap::new(),
1961 when: None,
1962 checkbox: None,
1963 },
1964 MenuItem::Separator { separator: true },
1965 MenuItem::Action {
1966 label: t!("menu.edit.select_all").to_string(),
1967 action: "select_all".to_string(),
1968 args: HashMap::new(),
1969 when: None,
1970 checkbox: None,
1971 },
1972 MenuItem::Separator { separator: true },
1973 MenuItem::Action {
1974 label: t!("menu.edit.find").to_string(),
1975 action: "search".to_string(),
1976 args: HashMap::new(),
1977 when: None,
1978 checkbox: None,
1979 },
1980 MenuItem::Action {
1981 label: t!("menu.edit.find_in_selection").to_string(),
1982 action: "find_in_selection".to_string(),
1983 args: HashMap::new(),
1984 when: Some(context_keys::HAS_SELECTION.to_string()),
1985 checkbox: None,
1986 },
1987 MenuItem::Action {
1988 label: t!("menu.edit.find_next").to_string(),
1989 action: "find_next".to_string(),
1990 args: HashMap::new(),
1991 when: None,
1992 checkbox: None,
1993 },
1994 MenuItem::Action {
1995 label: t!("menu.edit.find_previous").to_string(),
1996 action: "find_previous".to_string(),
1997 args: HashMap::new(),
1998 when: None,
1999 checkbox: None,
2000 },
2001 MenuItem::Action {
2002 label: t!("menu.edit.replace").to_string(),
2003 action: "query_replace".to_string(),
2004 args: HashMap::new(),
2005 when: None,
2006 checkbox: None,
2007 },
2008 MenuItem::Separator { separator: true },
2009 MenuItem::Action {
2010 label: t!("menu.edit.delete_line").to_string(),
2011 action: "delete_line".to_string(),
2012 args: HashMap::new(),
2013 when: None,
2014 checkbox: None,
2015 },
2016 MenuItem::Action {
2017 label: t!("menu.edit.format_buffer").to_string(),
2018 action: "format_buffer".to_string(),
2019 args: HashMap::new(),
2020 when: Some(context_keys::FORMATTER_AVAILABLE.to_string()),
2021 checkbox: None,
2022 },
2023 MenuItem::Separator { separator: true },
2024 MenuItem::Action {
2025 label: t!("menu.edit.settings").to_string(),
2026 action: "open_settings".to_string(),
2027 args: HashMap::new(),
2028 when: None,
2029 checkbox: None,
2030 },
2031 MenuItem::Action {
2032 label: t!("menu.edit.keybinding_editor").to_string(),
2033 action: "open_keybinding_editor".to_string(),
2034 args: HashMap::new(),
2035 when: None,
2036 checkbox: None,
2037 },
2038 ],
2039 },
2040 Menu {
2042 id: Some("View".to_string()),
2043 label: t!("menu.view").to_string(),
2044 when: None,
2045 items: vec![
2046 MenuItem::Action {
2047 label: t!("menu.view.file_explorer").to_string(),
2048 action: "toggle_file_explorer".to_string(),
2049 args: HashMap::new(),
2050 when: None,
2051 checkbox: Some(context_keys::FILE_EXPLORER.to_string()),
2052 },
2053 MenuItem::Separator { separator: true },
2054 MenuItem::Action {
2055 label: t!("menu.view.line_numbers").to_string(),
2056 action: "toggle_line_numbers".to_string(),
2057 args: HashMap::new(),
2058 when: None,
2059 checkbox: Some(context_keys::LINE_NUMBERS.to_string()),
2060 },
2061 MenuItem::Action {
2062 label: t!("menu.view.line_wrap").to_string(),
2063 action: "toggle_line_wrap".to_string(),
2064 args: HashMap::new(),
2065 when: None,
2066 checkbox: Some(context_keys::LINE_WRAP.to_string()),
2067 },
2068 MenuItem::Action {
2069 label: t!("menu.view.mouse_support").to_string(),
2070 action: "toggle_mouse_capture".to_string(),
2071 args: HashMap::new(),
2072 when: None,
2073 checkbox: Some(context_keys::MOUSE_CAPTURE.to_string()),
2074 },
2075 MenuItem::Separator { separator: true },
2076 MenuItem::Action {
2077 label: t!("menu.view.vertical_scrollbar").to_string(),
2078 action: "toggle_vertical_scrollbar".to_string(),
2079 args: HashMap::new(),
2080 when: None,
2081 checkbox: Some(context_keys::VERTICAL_SCROLLBAR.to_string()),
2082 },
2083 MenuItem::Action {
2084 label: t!("menu.view.horizontal_scrollbar").to_string(),
2085 action: "toggle_horizontal_scrollbar".to_string(),
2086 args: HashMap::new(),
2087 when: None,
2088 checkbox: Some(context_keys::HORIZONTAL_SCROLLBAR.to_string()),
2089 },
2090 MenuItem::Separator { separator: true },
2091 MenuItem::Action {
2092 label: t!("menu.view.set_background").to_string(),
2093 action: "set_background".to_string(),
2094 args: HashMap::new(),
2095 when: None,
2096 checkbox: None,
2097 },
2098 MenuItem::Action {
2099 label: t!("menu.view.set_background_blend").to_string(),
2100 action: "set_background_blend".to_string(),
2101 args: HashMap::new(),
2102 when: None,
2103 checkbox: None,
2104 },
2105 MenuItem::Action {
2106 label: t!("menu.view.set_page_width").to_string(),
2107 action: "set_page_width".to_string(),
2108 args: HashMap::new(),
2109 when: None,
2110 checkbox: None,
2111 },
2112 MenuItem::Separator { separator: true },
2113 MenuItem::Action {
2114 label: t!("menu.view.select_theme").to_string(),
2115 action: "select_theme".to_string(),
2116 args: HashMap::new(),
2117 when: None,
2118 checkbox: None,
2119 },
2120 MenuItem::Action {
2121 label: t!("menu.view.select_locale").to_string(),
2122 action: "select_locale".to_string(),
2123 args: HashMap::new(),
2124 when: None,
2125 checkbox: None,
2126 },
2127 MenuItem::Action {
2128 label: t!("menu.view.settings").to_string(),
2129 action: "open_settings".to_string(),
2130 args: HashMap::new(),
2131 when: None,
2132 checkbox: None,
2133 },
2134 MenuItem::Action {
2135 label: t!("menu.view.calibrate_input").to_string(),
2136 action: "calibrate_input".to_string(),
2137 args: HashMap::new(),
2138 when: None,
2139 checkbox: None,
2140 },
2141 MenuItem::Separator { separator: true },
2142 MenuItem::Action {
2143 label: t!("menu.view.split_horizontal").to_string(),
2144 action: "split_horizontal".to_string(),
2145 args: HashMap::new(),
2146 when: None,
2147 checkbox: None,
2148 },
2149 MenuItem::Action {
2150 label: t!("menu.view.split_vertical").to_string(),
2151 action: "split_vertical".to_string(),
2152 args: HashMap::new(),
2153 when: None,
2154 checkbox: None,
2155 },
2156 MenuItem::Action {
2157 label: t!("menu.view.close_split").to_string(),
2158 action: "close_split".to_string(),
2159 args: HashMap::new(),
2160 when: None,
2161 checkbox: None,
2162 },
2163 MenuItem::Action {
2164 label: t!("menu.view.scroll_sync").to_string(),
2165 action: "toggle_scroll_sync".to_string(),
2166 args: HashMap::new(),
2167 when: Some(context_keys::HAS_SAME_BUFFER_SPLITS.to_string()),
2168 checkbox: Some(context_keys::SCROLL_SYNC.to_string()),
2169 },
2170 MenuItem::Action {
2171 label: t!("menu.view.focus_next_split").to_string(),
2172 action: "next_split".to_string(),
2173 args: HashMap::new(),
2174 when: None,
2175 checkbox: None,
2176 },
2177 MenuItem::Action {
2178 label: t!("menu.view.focus_prev_split").to_string(),
2179 action: "prev_split".to_string(),
2180 args: HashMap::new(),
2181 when: None,
2182 checkbox: None,
2183 },
2184 MenuItem::Action {
2185 label: t!("menu.view.toggle_maximize_split").to_string(),
2186 action: "toggle_maximize_split".to_string(),
2187 args: HashMap::new(),
2188 when: None,
2189 checkbox: None,
2190 },
2191 MenuItem::Separator { separator: true },
2192 MenuItem::Submenu {
2193 label: t!("menu.terminal").to_string(),
2194 items: vec![
2195 MenuItem::Action {
2196 label: t!("menu.terminal.open").to_string(),
2197 action: "open_terminal".to_string(),
2198 args: HashMap::new(),
2199 when: None,
2200 checkbox: None,
2201 },
2202 MenuItem::Action {
2203 label: t!("menu.terminal.close").to_string(),
2204 action: "close_terminal".to_string(),
2205 args: HashMap::new(),
2206 when: None,
2207 checkbox: None,
2208 },
2209 MenuItem::Separator { separator: true },
2210 MenuItem::Action {
2211 label: t!("menu.terminal.toggle_keyboard_capture").to_string(),
2212 action: "toggle_keyboard_capture".to_string(),
2213 args: HashMap::new(),
2214 when: None,
2215 checkbox: None,
2216 },
2217 ],
2218 },
2219 MenuItem::Separator { separator: true },
2220 MenuItem::Submenu {
2221 label: t!("menu.view.keybinding_style").to_string(),
2222 items: vec![
2223 MenuItem::Action {
2224 label: t!("menu.view.keybinding_default").to_string(),
2225 action: "switch_keybinding_map".to_string(),
2226 args: {
2227 let mut map = HashMap::new();
2228 map.insert("map".to_string(), serde_json::json!("default"));
2229 map
2230 },
2231 when: None,
2232 checkbox: Some(context_keys::KEYMAP_DEFAULT.to_string()),
2233 },
2234 MenuItem::Action {
2235 label: t!("menu.view.keybinding_emacs").to_string(),
2236 action: "switch_keybinding_map".to_string(),
2237 args: {
2238 let mut map = HashMap::new();
2239 map.insert("map".to_string(), serde_json::json!("emacs"));
2240 map
2241 },
2242 when: None,
2243 checkbox: Some(context_keys::KEYMAP_EMACS.to_string()),
2244 },
2245 MenuItem::Action {
2246 label: t!("menu.view.keybinding_vscode").to_string(),
2247 action: "switch_keybinding_map".to_string(),
2248 args: {
2249 let mut map = HashMap::new();
2250 map.insert("map".to_string(), serde_json::json!("vscode"));
2251 map
2252 },
2253 when: None,
2254 checkbox: Some(context_keys::KEYMAP_VSCODE.to_string()),
2255 },
2256 MenuItem::Action {
2257 label: "macOS GUI (⌘)".to_string(),
2258 action: "switch_keybinding_map".to_string(),
2259 args: {
2260 let mut map = HashMap::new();
2261 map.insert("map".to_string(), serde_json::json!("macos-gui"));
2262 map
2263 },
2264 when: None,
2265 checkbox: Some(context_keys::KEYMAP_MACOS_GUI.to_string()),
2266 },
2267 ],
2268 },
2269 ],
2270 },
2271 Menu {
2273 id: Some("Selection".to_string()),
2274 label: t!("menu.selection").to_string(),
2275 when: None,
2276 items: vec![
2277 MenuItem::Action {
2278 label: t!("menu.selection.select_all").to_string(),
2279 action: "select_all".to_string(),
2280 args: HashMap::new(),
2281 when: None,
2282 checkbox: None,
2283 },
2284 MenuItem::Action {
2285 label: t!("menu.selection.select_word").to_string(),
2286 action: "select_word".to_string(),
2287 args: HashMap::new(),
2288 when: None,
2289 checkbox: None,
2290 },
2291 MenuItem::Action {
2292 label: t!("menu.selection.select_line").to_string(),
2293 action: "select_line".to_string(),
2294 args: HashMap::new(),
2295 when: None,
2296 checkbox: None,
2297 },
2298 MenuItem::Action {
2299 label: t!("menu.selection.expand_selection").to_string(),
2300 action: "expand_selection".to_string(),
2301 args: HashMap::new(),
2302 when: None,
2303 checkbox: None,
2304 },
2305 MenuItem::Separator { separator: true },
2306 MenuItem::Action {
2307 label: t!("menu.selection.add_cursor_above").to_string(),
2308 action: "add_cursor_above".to_string(),
2309 args: HashMap::new(),
2310 when: None,
2311 checkbox: None,
2312 },
2313 MenuItem::Action {
2314 label: t!("menu.selection.add_cursor_below").to_string(),
2315 action: "add_cursor_below".to_string(),
2316 args: HashMap::new(),
2317 when: None,
2318 checkbox: None,
2319 },
2320 MenuItem::Action {
2321 label: t!("menu.selection.add_cursor_next_match").to_string(),
2322 action: "add_cursor_next_match".to_string(),
2323 args: HashMap::new(),
2324 when: None,
2325 checkbox: None,
2326 },
2327 MenuItem::Action {
2328 label: t!("menu.selection.remove_secondary_cursors").to_string(),
2329 action: "remove_secondary_cursors".to_string(),
2330 args: HashMap::new(),
2331 when: None,
2332 checkbox: None,
2333 },
2334 ],
2335 },
2336 Menu {
2338 id: Some("Go".to_string()),
2339 label: t!("menu.go").to_string(),
2340 when: None,
2341 items: vec![
2342 MenuItem::Action {
2343 label: t!("menu.go.goto_line").to_string(),
2344 action: "goto_line".to_string(),
2345 args: HashMap::new(),
2346 when: None,
2347 checkbox: None,
2348 },
2349 MenuItem::Action {
2350 label: t!("menu.go.goto_definition").to_string(),
2351 action: "lsp_goto_definition".to_string(),
2352 args: HashMap::new(),
2353 when: None,
2354 checkbox: None,
2355 },
2356 MenuItem::Action {
2357 label: t!("menu.go.find_references").to_string(),
2358 action: "lsp_references".to_string(),
2359 args: HashMap::new(),
2360 when: None,
2361 checkbox: None,
2362 },
2363 MenuItem::Separator { separator: true },
2364 MenuItem::Action {
2365 label: t!("menu.go.next_buffer").to_string(),
2366 action: "next_buffer".to_string(),
2367 args: HashMap::new(),
2368 when: None,
2369 checkbox: None,
2370 },
2371 MenuItem::Action {
2372 label: t!("menu.go.prev_buffer").to_string(),
2373 action: "prev_buffer".to_string(),
2374 args: HashMap::new(),
2375 when: None,
2376 checkbox: None,
2377 },
2378 MenuItem::Separator { separator: true },
2379 MenuItem::Action {
2380 label: t!("menu.go.command_palette").to_string(),
2381 action: "command_palette".to_string(),
2382 args: HashMap::new(),
2383 when: None,
2384 checkbox: None,
2385 },
2386 ],
2387 },
2388 Menu {
2390 id: Some("LSP".to_string()),
2391 label: t!("menu.lsp").to_string(),
2392 when: None,
2393 items: vec![
2394 MenuItem::Action {
2395 label: t!("menu.lsp.show_hover").to_string(),
2396 action: "lsp_hover".to_string(),
2397 args: HashMap::new(),
2398 when: Some(context_keys::LSP_AVAILABLE.to_string()),
2399 checkbox: None,
2400 },
2401 MenuItem::Action {
2402 label: t!("menu.lsp.goto_definition").to_string(),
2403 action: "lsp_goto_definition".to_string(),
2404 args: HashMap::new(),
2405 when: Some(context_keys::LSP_AVAILABLE.to_string()),
2406 checkbox: None,
2407 },
2408 MenuItem::Action {
2409 label: t!("menu.lsp.find_references").to_string(),
2410 action: "lsp_references".to_string(),
2411 args: HashMap::new(),
2412 when: Some(context_keys::LSP_AVAILABLE.to_string()),
2413 checkbox: None,
2414 },
2415 MenuItem::Action {
2416 label: t!("menu.lsp.rename_symbol").to_string(),
2417 action: "lsp_rename".to_string(),
2418 args: HashMap::new(),
2419 when: Some(context_keys::LSP_AVAILABLE.to_string()),
2420 checkbox: None,
2421 },
2422 MenuItem::Separator { separator: true },
2423 MenuItem::Action {
2424 label: t!("menu.lsp.show_completions").to_string(),
2425 action: "lsp_completion".to_string(),
2426 args: HashMap::new(),
2427 when: Some(context_keys::LSP_AVAILABLE.to_string()),
2428 checkbox: None,
2429 },
2430 MenuItem::Action {
2431 label: t!("menu.lsp.show_signature").to_string(),
2432 action: "lsp_signature_help".to_string(),
2433 args: HashMap::new(),
2434 when: Some(context_keys::LSP_AVAILABLE.to_string()),
2435 checkbox: None,
2436 },
2437 MenuItem::Action {
2438 label: t!("menu.lsp.code_actions").to_string(),
2439 action: "lsp_code_actions".to_string(),
2440 args: HashMap::new(),
2441 when: Some(context_keys::LSP_AVAILABLE.to_string()),
2442 checkbox: None,
2443 },
2444 MenuItem::Separator { separator: true },
2445 MenuItem::Action {
2446 label: t!("menu.lsp.toggle_inlay_hints").to_string(),
2447 action: "toggle_inlay_hints".to_string(),
2448 args: HashMap::new(),
2449 when: Some(context_keys::LSP_AVAILABLE.to_string()),
2450 checkbox: Some(context_keys::INLAY_HINTS.to_string()),
2451 },
2452 MenuItem::Action {
2453 label: t!("menu.lsp.toggle_mouse_hover").to_string(),
2454 action: "toggle_mouse_hover".to_string(),
2455 args: HashMap::new(),
2456 when: None,
2457 checkbox: Some(context_keys::MOUSE_HOVER.to_string()),
2458 },
2459 MenuItem::Separator { separator: true },
2460 MenuItem::Action {
2461 label: t!("menu.lsp.restart_server").to_string(),
2462 action: "lsp_restart".to_string(),
2463 args: HashMap::new(),
2464 when: None,
2465 checkbox: None,
2466 },
2467 MenuItem::Action {
2468 label: t!("menu.lsp.stop_server").to_string(),
2469 action: "lsp_stop".to_string(),
2470 args: HashMap::new(),
2471 when: None,
2472 checkbox: None,
2473 },
2474 MenuItem::Separator { separator: true },
2475 MenuItem::Action {
2476 label: t!("menu.lsp.toggle_for_buffer").to_string(),
2477 action: "lsp_toggle_for_buffer".to_string(),
2478 args: HashMap::new(),
2479 when: None,
2480 checkbox: None,
2481 },
2482 ],
2483 },
2484 Menu {
2486 id: Some("Explorer".to_string()),
2487 label: t!("menu.explorer").to_string(),
2488 when: Some(context_keys::FILE_EXPLORER_FOCUSED.to_string()),
2489 items: vec![
2490 MenuItem::Action {
2491 label: t!("menu.explorer.new_file").to_string(),
2492 action: "file_explorer_new_file".to_string(),
2493 args: HashMap::new(),
2494 when: Some(context_keys::FILE_EXPLORER_FOCUSED.to_string()),
2495 checkbox: None,
2496 },
2497 MenuItem::Action {
2498 label: t!("menu.explorer.new_folder").to_string(),
2499 action: "file_explorer_new_directory".to_string(),
2500 args: HashMap::new(),
2501 when: Some(context_keys::FILE_EXPLORER_FOCUSED.to_string()),
2502 checkbox: None,
2503 },
2504 MenuItem::Separator { separator: true },
2505 MenuItem::Action {
2506 label: t!("menu.explorer.open").to_string(),
2507 action: "file_explorer_open".to_string(),
2508 args: HashMap::new(),
2509 when: Some(context_keys::FILE_EXPLORER_FOCUSED.to_string()),
2510 checkbox: None,
2511 },
2512 MenuItem::Action {
2513 label: t!("menu.explorer.rename").to_string(),
2514 action: "file_explorer_rename".to_string(),
2515 args: HashMap::new(),
2516 when: Some(context_keys::FILE_EXPLORER_FOCUSED.to_string()),
2517 checkbox: None,
2518 },
2519 MenuItem::Action {
2520 label: t!("menu.explorer.delete").to_string(),
2521 action: "file_explorer_delete".to_string(),
2522 args: HashMap::new(),
2523 when: Some(context_keys::FILE_EXPLORER_FOCUSED.to_string()),
2524 checkbox: None,
2525 },
2526 MenuItem::Separator { separator: true },
2527 MenuItem::Action {
2528 label: t!("menu.explorer.refresh").to_string(),
2529 action: "file_explorer_refresh".to_string(),
2530 args: HashMap::new(),
2531 when: Some(context_keys::FILE_EXPLORER_FOCUSED.to_string()),
2532 checkbox: None,
2533 },
2534 MenuItem::Separator { separator: true },
2535 MenuItem::Action {
2536 label: t!("menu.explorer.show_hidden").to_string(),
2537 action: "file_explorer_toggle_hidden".to_string(),
2538 args: HashMap::new(),
2539 when: Some(context_keys::FILE_EXPLORER.to_string()),
2540 checkbox: Some(context_keys::FILE_EXPLORER_SHOW_HIDDEN.to_string()),
2541 },
2542 MenuItem::Action {
2543 label: t!("menu.explorer.show_gitignored").to_string(),
2544 action: "file_explorer_toggle_gitignored".to_string(),
2545 args: HashMap::new(),
2546 when: Some(context_keys::FILE_EXPLORER.to_string()),
2547 checkbox: Some(context_keys::FILE_EXPLORER_SHOW_GITIGNORED.to_string()),
2548 },
2549 ],
2550 },
2551 Menu {
2553 id: Some("Help".to_string()),
2554 label: t!("menu.help").to_string(),
2555 when: None,
2556 items: vec![
2557 MenuItem::Label {
2558 info: format!("Fresh v{}", env!("CARGO_PKG_VERSION")),
2559 },
2560 MenuItem::Separator { separator: true },
2561 MenuItem::Action {
2562 label: t!("menu.help.show_manual").to_string(),
2563 action: "show_help".to_string(),
2564 args: HashMap::new(),
2565 when: None,
2566 checkbox: None,
2567 },
2568 MenuItem::Action {
2569 label: t!("menu.help.keyboard_shortcuts").to_string(),
2570 action: "keyboard_shortcuts".to_string(),
2571 args: HashMap::new(),
2572 when: None,
2573 checkbox: None,
2574 },
2575 MenuItem::Separator { separator: true },
2576 MenuItem::Action {
2577 label: t!("menu.help.event_debug").to_string(),
2578 action: "event_debug".to_string(),
2579 args: HashMap::new(),
2580 when: None,
2581 checkbox: None,
2582 },
2583 ],
2584 },
2585 ]
2586 }
2587}
2588
2589impl Config {
2590 pub(crate) const FILENAME: &'static str = "config.json";
2592
2593 pub(crate) fn local_config_path(working_dir: &Path) -> std::path::PathBuf {
2595 working_dir.join(Self::FILENAME)
2596 }
2597
2598 pub fn load_from_file<P: AsRef<Path>>(path: P) -> Result<Self, ConfigError> {
2604 let contents = std::fs::read_to_string(path.as_ref())
2605 .map_err(|e| ConfigError::IoError(e.to_string()))?;
2606
2607 let partial: crate::partial_config::PartialConfig =
2609 serde_json::from_str(&contents).map_err(|e| ConfigError::ParseError(e.to_string()))?;
2610
2611 Ok(partial.resolve())
2612 }
2613
2614 fn load_builtin_keymap(name: &str) -> Option<KeymapConfig> {
2616 let json_content = match name {
2617 "default" => include_str!("../keymaps/default.json"),
2618 "emacs" => include_str!("../keymaps/emacs.json"),
2619 "vscode" => include_str!("../keymaps/vscode.json"),
2620 "macos" => include_str!("../keymaps/macos.json"),
2621 "macos-gui" => include_str!("../keymaps/macos-gui.json"),
2622 _ => return None,
2623 };
2624
2625 match serde_json::from_str(json_content) {
2626 Ok(config) => Some(config),
2627 Err(e) => {
2628 eprintln!("Failed to parse builtin keymap '{}': {}", name, e);
2629 None
2630 }
2631 }
2632 }
2633
2634 pub fn resolve_keymap(&self, map_name: &str) -> Vec<Keybinding> {
2637 let mut visited = std::collections::HashSet::new();
2638 self.resolve_keymap_recursive(map_name, &mut visited)
2639 }
2640
2641 fn resolve_keymap_recursive(
2643 &self,
2644 map_name: &str,
2645 visited: &mut std::collections::HashSet<String>,
2646 ) -> Vec<Keybinding> {
2647 if visited.contains(map_name) {
2649 eprintln!(
2650 "Warning: Circular inheritance detected in keymap '{}'",
2651 map_name
2652 );
2653 return Vec::new();
2654 }
2655 visited.insert(map_name.to_string());
2656
2657 let keymap = self
2659 .keybinding_maps
2660 .get(map_name)
2661 .cloned()
2662 .or_else(|| Self::load_builtin_keymap(map_name));
2663
2664 let Some(keymap) = keymap else {
2665 return Vec::new();
2666 };
2667
2668 let mut all_bindings = if let Some(ref parent_name) = keymap.inherits {
2670 self.resolve_keymap_recursive(parent_name, visited)
2671 } else {
2672 Vec::new()
2673 };
2674
2675 all_bindings.extend(keymap.bindings);
2677
2678 all_bindings
2679 }
2680 fn default_languages() -> HashMap<String, LanguageConfig> {
2682 let mut languages = HashMap::new();
2683
2684 languages.insert(
2685 "rust".to_string(),
2686 LanguageConfig {
2687 extensions: vec!["rs".to_string()],
2688 filenames: vec![],
2689 grammar: "rust".to_string(),
2690 comment_prefix: Some("//".to_string()),
2691 auto_indent: true,
2692 auto_close: None,
2693 auto_surround: None,
2694 textmate_grammar: None,
2695 show_whitespace_tabs: true,
2696 line_wrap: None,
2697 wrap_column: None,
2698 page_view: None,
2699 page_width: None,
2700 use_tabs: None,
2701 tab_size: None,
2702 formatter: Some(FormatterConfig {
2703 command: "rustfmt".to_string(),
2704 args: vec!["--edition".to_string(), "2021".to_string()],
2705 stdin: true,
2706 timeout_ms: 10000,
2707 }),
2708 format_on_save: false,
2709 on_save: vec![],
2710 word_characters: None,
2711 },
2712 );
2713
2714 languages.insert(
2715 "javascript".to_string(),
2716 LanguageConfig {
2717 extensions: vec!["js".to_string(), "jsx".to_string(), "mjs".to_string()],
2718 filenames: vec![],
2719 grammar: "javascript".to_string(),
2720 comment_prefix: Some("//".to_string()),
2721 auto_indent: true,
2722 auto_close: None,
2723 auto_surround: None,
2724 textmate_grammar: None,
2725 show_whitespace_tabs: true,
2726 line_wrap: None,
2727 wrap_column: None,
2728 page_view: None,
2729 page_width: None,
2730 use_tabs: None,
2731 tab_size: None,
2732 formatter: Some(FormatterConfig {
2733 command: "prettier".to_string(),
2734 args: vec!["--stdin-filepath".to_string(), "$FILE".to_string()],
2735 stdin: true,
2736 timeout_ms: 10000,
2737 }),
2738 format_on_save: false,
2739 on_save: vec![],
2740 word_characters: None,
2741 },
2742 );
2743
2744 languages.insert(
2745 "typescript".to_string(),
2746 LanguageConfig {
2747 extensions: vec!["ts".to_string(), "tsx".to_string(), "mts".to_string()],
2748 filenames: vec![],
2749 grammar: "typescript".to_string(),
2750 comment_prefix: Some("//".to_string()),
2751 auto_indent: true,
2752 auto_close: None,
2753 auto_surround: None,
2754 textmate_grammar: None,
2755 show_whitespace_tabs: true,
2756 line_wrap: None,
2757 wrap_column: None,
2758 page_view: None,
2759 page_width: None,
2760 use_tabs: None,
2761 tab_size: None,
2762 formatter: Some(FormatterConfig {
2763 command: "prettier".to_string(),
2764 args: vec!["--stdin-filepath".to_string(), "$FILE".to_string()],
2765 stdin: true,
2766 timeout_ms: 10000,
2767 }),
2768 format_on_save: false,
2769 on_save: vec![],
2770 word_characters: None,
2771 },
2772 );
2773
2774 languages.insert(
2775 "python".to_string(),
2776 LanguageConfig {
2777 extensions: vec!["py".to_string(), "pyi".to_string()],
2778 filenames: vec![],
2779 grammar: "python".to_string(),
2780 comment_prefix: Some("#".to_string()),
2781 auto_indent: true,
2782 auto_close: None,
2783 auto_surround: None,
2784 textmate_grammar: None,
2785 show_whitespace_tabs: true,
2786 line_wrap: None,
2787 wrap_column: None,
2788 page_view: None,
2789 page_width: None,
2790 use_tabs: None,
2791 tab_size: None,
2792 formatter: Some(FormatterConfig {
2793 command: "ruff".to_string(),
2794 args: vec![
2795 "format".to_string(),
2796 "--stdin-filename".to_string(),
2797 "$FILE".to_string(),
2798 ],
2799 stdin: true,
2800 timeout_ms: 10000,
2801 }),
2802 format_on_save: false,
2803 on_save: vec![],
2804 word_characters: None,
2805 },
2806 );
2807
2808 languages.insert(
2809 "c".to_string(),
2810 LanguageConfig {
2811 extensions: vec!["c".to_string(), "h".to_string()],
2812 filenames: vec![],
2813 grammar: "c".to_string(),
2814 comment_prefix: Some("//".to_string()),
2815 auto_indent: true,
2816 auto_close: None,
2817 auto_surround: None,
2818 textmate_grammar: None,
2819 show_whitespace_tabs: true,
2820 line_wrap: None,
2821 wrap_column: None,
2822 page_view: None,
2823 page_width: None,
2824 use_tabs: None,
2825 tab_size: None,
2826 formatter: Some(FormatterConfig {
2827 command: "clang-format".to_string(),
2828 args: vec![],
2829 stdin: true,
2830 timeout_ms: 10000,
2831 }),
2832 format_on_save: false,
2833 on_save: vec![],
2834 word_characters: None,
2835 },
2836 );
2837
2838 languages.insert(
2839 "cpp".to_string(),
2840 LanguageConfig {
2841 extensions: vec![
2842 "cpp".to_string(),
2843 "cc".to_string(),
2844 "cxx".to_string(),
2845 "hpp".to_string(),
2846 "hh".to_string(),
2847 "hxx".to_string(),
2848 ],
2849 filenames: vec![],
2850 grammar: "cpp".to_string(),
2851 comment_prefix: Some("//".to_string()),
2852 auto_indent: true,
2853 auto_close: None,
2854 auto_surround: None,
2855 textmate_grammar: None,
2856 show_whitespace_tabs: true,
2857 line_wrap: None,
2858 wrap_column: None,
2859 page_view: None,
2860 page_width: None,
2861 use_tabs: None,
2862 tab_size: None,
2863 formatter: Some(FormatterConfig {
2864 command: "clang-format".to_string(),
2865 args: vec![],
2866 stdin: true,
2867 timeout_ms: 10000,
2868 }),
2869 format_on_save: false,
2870 on_save: vec![],
2871 word_characters: None,
2872 },
2873 );
2874
2875 languages.insert(
2876 "csharp".to_string(),
2877 LanguageConfig {
2878 extensions: vec!["cs".to_string()],
2879 filenames: vec![],
2880 grammar: "C#".to_string(),
2881 comment_prefix: Some("//".to_string()),
2882 auto_indent: true,
2883 auto_close: None,
2884 auto_surround: None,
2885 textmate_grammar: None,
2886 show_whitespace_tabs: true,
2887 line_wrap: None,
2888 wrap_column: None,
2889 page_view: None,
2890 page_width: None,
2891 use_tabs: None,
2892 tab_size: None,
2893 formatter: None,
2894 format_on_save: false,
2895 on_save: vec![],
2896 word_characters: None,
2897 },
2898 );
2899
2900 languages.insert(
2901 "bash".to_string(),
2902 LanguageConfig {
2903 extensions: vec!["sh".to_string(), "bash".to_string()],
2904 filenames: vec![
2905 ".bash_aliases".to_string(),
2906 ".bash_logout".to_string(),
2907 ".bash_profile".to_string(),
2908 ".bashrc".to_string(),
2909 ".env".to_string(),
2910 ".profile".to_string(),
2911 ".zlogin".to_string(),
2912 ".zlogout".to_string(),
2913 ".zprofile".to_string(),
2914 ".zshenv".to_string(),
2915 ".zshrc".to_string(),
2916 "PKGBUILD".to_string(),
2918 "APKBUILD".to_string(),
2919 ],
2920 grammar: "bash".to_string(),
2921 comment_prefix: Some("#".to_string()),
2922 auto_indent: true,
2923 auto_close: None,
2924 auto_surround: None,
2925 textmate_grammar: None,
2926 show_whitespace_tabs: true,
2927 line_wrap: None,
2928 wrap_column: None,
2929 page_view: None,
2930 page_width: None,
2931 use_tabs: None,
2932 tab_size: None,
2933 formatter: None,
2934 format_on_save: false,
2935 on_save: vec![],
2936 word_characters: None,
2937 },
2938 );
2939
2940 languages.insert(
2941 "makefile".to_string(),
2942 LanguageConfig {
2943 extensions: vec!["mk".to_string()],
2944 filenames: vec![
2945 "Makefile".to_string(),
2946 "makefile".to_string(),
2947 "GNUmakefile".to_string(),
2948 ],
2949 grammar: "Makefile".to_string(),
2950 comment_prefix: Some("#".to_string()),
2951 auto_indent: false,
2952 auto_close: None,
2953 auto_surround: None,
2954 textmate_grammar: None,
2955 show_whitespace_tabs: true,
2956 line_wrap: None,
2957 wrap_column: None,
2958 page_view: None,
2959 page_width: None,
2960 use_tabs: Some(true), tab_size: Some(8), formatter: None,
2963 format_on_save: false,
2964 on_save: vec![],
2965 word_characters: None,
2966 },
2967 );
2968
2969 languages.insert(
2970 "dockerfile".to_string(),
2971 LanguageConfig {
2972 extensions: vec!["dockerfile".to_string()],
2973 filenames: vec!["Dockerfile".to_string(), "Containerfile".to_string()],
2974 grammar: "dockerfile".to_string(),
2975 comment_prefix: Some("#".to_string()),
2976 auto_indent: true,
2977 auto_close: None,
2978 auto_surround: None,
2979 textmate_grammar: None,
2980 show_whitespace_tabs: true,
2981 line_wrap: None,
2982 wrap_column: None,
2983 page_view: None,
2984 page_width: None,
2985 use_tabs: None,
2986 tab_size: None,
2987 formatter: None,
2988 format_on_save: false,
2989 on_save: vec![],
2990 word_characters: None,
2991 },
2992 );
2993
2994 languages.insert(
2995 "json".to_string(),
2996 LanguageConfig {
2997 extensions: vec!["json".to_string(), "jsonc".to_string()],
2998 filenames: vec![],
2999 grammar: "json".to_string(),
3000 comment_prefix: None,
3001 auto_indent: true,
3002 auto_close: None,
3003 auto_surround: None,
3004 textmate_grammar: None,
3005 show_whitespace_tabs: true,
3006 line_wrap: None,
3007 wrap_column: None,
3008 page_view: None,
3009 page_width: None,
3010 use_tabs: None,
3011 tab_size: None,
3012 formatter: Some(FormatterConfig {
3013 command: "prettier".to_string(),
3014 args: vec!["--stdin-filepath".to_string(), "$FILE".to_string()],
3015 stdin: true,
3016 timeout_ms: 10000,
3017 }),
3018 format_on_save: false,
3019 on_save: vec![],
3020 word_characters: None,
3021 },
3022 );
3023
3024 languages.insert(
3025 "toml".to_string(),
3026 LanguageConfig {
3027 extensions: vec!["toml".to_string()],
3028 filenames: vec!["Cargo.lock".to_string()],
3029 grammar: "toml".to_string(),
3030 comment_prefix: Some("#".to_string()),
3031 auto_indent: true,
3032 auto_close: None,
3033 auto_surround: None,
3034 textmate_grammar: None,
3035 show_whitespace_tabs: true,
3036 line_wrap: None,
3037 wrap_column: None,
3038 page_view: None,
3039 page_width: None,
3040 use_tabs: None,
3041 tab_size: None,
3042 formatter: None,
3043 format_on_save: false,
3044 on_save: vec![],
3045 word_characters: None,
3046 },
3047 );
3048
3049 languages.insert(
3050 "yaml".to_string(),
3051 LanguageConfig {
3052 extensions: vec!["yml".to_string(), "yaml".to_string()],
3053 filenames: vec![],
3054 grammar: "yaml".to_string(),
3055 comment_prefix: Some("#".to_string()),
3056 auto_indent: true,
3057 auto_close: None,
3058 auto_surround: None,
3059 textmate_grammar: None,
3060 show_whitespace_tabs: true,
3061 line_wrap: None,
3062 wrap_column: None,
3063 page_view: None,
3064 page_width: None,
3065 use_tabs: None,
3066 tab_size: None,
3067 formatter: Some(FormatterConfig {
3068 command: "prettier".to_string(),
3069 args: vec!["--stdin-filepath".to_string(), "$FILE".to_string()],
3070 stdin: true,
3071 timeout_ms: 10000,
3072 }),
3073 format_on_save: false,
3074 on_save: vec![],
3075 word_characters: None,
3076 },
3077 );
3078
3079 languages.insert(
3080 "markdown".to_string(),
3081 LanguageConfig {
3082 extensions: vec!["md".to_string(), "markdown".to_string()],
3083 filenames: vec!["README".to_string()],
3084 grammar: "markdown".to_string(),
3085 comment_prefix: None,
3086 auto_indent: false,
3087 auto_close: None,
3088 auto_surround: None,
3089 textmate_grammar: None,
3090 show_whitespace_tabs: true,
3091 line_wrap: None,
3092 wrap_column: None,
3093 page_view: None,
3094 page_width: None,
3095 use_tabs: None,
3096 tab_size: None,
3097 formatter: None,
3098 format_on_save: false,
3099 on_save: vec![],
3100 word_characters: None,
3101 },
3102 );
3103
3104 languages.insert(
3106 "go".to_string(),
3107 LanguageConfig {
3108 extensions: vec!["go".to_string()],
3109 filenames: vec![],
3110 grammar: "go".to_string(),
3111 comment_prefix: Some("//".to_string()),
3112 auto_indent: true,
3113 auto_close: None,
3114 auto_surround: None,
3115 textmate_grammar: None,
3116 show_whitespace_tabs: false,
3117 line_wrap: None,
3118 wrap_column: None,
3119 page_view: None,
3120 page_width: None,
3121 use_tabs: Some(true), tab_size: Some(8), formatter: Some(FormatterConfig {
3124 command: "gofmt".to_string(),
3125 args: vec![],
3126 stdin: true,
3127 timeout_ms: 10000,
3128 }),
3129 format_on_save: false,
3130 on_save: vec![],
3131 word_characters: None,
3132 },
3133 );
3134
3135 languages.insert(
3136 "odin".to_string(),
3137 LanguageConfig {
3138 extensions: vec!["odin".to_string()],
3139 filenames: vec![],
3140 grammar: "odin".to_string(),
3141 comment_prefix: Some("//".to_string()),
3142 auto_indent: true,
3143 auto_close: None,
3144 auto_surround: None,
3145 textmate_grammar: None,
3146 show_whitespace_tabs: false,
3147 line_wrap: None,
3148 wrap_column: None,
3149 page_view: None,
3150 page_width: None,
3151 use_tabs: Some(true),
3152 tab_size: Some(8),
3153 formatter: None,
3154 format_on_save: false,
3155 on_save: vec![],
3156 word_characters: None,
3157 },
3158 );
3159
3160 languages.insert(
3161 "zig".to_string(),
3162 LanguageConfig {
3163 extensions: vec!["zig".to_string(), "zon".to_string()],
3164 filenames: vec![],
3165 grammar: "zig".to_string(),
3166 comment_prefix: Some("//".to_string()),
3167 auto_indent: true,
3168 auto_close: None,
3169 auto_surround: None,
3170 textmate_grammar: None,
3171 show_whitespace_tabs: true,
3172 line_wrap: None,
3173 wrap_column: None,
3174 page_view: None,
3175 page_width: None,
3176 use_tabs: None,
3177 tab_size: None,
3178 formatter: None,
3179 format_on_save: false,
3180 on_save: vec![],
3181 word_characters: None,
3182 },
3183 );
3184
3185 languages.insert(
3186 "java".to_string(),
3187 LanguageConfig {
3188 extensions: vec!["java".to_string()],
3189 filenames: vec![],
3190 grammar: "java".to_string(),
3191 comment_prefix: Some("//".to_string()),
3192 auto_indent: true,
3193 auto_close: None,
3194 auto_surround: None,
3195 textmate_grammar: None,
3196 show_whitespace_tabs: true,
3197 line_wrap: None,
3198 wrap_column: None,
3199 page_view: None,
3200 page_width: None,
3201 use_tabs: None,
3202 tab_size: None,
3203 formatter: None,
3204 format_on_save: false,
3205 on_save: vec![],
3206 word_characters: None,
3207 },
3208 );
3209
3210 languages.insert(
3211 "latex".to_string(),
3212 LanguageConfig {
3213 extensions: vec![
3214 "tex".to_string(),
3215 "latex".to_string(),
3216 "ltx".to_string(),
3217 "sty".to_string(),
3218 "cls".to_string(),
3219 "bib".to_string(),
3220 ],
3221 filenames: vec![],
3222 grammar: "latex".to_string(),
3223 comment_prefix: Some("%".to_string()),
3224 auto_indent: true,
3225 auto_close: None,
3226 auto_surround: None,
3227 textmate_grammar: None,
3228 show_whitespace_tabs: true,
3229 line_wrap: None,
3230 wrap_column: None,
3231 page_view: None,
3232 page_width: None,
3233 use_tabs: None,
3234 tab_size: None,
3235 formatter: None,
3236 format_on_save: false,
3237 on_save: vec![],
3238 word_characters: None,
3239 },
3240 );
3241
3242 languages.insert(
3243 "templ".to_string(),
3244 LanguageConfig {
3245 extensions: vec!["templ".to_string()],
3246 filenames: vec![],
3247 grammar: "go".to_string(), comment_prefix: Some("//".to_string()),
3249 auto_indent: true,
3250 auto_close: None,
3251 auto_surround: None,
3252 textmate_grammar: None,
3253 show_whitespace_tabs: true,
3254 line_wrap: None,
3255 wrap_column: None,
3256 page_view: None,
3257 page_width: None,
3258 use_tabs: None,
3259 tab_size: None,
3260 formatter: None,
3261 format_on_save: false,
3262 on_save: vec![],
3263 word_characters: None,
3264 },
3265 );
3266
3267 languages.insert(
3269 "git-rebase".to_string(),
3270 LanguageConfig {
3271 extensions: vec![],
3272 filenames: vec!["git-rebase-todo".to_string()],
3273 grammar: "Git Rebase Todo".to_string(),
3274 comment_prefix: Some("#".to_string()),
3275 auto_indent: false,
3276 auto_close: None,
3277 auto_surround: None,
3278 textmate_grammar: None,
3279 show_whitespace_tabs: true,
3280 line_wrap: None,
3281 wrap_column: None,
3282 page_view: None,
3283 page_width: None,
3284 use_tabs: None,
3285 tab_size: None,
3286 formatter: None,
3287 format_on_save: false,
3288 on_save: vec![],
3289 word_characters: None,
3290 },
3291 );
3292
3293 languages.insert(
3294 "git-commit".to_string(),
3295 LanguageConfig {
3296 extensions: vec![],
3297 filenames: vec![
3298 "COMMIT_EDITMSG".to_string(),
3299 "MERGE_MSG".to_string(),
3300 "SQUASH_MSG".to_string(),
3301 "TAG_EDITMSG".to_string(),
3302 ],
3303 grammar: "Git Commit Message".to_string(),
3304 comment_prefix: Some("#".to_string()),
3305 auto_indent: false,
3306 auto_close: None,
3307 auto_surround: None,
3308 textmate_grammar: None,
3309 show_whitespace_tabs: true,
3310 line_wrap: None,
3311 wrap_column: None,
3312 page_view: None,
3313 page_width: None,
3314 use_tabs: None,
3315 tab_size: None,
3316 formatter: None,
3317 format_on_save: false,
3318 on_save: vec![],
3319 word_characters: None,
3320 },
3321 );
3322
3323 languages.insert(
3324 "gitignore".to_string(),
3325 LanguageConfig {
3326 extensions: vec!["gitignore".to_string()],
3327 filenames: vec![
3328 ".gitignore".to_string(),
3329 ".dockerignore".to_string(),
3330 ".npmignore".to_string(),
3331 ".hgignore".to_string(),
3332 ],
3333 grammar: "Gitignore".to_string(),
3334 comment_prefix: Some("#".to_string()),
3335 auto_indent: false,
3336 auto_close: None,
3337 auto_surround: None,
3338 textmate_grammar: None,
3339 show_whitespace_tabs: true,
3340 line_wrap: None,
3341 wrap_column: None,
3342 page_view: None,
3343 page_width: None,
3344 use_tabs: None,
3345 tab_size: None,
3346 formatter: None,
3347 format_on_save: false,
3348 on_save: vec![],
3349 word_characters: None,
3350 },
3351 );
3352
3353 languages.insert(
3354 "gitconfig".to_string(),
3355 LanguageConfig {
3356 extensions: vec!["gitconfig".to_string()],
3357 filenames: vec![".gitconfig".to_string(), ".gitmodules".to_string()],
3358 grammar: "Git Config".to_string(),
3359 comment_prefix: Some("#".to_string()),
3360 auto_indent: true,
3361 auto_close: None,
3362 auto_surround: None,
3363 textmate_grammar: None,
3364 show_whitespace_tabs: true,
3365 line_wrap: None,
3366 wrap_column: None,
3367 page_view: None,
3368 page_width: None,
3369 use_tabs: None,
3370 tab_size: None,
3371 formatter: None,
3372 format_on_save: false,
3373 on_save: vec![],
3374 word_characters: None,
3375 },
3376 );
3377
3378 languages.insert(
3379 "gitattributes".to_string(),
3380 LanguageConfig {
3381 extensions: vec!["gitattributes".to_string()],
3382 filenames: vec![".gitattributes".to_string()],
3383 grammar: "Git Attributes".to_string(),
3384 comment_prefix: Some("#".to_string()),
3385 auto_indent: false,
3386 auto_close: None,
3387 auto_surround: None,
3388 textmate_grammar: None,
3389 show_whitespace_tabs: true,
3390 line_wrap: None,
3391 wrap_column: None,
3392 page_view: None,
3393 page_width: None,
3394 use_tabs: None,
3395 tab_size: None,
3396 formatter: None,
3397 format_on_save: false,
3398 on_save: vec![],
3399 word_characters: None,
3400 },
3401 );
3402
3403 languages.insert(
3404 "typst".to_string(),
3405 LanguageConfig {
3406 extensions: vec!["typ".to_string()],
3407 filenames: vec![],
3408 grammar: "Typst".to_string(),
3409 comment_prefix: Some("//".to_string()),
3410 auto_indent: true,
3411 auto_close: None,
3412 auto_surround: None,
3413 textmate_grammar: None,
3414 show_whitespace_tabs: true,
3415 line_wrap: None,
3416 wrap_column: None,
3417 page_view: None,
3418 page_width: None,
3419 use_tabs: None,
3420 tab_size: None,
3421 formatter: None,
3422 format_on_save: false,
3423 on_save: vec![],
3424 word_characters: None,
3425 },
3426 );
3427
3428 languages.insert(
3433 "kotlin".to_string(),
3434 LanguageConfig {
3435 extensions: vec!["kt".to_string(), "kts".to_string()],
3436 filenames: vec![],
3437 grammar: "Kotlin".to_string(),
3438 comment_prefix: Some("//".to_string()),
3439 auto_indent: true,
3440 auto_close: None,
3441 auto_surround: None,
3442 textmate_grammar: None,
3443 show_whitespace_tabs: true,
3444 line_wrap: None,
3445 wrap_column: None,
3446 page_view: None,
3447 page_width: None,
3448 use_tabs: None,
3449 tab_size: None,
3450 formatter: None,
3451 format_on_save: false,
3452 on_save: vec![],
3453 word_characters: None,
3454 },
3455 );
3456
3457 languages.insert(
3458 "swift".to_string(),
3459 LanguageConfig {
3460 extensions: vec!["swift".to_string()],
3461 filenames: vec![],
3462 grammar: "Swift".to_string(),
3463 comment_prefix: Some("//".to_string()),
3464 auto_indent: true,
3465 auto_close: None,
3466 auto_surround: None,
3467 textmate_grammar: None,
3468 show_whitespace_tabs: true,
3469 line_wrap: None,
3470 wrap_column: None,
3471 page_view: None,
3472 page_width: None,
3473 use_tabs: None,
3474 tab_size: None,
3475 formatter: None,
3476 format_on_save: false,
3477 on_save: vec![],
3478 word_characters: None,
3479 },
3480 );
3481
3482 languages.insert(
3483 "scala".to_string(),
3484 LanguageConfig {
3485 extensions: vec!["scala".to_string(), "sc".to_string()],
3486 filenames: vec![],
3487 grammar: "Scala".to_string(),
3488 comment_prefix: Some("//".to_string()),
3489 auto_indent: true,
3490 auto_close: None,
3491 auto_surround: None,
3492 textmate_grammar: None,
3493 show_whitespace_tabs: true,
3494 line_wrap: None,
3495 wrap_column: None,
3496 page_view: None,
3497 page_width: None,
3498 use_tabs: None,
3499 tab_size: None,
3500 formatter: None,
3501 format_on_save: false,
3502 on_save: vec![],
3503 word_characters: None,
3504 },
3505 );
3506
3507 languages.insert(
3508 "dart".to_string(),
3509 LanguageConfig {
3510 extensions: vec!["dart".to_string()],
3511 filenames: vec![],
3512 grammar: "Dart".to_string(),
3513 comment_prefix: Some("//".to_string()),
3514 auto_indent: true,
3515 auto_close: None,
3516 auto_surround: None,
3517 textmate_grammar: None,
3518 show_whitespace_tabs: true,
3519 line_wrap: None,
3520 wrap_column: None,
3521 page_view: None,
3522 page_width: None,
3523 use_tabs: None,
3524 tab_size: None,
3525 formatter: None,
3526 format_on_save: false,
3527 on_save: vec![],
3528 word_characters: None,
3529 },
3530 );
3531
3532 languages.insert(
3533 "elixir".to_string(),
3534 LanguageConfig {
3535 extensions: vec!["ex".to_string(), "exs".to_string()],
3536 filenames: vec![],
3537 grammar: "Elixir".to_string(),
3538 comment_prefix: Some("#".to_string()),
3539 auto_indent: true,
3540 auto_close: None,
3541 auto_surround: None,
3542 textmate_grammar: None,
3543 show_whitespace_tabs: true,
3544 line_wrap: None,
3545 wrap_column: None,
3546 page_view: None,
3547 page_width: None,
3548 use_tabs: None,
3549 tab_size: None,
3550 formatter: None,
3551 format_on_save: false,
3552 on_save: vec![],
3553 word_characters: None,
3554 },
3555 );
3556
3557 languages.insert(
3558 "erlang".to_string(),
3559 LanguageConfig {
3560 extensions: vec!["erl".to_string(), "hrl".to_string()],
3561 filenames: vec![],
3562 grammar: "Erlang".to_string(),
3563 comment_prefix: Some("%".to_string()),
3564 auto_indent: true,
3565 auto_close: None,
3566 auto_surround: None,
3567 textmate_grammar: None,
3568 show_whitespace_tabs: true,
3569 line_wrap: None,
3570 wrap_column: None,
3571 page_view: None,
3572 page_width: None,
3573 use_tabs: None,
3574 tab_size: None,
3575 formatter: None,
3576 format_on_save: false,
3577 on_save: vec![],
3578 word_characters: None,
3579 },
3580 );
3581
3582 languages.insert(
3583 "haskell".to_string(),
3584 LanguageConfig {
3585 extensions: vec!["hs".to_string(), "lhs".to_string()],
3586 filenames: vec![],
3587 grammar: "Haskell".to_string(),
3588 comment_prefix: Some("--".to_string()),
3589 auto_indent: true,
3590 auto_close: None,
3591 auto_surround: None,
3592 textmate_grammar: None,
3593 show_whitespace_tabs: true,
3594 line_wrap: None,
3595 wrap_column: None,
3596 page_view: None,
3597 page_width: None,
3598 use_tabs: None,
3599 tab_size: None,
3600 formatter: None,
3601 format_on_save: false,
3602 on_save: vec![],
3603 word_characters: None,
3604 },
3605 );
3606
3607 languages.insert(
3608 "ocaml".to_string(),
3609 LanguageConfig {
3610 extensions: vec!["ml".to_string(), "mli".to_string()],
3611 filenames: vec![],
3612 grammar: "OCaml".to_string(),
3613 comment_prefix: None,
3614 auto_indent: true,
3615 auto_close: None,
3616 auto_surround: None,
3617 textmate_grammar: None,
3618 show_whitespace_tabs: true,
3619 line_wrap: None,
3620 wrap_column: None,
3621 page_view: None,
3622 page_width: None,
3623 use_tabs: None,
3624 tab_size: None,
3625 formatter: None,
3626 format_on_save: false,
3627 on_save: vec![],
3628 word_characters: None,
3629 },
3630 );
3631
3632 languages.insert(
3633 "clojure".to_string(),
3634 LanguageConfig {
3635 extensions: vec![
3636 "clj".to_string(),
3637 "cljs".to_string(),
3638 "cljc".to_string(),
3639 "edn".to_string(),
3640 ],
3641 filenames: vec![],
3642 grammar: "Clojure".to_string(),
3643 comment_prefix: Some(";".to_string()),
3644 auto_indent: true,
3645 auto_close: None,
3646 auto_surround: None,
3647 textmate_grammar: None,
3648 show_whitespace_tabs: true,
3649 line_wrap: None,
3650 wrap_column: None,
3651 page_view: None,
3652 page_width: None,
3653 use_tabs: None,
3654 tab_size: None,
3655 formatter: None,
3656 format_on_save: false,
3657 on_save: vec![],
3658 word_characters: None,
3659 },
3660 );
3661
3662 languages.insert(
3663 "r".to_string(),
3664 LanguageConfig {
3665 extensions: vec!["r".to_string(), "R".to_string(), "rmd".to_string()],
3666 filenames: vec![],
3667 grammar: "R".to_string(),
3668 comment_prefix: Some("#".to_string()),
3669 auto_indent: true,
3670 auto_close: None,
3671 auto_surround: None,
3672 textmate_grammar: None,
3673 show_whitespace_tabs: true,
3674 line_wrap: None,
3675 wrap_column: None,
3676 page_view: None,
3677 page_width: None,
3678 use_tabs: None,
3679 tab_size: None,
3680 formatter: None,
3681 format_on_save: false,
3682 on_save: vec![],
3683 word_characters: None,
3684 },
3685 );
3686
3687 languages.insert(
3688 "julia".to_string(),
3689 LanguageConfig {
3690 extensions: vec!["jl".to_string()],
3691 filenames: vec![],
3692 grammar: "Julia".to_string(),
3693 comment_prefix: Some("#".to_string()),
3694 auto_indent: true,
3695 auto_close: None,
3696 auto_surround: None,
3697 textmate_grammar: None,
3698 show_whitespace_tabs: true,
3699 line_wrap: None,
3700 wrap_column: None,
3701 page_view: None,
3702 page_width: None,
3703 use_tabs: None,
3704 tab_size: None,
3705 formatter: None,
3706 format_on_save: false,
3707 on_save: vec![],
3708 word_characters: None,
3709 },
3710 );
3711
3712 languages.insert(
3713 "perl".to_string(),
3714 LanguageConfig {
3715 extensions: vec!["pl".to_string(), "pm".to_string(), "t".to_string()],
3716 filenames: vec![],
3717 grammar: "Perl".to_string(),
3718 comment_prefix: Some("#".to_string()),
3719 auto_indent: true,
3720 auto_close: None,
3721 auto_surround: None,
3722 textmate_grammar: None,
3723 show_whitespace_tabs: true,
3724 line_wrap: None,
3725 wrap_column: None,
3726 page_view: None,
3727 page_width: None,
3728 use_tabs: None,
3729 tab_size: None,
3730 formatter: None,
3731 format_on_save: false,
3732 on_save: vec![],
3733 word_characters: None,
3734 },
3735 );
3736
3737 languages.insert(
3738 "nim".to_string(),
3739 LanguageConfig {
3740 extensions: vec!["nim".to_string(), "nims".to_string(), "nimble".to_string()],
3741 filenames: vec![],
3742 grammar: "Nim".to_string(),
3743 comment_prefix: Some("#".to_string()),
3744 auto_indent: true,
3745 auto_close: None,
3746 auto_surround: None,
3747 textmate_grammar: None,
3748 show_whitespace_tabs: true,
3749 line_wrap: None,
3750 wrap_column: None,
3751 page_view: None,
3752 page_width: None,
3753 use_tabs: None,
3754 tab_size: None,
3755 formatter: None,
3756 format_on_save: false,
3757 on_save: vec![],
3758 word_characters: None,
3759 },
3760 );
3761
3762 languages.insert(
3763 "gleam".to_string(),
3764 LanguageConfig {
3765 extensions: vec!["gleam".to_string()],
3766 filenames: vec![],
3767 grammar: "Gleam".to_string(),
3768 comment_prefix: Some("//".to_string()),
3769 auto_indent: true,
3770 auto_close: None,
3771 auto_surround: None,
3772 textmate_grammar: None,
3773 show_whitespace_tabs: true,
3774 line_wrap: None,
3775 wrap_column: None,
3776 page_view: None,
3777 page_width: None,
3778 use_tabs: None,
3779 tab_size: None,
3780 formatter: None,
3781 format_on_save: false,
3782 on_save: vec![],
3783 word_characters: None,
3784 },
3785 );
3786
3787 languages.insert(
3788 "fsharp".to_string(),
3789 LanguageConfig {
3790 extensions: vec!["fs".to_string(), "fsi".to_string(), "fsx".to_string()],
3791 filenames: vec![],
3792 grammar: "FSharp".to_string(),
3793 comment_prefix: Some("//".to_string()),
3794 auto_indent: true,
3795 auto_close: None,
3796 auto_surround: None,
3797 textmate_grammar: None,
3798 show_whitespace_tabs: true,
3799 line_wrap: None,
3800 wrap_column: None,
3801 page_view: None,
3802 page_width: None,
3803 use_tabs: None,
3804 tab_size: None,
3805 formatter: None,
3806 format_on_save: false,
3807 on_save: vec![],
3808 word_characters: None,
3809 },
3810 );
3811
3812 languages.insert(
3813 "nix".to_string(),
3814 LanguageConfig {
3815 extensions: vec!["nix".to_string()],
3816 filenames: vec![],
3817 grammar: "Nix".to_string(),
3818 comment_prefix: Some("#".to_string()),
3819 auto_indent: true,
3820 auto_close: None,
3821 auto_surround: None,
3822 textmate_grammar: None,
3823 show_whitespace_tabs: true,
3824 line_wrap: None,
3825 wrap_column: None,
3826 page_view: None,
3827 page_width: None,
3828 use_tabs: None,
3829 tab_size: None,
3830 formatter: None,
3831 format_on_save: false,
3832 on_save: vec![],
3833 word_characters: None,
3834 },
3835 );
3836
3837 languages.insert(
3838 "nushell".to_string(),
3839 LanguageConfig {
3840 extensions: vec!["nu".to_string()],
3841 filenames: vec![],
3842 grammar: "Nushell".to_string(),
3843 comment_prefix: Some("#".to_string()),
3844 auto_indent: true,
3845 auto_close: None,
3846 auto_surround: None,
3847 textmate_grammar: None,
3848 show_whitespace_tabs: true,
3849 line_wrap: None,
3850 wrap_column: None,
3851 page_view: None,
3852 page_width: None,
3853 use_tabs: None,
3854 tab_size: None,
3855 formatter: None,
3856 format_on_save: false,
3857 on_save: vec![],
3858 word_characters: None,
3859 },
3860 );
3861
3862 languages.insert(
3863 "solidity".to_string(),
3864 LanguageConfig {
3865 extensions: vec!["sol".to_string()],
3866 filenames: vec![],
3867 grammar: "Solidity".to_string(),
3868 comment_prefix: Some("//".to_string()),
3869 auto_indent: true,
3870 auto_close: None,
3871 auto_surround: None,
3872 textmate_grammar: None,
3873 show_whitespace_tabs: true,
3874 line_wrap: None,
3875 wrap_column: None,
3876 page_view: None,
3877 page_width: None,
3878 use_tabs: None,
3879 tab_size: None,
3880 formatter: None,
3881 format_on_save: false,
3882 on_save: vec![],
3883 word_characters: None,
3884 },
3885 );
3886
3887 languages.insert(
3888 "ruby".to_string(),
3889 LanguageConfig {
3890 extensions: vec!["rb".to_string(), "rake".to_string(), "gemspec".to_string()],
3891 filenames: vec![
3892 "Gemfile".to_string(),
3893 "Rakefile".to_string(),
3894 "Guardfile".to_string(),
3895 ],
3896 grammar: "Ruby".to_string(),
3897 comment_prefix: Some("#".to_string()),
3898 auto_indent: true,
3899 auto_close: None,
3900 auto_surround: None,
3901 textmate_grammar: None,
3902 show_whitespace_tabs: true,
3903 line_wrap: None,
3904 wrap_column: None,
3905 page_view: None,
3906 page_width: None,
3907 use_tabs: None,
3908 tab_size: None,
3909 formatter: None,
3910 format_on_save: false,
3911 on_save: vec![],
3912 word_characters: None,
3913 },
3914 );
3915
3916 languages.insert(
3917 "php".to_string(),
3918 LanguageConfig {
3919 extensions: vec!["php".to_string(), "phtml".to_string()],
3920 filenames: vec![],
3921 grammar: "PHP".to_string(),
3922 comment_prefix: Some("//".to_string()),
3923 auto_indent: true,
3924 auto_close: None,
3925 auto_surround: None,
3926 textmate_grammar: None,
3927 show_whitespace_tabs: true,
3928 line_wrap: None,
3929 wrap_column: None,
3930 page_view: None,
3931 page_width: None,
3932 use_tabs: None,
3933 tab_size: None,
3934 formatter: None,
3935 format_on_save: false,
3936 on_save: vec![],
3937 word_characters: None,
3938 },
3939 );
3940
3941 languages.insert(
3942 "lua".to_string(),
3943 LanguageConfig {
3944 extensions: vec!["lua".to_string()],
3945 filenames: vec![],
3946 grammar: "Lua".to_string(),
3947 comment_prefix: Some("--".to_string()),
3948 auto_indent: true,
3949 auto_close: None,
3950 auto_surround: None,
3951 textmate_grammar: None,
3952 show_whitespace_tabs: true,
3953 line_wrap: None,
3954 wrap_column: None,
3955 page_view: None,
3956 page_width: None,
3957 use_tabs: None,
3958 tab_size: None,
3959 formatter: None,
3960 format_on_save: false,
3961 on_save: vec![],
3962 word_characters: None,
3963 },
3964 );
3965
3966 languages.insert(
3967 "html".to_string(),
3968 LanguageConfig {
3969 extensions: vec!["html".to_string(), "htm".to_string()],
3970 filenames: vec![],
3971 grammar: "HTML".to_string(),
3972 comment_prefix: None,
3973 auto_indent: true,
3974 auto_close: None,
3975 auto_surround: None,
3976 textmate_grammar: None,
3977 show_whitespace_tabs: true,
3978 line_wrap: None,
3979 wrap_column: None,
3980 page_view: None,
3981 page_width: None,
3982 use_tabs: None,
3983 tab_size: None,
3984 formatter: None,
3985 format_on_save: false,
3986 on_save: vec![],
3987 word_characters: None,
3988 },
3989 );
3990
3991 languages.insert(
3992 "css".to_string(),
3993 LanguageConfig {
3994 extensions: vec!["css".to_string()],
3995 filenames: vec![],
3996 grammar: "CSS".to_string(),
3997 comment_prefix: None,
3998 auto_indent: true,
3999 auto_close: None,
4000 auto_surround: None,
4001 textmate_grammar: None,
4002 show_whitespace_tabs: true,
4003 line_wrap: None,
4004 wrap_column: None,
4005 page_view: None,
4006 page_width: None,
4007 use_tabs: None,
4008 tab_size: None,
4009 formatter: None,
4010 format_on_save: false,
4011 on_save: vec![],
4012 word_characters: None,
4013 },
4014 );
4015
4016 languages.insert(
4017 "sql".to_string(),
4018 LanguageConfig {
4019 extensions: vec!["sql".to_string()],
4020 filenames: vec![],
4021 grammar: "SQL".to_string(),
4022 comment_prefix: Some("--".to_string()),
4023 auto_indent: true,
4024 auto_close: None,
4025 auto_surround: None,
4026 textmate_grammar: None,
4027 show_whitespace_tabs: true,
4028 line_wrap: None,
4029 wrap_column: None,
4030 page_view: None,
4031 page_width: None,
4032 use_tabs: None,
4033 tab_size: None,
4034 formatter: None,
4035 format_on_save: false,
4036 on_save: vec![],
4037 word_characters: None,
4038 },
4039 );
4040
4041 languages.insert(
4042 "graphql".to_string(),
4043 LanguageConfig {
4044 extensions: vec!["graphql".to_string(), "gql".to_string()],
4045 filenames: vec![],
4046 grammar: "GraphQL".to_string(),
4047 comment_prefix: Some("#".to_string()),
4048 auto_indent: true,
4049 auto_close: None,
4050 auto_surround: None,
4051 textmate_grammar: None,
4052 show_whitespace_tabs: true,
4053 line_wrap: None,
4054 wrap_column: None,
4055 page_view: None,
4056 page_width: None,
4057 use_tabs: None,
4058 tab_size: None,
4059 formatter: None,
4060 format_on_save: false,
4061 on_save: vec![],
4062 word_characters: None,
4063 },
4064 );
4065
4066 languages.insert(
4067 "protobuf".to_string(),
4068 LanguageConfig {
4069 extensions: vec!["proto".to_string()],
4070 filenames: vec![],
4071 grammar: "Protocol Buffers".to_string(),
4072 comment_prefix: Some("//".to_string()),
4073 auto_indent: true,
4074 auto_close: None,
4075 auto_surround: None,
4076 textmate_grammar: None,
4077 show_whitespace_tabs: true,
4078 line_wrap: None,
4079 wrap_column: None,
4080 page_view: None,
4081 page_width: None,
4082 use_tabs: None,
4083 tab_size: None,
4084 formatter: None,
4085 format_on_save: false,
4086 on_save: vec![],
4087 word_characters: None,
4088 },
4089 );
4090
4091 languages.insert(
4092 "cmake".to_string(),
4093 LanguageConfig {
4094 extensions: vec!["cmake".to_string()],
4095 filenames: vec!["CMakeLists.txt".to_string()],
4096 grammar: "CMake".to_string(),
4097 comment_prefix: Some("#".to_string()),
4098 auto_indent: true,
4099 auto_close: None,
4100 auto_surround: None,
4101 textmate_grammar: None,
4102 show_whitespace_tabs: true,
4103 line_wrap: None,
4104 wrap_column: None,
4105 page_view: None,
4106 page_width: None,
4107 use_tabs: None,
4108 tab_size: None,
4109 formatter: None,
4110 format_on_save: false,
4111 on_save: vec![],
4112 word_characters: None,
4113 },
4114 );
4115
4116 languages.insert(
4117 "terraform".to_string(),
4118 LanguageConfig {
4119 extensions: vec!["tf".to_string(), "tfvars".to_string(), "hcl".to_string()],
4120 filenames: vec![],
4121 grammar: "HCL".to_string(),
4122 comment_prefix: Some("#".to_string()),
4123 auto_indent: true,
4124 auto_close: None,
4125 auto_surround: None,
4126 textmate_grammar: None,
4127 show_whitespace_tabs: true,
4128 line_wrap: None,
4129 wrap_column: None,
4130 page_view: None,
4131 page_width: None,
4132 use_tabs: None,
4133 tab_size: None,
4134 formatter: None,
4135 format_on_save: false,
4136 on_save: vec![],
4137 word_characters: None,
4138 },
4139 );
4140
4141 languages.insert(
4142 "vue".to_string(),
4143 LanguageConfig {
4144 extensions: vec!["vue".to_string()],
4145 filenames: vec![],
4146 grammar: "Vue".to_string(),
4147 comment_prefix: None,
4148 auto_indent: true,
4149 auto_close: None,
4150 auto_surround: None,
4151 textmate_grammar: None,
4152 show_whitespace_tabs: true,
4153 line_wrap: None,
4154 wrap_column: None,
4155 page_view: None,
4156 page_width: None,
4157 use_tabs: None,
4158 tab_size: None,
4159 formatter: None,
4160 format_on_save: false,
4161 on_save: vec![],
4162 word_characters: None,
4163 },
4164 );
4165
4166 languages.insert(
4167 "svelte".to_string(),
4168 LanguageConfig {
4169 extensions: vec!["svelte".to_string()],
4170 filenames: vec![],
4171 grammar: "Svelte".to_string(),
4172 comment_prefix: None,
4173 auto_indent: true,
4174 auto_close: None,
4175 auto_surround: None,
4176 textmate_grammar: None,
4177 show_whitespace_tabs: true,
4178 line_wrap: None,
4179 wrap_column: None,
4180 page_view: None,
4181 page_width: None,
4182 use_tabs: None,
4183 tab_size: None,
4184 formatter: None,
4185 format_on_save: false,
4186 on_save: vec![],
4187 word_characters: None,
4188 },
4189 );
4190
4191 languages.insert(
4192 "astro".to_string(),
4193 LanguageConfig {
4194 extensions: vec!["astro".to_string()],
4195 filenames: vec![],
4196 grammar: "Astro".to_string(),
4197 comment_prefix: None,
4198 auto_indent: true,
4199 auto_close: None,
4200 auto_surround: None,
4201 textmate_grammar: None,
4202 show_whitespace_tabs: true,
4203 line_wrap: None,
4204 wrap_column: None,
4205 page_view: None,
4206 page_width: None,
4207 use_tabs: None,
4208 tab_size: None,
4209 formatter: None,
4210 format_on_save: false,
4211 on_save: vec![],
4212 word_characters: None,
4213 },
4214 );
4215
4216 languages.insert(
4219 "scss".to_string(),
4220 LanguageConfig {
4221 extensions: vec!["scss".to_string()],
4222 filenames: vec![],
4223 grammar: "SCSS".to_string(),
4224 comment_prefix: Some("//".to_string()),
4225 auto_indent: true,
4226 auto_close: None,
4227 auto_surround: None,
4228 textmate_grammar: None,
4229 show_whitespace_tabs: true,
4230 line_wrap: None,
4231 wrap_column: None,
4232 page_view: None,
4233 page_width: None,
4234 use_tabs: None,
4235 tab_size: None,
4236 formatter: None,
4237 format_on_save: false,
4238 on_save: vec![],
4239 word_characters: None,
4240 },
4241 );
4242
4243 languages.insert(
4244 "less".to_string(),
4245 LanguageConfig {
4246 extensions: vec!["less".to_string()],
4247 filenames: vec![],
4248 grammar: "LESS".to_string(),
4249 comment_prefix: Some("//".to_string()),
4250 auto_indent: true,
4251 auto_close: None,
4252 auto_surround: None,
4253 textmate_grammar: None,
4254 show_whitespace_tabs: true,
4255 line_wrap: None,
4256 wrap_column: None,
4257 page_view: None,
4258 page_width: None,
4259 use_tabs: None,
4260 tab_size: None,
4261 formatter: None,
4262 format_on_save: false,
4263 on_save: vec![],
4264 word_characters: None,
4265 },
4266 );
4267
4268 languages.insert(
4269 "powershell".to_string(),
4270 LanguageConfig {
4271 extensions: vec!["ps1".to_string(), "psm1".to_string(), "psd1".to_string()],
4272 filenames: vec![],
4273 grammar: "PowerShell".to_string(),
4274 comment_prefix: Some("#".to_string()),
4275 auto_indent: true,
4276 auto_close: None,
4277 auto_surround: None,
4278 textmate_grammar: None,
4279 show_whitespace_tabs: true,
4280 line_wrap: None,
4281 wrap_column: None,
4282 page_view: None,
4283 page_width: None,
4284 use_tabs: None,
4285 tab_size: None,
4286 formatter: None,
4287 format_on_save: false,
4288 on_save: vec![],
4289 word_characters: None,
4290 },
4291 );
4292
4293 languages.insert(
4294 "kdl".to_string(),
4295 LanguageConfig {
4296 extensions: vec!["kdl".to_string()],
4297 filenames: vec![],
4298 grammar: "KDL".to_string(),
4299 comment_prefix: Some("//".to_string()),
4300 auto_indent: true,
4301 auto_close: None,
4302 auto_surround: None,
4303 textmate_grammar: None,
4304 show_whitespace_tabs: true,
4305 line_wrap: None,
4306 wrap_column: None,
4307 page_view: None,
4308 page_width: None,
4309 use_tabs: None,
4310 tab_size: None,
4311 formatter: None,
4312 format_on_save: false,
4313 on_save: vec![],
4314 word_characters: None,
4315 },
4316 );
4317
4318 languages.insert(
4319 "starlark".to_string(),
4320 LanguageConfig {
4321 extensions: vec!["bzl".to_string(), "star".to_string()],
4322 filenames: vec!["BUILD".to_string(), "WORKSPACE".to_string()],
4323 grammar: "Starlark".to_string(),
4324 comment_prefix: Some("#".to_string()),
4325 auto_indent: true,
4326 auto_close: None,
4327 auto_surround: None,
4328 textmate_grammar: None,
4329 show_whitespace_tabs: true,
4330 line_wrap: None,
4331 wrap_column: None,
4332 page_view: None,
4333 page_width: None,
4334 use_tabs: None,
4335 tab_size: None,
4336 formatter: None,
4337 format_on_save: false,
4338 on_save: vec![],
4339 word_characters: None,
4340 },
4341 );
4342
4343 languages.insert(
4344 "justfile".to_string(),
4345 LanguageConfig {
4346 extensions: vec![],
4347 filenames: vec![
4348 "justfile".to_string(),
4349 "Justfile".to_string(),
4350 ".justfile".to_string(),
4351 ],
4352 grammar: "Justfile".to_string(),
4353 comment_prefix: Some("#".to_string()),
4354 auto_indent: true,
4355 auto_close: None,
4356 auto_surround: None,
4357 textmate_grammar: None,
4358 show_whitespace_tabs: true,
4359 line_wrap: None,
4360 wrap_column: None,
4361 page_view: None,
4362 page_width: None,
4363 use_tabs: Some(true),
4364 tab_size: None,
4365 formatter: None,
4366 format_on_save: false,
4367 on_save: vec![],
4368 word_characters: None,
4369 },
4370 );
4371
4372 languages.insert(
4373 "earthfile".to_string(),
4374 LanguageConfig {
4375 extensions: vec!["earth".to_string()],
4376 filenames: vec!["Earthfile".to_string()],
4377 grammar: "Earthfile".to_string(),
4378 comment_prefix: Some("#".to_string()),
4379 auto_indent: true,
4380 auto_close: None,
4381 auto_surround: None,
4382 textmate_grammar: None,
4383 show_whitespace_tabs: true,
4384 line_wrap: None,
4385 wrap_column: None,
4386 page_view: None,
4387 page_width: None,
4388 use_tabs: None,
4389 tab_size: None,
4390 formatter: None,
4391 format_on_save: false,
4392 on_save: vec![],
4393 word_characters: None,
4394 },
4395 );
4396
4397 languages.insert(
4398 "gomod".to_string(),
4399 LanguageConfig {
4400 extensions: vec![],
4401 filenames: vec!["go.mod".to_string(), "go.sum".to_string()],
4402 grammar: "Go Module".to_string(),
4403 comment_prefix: Some("//".to_string()),
4404 auto_indent: true,
4405 auto_close: None,
4406 auto_surround: None,
4407 textmate_grammar: None,
4408 show_whitespace_tabs: true,
4409 line_wrap: None,
4410 wrap_column: None,
4411 page_view: None,
4412 page_width: None,
4413 use_tabs: Some(true),
4414 tab_size: None,
4415 formatter: None,
4416 format_on_save: false,
4417 on_save: vec![],
4418 word_characters: None,
4419 },
4420 );
4421
4422 languages.insert(
4423 "vlang".to_string(),
4424 LanguageConfig {
4425 extensions: vec!["v".to_string(), "vv".to_string()],
4426 filenames: vec![],
4427 grammar: "V".to_string(),
4428 comment_prefix: Some("//".to_string()),
4429 auto_indent: true,
4430 auto_close: None,
4431 auto_surround: None,
4432 textmate_grammar: None,
4433 show_whitespace_tabs: true,
4434 line_wrap: None,
4435 wrap_column: None,
4436 page_view: None,
4437 page_width: None,
4438 use_tabs: None,
4439 tab_size: None,
4440 formatter: None,
4441 format_on_save: false,
4442 on_save: vec![],
4443 word_characters: None,
4444 },
4445 );
4446
4447 languages.insert(
4448 "ini".to_string(),
4449 LanguageConfig {
4450 extensions: vec!["ini".to_string(), "cfg".to_string()],
4451 filenames: vec![],
4452 grammar: "INI".to_string(),
4453 comment_prefix: Some(";".to_string()),
4454 auto_indent: false,
4455 auto_close: None,
4456 auto_surround: None,
4457 textmate_grammar: None,
4458 show_whitespace_tabs: true,
4459 line_wrap: None,
4460 wrap_column: None,
4461 page_view: None,
4462 page_width: None,
4463 use_tabs: None,
4464 tab_size: None,
4465 formatter: None,
4466 format_on_save: false,
4467 on_save: vec![],
4468 word_characters: None,
4469 },
4470 );
4471
4472 languages.insert(
4473 "hyprlang".to_string(),
4474 LanguageConfig {
4475 extensions: vec!["hl".to_string()],
4476 filenames: vec!["hyprland.conf".to_string()],
4477 grammar: "Hyprlang".to_string(),
4478 comment_prefix: Some("#".to_string()),
4479 auto_indent: true,
4480 auto_close: None,
4481 auto_surround: None,
4482 textmate_grammar: None,
4483 show_whitespace_tabs: true,
4484 line_wrap: None,
4485 wrap_column: None,
4486 page_view: None,
4487 page_width: None,
4488 use_tabs: None,
4489 tab_size: None,
4490 formatter: None,
4491 format_on_save: false,
4492 on_save: vec![],
4493 word_characters: None,
4494 },
4495 );
4496
4497 languages
4498 }
4499
4500 #[cfg(feature = "runtime")]
4502 fn default_lsp_config() -> HashMap<String, LspLanguageConfig> {
4503 let mut lsp = HashMap::new();
4504
4505 let ra_log_path = crate::services::log_dirs::lsp_log_path("rust-analyzer")
4508 .to_string_lossy()
4509 .to_string();
4510
4511 Self::populate_lsp_config(&mut lsp, ra_log_path);
4512 lsp
4513 }
4514
4515 #[cfg(not(feature = "runtime"))]
4517 fn default_lsp_config() -> HashMap<String, LspLanguageConfig> {
4518 HashMap::new()
4520 }
4521
4522 #[cfg(feature = "runtime")]
4524 fn default_universal_lsp_config() -> HashMap<String, LspLanguageConfig> {
4525 let mut universal = HashMap::new();
4526
4527 universal.insert(
4536 "quicklsp".to_string(),
4537 LspLanguageConfig::Multi(vec![LspServerConfig {
4538 command: "quicklsp".to_string(),
4539 args: vec![],
4540 enabled: false,
4541 auto_start: false,
4542 process_limits: ProcessLimits::default(),
4543 initialization_options: None,
4544 env: Default::default(),
4545 language_id_overrides: Default::default(),
4546 name: Some("QuickLSP".to_string()),
4547 only_features: Some(vec![
4548 LspFeature::Hover,
4549 LspFeature::SignatureHelp,
4550 LspFeature::DocumentSymbols,
4551 LspFeature::WorkspaceSymbols,
4552 ]),
4553 except_features: None,
4554 root_markers: vec![
4555 "Cargo.toml".to_string(),
4556 "package.json".to_string(),
4557 "go.mod".to_string(),
4558 "pyproject.toml".to_string(),
4559 "requirements.txt".to_string(),
4560 ".git".to_string(),
4561 ],
4562 }]),
4563 );
4564
4565 universal
4566 }
4567
4568 #[cfg(not(feature = "runtime"))]
4570 fn default_universal_lsp_config() -> HashMap<String, LspLanguageConfig> {
4571 HashMap::new()
4572 }
4573
4574 #[cfg(feature = "runtime")]
4575 fn populate_lsp_config(lsp: &mut HashMap<String, LspLanguageConfig>, ra_log_path: String) {
4576 lsp.insert(
4580 "rust".to_string(),
4581 LspLanguageConfig::Multi(vec![LspServerConfig {
4582 command: "rust-analyzer".to_string(),
4583 args: vec!["--log-file".to_string(), ra_log_path],
4584 enabled: true,
4585 auto_start: false,
4586 process_limits: ProcessLimits::unlimited(),
4587 initialization_options: None,
4588 env: Default::default(),
4589 language_id_overrides: Default::default(),
4590 name: None,
4591 only_features: None,
4592 except_features: None,
4593 root_markers: vec![
4594 "Cargo.toml".to_string(),
4595 "rust-project.json".to_string(),
4596 ".git".to_string(),
4597 ],
4598 }]),
4599 );
4600
4601 lsp.insert(
4603 "python".to_string(),
4604 LspLanguageConfig::Multi(vec![LspServerConfig {
4605 command: "pylsp".to_string(),
4606 args: vec![],
4607 enabled: true,
4608 auto_start: false,
4609 process_limits: ProcessLimits::default(),
4610 initialization_options: None,
4611 env: Default::default(),
4612 language_id_overrides: Default::default(),
4613 name: None,
4614 only_features: None,
4615 except_features: None,
4616 root_markers: vec![
4617 "pyproject.toml".to_string(),
4618 "setup.py".to_string(),
4619 "setup.cfg".to_string(),
4620 "pyrightconfig.json".to_string(),
4621 ".git".to_string(),
4622 ],
4623 }]),
4624 );
4625
4626 lsp.insert(
4629 "javascript".to_string(),
4630 LspLanguageConfig::Multi(vec![LspServerConfig {
4631 command: "typescript-language-server".to_string(),
4632 args: vec!["--stdio".to_string()],
4633 enabled: true,
4634 auto_start: false,
4635 process_limits: ProcessLimits::default(),
4636 initialization_options: None,
4637 env: Default::default(),
4638 language_id_overrides: HashMap::from([(
4639 "jsx".to_string(),
4640 "javascriptreact".to_string(),
4641 )]),
4642 name: None,
4643 only_features: None,
4644 except_features: None,
4645 root_markers: vec![
4646 "tsconfig.json".to_string(),
4647 "jsconfig.json".to_string(),
4648 "package.json".to_string(),
4649 ".git".to_string(),
4650 ],
4651 }]),
4652 );
4653 lsp.insert(
4654 "typescript".to_string(),
4655 LspLanguageConfig::Multi(vec![LspServerConfig {
4656 command: "typescript-language-server".to_string(),
4657 args: vec!["--stdio".to_string()],
4658 enabled: true,
4659 auto_start: false,
4660 process_limits: ProcessLimits::default(),
4661 initialization_options: None,
4662 env: Default::default(),
4663 language_id_overrides: HashMap::from([(
4664 "tsx".to_string(),
4665 "typescriptreact".to_string(),
4666 )]),
4667 name: None,
4668 only_features: None,
4669 except_features: None,
4670 root_markers: vec![
4671 "tsconfig.json".to_string(),
4672 "jsconfig.json".to_string(),
4673 "package.json".to_string(),
4674 ".git".to_string(),
4675 ],
4676 }]),
4677 );
4678
4679 lsp.insert(
4681 "html".to_string(),
4682 LspLanguageConfig::Multi(vec![LspServerConfig {
4683 command: "vscode-html-language-server".to_string(),
4684 args: vec!["--stdio".to_string()],
4685 enabled: true,
4686 auto_start: false,
4687 process_limits: ProcessLimits::default(),
4688 initialization_options: None,
4689 env: Default::default(),
4690 language_id_overrides: Default::default(),
4691 name: None,
4692 only_features: None,
4693 except_features: None,
4694 root_markers: Default::default(),
4695 }]),
4696 );
4697
4698 lsp.insert(
4700 "css".to_string(),
4701 LspLanguageConfig::Multi(vec![LspServerConfig {
4702 command: "vscode-css-language-server".to_string(),
4703 args: vec!["--stdio".to_string()],
4704 enabled: true,
4705 auto_start: false,
4706 process_limits: ProcessLimits::default(),
4707 initialization_options: None,
4708 env: Default::default(),
4709 language_id_overrides: Default::default(),
4710 name: None,
4711 only_features: None,
4712 except_features: None,
4713 root_markers: Default::default(),
4714 }]),
4715 );
4716
4717 lsp.insert(
4719 "c".to_string(),
4720 LspLanguageConfig::Multi(vec![LspServerConfig {
4721 command: "clangd".to_string(),
4722 args: vec![],
4723 enabled: true,
4724 auto_start: false,
4725 process_limits: ProcessLimits::default(),
4726 initialization_options: None,
4727 env: Default::default(),
4728 language_id_overrides: Default::default(),
4729 name: None,
4730 only_features: None,
4731 except_features: None,
4732 root_markers: vec![
4733 "compile_commands.json".to_string(),
4734 "CMakeLists.txt".to_string(),
4735 "Makefile".to_string(),
4736 ".git".to_string(),
4737 ],
4738 }]),
4739 );
4740 lsp.insert(
4741 "cpp".to_string(),
4742 LspLanguageConfig::Multi(vec![LspServerConfig {
4743 command: "clangd".to_string(),
4744 args: vec![],
4745 enabled: true,
4746 auto_start: false,
4747 process_limits: ProcessLimits::default(),
4748 initialization_options: None,
4749 env: Default::default(),
4750 language_id_overrides: Default::default(),
4751 name: None,
4752 only_features: None,
4753 except_features: None,
4754 root_markers: vec![
4755 "compile_commands.json".to_string(),
4756 "CMakeLists.txt".to_string(),
4757 "Makefile".to_string(),
4758 ".git".to_string(),
4759 ],
4760 }]),
4761 );
4762
4763 lsp.insert(
4765 "go".to_string(),
4766 LspLanguageConfig::Multi(vec![LspServerConfig {
4767 command: "gopls".to_string(),
4768 args: vec![],
4769 enabled: true,
4770 auto_start: false,
4771 process_limits: ProcessLimits::default(),
4772 initialization_options: None,
4773 env: Default::default(),
4774 language_id_overrides: Default::default(),
4775 name: None,
4776 only_features: None,
4777 except_features: None,
4778 root_markers: vec![
4779 "go.mod".to_string(),
4780 "go.work".to_string(),
4781 ".git".to_string(),
4782 ],
4783 }]),
4784 );
4785
4786 lsp.insert(
4788 "json".to_string(),
4789 LspLanguageConfig::Multi(vec![LspServerConfig {
4790 command: "vscode-json-language-server".to_string(),
4791 args: vec!["--stdio".to_string()],
4792 enabled: true,
4793 auto_start: false,
4794 process_limits: ProcessLimits::default(),
4795 initialization_options: None,
4796 env: Default::default(),
4797 language_id_overrides: Default::default(),
4798 name: None,
4799 only_features: None,
4800 except_features: None,
4801 root_markers: Default::default(),
4802 }]),
4803 );
4804
4805 lsp.insert(
4807 "csharp".to_string(),
4808 LspLanguageConfig::Multi(vec![LspServerConfig {
4809 command: "csharp-ls".to_string(),
4810 args: vec![],
4811 enabled: true,
4812 auto_start: false,
4813 process_limits: ProcessLimits::default(),
4814 initialization_options: None,
4815 env: Default::default(),
4816 language_id_overrides: Default::default(),
4817 name: None,
4818 only_features: None,
4819 except_features: None,
4820 root_markers: vec![
4821 "*.csproj".to_string(),
4822 "*.sln".to_string(),
4823 ".git".to_string(),
4824 ],
4825 }]),
4826 );
4827
4828 lsp.insert(
4831 "odin".to_string(),
4832 LspLanguageConfig::Multi(vec![LspServerConfig {
4833 command: "ols".to_string(),
4834 args: vec![],
4835 enabled: true,
4836 auto_start: false,
4837 process_limits: ProcessLimits::default(),
4838 initialization_options: None,
4839 env: Default::default(),
4840 language_id_overrides: Default::default(),
4841 name: None,
4842 only_features: None,
4843 except_features: None,
4844 root_markers: Default::default(),
4845 }]),
4846 );
4847
4848 lsp.insert(
4851 "zig".to_string(),
4852 LspLanguageConfig::Multi(vec![LspServerConfig {
4853 command: "zls".to_string(),
4854 args: vec![],
4855 enabled: true,
4856 auto_start: false,
4857 process_limits: ProcessLimits::default(),
4858 initialization_options: None,
4859 env: Default::default(),
4860 language_id_overrides: Default::default(),
4861 name: None,
4862 only_features: None,
4863 except_features: None,
4864 root_markers: Default::default(),
4865 }]),
4866 );
4867
4868 lsp.insert(
4871 "java".to_string(),
4872 LspLanguageConfig::Multi(vec![LspServerConfig {
4873 command: "jdtls".to_string(),
4874 args: vec![],
4875 enabled: true,
4876 auto_start: false,
4877 process_limits: ProcessLimits::default(),
4878 initialization_options: None,
4879 env: Default::default(),
4880 language_id_overrides: Default::default(),
4881 name: None,
4882 only_features: None,
4883 except_features: None,
4884 root_markers: vec![
4885 "pom.xml".to_string(),
4886 "build.gradle".to_string(),
4887 "build.gradle.kts".to_string(),
4888 ".git".to_string(),
4889 ],
4890 }]),
4891 );
4892
4893 lsp.insert(
4896 "latex".to_string(),
4897 LspLanguageConfig::Multi(vec![LspServerConfig {
4898 command: "texlab".to_string(),
4899 args: vec![],
4900 enabled: true,
4901 auto_start: false,
4902 process_limits: ProcessLimits::default(),
4903 initialization_options: None,
4904 env: Default::default(),
4905 language_id_overrides: Default::default(),
4906 name: None,
4907 only_features: None,
4908 except_features: None,
4909 root_markers: Default::default(),
4910 }]),
4911 );
4912
4913 lsp.insert(
4916 "markdown".to_string(),
4917 LspLanguageConfig::Multi(vec![LspServerConfig {
4918 command: "marksman".to_string(),
4919 args: vec!["server".to_string()],
4920 enabled: true,
4921 auto_start: false,
4922 process_limits: ProcessLimits::default(),
4923 initialization_options: None,
4924 env: Default::default(),
4925 language_id_overrides: Default::default(),
4926 name: None,
4927 only_features: None,
4928 except_features: None,
4929 root_markers: Default::default(),
4930 }]),
4931 );
4932
4933 lsp.insert(
4936 "templ".to_string(),
4937 LspLanguageConfig::Multi(vec![LspServerConfig {
4938 command: "templ".to_string(),
4939 args: vec!["lsp".to_string()],
4940 enabled: true,
4941 auto_start: false,
4942 process_limits: ProcessLimits::default(),
4943 initialization_options: None,
4944 env: Default::default(),
4945 language_id_overrides: Default::default(),
4946 name: None,
4947 only_features: None,
4948 except_features: None,
4949 root_markers: Default::default(),
4950 }]),
4951 );
4952
4953 lsp.insert(
4956 "typst".to_string(),
4957 LspLanguageConfig::Multi(vec![LspServerConfig {
4958 command: "tinymist".to_string(),
4959 args: vec![],
4960 enabled: true,
4961 auto_start: false,
4962 process_limits: ProcessLimits::default(),
4963 initialization_options: None,
4964 env: Default::default(),
4965 language_id_overrides: Default::default(),
4966 name: None,
4967 only_features: None,
4968 except_features: None,
4969 root_markers: Default::default(),
4970 }]),
4971 );
4972
4973 lsp.insert(
4975 "bash".to_string(),
4976 LspLanguageConfig::Multi(vec![LspServerConfig {
4977 command: "bash-language-server".to_string(),
4978 args: vec!["start".to_string()],
4979 enabled: true,
4980 auto_start: false,
4981 process_limits: ProcessLimits::default(),
4982 initialization_options: None,
4983 env: Default::default(),
4984 language_id_overrides: Default::default(),
4985 name: None,
4986 only_features: None,
4987 except_features: None,
4988 root_markers: Default::default(),
4989 }]),
4990 );
4991
4992 lsp.insert(
4995 "lua".to_string(),
4996 LspLanguageConfig::Multi(vec![LspServerConfig {
4997 command: "lua-language-server".to_string(),
4998 args: vec![],
4999 enabled: true,
5000 auto_start: false,
5001 process_limits: ProcessLimits::default(),
5002 initialization_options: None,
5003 env: Default::default(),
5004 language_id_overrides: Default::default(),
5005 name: None,
5006 only_features: None,
5007 except_features: None,
5008 root_markers: vec![
5009 ".luarc.json".to_string(),
5010 ".luarc.jsonc".to_string(),
5011 ".luacheckrc".to_string(),
5012 ".stylua.toml".to_string(),
5013 ".git".to_string(),
5014 ],
5015 }]),
5016 );
5017
5018 lsp.insert(
5020 "ruby".to_string(),
5021 LspLanguageConfig::Multi(vec![LspServerConfig {
5022 command: "solargraph".to_string(),
5023 args: vec!["stdio".to_string()],
5024 enabled: true,
5025 auto_start: false,
5026 process_limits: ProcessLimits::default(),
5027 initialization_options: None,
5028 env: Default::default(),
5029 language_id_overrides: Default::default(),
5030 name: None,
5031 only_features: None,
5032 except_features: None,
5033 root_markers: vec![
5034 "Gemfile".to_string(),
5035 ".ruby-version".to_string(),
5036 ".git".to_string(),
5037 ],
5038 }]),
5039 );
5040
5041 lsp.insert(
5044 "php".to_string(),
5045 LspLanguageConfig::Multi(vec![LspServerConfig {
5046 command: "phpactor".to_string(),
5047 args: vec!["language-server".to_string()],
5048 enabled: true,
5049 auto_start: false,
5050 process_limits: ProcessLimits::default(),
5051 initialization_options: None,
5052 env: Default::default(),
5053 language_id_overrides: Default::default(),
5054 name: None,
5055 only_features: None,
5056 except_features: None,
5057 root_markers: vec!["composer.json".to_string(), ".git".to_string()],
5058 }]),
5059 );
5060
5061 lsp.insert(
5063 "yaml".to_string(),
5064 LspLanguageConfig::Multi(vec![LspServerConfig {
5065 command: "yaml-language-server".to_string(),
5066 args: vec!["--stdio".to_string()],
5067 enabled: true,
5068 auto_start: false,
5069 process_limits: ProcessLimits::default(),
5070 initialization_options: None,
5071 env: Default::default(),
5072 language_id_overrides: Default::default(),
5073 name: None,
5074 only_features: None,
5075 except_features: None,
5076 root_markers: Default::default(),
5077 }]),
5078 );
5079
5080 lsp.insert(
5083 "toml".to_string(),
5084 LspLanguageConfig::Multi(vec![LspServerConfig {
5085 command: "taplo".to_string(),
5086 args: vec!["lsp".to_string(), "stdio".to_string()],
5087 enabled: true,
5088 auto_start: false,
5089 process_limits: ProcessLimits::default(),
5090 initialization_options: None,
5091 env: Default::default(),
5092 language_id_overrides: Default::default(),
5093 name: None,
5094 only_features: None,
5095 except_features: None,
5096 root_markers: Default::default(),
5097 }]),
5098 );
5099
5100 lsp.insert(
5103 "dart".to_string(),
5104 LspLanguageConfig::Multi(vec![LspServerConfig {
5105 command: "dart".to_string(),
5106 args: vec!["language-server".to_string(), "--protocol=lsp".to_string()],
5107 enabled: true,
5108 auto_start: false,
5109 process_limits: ProcessLimits::default(),
5110 initialization_options: None,
5111 env: Default::default(),
5112 language_id_overrides: Default::default(),
5113 name: None,
5114 only_features: None,
5115 except_features: None,
5116 root_markers: vec!["pubspec.yaml".to_string(), ".git".to_string()],
5117 }]),
5118 );
5119
5120 lsp.insert(
5123 "nushell".to_string(),
5124 LspLanguageConfig::Multi(vec![LspServerConfig {
5125 command: "nu".to_string(),
5126 args: vec!["--lsp".to_string()],
5127 enabled: true,
5128 auto_start: false,
5129 process_limits: ProcessLimits::default(),
5130 initialization_options: None,
5131 env: Default::default(),
5132 language_id_overrides: Default::default(),
5133 name: None,
5134 only_features: None,
5135 except_features: None,
5136 root_markers: Default::default(),
5137 }]),
5138 );
5139
5140 lsp.insert(
5143 "solidity".to_string(),
5144 LspLanguageConfig::Multi(vec![LspServerConfig {
5145 command: "nomicfoundation-solidity-language-server".to_string(),
5146 args: vec!["--stdio".to_string()],
5147 enabled: true,
5148 auto_start: false,
5149 process_limits: ProcessLimits::default(),
5150 initialization_options: None,
5151 env: Default::default(),
5152 language_id_overrides: Default::default(),
5153 name: None,
5154 only_features: None,
5155 except_features: None,
5156 root_markers: Default::default(),
5157 }]),
5158 );
5159
5160 lsp.insert(
5165 "terraform".to_string(),
5166 LspLanguageConfig::Multi(vec![LspServerConfig {
5167 command: "terraform-ls".to_string(),
5168 args: vec!["serve".to_string()],
5169 enabled: true,
5170 auto_start: false,
5171 process_limits: ProcessLimits::default(),
5172 initialization_options: None,
5173 env: Default::default(),
5174 language_id_overrides: Default::default(),
5175 name: None,
5176 only_features: None,
5177 except_features: None,
5178 root_markers: vec![
5179 "*.tf".to_string(),
5180 ".terraform".to_string(),
5181 ".git".to_string(),
5182 ],
5183 }]),
5184 );
5185
5186 lsp.insert(
5189 "cmake".to_string(),
5190 LspLanguageConfig::Multi(vec![LspServerConfig {
5191 command: "cmake-language-server".to_string(),
5192 args: vec![],
5193 enabled: true,
5194 auto_start: false,
5195 process_limits: ProcessLimits::default(),
5196 initialization_options: None,
5197 env: Default::default(),
5198 language_id_overrides: Default::default(),
5199 name: None,
5200 only_features: None,
5201 except_features: None,
5202 root_markers: vec!["CMakeLists.txt".to_string(), ".git".to_string()],
5203 }]),
5204 );
5205
5206 lsp.insert(
5209 "protobuf".to_string(),
5210 LspLanguageConfig::Multi(vec![LspServerConfig {
5211 command: "buf".to_string(),
5212 args: vec!["beta".to_string(), "lsp".to_string()],
5213 enabled: true,
5214 auto_start: false,
5215 process_limits: ProcessLimits::default(),
5216 initialization_options: None,
5217 env: Default::default(),
5218 language_id_overrides: Default::default(),
5219 name: None,
5220 only_features: None,
5221 except_features: None,
5222 root_markers: Default::default(),
5223 }]),
5224 );
5225
5226 lsp.insert(
5229 "graphql".to_string(),
5230 LspLanguageConfig::Multi(vec![LspServerConfig {
5231 command: "graphql-lsp".to_string(),
5232 args: vec!["server".to_string(), "-m".to_string(), "stream".to_string()],
5233 enabled: true,
5234 auto_start: false,
5235 process_limits: ProcessLimits::default(),
5236 initialization_options: None,
5237 env: Default::default(),
5238 language_id_overrides: Default::default(),
5239 name: None,
5240 only_features: None,
5241 except_features: None,
5242 root_markers: Default::default(),
5243 }]),
5244 );
5245
5246 lsp.insert(
5249 "sql".to_string(),
5250 LspLanguageConfig::Multi(vec![LspServerConfig {
5251 command: "sqls".to_string(),
5252 args: vec![],
5253 enabled: true,
5254 auto_start: false,
5255 process_limits: ProcessLimits::default(),
5256 initialization_options: None,
5257 env: Default::default(),
5258 language_id_overrides: Default::default(),
5259 name: None,
5260 only_features: None,
5261 except_features: None,
5262 root_markers: Default::default(),
5263 }]),
5264 );
5265
5266 lsp.insert(
5270 "vue".to_string(),
5271 LspLanguageConfig::Multi(vec![LspServerConfig {
5272 command: "vue-language-server".to_string(),
5273 args: vec!["--stdio".to_string()],
5274 enabled: true,
5275 auto_start: false,
5276 process_limits: ProcessLimits::default(),
5277 initialization_options: None,
5278 env: Default::default(),
5279 language_id_overrides: Default::default(),
5280 name: None,
5281 only_features: None,
5282 except_features: None,
5283 root_markers: Default::default(),
5284 }]),
5285 );
5286
5287 lsp.insert(
5289 "svelte".to_string(),
5290 LspLanguageConfig::Multi(vec![LspServerConfig {
5291 command: "svelteserver".to_string(),
5292 args: vec!["--stdio".to_string()],
5293 enabled: true,
5294 auto_start: false,
5295 process_limits: ProcessLimits::default(),
5296 initialization_options: None,
5297 env: Default::default(),
5298 language_id_overrides: Default::default(),
5299 name: None,
5300 only_features: None,
5301 except_features: None,
5302 root_markers: Default::default(),
5303 }]),
5304 );
5305
5306 lsp.insert(
5308 "astro".to_string(),
5309 LspLanguageConfig::Multi(vec![LspServerConfig {
5310 command: "astro-ls".to_string(),
5311 args: vec!["--stdio".to_string()],
5312 enabled: true,
5313 auto_start: false,
5314 process_limits: ProcessLimits::default(),
5315 initialization_options: None,
5316 env: Default::default(),
5317 language_id_overrides: Default::default(),
5318 name: None,
5319 only_features: None,
5320 except_features: None,
5321 root_markers: Default::default(),
5322 }]),
5323 );
5324
5325 lsp.insert(
5327 "tailwindcss".to_string(),
5328 LspLanguageConfig::Multi(vec![LspServerConfig {
5329 command: "tailwindcss-language-server".to_string(),
5330 args: vec!["--stdio".to_string()],
5331 enabled: true,
5332 auto_start: false,
5333 process_limits: ProcessLimits::default(),
5334 initialization_options: None,
5335 env: Default::default(),
5336 language_id_overrides: Default::default(),
5337 name: None,
5338 only_features: None,
5339 except_features: None,
5340 root_markers: Default::default(),
5341 }]),
5342 );
5343
5344 lsp.insert(
5349 "nix".to_string(),
5350 LspLanguageConfig::Multi(vec![LspServerConfig {
5351 command: "nil".to_string(),
5352 args: vec![],
5353 enabled: true,
5354 auto_start: false,
5355 process_limits: ProcessLimits::default(),
5356 initialization_options: None,
5357 env: Default::default(),
5358 language_id_overrides: Default::default(),
5359 name: None,
5360 only_features: None,
5361 except_features: None,
5362 root_markers: Default::default(),
5363 }]),
5364 );
5365
5366 lsp.insert(
5369 "kotlin".to_string(),
5370 LspLanguageConfig::Multi(vec![LspServerConfig {
5371 command: "kotlin-language-server".to_string(),
5372 args: vec![],
5373 enabled: true,
5374 auto_start: false,
5375 process_limits: ProcessLimits::default(),
5376 initialization_options: None,
5377 env: Default::default(),
5378 language_id_overrides: Default::default(),
5379 name: None,
5380 only_features: None,
5381 except_features: None,
5382 root_markers: Default::default(),
5383 }]),
5384 );
5385
5386 lsp.insert(
5388 "swift".to_string(),
5389 LspLanguageConfig::Multi(vec![LspServerConfig {
5390 command: "sourcekit-lsp".to_string(),
5391 args: vec![],
5392 enabled: true,
5393 auto_start: false,
5394 process_limits: ProcessLimits::default(),
5395 initialization_options: None,
5396 env: Default::default(),
5397 language_id_overrides: Default::default(),
5398 name: None,
5399 only_features: None,
5400 except_features: None,
5401 root_markers: Default::default(),
5402 }]),
5403 );
5404
5405 lsp.insert(
5408 "scala".to_string(),
5409 LspLanguageConfig::Multi(vec![LspServerConfig {
5410 command: "metals".to_string(),
5411 args: vec![],
5412 enabled: true,
5413 auto_start: false,
5414 process_limits: ProcessLimits::default(),
5415 initialization_options: None,
5416 env: Default::default(),
5417 language_id_overrides: Default::default(),
5418 name: None,
5419 only_features: None,
5420 except_features: None,
5421 root_markers: Default::default(),
5422 }]),
5423 );
5424
5425 lsp.insert(
5428 "elixir".to_string(),
5429 LspLanguageConfig::Multi(vec![LspServerConfig {
5430 command: "elixir-ls".to_string(),
5431 args: vec![],
5432 enabled: true,
5433 auto_start: false,
5434 process_limits: ProcessLimits::default(),
5435 initialization_options: None,
5436 env: Default::default(),
5437 language_id_overrides: Default::default(),
5438 name: None,
5439 only_features: None,
5440 except_features: None,
5441 root_markers: Default::default(),
5442 }]),
5443 );
5444
5445 lsp.insert(
5447 "erlang".to_string(),
5448 LspLanguageConfig::Multi(vec![LspServerConfig {
5449 command: "erlang_ls".to_string(),
5450 args: vec![],
5451 enabled: true,
5452 auto_start: false,
5453 process_limits: ProcessLimits::default(),
5454 initialization_options: None,
5455 env: Default::default(),
5456 language_id_overrides: Default::default(),
5457 name: None,
5458 only_features: None,
5459 except_features: None,
5460 root_markers: Default::default(),
5461 }]),
5462 );
5463
5464 lsp.insert(
5467 "haskell".to_string(),
5468 LspLanguageConfig::Multi(vec![LspServerConfig {
5469 command: "haskell-language-server-wrapper".to_string(),
5470 args: vec!["--lsp".to_string()],
5471 enabled: true,
5472 auto_start: false,
5473 process_limits: ProcessLimits::default(),
5474 initialization_options: None,
5475 env: Default::default(),
5476 language_id_overrides: Default::default(),
5477 name: None,
5478 only_features: None,
5479 except_features: None,
5480 root_markers: Default::default(),
5481 }]),
5482 );
5483
5484 lsp.insert(
5487 "ocaml".to_string(),
5488 LspLanguageConfig::Multi(vec![LspServerConfig {
5489 command: "ocamllsp".to_string(),
5490 args: vec![],
5491 enabled: true,
5492 auto_start: false,
5493 process_limits: ProcessLimits::default(),
5494 initialization_options: None,
5495 env: Default::default(),
5496 language_id_overrides: Default::default(),
5497 name: None,
5498 only_features: None,
5499 except_features: None,
5500 root_markers: Default::default(),
5501 }]),
5502 );
5503
5504 lsp.insert(
5507 "clojure".to_string(),
5508 LspLanguageConfig::Multi(vec![LspServerConfig {
5509 command: "clojure-lsp".to_string(),
5510 args: vec![],
5511 enabled: true,
5512 auto_start: false,
5513 process_limits: ProcessLimits::default(),
5514 initialization_options: None,
5515 env: Default::default(),
5516 language_id_overrides: Default::default(),
5517 name: None,
5518 only_features: None,
5519 except_features: None,
5520 root_markers: Default::default(),
5521 }]),
5522 );
5523
5524 lsp.insert(
5527 "r".to_string(),
5528 LspLanguageConfig::Multi(vec![LspServerConfig {
5529 command: "R".to_string(),
5530 args: vec![
5531 "--vanilla".to_string(),
5532 "-e".to_string(),
5533 "languageserver::run()".to_string(),
5534 ],
5535 enabled: true,
5536 auto_start: false,
5537 process_limits: ProcessLimits::default(),
5538 initialization_options: None,
5539 env: Default::default(),
5540 language_id_overrides: Default::default(),
5541 name: None,
5542 only_features: None,
5543 except_features: None,
5544 root_markers: Default::default(),
5545 }]),
5546 );
5547
5548 lsp.insert(
5551 "julia".to_string(),
5552 LspLanguageConfig::Multi(vec![LspServerConfig {
5553 command: "julia".to_string(),
5554 args: vec![
5555 "--startup-file=no".to_string(),
5556 "--history-file=no".to_string(),
5557 "-e".to_string(),
5558 "using LanguageServer; runserver()".to_string(),
5559 ],
5560 enabled: true,
5561 auto_start: false,
5562 process_limits: ProcessLimits::default(),
5563 initialization_options: None,
5564 env: Default::default(),
5565 language_id_overrides: Default::default(),
5566 name: None,
5567 only_features: None,
5568 except_features: None,
5569 root_markers: Default::default(),
5570 }]),
5571 );
5572
5573 lsp.insert(
5576 "perl".to_string(),
5577 LspLanguageConfig::Multi(vec![LspServerConfig {
5578 command: "perlnavigator".to_string(),
5579 args: vec!["--stdio".to_string()],
5580 enabled: true,
5581 auto_start: false,
5582 process_limits: ProcessLimits::default(),
5583 initialization_options: None,
5584 env: Default::default(),
5585 language_id_overrides: Default::default(),
5586 name: None,
5587 only_features: None,
5588 except_features: None,
5589 root_markers: Default::default(),
5590 }]),
5591 );
5592
5593 lsp.insert(
5596 "nim".to_string(),
5597 LspLanguageConfig::Multi(vec![LspServerConfig {
5598 command: "nimlangserver".to_string(),
5599 args: vec![],
5600 enabled: true,
5601 auto_start: false,
5602 process_limits: ProcessLimits::default(),
5603 initialization_options: None,
5604 env: Default::default(),
5605 language_id_overrides: Default::default(),
5606 name: None,
5607 only_features: None,
5608 except_features: None,
5609 root_markers: Default::default(),
5610 }]),
5611 );
5612
5613 lsp.insert(
5615 "gleam".to_string(),
5616 LspLanguageConfig::Multi(vec![LspServerConfig {
5617 command: "gleam".to_string(),
5618 args: vec!["lsp".to_string()],
5619 enabled: true,
5620 auto_start: false,
5621 process_limits: ProcessLimits::default(),
5622 initialization_options: None,
5623 env: Default::default(),
5624 language_id_overrides: Default::default(),
5625 name: None,
5626 only_features: None,
5627 except_features: None,
5628 root_markers: Default::default(),
5629 }]),
5630 );
5631
5632 lsp.insert(
5635 "fsharp".to_string(),
5636 LspLanguageConfig::Multi(vec![LspServerConfig {
5637 command: "fsautocomplete".to_string(),
5638 args: vec!["--adaptive-lsp-server-enabled".to_string()],
5639 enabled: true,
5640 auto_start: false,
5641 process_limits: ProcessLimits::default(),
5642 initialization_options: None,
5643 env: Default::default(),
5644 language_id_overrides: Default::default(),
5645 name: None,
5646 only_features: None,
5647 except_features: None,
5648 root_markers: Default::default(),
5649 }]),
5650 );
5651 }
5652 pub fn validate(&self) -> Result<(), ConfigError> {
5653 if self.editor.tab_size == 0 {
5655 return Err(ConfigError::ValidationError(
5656 "tab_size must be greater than 0".to_string(),
5657 ));
5658 }
5659
5660 if self.editor.scroll_offset > 100 {
5662 return Err(ConfigError::ValidationError(
5663 "scroll_offset must be <= 100".to_string(),
5664 ));
5665 }
5666
5667 for binding in &self.keybindings {
5669 if binding.key.is_empty() {
5670 return Err(ConfigError::ValidationError(
5671 "keybinding key cannot be empty".to_string(),
5672 ));
5673 }
5674 if binding.action.is_empty() {
5675 return Err(ConfigError::ValidationError(
5676 "keybinding action cannot be empty".to_string(),
5677 ));
5678 }
5679 }
5680
5681 Ok(())
5682 }
5683}
5684
5685#[derive(Debug)]
5687pub enum ConfigError {
5688 IoError(String),
5689 ParseError(String),
5690 SerializeError(String),
5691 ValidationError(String),
5692}
5693
5694impl std::fmt::Display for ConfigError {
5695 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
5696 match self {
5697 Self::IoError(msg) => write!(f, "IO error: {msg}"),
5698 Self::ParseError(msg) => write!(f, "Parse error: {msg}"),
5699 Self::SerializeError(msg) => write!(f, "Serialize error: {msg}"),
5700 Self::ValidationError(msg) => write!(f, "Validation error: {msg}"),
5701 }
5702 }
5703}
5704
5705impl std::error::Error for ConfigError {}
5706
5707#[cfg(test)]
5708mod tests {
5709 use super::*;
5710
5711 #[test]
5712 fn test_default_config() {
5713 let config = Config::default();
5714 assert_eq!(config.editor.tab_size, 4);
5715 assert!(config.editor.line_numbers);
5716 assert!(config.editor.syntax_highlighting);
5717 assert!(config.keybindings.is_empty());
5720 let resolved = config.resolve_keymap(&config.active_keybinding_map);
5722 assert!(!resolved.is_empty());
5723 }
5724
5725 #[test]
5726 fn test_all_builtin_keymaps_loadable() {
5727 for name in KeybindingMapName::BUILTIN_OPTIONS {
5728 let keymap = Config::load_builtin_keymap(name);
5729 assert!(keymap.is_some(), "Failed to load builtin keymap '{}'", name);
5730 }
5731 }
5732
5733 #[test]
5734 fn test_config_validation() {
5735 let mut config = Config::default();
5736 assert!(config.validate().is_ok());
5737
5738 config.editor.tab_size = 0;
5739 assert!(config.validate().is_err());
5740 }
5741
5742 #[test]
5743 fn test_macos_keymap_inherits_enter_bindings() {
5744 let config = Config::default();
5745 let bindings = config.resolve_keymap("macos");
5746
5747 let enter_bindings: Vec<_> = bindings.iter().filter(|b| b.key == "Enter").collect();
5748 assert!(
5749 !enter_bindings.is_empty(),
5750 "macos keymap should inherit Enter bindings from default, got {} Enter bindings",
5751 enter_bindings.len()
5752 );
5753 let has_insert_newline = enter_bindings.iter().any(|b| b.action == "insert_newline");
5755 assert!(
5756 has_insert_newline,
5757 "macos keymap should have insert_newline action for Enter key"
5758 );
5759 }
5760
5761 #[test]
5762 fn test_config_serialize_deserialize() {
5763 let config = Config::default();
5765
5766 let json = serde_json::to_string_pretty(&config).unwrap();
5768
5769 let loaded: Config = serde_json::from_str(&json).unwrap();
5771
5772 assert_eq!(config.editor.tab_size, loaded.editor.tab_size);
5773 assert_eq!(config.theme, loaded.theme);
5774 }
5775
5776 #[test]
5777 fn test_config_with_custom_keybinding() {
5778 let json = r#"{
5779 "editor": {
5780 "tab_size": 2
5781 },
5782 "keybindings": [
5783 {
5784 "key": "x",
5785 "modifiers": ["ctrl", "shift"],
5786 "action": "custom_action",
5787 "args": {},
5788 "when": null
5789 }
5790 ]
5791 }"#;
5792
5793 let config: Config = serde_json::from_str(json).unwrap();
5794 assert_eq!(config.editor.tab_size, 2);
5795 assert_eq!(config.keybindings.len(), 1);
5796 assert_eq!(config.keybindings[0].key, "x");
5797 assert_eq!(config.keybindings[0].modifiers.len(), 2);
5798 }
5799
5800 #[test]
5801 fn test_sparse_config_merges_with_defaults() {
5802 let temp_dir = tempfile::tempdir().unwrap();
5804 let config_path = temp_dir.path().join("config.json");
5805
5806 let sparse_config = r#"{
5808 "lsp": {
5809 "rust": {
5810 "command": "custom-rust-analyzer",
5811 "args": ["--custom-arg"]
5812 }
5813 }
5814 }"#;
5815 std::fs::write(&config_path, sparse_config).unwrap();
5816
5817 let loaded = Config::load_from_file(&config_path).unwrap();
5819
5820 assert!(loaded.lsp.contains_key("rust"));
5822 assert_eq!(
5823 loaded.lsp["rust"].as_slice()[0].command,
5824 "custom-rust-analyzer".to_string()
5825 );
5826
5827 assert!(
5829 loaded.lsp.contains_key("python"),
5830 "python LSP should be merged from defaults"
5831 );
5832 assert!(
5833 loaded.lsp.contains_key("typescript"),
5834 "typescript LSP should be merged from defaults"
5835 );
5836 assert!(
5837 loaded.lsp.contains_key("javascript"),
5838 "javascript LSP should be merged from defaults"
5839 );
5840
5841 assert!(loaded.languages.contains_key("rust"));
5843 assert!(loaded.languages.contains_key("python"));
5844 assert!(loaded.languages.contains_key("typescript"));
5845 }
5846
5847 #[test]
5848 fn test_empty_config_gets_all_defaults() {
5849 let temp_dir = tempfile::tempdir().unwrap();
5850 let config_path = temp_dir.path().join("config.json");
5851
5852 std::fs::write(&config_path, "{}").unwrap();
5854
5855 let loaded = Config::load_from_file(&config_path).unwrap();
5856 let defaults = Config::default();
5857
5858 assert_eq!(loaded.lsp.len(), defaults.lsp.len());
5860
5861 assert_eq!(loaded.languages.len(), defaults.languages.len());
5863 }
5864
5865 #[test]
5866 fn test_dynamic_submenu_expansion() {
5867 let temp_dir = tempfile::tempdir().unwrap();
5869 let themes_dir = temp_dir.path().to_path_buf();
5870
5871 let dynamic = MenuItem::DynamicSubmenu {
5872 label: "Test".to_string(),
5873 source: "copy_with_theme".to_string(),
5874 };
5875
5876 let expanded = dynamic.expand_dynamic(&themes_dir);
5877
5878 match expanded {
5880 MenuItem::Submenu { label, items } => {
5881 assert_eq!(label, "Test");
5882 let loader = crate::view::theme::ThemeLoader::embedded_only();
5884 let registry = loader.load_all(&[]);
5885 assert_eq!(items.len(), registry.len());
5886
5887 for (item, theme_info) in items.iter().zip(registry.list().iter()) {
5889 match item {
5890 MenuItem::Action {
5891 label,
5892 action,
5893 args,
5894 ..
5895 } => {
5896 assert_eq!(label, &theme_info.name);
5897 assert_eq!(action, "copy_with_theme");
5898 assert_eq!(
5899 args.get("theme").and_then(|v| v.as_str()),
5900 Some(theme_info.name.as_str())
5901 );
5902 }
5903 _ => panic!("Expected Action item"),
5904 }
5905 }
5906 }
5907 _ => panic!("Expected Submenu after expansion"),
5908 }
5909 }
5910
5911 #[test]
5912 fn test_non_dynamic_item_unchanged() {
5913 let temp_dir = tempfile::tempdir().unwrap();
5915 let themes_dir = temp_dir.path();
5916
5917 let action = MenuItem::Action {
5918 label: "Test".to_string(),
5919 action: "test".to_string(),
5920 args: HashMap::new(),
5921 when: None,
5922 checkbox: None,
5923 };
5924
5925 let expanded = action.expand_dynamic(themes_dir);
5926 match expanded {
5927 MenuItem::Action { label, action, .. } => {
5928 assert_eq!(label, "Test");
5929 assert_eq!(action, "test");
5930 }
5931 _ => panic!("Action should remain Action after expand_dynamic"),
5932 }
5933 }
5934
5935 #[test]
5936 fn test_buffer_config_uses_global_defaults() {
5937 let config = Config::default();
5938 let buffer_config = BufferConfig::resolve(&config, None);
5939
5940 assert_eq!(buffer_config.tab_size, config.editor.tab_size);
5941 assert_eq!(buffer_config.auto_indent, config.editor.auto_indent);
5942 assert!(!buffer_config.use_tabs); assert!(buffer_config.whitespace.any_tabs()); assert!(buffer_config.formatter.is_none());
5945 assert!(!buffer_config.format_on_save);
5946 }
5947
5948 #[test]
5949 fn test_buffer_config_applies_language_overrides() {
5950 let mut config = Config::default();
5951
5952 config.languages.insert(
5954 "go".to_string(),
5955 LanguageConfig {
5956 extensions: vec!["go".to_string()],
5957 filenames: vec![],
5958 grammar: "go".to_string(),
5959 comment_prefix: Some("//".to_string()),
5960 auto_indent: true,
5961 auto_close: None,
5962 auto_surround: None,
5963 textmate_grammar: None,
5964 show_whitespace_tabs: false, line_wrap: None,
5966 wrap_column: None,
5967 page_view: None,
5968 page_width: None,
5969 use_tabs: Some(true), tab_size: Some(8), formatter: Some(FormatterConfig {
5972 command: "gofmt".to_string(),
5973 args: vec![],
5974 stdin: true,
5975 timeout_ms: 10000,
5976 }),
5977 format_on_save: true,
5978 on_save: vec![],
5979 word_characters: None,
5980 },
5981 );
5982
5983 let buffer_config = BufferConfig::resolve(&config, Some("go"));
5984
5985 assert_eq!(buffer_config.tab_size, 8);
5986 assert!(buffer_config.use_tabs);
5987 assert!(!buffer_config.whitespace.any_tabs()); assert!(buffer_config.format_on_save);
5989 assert!(buffer_config.formatter.is_some());
5990 assert_eq!(buffer_config.formatter.as_ref().unwrap().command, "gofmt");
5991 }
5992
5993 #[test]
5994 fn test_buffer_config_unknown_language_uses_global() {
5995 let config = Config::default();
5996 let buffer_config = BufferConfig::resolve(&config, Some("unknown_lang"));
5997
5998 assert_eq!(buffer_config.tab_size, config.editor.tab_size);
6000 assert!(!buffer_config.use_tabs);
6001 }
6002
6003 #[test]
6004 fn test_buffer_config_per_language_line_wrap() {
6005 let mut config = Config::default();
6006 config.editor.line_wrap = false;
6007
6008 config.languages.insert(
6010 "markdown".to_string(),
6011 LanguageConfig {
6012 extensions: vec!["md".to_string()],
6013 line_wrap: Some(true),
6014 ..Default::default()
6015 },
6016 );
6017
6018 let md_config = BufferConfig::resolve(&config, Some("markdown"));
6020 assert!(md_config.line_wrap, "Markdown should have line_wrap=true");
6021
6022 let other_config = BufferConfig::resolve(&config, Some("rust"));
6024 assert!(
6025 !other_config.line_wrap,
6026 "Non-configured languages should use global line_wrap=false"
6027 );
6028
6029 let no_lang_config = BufferConfig::resolve(&config, None);
6031 assert!(
6032 !no_lang_config.line_wrap,
6033 "No language should use global line_wrap=false"
6034 );
6035 }
6036
6037 #[test]
6038 fn test_buffer_config_per_language_wrap_column() {
6039 let mut config = Config::default();
6040 config.editor.wrap_column = Some(120);
6041
6042 config.languages.insert(
6044 "markdown".to_string(),
6045 LanguageConfig {
6046 extensions: vec!["md".to_string()],
6047 wrap_column: Some(80),
6048 ..Default::default()
6049 },
6050 );
6051
6052 let md_config = BufferConfig::resolve(&config, Some("markdown"));
6054 assert_eq!(md_config.wrap_column, Some(80));
6055
6056 let other_config = BufferConfig::resolve(&config, Some("rust"));
6058 assert_eq!(other_config.wrap_column, Some(120));
6059
6060 let no_lang_config = BufferConfig::resolve(&config, None);
6062 assert_eq!(no_lang_config.wrap_column, Some(120));
6063 }
6064
6065 #[test]
6066 fn test_buffer_config_indent_string() {
6067 let config = Config::default();
6068
6069 let spaces_config = BufferConfig::resolve(&config, None);
6071 assert_eq!(spaces_config.indent_string(), " "); let mut config_with_tabs = Config::default();
6075 config_with_tabs.languages.insert(
6076 "makefile".to_string(),
6077 LanguageConfig {
6078 use_tabs: Some(true),
6079 tab_size: Some(8),
6080 ..Default::default()
6081 },
6082 );
6083 let tabs_config = BufferConfig::resolve(&config_with_tabs, Some("makefile"));
6084 assert_eq!(tabs_config.indent_string(), "\t");
6085 }
6086
6087 #[test]
6088 fn test_buffer_config_global_use_tabs_inherited() {
6089 let mut config = Config::default();
6092 config.editor.use_tabs = true;
6093
6094 let buffer_config = BufferConfig::resolve(&config, Some("unknown_lang"));
6096 assert!(buffer_config.use_tabs);
6097
6098 let buffer_config = BufferConfig::resolve(&config, None);
6100 assert!(buffer_config.use_tabs);
6101
6102 config.languages.insert(
6104 "python".to_string(),
6105 LanguageConfig {
6106 use_tabs: Some(false),
6107 ..Default::default()
6108 },
6109 );
6110 let buffer_config = BufferConfig::resolve(&config, Some("python"));
6111 assert!(!buffer_config.use_tabs);
6112
6113 config.languages.insert(
6115 "rust".to_string(),
6116 LanguageConfig {
6117 use_tabs: None,
6118 ..Default::default()
6119 },
6120 );
6121 let buffer_config = BufferConfig::resolve(&config, Some("rust"));
6122 assert!(buffer_config.use_tabs);
6123 }
6124
6125 #[test]
6131 #[cfg(feature = "runtime")]
6132 fn test_lsp_languages_have_language_config() {
6133 let config = Config::default();
6134 let exceptions = ["tailwindcss"];
6135 for lsp_key in config.lsp.keys() {
6136 if exceptions.contains(&lsp_key.as_str()) {
6137 continue;
6138 }
6139 assert!(
6140 config.languages.contains_key(lsp_key),
6141 "LSP config key '{}' has no matching entry in default_languages(). \
6142 Add a LanguageConfig with the correct file extensions so detect_language() \
6143 can map files to this language.",
6144 lsp_key
6145 );
6146 }
6147 }
6148
6149 #[test]
6150 #[cfg(feature = "runtime")]
6151 fn test_default_config_has_quicklsp_in_universal_lsp() {
6152 let config = Config::default();
6153 assert!(
6154 config.universal_lsp.contains_key("quicklsp"),
6155 "Default config should contain quicklsp in universal_lsp"
6156 );
6157 let quicklsp = &config.universal_lsp["quicklsp"];
6158 let server = &quicklsp.as_slice()[0];
6159 assert_eq!(server.command, "quicklsp");
6160 assert!(!server.enabled, "quicklsp should be disabled by default");
6161 assert_eq!(server.name.as_deref(), Some("QuickLSP"));
6162 }
6163
6164 #[test]
6165 fn test_empty_config_preserves_universal_lsp_defaults() {
6166 let temp_dir = tempfile::tempdir().unwrap();
6167 let config_path = temp_dir.path().join("config.json");
6168
6169 std::fs::write(&config_path, "{}").unwrap();
6171
6172 let loaded = Config::load_from_file(&config_path).unwrap();
6173 let defaults = Config::default();
6174
6175 assert_eq!(
6177 loaded.universal_lsp.len(),
6178 defaults.universal_lsp.len(),
6179 "Empty config should preserve all default universal_lsp entries"
6180 );
6181 }
6182
6183 #[test]
6184 fn test_universal_lsp_config_merges_with_defaults() {
6185 let temp_dir = tempfile::tempdir().unwrap();
6186 let config_path = temp_dir.path().join("config.json");
6187
6188 let config_json = r#"{
6190 "universal_lsp": {
6191 "quicklsp": {
6192 "enabled": true
6193 }
6194 }
6195 }"#;
6196 std::fs::write(&config_path, config_json).unwrap();
6197
6198 let loaded = Config::load_from_file(&config_path).unwrap();
6199
6200 assert!(loaded.universal_lsp.contains_key("quicklsp"));
6202 let server = &loaded.universal_lsp["quicklsp"].as_slice()[0];
6203 assert!(server.enabled, "User override should enable quicklsp");
6204 assert_eq!(
6206 server.command, "quicklsp",
6207 "Default command should be merged when not specified by user"
6208 );
6209 }
6210
6211 #[test]
6212 fn test_universal_lsp_custom_server_added() {
6213 let temp_dir = tempfile::tempdir().unwrap();
6214 let config_path = temp_dir.path().join("config.json");
6215
6216 let config_json = r#"{
6218 "universal_lsp": {
6219 "my-custom-server": {
6220 "command": "my-server",
6221 "enabled": true,
6222 "auto_start": true
6223 }
6224 }
6225 }"#;
6226 std::fs::write(&config_path, config_json).unwrap();
6227
6228 let loaded = Config::load_from_file(&config_path).unwrap();
6229
6230 assert!(
6232 loaded.universal_lsp.contains_key("my-custom-server"),
6233 "Custom universal server should be loaded"
6234 );
6235 let server = &loaded.universal_lsp["my-custom-server"].as_slice()[0];
6236 assert_eq!(server.command, "my-server");
6237 assert!(server.enabled);
6238 assert!(server.auto_start);
6239
6240 assert!(
6242 loaded.universal_lsp.contains_key("quicklsp"),
6243 "Default quicklsp should be merged from defaults"
6244 );
6245 }
6246}