1use collections::HashMap;
2use gpui::AppContext;
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use settings::{Settings, SettingsSources};
6use std::{sync::Arc, time::Duration};
7
8#[derive(Clone, Default, Serialize, Deserialize, JsonSchema)]
9pub struct ProjectSettings {
10 /// Configuration for language servers.
11 ///
12 /// The following settings can be overridden for specific language servers:
13 /// - initialization_options
14 /// To override settings for a language, add an entry for that language server's
15 /// name to the lsp value.
16 /// Default: null
17 #[serde(default)]
18 pub lsp: HashMap<Arc<str>, LspSettings>,
19
20 /// Configuration for Git-related features
21 #[serde(default)]
22 pub git: GitSettings,
23
24 /// Configuration for how direnv configuration should be loaded
25 #[serde(default)]
26 pub load_direnv: DirenvSettings,
27
28 /// Configuration for session-related features
29 #[serde(default)]
30 pub session: SessionSettings,
31}
32
33#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
34#[serde(rename_all = "snake_case")]
35pub enum DirenvSettings {
36 /// Load direnv configuration through a shell hook
37 #[default]
38 ShellHook,
39 /// Load direnv configuration directly using `direnv export json`
40 ///
41 /// Warning: This option is experimental and might cause some inconsistent behaviour compared to using the shell hook.
42 /// If it does, please report it to GitHub
43 Direct,
44}
45
46#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
47pub struct GitSettings {
48 /// Whether or not to show the git gutter.
49 ///
50 /// Default: tracked_files
51 pub git_gutter: Option<GitGutterSetting>,
52 pub gutter_debounce: Option<u64>,
53 /// Whether or not to show git blame data inline in
54 /// the currently focused line.
55 ///
56 /// Default: on
57 pub inline_blame: Option<InlineBlameSettings>,
58}
59
60impl GitSettings {
61 pub fn inline_blame_enabled(&self) -> bool {
62 #[allow(unknown_lints, clippy::manual_unwrap_or_default)]
63 match self.inline_blame {
64 Some(InlineBlameSettings { enabled, .. }) => enabled,
65 _ => false,
66 }
67 }
68
69 pub fn inline_blame_delay(&self) -> Option<Duration> {
70 match self.inline_blame {
71 Some(InlineBlameSettings {
72 delay_ms: Some(delay_ms),
73 ..
74 }) if delay_ms > 0 => Some(Duration::from_millis(delay_ms)),
75 _ => None,
76 }
77 }
78}
79
80#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
81#[serde(rename_all = "snake_case")]
82pub enum GitGutterSetting {
83 /// Show git gutter in tracked files.
84 #[default]
85 TrackedFiles,
86 /// Hide git gutter
87 Hide,
88}
89
90#[derive(Clone, Copy, Debug, Default, Serialize, Deserialize, JsonSchema)]
91#[serde(rename_all = "snake_case")]
92pub struct InlineBlameSettings {
93 /// Whether or not to show git blame data inline in
94 /// the currently focused line.
95 ///
96 /// Default: true
97 #[serde(default = "true_value")]
98 pub enabled: bool,
99 /// Whether to only show the inline blame information
100 /// after a delay once the cursor stops moving.
101 ///
102 /// Default: 0
103 pub delay_ms: Option<u64>,
104 /// The minimum column number to show the inline blame information at
105 ///
106 /// Default: 0
107 pub min_column: Option<u32>,
108}
109
110const fn true_value() -> bool {
111 true
112}
113
114#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
115pub struct BinarySettings {
116 pub path: Option<String>,
117 pub arguments: Option<Vec<String>>,
118 pub path_lookup: Option<bool>,
119}
120
121#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
122#[serde(rename_all = "snake_case")]
123pub struct LspSettings {
124 pub binary: Option<BinarySettings>,
125 pub initialization_options: Option<serde_json::Value>,
126 pub settings: Option<serde_json::Value>,
127}
128
129#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema)]
130pub struct SessionSettings {
131 /// Whether or not to restore unsaved buffers on restart.
132 ///
133 /// If this is true, user won't be prompted whether to save/discard
134 /// dirty files when closing the application.
135 ///
136 /// Default: true
137 pub restore_unsaved_buffers: bool,
138}
139
140impl Default for SessionSettings {
141 fn default() -> Self {
142 Self {
143 restore_unsaved_buffers: true,
144 }
145 }
146}
147
148impl Settings for ProjectSettings {
149 const KEY: Option<&'static str> = None;
150
151 type FileContent = Self;
152
153 fn load(
154 sources: SettingsSources<Self::FileContent>,
155 _: &mut AppContext,
156 ) -> anyhow::Result<Self> {
157 sources.json_merge()
158 }
159}