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