|
7 | 7 | //! configure the server itself, feature flags are passed into analysis, and |
8 | 8 | //! tweak things like automatic insertion of `()` in completions. |
9 | 9 |
|
10 | | -use rustc_hash::FxHashMap; |
11 | | - |
12 | | -use crate::feature_flags::FeatureFlags; |
13 | 10 | use lsp_types::TextDocumentClientCapabilities; |
14 | 11 | use ra_flycheck::FlycheckConfig; |
15 | 12 | use ra_ide::{CompletionConfig, InlayHintsConfig}; |
16 | 13 | use ra_project_model::CargoFeatures; |
17 | | -use serde::{Deserialize, Deserializer}; |
| 14 | +use serde::Deserialize; |
18 | 15 |
|
19 | 16 | #[derive(Debug, Clone)] |
20 | 17 | pub struct Config { |
@@ -61,171 +58,109 @@ impl Default for RustfmtConfig { |
61 | 58 | } |
62 | 59 | } |
63 | 60 |
|
64 | | -pub(crate) fn get_config( |
65 | | - config: &ServerConfig, |
66 | | - text_document_caps: Option<&TextDocumentClientCapabilities>, |
67 | | -) -> Config { |
68 | | - let feature_flags = get_feature_flags(config); |
69 | | - Config { |
70 | | - publish_decorations: config.publish_decorations, |
71 | | - publish_diagnostics: feature_flags.get("lsp.diagnostics"), |
72 | | - notifications: NotificationsConfig { |
73 | | - workspace_loaded: feature_flags.get("notifications.workspace-loaded"), |
74 | | - cargo_toml_not_found: feature_flags.get("notifications.cargo-toml-not-found"), |
75 | | - }, |
76 | | - supports_location_link: text_document_caps |
77 | | - .and_then(|it| it.definition) |
78 | | - .and_then(|it| it.link_support) |
79 | | - .unwrap_or(false), |
80 | | - line_folding_only: text_document_caps |
81 | | - .and_then(|it| it.folding_range.as_ref()) |
82 | | - .and_then(|it| it.line_folding_only) |
83 | | - .unwrap_or(false), |
84 | | - inlay_hints: InlayHintsConfig { |
85 | | - type_hints: config.inlay_hints_type, |
86 | | - parameter_hints: config.inlay_hints_parameter, |
87 | | - chaining_hints: config.inlay_hints_chaining, |
88 | | - max_length: config.inlay_hints_max_length, |
89 | | - }, |
90 | | - completion: CompletionConfig { |
91 | | - enable_postfix_completions: feature_flags.get("completion.enable-postfix"), |
92 | | - add_call_parenthesis: feature_flags.get("completion.insertion.add-call-parenthesis"), |
93 | | - add_call_argument_snippets: feature_flags |
94 | | - .get("completion.insertion.add-argument-snippets"), |
95 | | - }, |
96 | | - call_info_full: feature_flags.get("call-info.full"), |
97 | | - check: if config.cargo_watch_enable { |
98 | | - Some(FlycheckConfig::CargoCommand { |
99 | | - command: config.cargo_watch_command.clone(), |
100 | | - all_targets: config.cargo_watch_all_targets, |
101 | | - extra_args: config.cargo_watch_args.clone(), |
102 | | - }) |
103 | | - } else { |
104 | | - None |
105 | | - }, |
106 | | - rustfmt: RustfmtConfig::Rustfmt { extra_args: config.rustfmt_args.clone() }, |
107 | | - vscode_lldb: config.vscode_lldb, |
108 | | - proc_macro_srv: None, // FIXME: get this from config |
109 | | - lru_capacity: config.lru_capacity, |
110 | | - use_client_watching: config.use_client_watching, |
111 | | - exclude_globs: config.exclude_globs.clone(), |
112 | | - cargo: config.cargo_features.clone(), |
113 | | - with_sysroot: config.with_sysroot, |
114 | | - } |
115 | | -} |
116 | | - |
117 | | -fn get_feature_flags(config: &ServerConfig) -> FeatureFlags { |
118 | | - let mut ff = FeatureFlags::default(); |
119 | | - for (flag, &value) in &config.feature_flags { |
120 | | - if ff.set(flag.as_str(), value).is_err() { |
121 | | - log::error!("unknown feature flag: {:?}", flag); |
| 61 | +impl Default for Config { |
| 62 | + fn default() -> Self { |
| 63 | + Config { |
| 64 | + publish_decorations: false, |
| 65 | + publish_diagnostics: true, |
| 66 | + notifications: NotificationsConfig { |
| 67 | + workspace_loaded: true, |
| 68 | + cargo_toml_not_found: true, |
| 69 | + }, |
| 70 | + supports_location_link: false, |
| 71 | + line_folding_only: false, |
| 72 | + inlay_hints: InlayHintsConfig { |
| 73 | + type_hints: true, |
| 74 | + parameter_hints: true, |
| 75 | + chaining_hints: true, |
| 76 | + max_length: None, |
| 77 | + }, |
| 78 | + completion: CompletionConfig { |
| 79 | + enable_postfix_completions: true, |
| 80 | + add_call_parenthesis: true, |
| 81 | + add_call_argument_snippets: true, |
| 82 | + }, |
| 83 | + call_info_full: true, |
| 84 | + rustfmt: RustfmtConfig::default(), |
| 85 | + check: Some(FlycheckConfig::default()), |
| 86 | + vscode_lldb: false, |
| 87 | + proc_macro_srv: None, |
| 88 | + lru_capacity: None, |
| 89 | + use_client_watching: false, |
| 90 | + exclude_globs: Vec::new(), |
| 91 | + cargo: CargoFeatures::default(), |
| 92 | + with_sysroot: true, |
122 | 93 | } |
123 | 94 | } |
124 | | - log::info!("feature_flags: {:#?}", ff); |
125 | | - ff |
126 | 95 | } |
127 | 96 |
|
128 | | -/// Client provided initialization options |
129 | | -#[derive(Deserialize, Clone, Debug, PartialEq, Eq)] |
130 | | -#[serde(rename_all = "camelCase", default)] |
131 | | -pub struct ServerConfig { |
132 | | - /// Whether the client supports our custom highlighting publishing decorations. |
133 | | - /// This is different to the highlightingOn setting, which is whether the user |
134 | | - /// wants our custom highlighting to be used. |
135 | | - /// |
136 | | - /// Defaults to `false` |
137 | | - #[serde(deserialize_with = "nullable_bool_false")] |
138 | | - pub publish_decorations: bool, |
139 | | - |
140 | | - pub exclude_globs: Vec<String>, |
141 | | - #[serde(deserialize_with = "nullable_bool_false")] |
142 | | - pub use_client_watching: bool, |
143 | | - |
144 | | - pub lru_capacity: Option<usize>, |
145 | | - |
146 | | - #[serde(deserialize_with = "nullable_bool_true")] |
147 | | - pub inlay_hints_type: bool, |
148 | | - #[serde(deserialize_with = "nullable_bool_true")] |
149 | | - pub inlay_hints_parameter: bool, |
150 | | - #[serde(deserialize_with = "nullable_bool_true")] |
151 | | - pub inlay_hints_chaining: bool, |
152 | | - pub inlay_hints_max_length: Option<usize>, |
153 | | - |
154 | | - pub cargo_watch_enable: bool, |
155 | | - pub cargo_watch_args: Vec<String>, |
156 | | - pub cargo_watch_command: String, |
157 | | - pub cargo_watch_all_targets: bool, |
158 | | - |
159 | | - /// For internal usage to make integrated tests faster. |
160 | | - #[serde(deserialize_with = "nullable_bool_true")] |
161 | | - pub with_sysroot: bool, |
| 97 | +impl Config { |
| 98 | + #[rustfmt::skip] |
| 99 | + pub fn update(&mut self, value: &serde_json::Value) { |
| 100 | + let line_folding_only = self.line_folding_only; |
| 101 | + let supports_location_link = self.supports_location_link; |
| 102 | + *self = Default::default(); |
| 103 | + self.line_folding_only = line_folding_only; |
| 104 | + self.supports_location_link = supports_location_link; |
| 105 | + |
| 106 | + set(value, "publishDecorations", &mut self.publish_decorations); |
| 107 | + set(value, "excludeGlobs", &mut self.exclude_globs); |
| 108 | + set(value, "useClientWatching", &mut self.use_client_watching); |
| 109 | + set(value, "lruCapacity", &mut self.lru_capacity); |
| 110 | + |
| 111 | + set(value, "inlayHintsType", &mut self.inlay_hints.type_hints); |
| 112 | + set(value, "inlayHintsParameter", &mut self.inlay_hints.parameter_hints); |
| 113 | + set(value, "inlayHintsChaining", &mut self.inlay_hints.chaining_hints); |
| 114 | + set(value, "inlayHintsMaxLength", &mut self.inlay_hints.max_length); |
| 115 | + |
| 116 | + if let Some(false) = get(value, "cargo_watch_enable") { |
| 117 | + self.check = None |
| 118 | + } else { |
| 119 | + if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) = &mut self.check |
| 120 | + { |
| 121 | + set(value, "cargoWatchArgs", extra_args); |
| 122 | + set(value, "cargoWatchCommand", command); |
| 123 | + set(value, "cargoWatchAllTargets", all_targets); |
| 124 | + } |
| 125 | + }; |
| 126 | + |
| 127 | + set(value, "withSysroot", &mut self.with_sysroot); |
| 128 | + if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt { |
| 129 | + set(value, "rustfmtArgs", extra_args); |
| 130 | + } |
162 | 131 |
|
163 | | - /// Fine grained feature flags to disable specific features. |
164 | | - pub feature_flags: FxHashMap<String, bool>, |
| 132 | + set(value, "cargoFeatures/noDefaultFeatures", &mut self.cargo.no_default_features); |
| 133 | + set(value, "cargoFeatures/allFeatures", &mut self.cargo.all_features); |
| 134 | + set(value, "cargoFeatures/features", &mut self.cargo.features); |
| 135 | + set(value, "cargoFeatures/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check); |
165 | 136 |
|
166 | | - pub rustfmt_args: Vec<String>, |
| 137 | + set(value, "vscodeLldb", &mut self.vscode_lldb); |
167 | 138 |
|
168 | | - /// Cargo feature configurations. |
169 | | - pub cargo_features: CargoFeatures, |
| 139 | + set(value, "featureFlags/lsp.diagnostics", &mut self.publish_diagnostics); |
| 140 | + set(value, "featureFlags/notifications.workspace-loaded", &mut self.notifications.workspace_loaded); |
| 141 | + set(value, "featureFlags/notifications.cargo-toml-not-found", &mut self.notifications.cargo_toml_not_found); |
| 142 | + set(value, "featureFlags/completion.enable-postfix", &mut self.completion.enable_postfix_completions); |
| 143 | + set(value, "featureFlags/completion.insertion.add-call-parenthesis", &mut self.completion.add_call_parenthesis); |
| 144 | + set(value, "featureFlags/completion.insertion.add-argument-snippets", &mut self.completion.add_call_argument_snippets); |
| 145 | + set(value, "featureFlags/call-info.full", &mut self.call_info_full); |
170 | 146 |
|
171 | | - /// Enabled if the vscode_lldb extension is available. |
172 | | - pub vscode_lldb: bool, |
173 | | -} |
| 147 | + fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { |
| 148 | + value.pointer(pointer).and_then(|it| T::deserialize(it).ok()) |
| 149 | + } |
174 | 150 |
|
175 | | -impl Default for ServerConfig { |
176 | | - fn default() -> ServerConfig { |
177 | | - ServerConfig { |
178 | | - publish_decorations: false, |
179 | | - exclude_globs: Vec::new(), |
180 | | - use_client_watching: false, |
181 | | - lru_capacity: None, |
182 | | - inlay_hints_type: true, |
183 | | - inlay_hints_parameter: true, |
184 | | - inlay_hints_chaining: true, |
185 | | - inlay_hints_max_length: None, |
186 | | - cargo_watch_enable: true, |
187 | | - cargo_watch_args: Vec::new(), |
188 | | - cargo_watch_command: "check".to_string(), |
189 | | - cargo_watch_all_targets: true, |
190 | | - with_sysroot: true, |
191 | | - feature_flags: FxHashMap::default(), |
192 | | - cargo_features: Default::default(), |
193 | | - rustfmt_args: Vec::new(), |
194 | | - vscode_lldb: false, |
| 151 | + fn set<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) { |
| 152 | + if let Some(new_value) = get(value, pointer) { |
| 153 | + *slot = new_value |
| 154 | + } |
195 | 155 | } |
196 | 156 | } |
197 | | -} |
198 | | - |
199 | | -/// Deserializes a null value to a bool false by default |
200 | | -fn nullable_bool_false<'de, D>(deserializer: D) -> Result<bool, D::Error> |
201 | | -where |
202 | | - D: Deserializer<'de>, |
203 | | -{ |
204 | | - let opt = Option::deserialize(deserializer)?; |
205 | | - Ok(opt.unwrap_or(false)) |
206 | | -} |
207 | 157 |
|
208 | | -/// Deserializes a null value to a bool true by default |
209 | | -fn nullable_bool_true<'de, D>(deserializer: D) -> Result<bool, D::Error> |
210 | | -where |
211 | | - D: Deserializer<'de>, |
212 | | -{ |
213 | | - let opt = Option::deserialize(deserializer)?; |
214 | | - Ok(opt.unwrap_or(true)) |
215 | | -} |
216 | | - |
217 | | -#[cfg(test)] |
218 | | -mod test { |
219 | | - use super::*; |
220 | | - |
221 | | - #[test] |
222 | | - fn deserialize_init_options_defaults() { |
223 | | - // check that null == default for both fields |
224 | | - let default = ServerConfig::default(); |
225 | | - assert_eq!(default, serde_json::from_str(r#"{}"#).unwrap()); |
226 | | - assert_eq!( |
227 | | - default, |
228 | | - serde_json::from_str(r#"{"publishDecorations":null, "lruCapacity":null}"#).unwrap() |
229 | | - ); |
| 158 | + pub fn update_caps(&mut self, caps: &TextDocumentClientCapabilities) { |
| 159 | + if let Some(value) = caps.definition.as_ref().and_then(|it| it.link_support) { |
| 160 | + self.supports_location_link = value; |
| 161 | + } |
| 162 | + if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) { |
| 163 | + self.line_folding_only = value |
| 164 | + } |
230 | 165 | } |
231 | 166 | } |
0 commit comments