diff options
-rw-r--r-- | crates/ra_flycheck/src/lib.rs | 12 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/main.rs | 32 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 251 | ||||
-rw-r--r-- | crates/rust-analyzer/src/feature_flags.rs | 77 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lib.rs | 3 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 43 | ||||
-rw-r--r-- | crates/rust-analyzer/src/world.rs | 20 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/main.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/support.rs | 39 |
9 files changed, 151 insertions, 328 deletions
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 13494a731..f3d6f8f5f 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs | |||
@@ -22,12 +22,22 @@ use crate::conv::{map_rust_diagnostic_to_lsp, MappedRustDiagnostic}; | |||
22 | 22 | ||
23 | pub use crate::conv::url_from_path_with_drive_lowercasing; | 23 | pub use crate::conv::url_from_path_with_drive_lowercasing; |
24 | 24 | ||
25 | #[derive(Clone, Debug)] | 25 | #[derive(Clone, Debug, PartialEq, Eq)] |
26 | pub enum FlycheckConfig { | 26 | pub enum FlycheckConfig { |
27 | CargoCommand { command: String, all_targets: bool, extra_args: Vec<String> }, | 27 | CargoCommand { command: String, all_targets: bool, extra_args: Vec<String> }, |
28 | CustomCommand { command: String, args: Vec<String> }, | 28 | CustomCommand { command: String, args: Vec<String> }, |
29 | } | 29 | } |
30 | 30 | ||
31 | impl Default for FlycheckConfig { | ||
32 | fn default() -> Self { | ||
33 | FlycheckConfig::CargoCommand { | ||
34 | command: "check".to_string(), | ||
35 | all_targets: true, | ||
36 | extra_args: Vec::new(), | ||
37 | } | ||
38 | } | ||
39 | } | ||
40 | |||
31 | /// Flycheck wraps the shared state and communication machinery used for | 41 | /// Flycheck wraps the shared state and communication machinery used for |
32 | /// running `cargo check` (or other compatible command) and providing | 42 | /// running `cargo check` (or other compatible command) and providing |
33 | /// diagnostics based on the output. | 43 | /// diagnostics based on the output. |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index a744a6695..483f50ce6 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -5,7 +5,7 @@ mod args; | |||
5 | 5 | ||
6 | use lsp_server::Connection; | 6 | use lsp_server::Connection; |
7 | 7 | ||
8 | use rust_analyzer::{cli, from_json, show_message, Result, ServerConfig}; | 8 | use rust_analyzer::{cli, from_json, Config, Result}; |
9 | 9 | ||
10 | use crate::args::HelpPrinted; | 10 | use crate::args::HelpPrinted; |
11 | 11 | ||
@@ -78,24 +78,18 @@ fn run_server() -> Result<()> { | |||
78 | .filter(|workspaces| !workspaces.is_empty()) | 78 | .filter(|workspaces| !workspaces.is_empty()) |
79 | .unwrap_or_else(|| vec![root]); | 79 | .unwrap_or_else(|| vec![root]); |
80 | 80 | ||
81 | let server_config = initialize_params | 81 | let config = { |
82 | .initialization_options | 82 | let mut config = Config::default(); |
83 | .and_then(|v| { | 83 | if let Some(value) = &initialize_params.initialization_options { |
84 | from_json::<ServerConfig>("config", v) | 84 | config.update(value); |
85 | .map_err(|e| { | 85 | } |
86 | log::error!("{}", e); | 86 | if let Some(caps) = &initialize_params.capabilities.text_document { |
87 | show_message(lsp_types::MessageType::Error, e.to_string(), &connection.sender); | 87 | config.update_caps(caps); |
88 | }) | 88 | } |
89 | .ok() | 89 | config |
90 | }) | 90 | }; |
91 | .unwrap_or_default(); | 91 | |
92 | 92 | rust_analyzer::main_loop(workspace_roots, config, connection)?; | |
93 | rust_analyzer::main_loop( | ||
94 | workspace_roots, | ||
95 | initialize_params.capabilities, | ||
96 | server_config, | ||
97 | connection, | ||
98 | )?; | ||
99 | 93 | ||
100 | log::info!("shutting down IO..."); | 94 | log::info!("shutting down IO..."); |
101 | io_threads.join()?; | 95 | io_threads.join()?; |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 8a8e42ed8..c07626e5c 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -7,14 +7,11 @@ | |||
7 | //! configure the server itself, feature flags are passed into analysis, and | 7 | //! configure the server itself, feature flags are passed into analysis, and |
8 | //! tweak things like automatic insertion of `()` in completions. | 8 | //! tweak things like automatic insertion of `()` in completions. |
9 | 9 | ||
10 | use rustc_hash::FxHashMap; | ||
11 | |||
12 | use crate::feature_flags::FeatureFlags; | ||
13 | use lsp_types::TextDocumentClientCapabilities; | 10 | use lsp_types::TextDocumentClientCapabilities; |
14 | use ra_flycheck::FlycheckConfig; | 11 | use ra_flycheck::FlycheckConfig; |
15 | use ra_ide::{CompletionConfig, InlayHintsConfig}; | 12 | use ra_ide::{CompletionConfig, InlayHintsConfig}; |
16 | use ra_project_model::CargoFeatures; | 13 | use ra_project_model::CargoFeatures; |
17 | use serde::{Deserialize, Deserializer}; | 14 | use serde::Deserialize; |
18 | 15 | ||
19 | #[derive(Debug, Clone)] | 16 | #[derive(Debug, Clone)] |
20 | pub struct Config { | 17 | pub struct Config { |
@@ -61,171 +58,109 @@ impl Default for RustfmtConfig { | |||
61 | } | 58 | } |
62 | } | 59 | } |
63 | 60 | ||
64 | pub(crate) fn get_config( | 61 | impl Default for Config { |
65 | config: &ServerConfig, | 62 | fn default() -> Self { |
66 | text_document_caps: Option<&TextDocumentClientCapabilities>, | 63 | Config { |
67 | ) -> Config { | 64 | publish_decorations: false, |
68 | let feature_flags = get_feature_flags(config); | 65 | publish_diagnostics: true, |
69 | Config { | 66 | notifications: NotificationsConfig { |
70 | publish_decorations: config.publish_decorations, | 67 | workspace_loaded: true, |
71 | publish_diagnostics: feature_flags.get("lsp.diagnostics"), | 68 | cargo_toml_not_found: true, |
72 | notifications: NotificationsConfig { | 69 | }, |
73 | workspace_loaded: feature_flags.get("notifications.workspace-loaded"), | 70 | supports_location_link: false, |
74 | cargo_toml_not_found: feature_flags.get("notifications.cargo-toml-not-found"), | 71 | line_folding_only: false, |
75 | }, | 72 | inlay_hints: InlayHintsConfig { |
76 | supports_location_link: text_document_caps | 73 | type_hints: true, |
77 | .and_then(|it| it.definition) | 74 | parameter_hints: true, |
78 | .and_then(|it| it.link_support) | 75 | chaining_hints: true, |
79 | .unwrap_or(false), | 76 | max_length: None, |
80 | line_folding_only: text_document_caps | 77 | }, |
81 | .and_then(|it| it.folding_range.as_ref()) | 78 | completion: CompletionConfig { |
82 | .and_then(|it| it.line_folding_only) | 79 | enable_postfix_completions: true, |
83 | .unwrap_or(false), | 80 | add_call_parenthesis: true, |
84 | inlay_hints: InlayHintsConfig { | 81 | add_call_argument_snippets: true, |
85 | type_hints: config.inlay_hints_type, | 82 | }, |
86 | parameter_hints: config.inlay_hints_parameter, | 83 | call_info_full: true, |
87 | chaining_hints: config.inlay_hints_chaining, | 84 | rustfmt: RustfmtConfig::default(), |
88 | max_length: config.inlay_hints_max_length, | 85 | check: Some(FlycheckConfig::default()), |
89 | }, | 86 | vscode_lldb: false, |
90 | completion: CompletionConfig { | 87 | proc_macro_srv: None, |
91 | enable_postfix_completions: feature_flags.get("completion.enable-postfix"), | 88 | lru_capacity: None, |
92 | add_call_parenthesis: feature_flags.get("completion.insertion.add-call-parenthesis"), | 89 | use_client_watching: false, |
93 | add_call_argument_snippets: feature_flags | 90 | exclude_globs: Vec::new(), |
94 | .get("completion.insertion.add-argument-snippets"), | 91 | cargo: CargoFeatures::default(), |
95 | }, | 92 | with_sysroot: true, |
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); | ||
122 | } | 93 | } |
123 | } | 94 | } |
124 | log::info!("feature_flags: {:#?}", ff); | ||
125 | ff | ||
126 | } | 95 | } |
127 | 96 | ||
128 | /// Client provided initialization options | 97 | impl Config { |
129 | #[derive(Deserialize, Clone, Debug, PartialEq, Eq)] | 98 | #[rustfmt::skip] |
130 | #[serde(rename_all = "camelCase", default)] | 99 | pub fn update(&mut self, value: &serde_json::Value) { |
131 | pub struct ServerConfig { | 100 | let line_folding_only = self.line_folding_only; |
132 | /// Whether the client supports our custom highlighting publishing decorations. | 101 | let supports_location_link = self.supports_location_link; |
133 | /// This is different to the highlightingOn setting, which is whether the user | 102 | *self = Default::default(); |
134 | /// wants our custom highlighting to be used. | 103 | self.line_folding_only = line_folding_only; |
135 | /// | 104 | self.supports_location_link = supports_location_link; |
136 | /// Defaults to `false` | 105 | |
137 | #[serde(deserialize_with = "nullable_bool_false")] | 106 | set(value, "publishDecorations", &mut self.publish_decorations); |
138 | pub publish_decorations: bool, | 107 | set(value, "excludeGlobs", &mut self.exclude_globs); |
139 | 108 | set(value, "useClientWatching", &mut self.use_client_watching); | |
140 | pub exclude_globs: Vec<String>, | 109 | set(value, "lruCapacity", &mut self.lru_capacity); |
141 | #[serde(deserialize_with = "nullable_bool_false")] | 110 | |
142 | pub use_client_watching: bool, | 111 | set(value, "inlayHintsType", &mut self.inlay_hints.type_hints); |
143 | 112 | set(value, "inlayHintsParameter", &mut self.inlay_hints.parameter_hints); | |
144 | pub lru_capacity: Option<usize>, | 113 | set(value, "inlayHintsChaining", &mut self.inlay_hints.chaining_hints); |
145 | 114 | set(value, "inlayHintsMaxLength", &mut self.inlay_hints.max_length); | |
146 | #[serde(deserialize_with = "nullable_bool_true")] | 115 | |
147 | pub inlay_hints_type: bool, | 116 | if let Some(false) = get(value, "cargo_watch_enable") { |
148 | #[serde(deserialize_with = "nullable_bool_true")] | 117 | self.check = None |
149 | pub inlay_hints_parameter: bool, | 118 | } else { |
150 | #[serde(deserialize_with = "nullable_bool_true")] | 119 | if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) = &mut self.check |
151 | pub inlay_hints_chaining: bool, | 120 | { |
152 | pub inlay_hints_max_length: Option<usize>, | 121 | set(value, "cargoWatchArgs", extra_args); |
153 | 122 | set(value, "cargoWatchCommand", command); | |
154 | pub cargo_watch_enable: bool, | 123 | set(value, "cargoWatchAllTargets", all_targets); |
155 | pub cargo_watch_args: Vec<String>, | 124 | } |
156 | pub cargo_watch_command: String, | 125 | }; |
157 | pub cargo_watch_all_targets: bool, | 126 | |
158 | 127 | set(value, "withSysroot", &mut self.with_sysroot); | |
159 | /// For internal usage to make integrated tests faster. | 128 | if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt { |
160 | #[serde(deserialize_with = "nullable_bool_true")] | 129 | set(value, "rustfmtArgs", extra_args); |
161 | pub with_sysroot: bool, | 130 | } |
162 | 131 | ||
163 | /// Fine grained feature flags to disable specific features. | 132 | set(value, "cargoFeatures/noDefaultFeatures", &mut self.cargo.no_default_features); |
164 | pub feature_flags: FxHashMap<String, bool>, | 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. | 139 | set(value, "featureFlags/lsp.diagnostics", &mut self.publish_diagnostics); |
169 | pub cargo_features: CargoFeatures, | 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. | 147 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { |
172 | pub vscode_lldb: bool, | 148 | value.pointer(pointer).and_then(|it| T::deserialize(it).ok()) |
173 | } | 149 | } |
174 | 150 | ||
175 | impl Default for ServerConfig { | 151 | fn set<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) { |
176 | fn default() -> ServerConfig { | 152 | if let Some(new_value) = get(value, pointer) { |
177 | ServerConfig { | 153 | *slot = new_value |
178 | publish_decorations: false, | 154 | } |
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, | ||
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 | 158 | pub fn update_caps(&mut self, caps: &TextDocumentClientCapabilities) { |
209 | fn nullable_bool_true<'de, D>(deserializer: D) -> Result<bool, D::Error> | 159 | if let Some(value) = caps.definition.as_ref().and_then(|it| it.link_support) { |
210 | where | 160 | self.supports_location_link = value; |
211 | D: Deserializer<'de>, | 161 | } |
212 | { | 162 | if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) { |
213 | let opt = Option::deserialize(deserializer)?; | 163 | self.line_folding_only = value |
214 | Ok(opt.unwrap_or(true)) | 164 | } |
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 | ); | ||
230 | } | 165 | } |
231 | } | 166 | } |
diff --git a/crates/rust-analyzer/src/feature_flags.rs b/crates/rust-analyzer/src/feature_flags.rs deleted file mode 100644 index dbb3f50a0..000000000 --- a/crates/rust-analyzer/src/feature_flags.rs +++ /dev/null | |||
@@ -1,77 +0,0 @@ | |||
1 | //! See docs for `FeatureFlags`. | ||
2 | |||
3 | use rustc_hash::FxHashMap; | ||
4 | |||
5 | // FIXME: looks like a much better design is to pass options to each call, | ||
6 | // rather than to have a global ambient feature flags -- that way, the clients | ||
7 | // can issue two successive calls with different options. | ||
8 | |||
9 | /// Feature flags hold fine-grained toggles for all *user-visible* features of | ||
10 | /// rust-analyzer. | ||
11 | /// | ||
12 | /// The exists such that users are able to disable any annoying feature (and, | ||
13 | /// with many users and many features, some features are bound to be annoying | ||
14 | /// for some users) | ||
15 | /// | ||
16 | /// Note that we purposefully use run-time checked strings, and not something | ||
17 | /// checked at compile time, to keep things simple and flexible. | ||
18 | /// | ||
19 | /// Also note that, at the moment, `FeatureFlags` also store features for | ||
20 | /// `rust-analyzer`. This should be benign layering violation. | ||
21 | #[derive(Debug)] | ||
22 | pub struct FeatureFlags { | ||
23 | flags: FxHashMap<String, bool>, | ||
24 | } | ||
25 | |||
26 | impl FeatureFlags { | ||
27 | fn new(flags: &[(&str, bool)]) -> FeatureFlags { | ||
28 | let flags = flags | ||
29 | .iter() | ||
30 | .map(|&(name, value)| { | ||
31 | check_flag_name(name); | ||
32 | (name.to_string(), value) | ||
33 | }) | ||
34 | .collect(); | ||
35 | FeatureFlags { flags } | ||
36 | } | ||
37 | |||
38 | pub fn set(&mut self, flag: &str, value: bool) -> Result<(), ()> { | ||
39 | match self.flags.get_mut(flag) { | ||
40 | None => Err(()), | ||
41 | Some(slot) => { | ||
42 | *slot = value; | ||
43 | Ok(()) | ||
44 | } | ||
45 | } | ||
46 | } | ||
47 | |||
48 | pub fn get(&self, flag: &str) -> bool { | ||
49 | match self.flags.get(flag) { | ||
50 | None => panic!("unknown flag: {:?}", flag), | ||
51 | Some(value) => *value, | ||
52 | } | ||
53 | } | ||
54 | } | ||
55 | |||
56 | impl Default for FeatureFlags { | ||
57 | fn default() -> FeatureFlags { | ||
58 | FeatureFlags::new(&[ | ||
59 | ("lsp.diagnostics", true), | ||
60 | ("completion.insertion.add-call-parenthesis", true), | ||
61 | ("completion.insertion.add-argument-snippets", true), | ||
62 | ("completion.enable-postfix", true), | ||
63 | ("call-info.full", true), | ||
64 | ("notifications.workspace-loaded", true), | ||
65 | ("notifications.cargo-toml-not-found", true), | ||
66 | ]) | ||
67 | } | ||
68 | } | ||
69 | |||
70 | fn check_flag_name(flag: &str) { | ||
71 | for c in flag.bytes() { | ||
72 | match c { | ||
73 | b'a'..=b'z' | b'-' | b'.' => (), | ||
74 | _ => panic!("flag name does not match conventions: {:?}", flag), | ||
75 | } | ||
76 | } | ||
77 | } | ||
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs index e50e47b19..6062d0984 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -37,14 +37,13 @@ mod config; | |||
37 | mod world; | 37 | mod world; |
38 | mod diagnostics; | 38 | mod diagnostics; |
39 | mod semantic_tokens; | 39 | mod semantic_tokens; |
40 | mod feature_flags; | ||
41 | 40 | ||
42 | use serde::de::DeserializeOwned; | 41 | use serde::de::DeserializeOwned; |
43 | 42 | ||
44 | pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; | 43 | pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; |
45 | pub use crate::{ | 44 | pub use crate::{ |
45 | config::Config, | ||
46 | caps::server_capabilities, | 46 | caps::server_capabilities, |
47 | config::ServerConfig, | ||
48 | main_loop::LspError, | 47 | main_loop::LspError, |
49 | main_loop::{main_loop, show_message}, | 48 | main_loop::{main_loop, show_message}, |
50 | }; | 49 | }; |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index c973d43fa..00f92dbd5 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -17,9 +17,8 @@ use std::{ | |||
17 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; | 17 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; |
18 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 18 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
19 | use lsp_types::{ | 19 | use lsp_types::{ |
20 | ClientCapabilities, NumberOrString, TextDocumentClientCapabilities, WorkDoneProgress, | 20 | NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, |
21 | WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd, | 21 | WorkDoneProgressEnd, WorkDoneProgressReport, |
22 | WorkDoneProgressReport, | ||
23 | }; | 22 | }; |
24 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask}; | 23 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask}; |
25 | use ra_ide::{Canceled, FileId, LibraryData, SourceRootId}; | 24 | use ra_ide::{Canceled, FileId, LibraryData, SourceRootId}; |
@@ -31,7 +30,7 @@ use serde::{de::DeserializeOwned, Serialize}; | |||
31 | use threadpool::ThreadPool; | 30 | use threadpool::ThreadPool; |
32 | 31 | ||
33 | use crate::{ | 32 | use crate::{ |
34 | config::get_config, | 33 | config::Config, |
35 | diagnostics::DiagnosticTask, | 34 | diagnostics::DiagnosticTask, |
36 | main_loop::{ | 35 | main_loop::{ |
37 | pending_requests::{PendingRequest, PendingRequests}, | 36 | pending_requests::{PendingRequest, PendingRequests}, |
@@ -39,7 +38,7 @@ use crate::{ | |||
39 | }, | 38 | }, |
40 | req, | 39 | req, |
41 | world::{WorldSnapshot, WorldState}, | 40 | world::{WorldSnapshot, WorldState}, |
42 | Result, ServerConfig, | 41 | Result, |
43 | }; | 42 | }; |
44 | use req::ConfigurationParams; | 43 | use req::ConfigurationParams; |
45 | 44 | ||
@@ -65,14 +64,7 @@ impl fmt::Display for LspError { | |||
65 | 64 | ||
66 | impl Error for LspError {} | 65 | impl Error for LspError {} |
67 | 66 | ||
68 | pub fn main_loop( | 67 | pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> { |
69 | ws_roots: Vec<PathBuf>, | ||
70 | client_caps: ClientCapabilities, | ||
71 | config: ServerConfig, | ||
72 | connection: Connection, | ||
73 | ) -> Result<()> { | ||
74 | let text_document_caps = client_caps.text_document.as_ref(); | ||
75 | let config = get_config(&config, text_document_caps); | ||
76 | log::info!("initial config: {:#?}", config); | 68 | log::info!("initial config: {:#?}", config); |
77 | 69 | ||
78 | // Windows scheduler implements priority boosts: if thread waits for an | 70 | // Windows scheduler implements priority boosts: if thread waits for an |
@@ -205,7 +197,6 @@ pub fn main_loop( | |||
205 | &task_sender, | 197 | &task_sender, |
206 | &libdata_sender, | 198 | &libdata_sender, |
207 | &connection, | 199 | &connection, |
208 | text_document_caps, | ||
209 | &mut world_state, | 200 | &mut world_state, |
210 | &mut loop_state, | 201 | &mut loop_state, |
211 | event, | 202 | event, |
@@ -316,7 +307,6 @@ fn loop_turn( | |||
316 | task_sender: &Sender<Task>, | 307 | task_sender: &Sender<Task>, |
317 | libdata_sender: &Sender<LibraryData>, | 308 | libdata_sender: &Sender<LibraryData>, |
318 | connection: &Connection, | 309 | connection: &Connection, |
319 | text_document_caps: Option<&TextDocumentClientCapabilities>, | ||
320 | world_state: &mut WorldState, | 310 | world_state: &mut WorldState, |
321 | loop_state: &mut LoopState, | 311 | loop_state: &mut LoopState, |
322 | event: Event, | 312 | event: Event, |
@@ -370,27 +360,14 @@ fn loop_turn( | |||
370 | log::debug!("config update response: '{:?}", resp); | 360 | log::debug!("config update response: '{:?}", resp); |
371 | let Response { error, result, .. } = resp; | 361 | let Response { error, result, .. } = resp; |
372 | 362 | ||
373 | match ( | 363 | match (error, result) { |
374 | error, | ||
375 | result.map(|result| serde_json::from_value::<Vec<ServerConfig>>(result)), | ||
376 | ) { | ||
377 | (Some(err), _) => { | 364 | (Some(err), _) => { |
378 | log::error!("failed to fetch the server settings: {:?}", err) | 365 | log::error!("failed to fetch the server settings: {:?}", err) |
379 | } | 366 | } |
380 | (None, Some(Ok(new_config))) => { | 367 | (None, Some(new_config)) => { |
381 | let new_config = new_config | 368 | let mut config = world_state.config.clone(); |
382 | .first() | 369 | config.update(&new_config); |
383 | .expect( | 370 | world_state.update_configuration(config); |
384 | "the client is expected to always send a non-empty config data", | ||
385 | ) | ||
386 | .to_owned(); | ||
387 | world_state.update_configuration( | ||
388 | new_config.lru_capacity, | ||
389 | get_config(&new_config, text_document_caps), | ||
390 | ); | ||
391 | } | ||
392 | (None, Some(Err(e))) => { | ||
393 | log::error!("failed to parse client config response: {}", e) | ||
394 | } | 371 | } |
395 | (None, None) => { | 372 | (None, None) => { |
396 | log::error!("received empty server settings response from the client") | 373 | log::error!("received empty server settings response from the client") |
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index df1b7ceeb..5674f42ef 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs | |||
@@ -11,7 +11,7 @@ use std::{ | |||
11 | use crossbeam_channel::{unbounded, Receiver}; | 11 | use crossbeam_channel::{unbounded, Receiver}; |
12 | use lsp_types::Url; | 12 | use lsp_types::Url; |
13 | use parking_lot::RwLock; | 13 | use parking_lot::RwLock; |
14 | use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck}; | 14 | use ra_flycheck::{url_from_path_with_drive_lowercasing, Flycheck, FlycheckConfig}; |
15 | use ra_ide::{ | 15 | use ra_ide::{ |
16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, | 16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, |
17 | }; | 17 | }; |
@@ -30,9 +30,7 @@ use crate::{ | |||
30 | use ra_db::ExternSourceId; | 30 | use ra_db::ExternSourceId; |
31 | use rustc_hash::{FxHashMap, FxHashSet}; | 31 | use rustc_hash::{FxHashMap, FxHashSet}; |
32 | 32 | ||
33 | fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<Flycheck> { | 33 | fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> { |
34 | let check_config = config.check.as_ref()?; | ||
35 | |||
36 | // FIXME: Figure out the multi-workspace situation | 34 | // FIXME: Figure out the multi-workspace situation |
37 | workspaces | 35 | workspaces |
38 | .iter() | 36 | .iter() |
@@ -42,7 +40,7 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<F | |||
42 | }) | 40 | }) |
43 | .map(|cargo| { | 41 | .map(|cargo| { |
44 | let cargo_project_root = cargo.workspace_root().to_path_buf(); | 42 | let cargo_project_root = cargo.workspace_root().to_path_buf(); |
45 | Some(Flycheck::new(check_config.clone(), cargo_project_root)) | 43 | Some(Flycheck::new(config.clone(), cargo_project_root)) |
46 | }) | 44 | }) |
47 | .unwrap_or_else(|| { | 45 | .unwrap_or_else(|| { |
48 | log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); | 46 | log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); |
@@ -187,7 +185,7 @@ impl WorldState { | |||
187 | }); | 185 | }); |
188 | change.set_crate_graph(crate_graph); | 186 | change.set_crate_graph(crate_graph); |
189 | 187 | ||
190 | let flycheck = create_flycheck(&workspaces, &config); | 188 | let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c)); |
191 | 189 | ||
192 | let mut analysis_host = AnalysisHost::new(lru_capacity); | 190 | let mut analysis_host = AnalysisHost::new(lru_capacity); |
193 | analysis_host.apply_change(change); | 191 | analysis_host.apply_change(change); |
@@ -204,9 +202,13 @@ impl WorldState { | |||
204 | } | 202 | } |
205 | } | 203 | } |
206 | 204 | ||
207 | pub fn update_configuration(&mut self, lru_capacity: Option<usize>, config: Config) { | 205 | pub fn update_configuration(&mut self, config: Config) { |
208 | self.analysis_host.update_lru_capacity(lru_capacity); | 206 | self.analysis_host.update_lru_capacity(config.lru_capacity); |
209 | self.flycheck = create_flycheck(&self.workspaces, &config); | 207 | if config.check != self.config.check { |
208 | self.flycheck = | ||
209 | config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it)); | ||
210 | } | ||
211 | |||
210 | self.config = config; | 212 | self.config = config; |
211 | } | 213 | } |
212 | 214 | ||
diff --git a/crates/rust-analyzer/tests/heavy_tests/main.rs b/crates/rust-analyzer/tests/heavy_tests/main.rs index 5af5eaad2..638813311 100644 --- a/crates/rust-analyzer/tests/heavy_tests/main.rs +++ b/crates/rust-analyzer/tests/heavy_tests/main.rs | |||
@@ -615,7 +615,7 @@ fn main() { message(); } | |||
615 | "###, | 615 | "###, |
616 | ) | 616 | ) |
617 | .with_config(|config| { | 617 | .with_config(|config| { |
618 | config.cargo_features.load_out_dirs_from_check = true; | 618 | config.cargo.load_out_dirs_from_check = true; |
619 | }) | 619 | }) |
620 | .server(); | 620 | .server(); |
621 | server.wait_until_workspace_is_loaded(); | 621 | server.wait_until_workspace_is_loaded(); |
diff --git a/crates/rust-analyzer/tests/heavy_tests/support.rs b/crates/rust-analyzer/tests/heavy_tests/support.rs index d8bed6d7f..c83cb8adb 100644 --- a/crates/rust-analyzer/tests/heavy_tests/support.rs +++ b/crates/rust-analyzer/tests/heavy_tests/support.rs | |||
@@ -11,8 +11,7 @@ use lsp_server::{Connection, Message, Notification, Request}; | |||
11 | use lsp_types::{ | 11 | use lsp_types::{ |
12 | notification::{DidOpenTextDocument, Exit}, | 12 | notification::{DidOpenTextDocument, Exit}, |
13 | request::Shutdown, | 13 | request::Shutdown, |
14 | ClientCapabilities, DidOpenTextDocumentParams, GotoCapability, TextDocumentClientCapabilities, | 14 | DidOpenTextDocumentParams, TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress, |
15 | TextDocumentIdentifier, TextDocumentItem, Url, WorkDoneProgress, | ||
16 | }; | 15 | }; |
17 | use serde::Serialize; | 16 | use serde::Serialize; |
18 | use serde_json::{to_string_pretty, Value}; | 17 | use serde_json::{to_string_pretty, Value}; |
@@ -20,14 +19,14 @@ use tempfile::TempDir; | |||
20 | use test_utils::{find_mismatch, parse_fixture}; | 19 | use test_utils::{find_mismatch, parse_fixture}; |
21 | 20 | ||
22 | use req::{ProgressParams, ProgressParamsValue}; | 21 | use req::{ProgressParams, ProgressParamsValue}; |
23 | use rust_analyzer::{main_loop, req, ServerConfig}; | 22 | use rust_analyzer::{main_loop, req, Config}; |
24 | 23 | ||
25 | pub struct Project<'a> { | 24 | pub struct Project<'a> { |
26 | fixture: &'a str, | 25 | fixture: &'a str, |
27 | with_sysroot: bool, | 26 | with_sysroot: bool, |
28 | tmp_dir: Option<TempDir>, | 27 | tmp_dir: Option<TempDir>, |
29 | roots: Vec<PathBuf>, | 28 | roots: Vec<PathBuf>, |
30 | config: Option<Box<dyn Fn(&mut ServerConfig)>>, | 29 | config: Option<Box<dyn Fn(&mut Config)>>, |
31 | } | 30 | } |
32 | 31 | ||
33 | impl<'a> Project<'a> { | 32 | impl<'a> Project<'a> { |
@@ -50,7 +49,7 @@ impl<'a> Project<'a> { | |||
50 | self | 49 | self |
51 | } | 50 | } |
52 | 51 | ||
53 | pub fn with_config(mut self, config: impl Fn(&mut ServerConfig) + 'static) -> Project<'a> { | 52 | pub fn with_config(mut self, config: impl Fn(&mut Config) + 'static) -> Project<'a> { |
54 | self.config = Some(Box::new(config)); | 53 | self.config = Some(Box::new(config)); |
55 | self | 54 | self |
56 | } | 55 | } |
@@ -78,8 +77,11 @@ impl<'a> Project<'a> { | |||
78 | 77 | ||
79 | let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); | 78 | let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); |
80 | 79 | ||
81 | let mut config = | 80 | let mut config = Config { |
82 | ServerConfig { with_sysroot: self.with_sysroot, ..ServerConfig::default() }; | 81 | supports_location_link: true, |
82 | with_sysroot: self.with_sysroot, | ||
83 | ..Config::default() | ||
84 | }; | ||
83 | 85 | ||
84 | if let Some(f) = &self.config { | 86 | if let Some(f) = &self.config { |
85 | f(&mut config) | 87 | f(&mut config) |
@@ -105,7 +107,7 @@ pub struct Server { | |||
105 | impl Server { | 107 | impl Server { |
106 | fn new( | 108 | fn new( |
107 | dir: TempDir, | 109 | dir: TempDir, |
108 | config: ServerConfig, | 110 | config: Config, |
109 | roots: Vec<PathBuf>, | 111 | roots: Vec<PathBuf>, |
110 | files: Vec<(PathBuf, String)>, | 112 | files: Vec<(PathBuf, String)>, |
111 | ) -> Server { | 113 | ) -> Server { |
@@ -116,26 +118,7 @@ impl Server { | |||
116 | 118 | ||
117 | let _thread = jod_thread::Builder::new() | 119 | let _thread = jod_thread::Builder::new() |
118 | .name("test server".to_string()) | 120 | .name("test server".to_string()) |
119 | .spawn(move || { | 121 | .spawn(move || main_loop(roots, config, connection).unwrap()) |
120 | main_loop( | ||
121 | roots, | ||
122 | ClientCapabilities { | ||
123 | workspace: None, | ||
124 | text_document: Some(TextDocumentClientCapabilities { | ||
125 | definition: Some(GotoCapability { | ||
126 | dynamic_registration: None, | ||
127 | link_support: Some(true), | ||
128 | }), | ||
129 | ..Default::default() | ||
130 | }), | ||
131 | window: None, | ||
132 | experimental: None, | ||
133 | }, | ||
134 | config, | ||
135 | connection, | ||
136 | ) | ||
137 | .unwrap() | ||
138 | }) | ||
139 | .expect("failed to spawn a thread"); | 122 | .expect("failed to spawn a thread"); |
140 | 123 | ||
141 | let res = | 124 | let res = |