diff options
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_flycheck/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 33 | ||||
-rw-r--r-- | crates/ra_project_model/src/cargo_workspace.rs | 19 | ||||
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 6 | ||||
-rw-r--r-- | crates/rust-analyzer/src/bin/main.rs | 31 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/load_cargo.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 232 | ||||
-rw-r--r-- | crates/rust-analyzer/src/conv.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/feature_flags.rs | 77 | ||||
-rw-r--r-- | crates/rust-analyzer/src/lib.rs | 4 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 119 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop/handlers.rs | 44 | ||||
-rw-r--r-- | crates/rust-analyzer/src/world.rs | 46 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/main.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/tests/heavy_tests/support.rs | 42 |
15 files changed, 266 insertions, 397 deletions
diff --git a/crates/ra_flycheck/src/lib.rs b/crates/ra_flycheck/src/lib.rs index 13494a731..b54a30ab8 100644 --- a/crates/ra_flycheck/src/lib.rs +++ b/crates/ra_flycheck/src/lib.rs | |||
@@ -22,7 +22,7 @@ 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> }, |
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index d06fc03d3..4b133b19b 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -75,8 +75,7 @@ fn get_chaining_hints( | |||
75 | return None; | 75 | return None; |
76 | } | 76 | } |
77 | 77 | ||
78 | let ty = sema.type_of_expr(&expr)?; | 78 | if matches!(expr, ast::Expr::RecordLit(_)) { |
79 | if ty.is_unknown() { | ||
80 | return None; | 79 | return None; |
81 | } | 80 | } |
82 | 81 | ||
@@ -95,6 +94,17 @@ fn get_chaining_hints( | |||
95 | let next = tokens.next()?.kind(); | 94 | let next = tokens.next()?.kind(); |
96 | let next_next = tokens.next()?.kind(); | 95 | let next_next = tokens.next()?.kind(); |
97 | if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { | 96 | if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { |
97 | let ty = sema.type_of_expr(&expr)?; | ||
98 | if ty.is_unknown() { | ||
99 | return None; | ||
100 | } | ||
101 | if matches!(expr, ast::Expr::PathExpr(_)) { | ||
102 | if let Some(Adt::Struct(st)) = ty.as_adt() { | ||
103 | if st.fields(sema.db).is_empty() { | ||
104 | return None; | ||
105 | } | ||
106 | } | ||
107 | } | ||
98 | let label = ty.display_truncated(sema.db, config.max_length).to_string(); | 108 | let label = ty.display_truncated(sema.db, config.max_length).to_string(); |
99 | acc.push(InlayHint { | 109 | acc.push(InlayHint { |
100 | range: expr.syntax().text_range(), | 110 | range: expr.syntax().text_range(), |
@@ -1154,32 +1164,35 @@ fn main() { | |||
1154 | struct A { pub b: B } | 1164 | struct A { pub b: B } |
1155 | struct B { pub c: C } | 1165 | struct B { pub c: C } |
1156 | struct C(pub bool); | 1166 | struct C(pub bool); |
1167 | struct D; | ||
1168 | |||
1169 | impl D { | ||
1170 | fn foo(&self) -> i32 { 42 } | ||
1171 | } | ||
1157 | 1172 | ||
1158 | fn main() { | 1173 | fn main() { |
1159 | let x = A { b: B { c: C(true) } } | 1174 | let x = A { b: B { c: C(true) } } |
1160 | .b | 1175 | .b |
1161 | .c | 1176 | .c |
1162 | .0; | 1177 | .0; |
1178 | let x = D | ||
1179 | .foo(); | ||
1163 | }"#, | 1180 | }"#, |
1164 | ); | 1181 | ); |
1165 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" | 1182 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" |
1166 | [ | 1183 | [ |
1167 | InlayHint { | 1184 | InlayHint { |
1168 | range: [150; 221), | 1185 | range: [252; 323), |
1169 | kind: ChainingHint, | 1186 | kind: ChainingHint, |
1170 | label: "C", | 1187 | label: "C", |
1171 | }, | 1188 | }, |
1172 | InlayHint { | 1189 | InlayHint { |
1173 | range: [150; 198), | 1190 | range: [252; 300), |
1174 | kind: ChainingHint, | 1191 | kind: ChainingHint, |
1175 | label: "B", | 1192 | label: "B", |
1176 | }, | 1193 | }, |
1177 | InlayHint { | 1194 | ] |
1178 | range: [150; 175), | 1195 | "###); |
1179 | kind: ChainingHint, | ||
1180 | label: "A", | ||
1181 | }, | ||
1182 | ]"###); | ||
1183 | } | 1196 | } |
1184 | 1197 | ||
1185 | #[test] | 1198 | #[test] |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index f4fd6ad28..c1b6e1ddc 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -13,7 +13,6 @@ use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId} | |||
13 | use ra_arena::{Arena, Idx}; | 13 | use ra_arena::{Arena, Idx}; |
14 | use ra_db::Edition; | 14 | use ra_db::Edition; |
15 | use rustc_hash::FxHashMap; | 15 | use rustc_hash::FxHashMap; |
16 | use serde::Deserialize; | ||
17 | 16 | ||
18 | /// `CargoWorkspace` represents the logical structure of, well, a Cargo | 17 | /// `CargoWorkspace` represents the logical structure of, well, a Cargo |
19 | /// workspace. It pretty closely mirrors `cargo metadata` output. | 18 | /// workspace. It pretty closely mirrors `cargo metadata` output. |
@@ -43,9 +42,8 @@ impl ops::Index<Target> for CargoWorkspace { | |||
43 | } | 42 | } |
44 | } | 43 | } |
45 | 44 | ||
46 | #[derive(Deserialize, Clone, Debug, PartialEq, Eq)] | 45 | #[derive(Clone, Debug, PartialEq, Eq)] |
47 | #[serde(rename_all = "camelCase", default)] | 46 | pub struct CargoConfig { |
48 | pub struct CargoFeatures { | ||
49 | /// Do not activate the `default` feature. | 47 | /// Do not activate the `default` feature. |
50 | pub no_default_features: bool, | 48 | pub no_default_features: bool, |
51 | 49 | ||
@@ -60,9 +58,9 @@ pub struct CargoFeatures { | |||
60 | pub load_out_dirs_from_check: bool, | 58 | pub load_out_dirs_from_check: bool, |
61 | } | 59 | } |
62 | 60 | ||
63 | impl Default for CargoFeatures { | 61 | impl Default for CargoConfig { |
64 | fn default() -> Self { | 62 | fn default() -> Self { |
65 | CargoFeatures { | 63 | CargoConfig { |
66 | no_default_features: false, | 64 | no_default_features: false, |
67 | all_features: true, | 65 | all_features: true, |
68 | features: Vec::new(), | 66 | features: Vec::new(), |
@@ -141,7 +139,7 @@ impl PackageData { | |||
141 | impl CargoWorkspace { | 139 | impl CargoWorkspace { |
142 | pub fn from_cargo_metadata( | 140 | pub fn from_cargo_metadata( |
143 | cargo_toml: &Path, | 141 | cargo_toml: &Path, |
144 | cargo_features: &CargoFeatures, | 142 | cargo_features: &CargoConfig, |
145 | ) -> Result<CargoWorkspace> { | 143 | ) -> Result<CargoWorkspace> { |
146 | let mut meta = MetadataCommand::new(); | 144 | let mut meta = MetadataCommand::new(); |
147 | meta.manifest_path(cargo_toml); | 145 | meta.manifest_path(cargo_toml); |
@@ -275,7 +273,7 @@ pub struct ExternResources { | |||
275 | 273 | ||
276 | pub fn load_extern_resources( | 274 | pub fn load_extern_resources( |
277 | cargo_toml: &Path, | 275 | cargo_toml: &Path, |
278 | cargo_features: &CargoFeatures, | 276 | cargo_features: &CargoConfig, |
279 | ) -> Result<ExternResources> { | 277 | ) -> Result<ExternResources> { |
280 | let mut cmd = Command::new(cargo_binary()); | 278 | let mut cmd = Command::new(cargo_binary()); |
281 | cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); | 279 | cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); |
@@ -293,9 +291,8 @@ pub fn load_extern_resources( | |||
293 | 291 | ||
294 | let mut res = ExternResources::default(); | 292 | let mut res = ExternResources::default(); |
295 | 293 | ||
296 | let stdout = String::from_utf8(output.stdout)?; | 294 | for message in cargo_metadata::parse_messages(output.stdout.as_slice()) { |
297 | for line in stdout.lines() { | 295 | if let Ok(message) = message { |
298 | if let Ok(message) = serde_json::from_str::<cargo_metadata::Message>(&line) { | ||
299 | match message { | 296 | match message { |
300 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => { | 297 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => { |
301 | res.out_dirs.insert(package_id, out_dir); | 298 | res.out_dirs.insert(package_id, out_dir); |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index 444d3bb3f..dd9c80691 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -19,7 +19,7 @@ use rustc_hash::FxHashMap; | |||
19 | use serde_json::from_reader; | 19 | use serde_json::from_reader; |
20 | 20 | ||
21 | pub use crate::{ | 21 | pub use crate::{ |
22 | cargo_workspace::{CargoFeatures, CargoWorkspace, Package, Target, TargetKind}, | 22 | cargo_workspace::{CargoConfig, CargoWorkspace, Package, Target, TargetKind}, |
23 | json_project::JsonProject, | 23 | json_project::JsonProject, |
24 | sysroot::Sysroot, | 24 | sysroot::Sysroot, |
25 | }; | 25 | }; |
@@ -78,14 +78,14 @@ impl PackageRoot { | |||
78 | } | 78 | } |
79 | 79 | ||
80 | impl ProjectWorkspace { | 80 | impl ProjectWorkspace { |
81 | pub fn discover(path: &Path, cargo_features: &CargoFeatures) -> Result<ProjectWorkspace> { | 81 | pub fn discover(path: &Path, cargo_features: &CargoConfig) -> Result<ProjectWorkspace> { |
82 | ProjectWorkspace::discover_with_sysroot(path, true, cargo_features) | 82 | ProjectWorkspace::discover_with_sysroot(path, true, cargo_features) |
83 | } | 83 | } |
84 | 84 | ||
85 | pub fn discover_with_sysroot( | 85 | pub fn discover_with_sysroot( |
86 | path: &Path, | 86 | path: &Path, |
87 | with_sysroot: bool, | 87 | with_sysroot: bool, |
88 | cargo_features: &CargoFeatures, | 88 | cargo_features: &CargoConfig, |
89 | ) -> Result<ProjectWorkspace> { | 89 | ) -> Result<ProjectWorkspace> { |
90 | match find_rust_project_json(path) { | 90 | match find_rust_project_json(path) { |
91 | Some(json_path) => { | 91 | Some(json_path) => { |
diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index a744a6695..608f4f67b 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs | |||
@@ -4,8 +4,7 @@ | |||
4 | mod args; | 4 | mod args; |
5 | 5 | ||
6 | use lsp_server::Connection; | 6 | use lsp_server::Connection; |
7 | 7 | use rust_analyzer::{cli, config::Config, from_json, Result}; | |
8 | use rust_analyzer::{cli, from_json, show_message, Result, ServerConfig}; | ||
9 | 8 | ||
10 | use crate::args::HelpPrinted; | 9 | use crate::args::HelpPrinted; |
11 | 10 | ||
@@ -78,24 +77,18 @@ fn run_server() -> Result<()> { | |||
78 | .filter(|workspaces| !workspaces.is_empty()) | 77 | .filter(|workspaces| !workspaces.is_empty()) |
79 | .unwrap_or_else(|| vec![root]); | 78 | .unwrap_or_else(|| vec![root]); |
80 | 79 | ||
81 | let server_config = initialize_params | 80 | let config = { |
82 | .initialization_options | 81 | let mut config = Config::default(); |
83 | .and_then(|v| { | 82 | if let Some(value) = &initialize_params.initialization_options { |
84 | from_json::<ServerConfig>("config", v) | 83 | config.update(value); |
85 | .map_err(|e| { | 84 | } |
86 | log::error!("{}", e); | 85 | if let Some(caps) = &initialize_params.capabilities.text_document { |
87 | show_message(lsp_types::MessageType::Error, e.to_string(), &connection.sender); | 86 | config.update_caps(caps); |
88 | }) | 87 | } |
89 | .ok() | 88 | config |
90 | }) | 89 | }; |
91 | .unwrap_or_default(); | ||
92 | 90 | ||
93 | rust_analyzer::main_loop( | 91 | rust_analyzer::main_loop(workspace_roots, config, connection)?; |
94 | workspace_roots, | ||
95 | initialize_params.capabilities, | ||
96 | server_config, | ||
97 | connection, | ||
98 | )?; | ||
99 | 92 | ||
100 | log::info!("shutting down IO..."); | 93 | log::info!("shutting down IO..."); |
101 | io_threads.join()?; | 94 | io_threads.join()?; |
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 832f04226..2c0bde920 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -8,7 +8,7 @@ use crossbeam_channel::{unbounded, Receiver}; | |||
8 | use ra_db::{ExternSourceId, FileId, SourceRootId}; | 8 | use ra_db::{ExternSourceId, FileId, SourceRootId}; |
9 | use ra_ide::{AnalysisChange, AnalysisHost}; | 9 | use ra_ide::{AnalysisChange, AnalysisHost}; |
10 | use ra_project_model::{ | 10 | use ra_project_model::{ |
11 | get_rustc_cfg_options, CargoFeatures, PackageRoot, ProcMacroClient, ProjectWorkspace, | 11 | get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectWorkspace, |
12 | }; | 12 | }; |
13 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; | 13 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; |
14 | use rustc_hash::{FxHashMap, FxHashSet}; | 14 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -29,7 +29,7 @@ pub(crate) fn load_cargo( | |||
29 | let root = std::env::current_dir()?.join(root); | 29 | let root = std::env::current_dir()?.join(root); |
30 | let ws = ProjectWorkspace::discover( | 30 | let ws = ProjectWorkspace::discover( |
31 | root.as_ref(), | 31 | root.as_ref(), |
32 | &CargoFeatures { load_out_dirs_from_check, ..Default::default() }, | 32 | &CargoConfig { load_out_dirs_from_check, ..Default::default() }, |
33 | )?; | 33 | )?; |
34 | 34 | ||
35 | let mut extern_dirs = FxHashSet::default(); | 35 | let mut extern_dirs = FxHashSet::default(); |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 628ed107e..3c8f55f1e 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -7,112 +7,164 @@ | |||
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; | 10 | use lsp_types::TextDocumentClientCapabilities; |
11 | 11 | use ra_flycheck::FlycheckConfig; | |
12 | use ra_project_model::CargoFeatures; | 12 | use ra_ide::{CompletionConfig, InlayHintsConfig}; |
13 | use serde::{Deserialize, Deserializer}; | 13 | use ra_project_model::CargoConfig; |
14 | 14 | use serde::Deserialize; | |
15 | /// Client provided initialization options | 15 | |
16 | #[derive(Deserialize, Clone, Debug, PartialEq, Eq)] | 16 | #[derive(Debug, Clone)] |
17 | #[serde(rename_all = "camelCase", default)] | 17 | pub struct Config { |
18 | pub struct ServerConfig { | 18 | pub client_caps: ClientCapsConfig, |
19 | /// Whether the client supports our custom highlighting publishing decorations. | ||
20 | /// This is different to the highlightingOn setting, which is whether the user | ||
21 | /// wants our custom highlighting to be used. | ||
22 | /// | ||
23 | /// Defaults to `false` | ||
24 | #[serde(deserialize_with = "nullable_bool_false")] | ||
25 | pub publish_decorations: bool, | 19 | pub publish_decorations: bool, |
26 | 20 | pub publish_diagnostics: bool, | |
27 | pub exclude_globs: Vec<String>, | 21 | pub notifications: NotificationsConfig, |
28 | #[serde(deserialize_with = "nullable_bool_false")] | 22 | pub inlay_hints: InlayHintsConfig, |
29 | pub use_client_watching: bool, | 23 | pub completion: CompletionConfig, |
30 | 24 | pub call_info_full: bool, | |
25 | pub rustfmt: RustfmtConfig, | ||
26 | pub check: Option<FlycheckConfig>, | ||
27 | pub vscode_lldb: bool, | ||
28 | pub proc_macro_srv: Option<String>, | ||
31 | pub lru_capacity: Option<usize>, | 29 | pub lru_capacity: Option<usize>, |
32 | 30 | pub use_client_watching: bool, | |
33 | #[serde(deserialize_with = "nullable_bool_true")] | 31 | pub exclude_globs: Vec<String>, |
34 | pub inlay_hints_type: bool, | 32 | pub cargo: CargoConfig, |
35 | #[serde(deserialize_with = "nullable_bool_true")] | ||
36 | pub inlay_hints_parameter: bool, | ||
37 | #[serde(deserialize_with = "nullable_bool_true")] | ||
38 | pub inlay_hints_chaining: bool, | ||
39 | pub inlay_hints_max_length: Option<usize>, | ||
40 | |||
41 | pub cargo_watch_enable: bool, | ||
42 | pub cargo_watch_args: Vec<String>, | ||
43 | pub cargo_watch_command: String, | ||
44 | pub cargo_watch_all_targets: bool, | ||
45 | |||
46 | /// For internal usage to make integrated tests faster. | ||
47 | #[serde(deserialize_with = "nullable_bool_true")] | ||
48 | pub with_sysroot: bool, | 33 | pub with_sysroot: bool, |
34 | } | ||
49 | 35 | ||
50 | /// Fine grained feature flags to disable specific features. | 36 | #[derive(Debug, Clone)] |
51 | pub feature_flags: FxHashMap<String, bool>, | 37 | pub struct NotificationsConfig { |
52 | 38 | pub workspace_loaded: bool, | |
53 | pub rustfmt_args: Vec<String>, | 39 | pub cargo_toml_not_found: bool, |
40 | } | ||
54 | 41 | ||
55 | /// Cargo feature configurations. | 42 | #[derive(Debug, Clone)] |
56 | pub cargo_features: CargoFeatures, | 43 | pub enum RustfmtConfig { |
44 | Rustfmt { | ||
45 | extra_args: Vec<String>, | ||
46 | }, | ||
47 | #[allow(unused)] | ||
48 | CustomCommand { | ||
49 | command: String, | ||
50 | args: Vec<String>, | ||
51 | }, | ||
52 | } | ||
57 | 53 | ||
58 | /// Enabled if the vscode_lldb extension is available. | 54 | #[derive(Debug, Clone, Default)] |
59 | pub vscode_lldb: bool, | 55 | pub struct ClientCapsConfig { |
56 | pub location_link: bool, | ||
57 | pub line_folding_only: bool, | ||
60 | } | 58 | } |
61 | 59 | ||
62 | impl Default for ServerConfig { | 60 | impl Default for Config { |
63 | fn default() -> ServerConfig { | 61 | fn default() -> Self { |
64 | ServerConfig { | 62 | Config { |
65 | publish_decorations: false, | 63 | publish_decorations: false, |
66 | exclude_globs: Vec::new(), | 64 | publish_diagnostics: true, |
67 | use_client_watching: false, | 65 | notifications: NotificationsConfig { |
66 | workspace_loaded: true, | ||
67 | cargo_toml_not_found: true, | ||
68 | }, | ||
69 | client_caps: ClientCapsConfig::default(), | ||
70 | inlay_hints: InlayHintsConfig { | ||
71 | type_hints: true, | ||
72 | parameter_hints: true, | ||
73 | chaining_hints: true, | ||
74 | max_length: None, | ||
75 | }, | ||
76 | completion: CompletionConfig { | ||
77 | enable_postfix_completions: true, | ||
78 | add_call_parenthesis: true, | ||
79 | add_call_argument_snippets: true, | ||
80 | }, | ||
81 | call_info_full: true, | ||
82 | rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() }, | ||
83 | check: Some(FlycheckConfig::CargoCommand { | ||
84 | command: "check".to_string(), | ||
85 | all_targets: true, | ||
86 | extra_args: Vec::new(), | ||
87 | }), | ||
88 | vscode_lldb: false, | ||
89 | proc_macro_srv: None, | ||
68 | lru_capacity: None, | 90 | lru_capacity: None, |
69 | inlay_hints_type: true, | 91 | use_client_watching: false, |
70 | inlay_hints_parameter: true, | 92 | exclude_globs: Vec::new(), |
71 | inlay_hints_chaining: true, | 93 | cargo: CargoConfig::default(), |
72 | inlay_hints_max_length: None, | ||
73 | cargo_watch_enable: true, | ||
74 | cargo_watch_args: Vec::new(), | ||
75 | cargo_watch_command: "check".to_string(), | ||
76 | cargo_watch_all_targets: true, | ||
77 | with_sysroot: true, | 94 | with_sysroot: true, |
78 | feature_flags: FxHashMap::default(), | ||
79 | cargo_features: Default::default(), | ||
80 | rustfmt_args: Vec::new(), | ||
81 | vscode_lldb: false, | ||
82 | } | 95 | } |
83 | } | 96 | } |
84 | } | 97 | } |
85 | 98 | ||
86 | /// Deserializes a null value to a bool false by default | 99 | impl Config { |
87 | fn nullable_bool_false<'de, D>(deserializer: D) -> Result<bool, D::Error> | 100 | #[rustfmt::skip] |
88 | where | 101 | pub fn update(&mut self, value: &serde_json::Value) { |
89 | D: Deserializer<'de>, | 102 | log::info!("Config::update({:#})", value); |
90 | { | 103 | |
91 | let opt = Option::deserialize(deserializer)?; | 104 | let client_caps = self.client_caps.clone(); |
92 | Ok(opt.unwrap_or(false)) | 105 | *self = Default::default(); |
93 | } | 106 | self.client_caps = client_caps; |
107 | |||
108 | set(value, "/publishDecorations", &mut self.publish_decorations); | ||
109 | set(value, "/excludeGlobs", &mut self.exclude_globs); | ||
110 | set(value, "/useClientWatching", &mut self.use_client_watching); | ||
111 | set(value, "/lruCapacity", &mut self.lru_capacity); | ||
112 | |||
113 | set(value, "/inlayHintsType", &mut self.inlay_hints.type_hints); | ||
114 | set(value, "/inlayHintsParameter", &mut self.inlay_hints.parameter_hints); | ||
115 | set(value, "/inlayHintsChaining", &mut self.inlay_hints.chaining_hints); | ||
116 | set(value, "/inlayHintsMaxLength", &mut self.inlay_hints.max_length); | ||
117 | |||
118 | if let Some(false) = get(value, "cargo_watch_enable") { | ||
119 | self.check = None | ||
120 | } else { | ||
121 | if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) = &mut self.check | ||
122 | { | ||
123 | set(value, "/cargoWatchArgs", extra_args); | ||
124 | set(value, "/cargoWatchCommand", command); | ||
125 | set(value, "/cargoWatchAllTargets", all_targets); | ||
126 | } | ||
127 | }; | ||
128 | |||
129 | set(value, "/withSysroot", &mut self.with_sysroot); | ||
130 | if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt { | ||
131 | set(value, "/rustfmtArgs", extra_args); | ||
132 | } | ||
94 | 133 | ||
95 | /// Deserializes a null value to a bool true by default | 134 | set(value, "/cargoFeatures/noDefaultFeatures", &mut self.cargo.no_default_features); |
96 | fn nullable_bool_true<'de, D>(deserializer: D) -> Result<bool, D::Error> | 135 | set(value, "/cargoFeatures/allFeatures", &mut self.cargo.all_features); |
97 | where | 136 | set(value, "/cargoFeatures/features", &mut self.cargo.features); |
98 | D: Deserializer<'de>, | 137 | set(value, "/cargoFeatures/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check); |
99 | { | 138 | |
100 | let opt = Option::deserialize(deserializer)?; | 139 | set(value, "/vscodeLldb", &mut self.vscode_lldb); |
101 | Ok(opt.unwrap_or(true)) | 140 | |
102 | } | 141 | set(value, "/featureFlags/lsp.diagnostics", &mut self.publish_diagnostics); |
142 | set(value, "/featureFlags/notifications.workspace-loaded", &mut self.notifications.workspace_loaded); | ||
143 | set(value, "/featureFlags/notifications.cargo-toml-not-found", &mut self.notifications.cargo_toml_not_found); | ||
144 | set(value, "/featureFlags/completion.enable-postfix", &mut self.completion.enable_postfix_completions); | ||
145 | set(value, "/featureFlags/completion.insertion.add-call-parenthesis", &mut self.completion.add_call_parenthesis); | ||
146 | set(value, "/featureFlags/completion.insertion.add-argument-snippets", &mut self.completion.add_call_argument_snippets); | ||
147 | set(value, "/featureFlags/call-info.full", &mut self.call_info_full); | ||
103 | 148 | ||
104 | #[cfg(test)] | 149 | log::info!("Config::update() = {:#?}", self); |
105 | mod test { | 150 | |
106 | use super::*; | 151 | fn get<'a, T: Deserialize<'a>>(value: &'a serde_json::Value, pointer: &str) -> Option<T> { |
107 | 152 | value.pointer(pointer).and_then(|it| T::deserialize(it).ok()) | |
108 | #[test] | 153 | } |
109 | fn deserialize_init_options_defaults() { | 154 | |
110 | // check that null == default for both fields | 155 | fn set<'a, T: Deserialize<'a> + std::fmt::Debug>(value: &'a serde_json::Value, pointer: &str, slot: &mut T) { |
111 | let default = ServerConfig::default(); | 156 | if let Some(new_value) = get(value, pointer) { |
112 | assert_eq!(default, serde_json::from_str(r#"{}"#).unwrap()); | 157 | *slot = new_value |
113 | assert_eq!( | 158 | } |
114 | default, | 159 | } |
115 | serde_json::from_str(r#"{"publishDecorations":null, "lruCapacity":null}"#).unwrap() | 160 | } |
116 | ); | 161 | |
162 | pub fn update_caps(&mut self, caps: &TextDocumentClientCapabilities) { | ||
163 | if let Some(value) = caps.definition.as_ref().and_then(|it| it.link_support) { | ||
164 | self.client_caps.location_link = value; | ||
165 | } | ||
166 | if let Some(value) = caps.folding_range.as_ref().and_then(|it| it.line_folding_only) { | ||
167 | self.client_caps.line_folding_only = value | ||
168 | } | ||
117 | } | 169 | } |
118 | } | 170 | } |
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index e8dc953c3..57c4c8ce5 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs | |||
@@ -579,7 +579,7 @@ impl TryConvWith<&WorldSnapshot> for (FileId, RangeInfo<Vec<NavigationTarget>>) | |||
579 | .into_iter() | 579 | .into_iter() |
580 | .map(|nav| (file_id, RangeInfo::new(range, nav))) | 580 | .map(|nav| (file_id, RangeInfo::new(range, nav))) |
581 | .try_conv_with_to_vec(world)?; | 581 | .try_conv_with_to_vec(world)?; |
582 | if world.config.supports_location_link { | 582 | if world.config.client_caps.location_link { |
583 | Ok(links.into()) | 583 | Ok(links.into()) |
584 | } else { | 584 | } else { |
585 | let locations: Vec<Location> = links | 585 | let locations: Vec<Location> = links |
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..02953be30 100644 --- a/crates/rust-analyzer/src/lib.rs +++ b/crates/rust-analyzer/src/lib.rs | |||
@@ -33,18 +33,16 @@ mod conv; | |||
33 | mod main_loop; | 33 | mod main_loop; |
34 | mod markdown; | 34 | mod markdown; |
35 | pub mod req; | 35 | pub mod req; |
36 | mod config; | 36 | pub 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::{ |
46 | caps::server_capabilities, | 45 | caps::server_capabilities, |
47 | config::ServerConfig, | ||
48 | main_loop::LspError, | 46 | main_loop::LspError, |
49 | main_loop::{main_loop, show_message}, | 47 | main_loop::{main_loop, show_message}, |
50 | }; | 48 | }; |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index a89ea86ea..45ae0ad9d 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -17,12 +17,11 @@ 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, FlycheckConfig}; | 23 | use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask}; |
25 | use ra_ide::{Canceled, FileId, InlayHintsConfig, LibraryData, SourceRootId}; | 24 | use ra_ide::{Canceled, FileId, LibraryData, SourceRootId}; |
26 | use ra_prof::profile; | 25 | use ra_prof::profile; |
27 | use ra_vfs::{VfsFile, VfsTask, Watch}; | 26 | use ra_vfs::{VfsFile, VfsTask, Watch}; |
28 | use relative_path::RelativePathBuf; | 27 | use relative_path::RelativePathBuf; |
@@ -31,15 +30,15 @@ use serde::{de::DeserializeOwned, Serialize}; | |||
31 | use threadpool::ThreadPool; | 30 | use threadpool::ThreadPool; |
32 | 31 | ||
33 | use crate::{ | 32 | use crate::{ |
33 | config::Config, | ||
34 | diagnostics::DiagnosticTask, | 34 | diagnostics::DiagnosticTask, |
35 | feature_flags::FeatureFlags, | ||
36 | main_loop::{ | 35 | main_loop::{ |
37 | pending_requests::{PendingRequest, PendingRequests}, | 36 | pending_requests::{PendingRequest, PendingRequests}, |
38 | subscriptions::Subscriptions, | 37 | subscriptions::Subscriptions, |
39 | }, | 38 | }, |
40 | req, | 39 | req, |
41 | world::{Config, WorldSnapshot, WorldState}, | 40 | world::{WorldSnapshot, WorldState}, |
42 | Result, ServerConfig, | 41 | Result, |
43 | }; | 42 | }; |
44 | use req::ConfigurationParams; | 43 | use req::ConfigurationParams; |
45 | 44 | ||
@@ -65,64 +64,8 @@ impl fmt::Display for LspError { | |||
65 | 64 | ||
66 | impl Error for LspError {} | 65 | impl Error for LspError {} |
67 | 66 | ||
68 | fn get_feature_flags(config: &ServerConfig, connection: &Connection) -> FeatureFlags { | 67 | pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) -> Result<()> { |
69 | let mut ff = FeatureFlags::default(); | 68 | log::info!("initial config: {:#?}", config); |
70 | for (flag, &value) in &config.feature_flags { | ||
71 | if ff.set(flag.as_str(), value).is_err() { | ||
72 | log::error!("unknown feature flag: {:?}", flag); | ||
73 | show_message( | ||
74 | req::MessageType::Error, | ||
75 | format!("unknown feature flag: {:?}", flag), | ||
76 | &connection.sender, | ||
77 | ); | ||
78 | } | ||
79 | } | ||
80 | log::info!("feature_flags: {:#?}", ff); | ||
81 | ff | ||
82 | } | ||
83 | |||
84 | fn get_config( | ||
85 | config: &ServerConfig, | ||
86 | text_document_caps: Option<&TextDocumentClientCapabilities>, | ||
87 | ) -> Config { | ||
88 | Config { | ||
89 | publish_decorations: config.publish_decorations, | ||
90 | supports_location_link: text_document_caps | ||
91 | .and_then(|it| it.definition) | ||
92 | .and_then(|it| it.link_support) | ||
93 | .unwrap_or(false), | ||
94 | line_folding_only: text_document_caps | ||
95 | .and_then(|it| it.folding_range.as_ref()) | ||
96 | .and_then(|it| it.line_folding_only) | ||
97 | .unwrap_or(false), | ||
98 | inlay_hints: InlayHintsConfig { | ||
99 | type_hints: config.inlay_hints_type, | ||
100 | parameter_hints: config.inlay_hints_parameter, | ||
101 | chaining_hints: config.inlay_hints_chaining, | ||
102 | max_length: config.inlay_hints_max_length, | ||
103 | }, | ||
104 | check: if config.cargo_watch_enable { | ||
105 | Some(FlycheckConfig::CargoCommand { | ||
106 | command: config.cargo_watch_command.clone(), | ||
107 | all_targets: config.cargo_watch_all_targets, | ||
108 | extra_args: config.cargo_watch_args.clone(), | ||
109 | }) | ||
110 | } else { | ||
111 | None | ||
112 | }, | ||
113 | rustfmt_args: config.rustfmt_args.clone(), | ||
114 | vscode_lldb: config.vscode_lldb, | ||
115 | proc_macro_srv: None, // FIXME: get this from config | ||
116 | } | ||
117 | } | ||
118 | |||
119 | pub fn main_loop( | ||
120 | ws_roots: Vec<PathBuf>, | ||
121 | client_caps: ClientCapabilities, | ||
122 | config: ServerConfig, | ||
123 | connection: Connection, | ||
124 | ) -> Result<()> { | ||
125 | log::info!("server_config: {:#?}", config); | ||
126 | 69 | ||
127 | // Windows scheduler implements priority boosts: if thread waits for an | 70 | // Windows scheduler implements priority boosts: if thread waits for an |
128 | // event (like a condvar), and event fires, priority of the thread is | 71 | // event (like a condvar), and event fires, priority of the thread is |
@@ -143,11 +86,8 @@ pub fn main_loop( | |||
143 | SetThreadPriority(thread, thread_priority_above_normal); | 86 | SetThreadPriority(thread, thread_priority_above_normal); |
144 | } | 87 | } |
145 | 88 | ||
146 | let text_document_caps = client_caps.text_document.as_ref(); | ||
147 | let mut loop_state = LoopState::default(); | 89 | let mut loop_state = LoopState::default(); |
148 | let mut world_state = { | 90 | let mut world_state = { |
149 | let feature_flags = get_feature_flags(&config, &connection); | ||
150 | |||
151 | // FIXME: support dynamic workspace loading. | 91 | // FIXME: support dynamic workspace loading. |
152 | let workspaces = { | 92 | let workspaces = { |
153 | let mut loaded_workspaces = Vec::new(); | 93 | let mut loaded_workspaces = Vec::new(); |
@@ -155,7 +95,7 @@ pub fn main_loop( | |||
155 | let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( | 95 | let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( |
156 | ws_root.as_path(), | 96 | ws_root.as_path(), |
157 | config.with_sysroot, | 97 | config.with_sysroot, |
158 | &config.cargo_features, | 98 | &config.cargo, |
159 | ); | 99 | ); |
160 | match workspace { | 100 | match workspace { |
161 | Ok(workspace) => loaded_workspaces.push(workspace), | 101 | Ok(workspace) => loaded_workspaces.push(workspace), |
@@ -165,7 +105,7 @@ pub fn main_loop( | |||
165 | if let Some(ra_project_model::CargoTomlNotFoundError { .. }) = | 105 | if let Some(ra_project_model::CargoTomlNotFoundError { .. }) = |
166 | e.downcast_ref() | 106 | e.downcast_ref() |
167 | { | 107 | { |
168 | if !feature_flags.get("notifications.cargo-toml-not-found") { | 108 | if !config.notifications.cargo_toml_not_found { |
169 | continue; | 109 | continue; |
170 | } | 110 | } |
171 | } | 111 | } |
@@ -214,8 +154,7 @@ pub fn main_loop( | |||
214 | config.lru_capacity, | 154 | config.lru_capacity, |
215 | &globs, | 155 | &globs, |
216 | Watch(!config.use_client_watching), | 156 | Watch(!config.use_client_watching), |
217 | get_config(&config, text_document_caps), | 157 | config, |
218 | feature_flags, | ||
219 | ) | 158 | ) |
220 | }; | 159 | }; |
221 | 160 | ||
@@ -258,7 +197,6 @@ pub fn main_loop( | |||
258 | &task_sender, | 197 | &task_sender, |
259 | &libdata_sender, | 198 | &libdata_sender, |
260 | &connection, | 199 | &connection, |
261 | text_document_caps, | ||
262 | &mut world_state, | 200 | &mut world_state, |
263 | &mut loop_state, | 201 | &mut loop_state, |
264 | event, | 202 | event, |
@@ -369,7 +307,6 @@ fn loop_turn( | |||
369 | task_sender: &Sender<Task>, | 307 | task_sender: &Sender<Task>, |
370 | libdata_sender: &Sender<LibraryData>, | 308 | libdata_sender: &Sender<LibraryData>, |
371 | connection: &Connection, | 309 | connection: &Connection, |
372 | text_document_caps: Option<&TextDocumentClientCapabilities>, | ||
373 | world_state: &mut WorldState, | 310 | world_state: &mut WorldState, |
374 | loop_state: &mut LoopState, | 311 | loop_state: &mut LoopState, |
375 | event: Event, | 312 | event: Event, |
@@ -423,28 +360,16 @@ fn loop_turn( | |||
423 | log::debug!("config update response: '{:?}", resp); | 360 | log::debug!("config update response: '{:?}", resp); |
424 | let Response { error, result, .. } = resp; | 361 | let Response { error, result, .. } = resp; |
425 | 362 | ||
426 | match ( | 363 | match (error, result) { |
427 | error, | ||
428 | result.map(|result| serde_json::from_value::<Vec<ServerConfig>>(result)), | ||
429 | ) { | ||
430 | (Some(err), _) => { | 364 | (Some(err), _) => { |
431 | log::error!("failed to fetch the server settings: {:?}", err) | 365 | log::error!("failed to fetch the server settings: {:?}", err) |
432 | } | 366 | } |
433 | (None, Some(Ok(new_config))) => { | 367 | (None, Some(configs)) => { |
434 | let new_config = new_config | 368 | if let Some(new_config) = configs.get(0) { |
435 | .first() | 369 | let mut config = world_state.config.clone(); |
436 | .expect( | 370 | config.update(&new_config); |
437 | "the client is expected to always send a non-empty config data", | 371 | world_state.update_configuration(config); |
438 | ) | 372 | } |
439 | .to_owned(); | ||
440 | world_state.update_configuration( | ||
441 | new_config.lru_capacity, | ||
442 | get_config(&new_config, text_document_caps), | ||
443 | get_feature_flags(&new_config, connection), | ||
444 | ); | ||
445 | } | ||
446 | (None, Some(Err(e))) => { | ||
447 | log::error!("failed to parse client config response: {}", e) | ||
448 | } | 373 | } |
449 | (None, None) => { | 374 | (None, None) => { |
450 | log::error!("received empty server settings response from the client") | 375 | log::error!("received empty server settings response from the client") |
@@ -475,8 +400,8 @@ fn loop_turn( | |||
475 | }); | 400 | }); |
476 | } | 401 | } |
477 | 402 | ||
478 | let show_progress = !loop_state.workspace_loaded | 403 | let show_progress = |
479 | && world_state.feature_flags.get("notifications.workspace-loaded"); | 404 | !loop_state.workspace_loaded && world_state.config.notifications.workspace_loaded; |
480 | 405 | ||
481 | if !loop_state.workspace_loaded | 406 | if !loop_state.workspace_loaded |
482 | && loop_state.roots_scanned == loop_state.roots_total | 407 | && loop_state.roots_scanned == loop_state.roots_total |
@@ -964,7 +889,7 @@ fn update_file_notifications_on_threadpool( | |||
964 | subscriptions: Vec<FileId>, | 889 | subscriptions: Vec<FileId>, |
965 | ) { | 890 | ) { |
966 | log::trace!("updating notifications for {:?}", subscriptions); | 891 | log::trace!("updating notifications for {:?}", subscriptions); |
967 | let publish_diagnostics = world.feature_flags.get("lsp.diagnostics"); | 892 | let publish_diagnostics = world.config.publish_diagnostics; |
968 | pool.execute(move || { | 893 | pool.execute(move || { |
969 | for file_id in subscriptions { | 894 | for file_id in subscriptions { |
970 | if publish_diagnostics { | 895 | if publish_diagnostics { |
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs index d5cb5d137..23e48c089 100644 --- a/crates/rust-analyzer/src/main_loop/handlers.rs +++ b/crates/rust-analyzer/src/main_loop/handlers.rs | |||
@@ -19,8 +19,8 @@ use lsp_types::{ | |||
19 | TextEdit, WorkspaceEdit, | 19 | TextEdit, WorkspaceEdit, |
20 | }; | 20 | }; |
21 | use ra_ide::{ | 21 | use ra_ide::{ |
22 | Assist, AssistId, CompletionConfig, FileId, FilePosition, FileRange, Query, RangeInfo, | 22 | Assist, AssistId, FileId, FilePosition, FileRange, Query, RangeInfo, Runnable, RunnableKind, |
23 | Runnable, RunnableKind, SearchScope, | 23 | SearchScope, |
24 | }; | 24 | }; |
25 | use ra_prof::profile; | 25 | use ra_prof::profile; |
26 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; | 26 | use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; |
@@ -31,6 +31,7 @@ use stdx::format_to; | |||
31 | 31 | ||
32 | use crate::{ | 32 | use crate::{ |
33 | cargo_target_spec::CargoTargetSpec, | 33 | cargo_target_spec::CargoTargetSpec, |
34 | config::RustfmtConfig, | ||
34 | conv::{ | 35 | conv::{ |
35 | to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, | 36 | to_call_hierarchy_item, to_location, Conv, ConvWith, FoldConvCtx, MapConvWith, TryConvWith, |
36 | TryConvWithToVec, | 37 | TryConvWithToVec, |
@@ -425,15 +426,7 @@ pub fn handle_completion( | |||
425 | return Ok(None); | 426 | return Ok(None); |
426 | } | 427 | } |
427 | 428 | ||
428 | let config = CompletionConfig { | 429 | let items = match world.analysis().completions(position, &world.config.completion)? { |
429 | enable_postfix_completions: world.feature_flags.get("completion.enable-postfix"), | ||
430 | add_call_parenthesis: world.feature_flags.get("completion.insertion.add-call-parenthesis"), | ||
431 | add_call_argument_snippets: world | ||
432 | .feature_flags | ||
433 | .get("completion.insertion.add-argument-snippets"), | ||
434 | }; | ||
435 | |||
436 | let items = match world.analysis().completions(position, &config)? { | ||
437 | None => return Ok(None), | 430 | None => return Ok(None), |
438 | Some(items) => items, | 431 | Some(items) => items, |
439 | }; | 432 | }; |
@@ -457,7 +450,7 @@ pub fn handle_folding_range( | |||
457 | let ctx = FoldConvCtx { | 450 | let ctx = FoldConvCtx { |
458 | text: &text, | 451 | text: &text, |
459 | line_index: &line_index, | 452 | line_index: &line_index, |
460 | line_folding_only: world.config.line_folding_only, | 453 | line_folding_only: world.config.client_caps.line_folding_only, |
461 | }; | 454 | }; |
462 | let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); | 455 | let res = Some(folds.into_iter().map_conv_with(&ctx).collect()); |
463 | Ok(res) | 456 | Ok(res) |
@@ -470,7 +463,7 @@ pub fn handle_signature_help( | |||
470 | let _p = profile("handle_signature_help"); | 463 | let _p = profile("handle_signature_help"); |
471 | let position = params.try_conv_with(&world)?; | 464 | let position = params.try_conv_with(&world)?; |
472 | if let Some(call_info) = world.analysis().call_info(position)? { | 465 | if let Some(call_info) = world.analysis().call_info(position)? { |
473 | let concise = !world.feature_flags.get("call-info.full"); | 466 | let concise = !world.config.call_info_full; |
474 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); | 467 | let mut active_parameter = call_info.active_parameter.map(|it| it as i64); |
475 | if concise && call_info.signature.has_self_param { | 468 | if concise && call_info.signature.has_self_param { |
476 | active_parameter = active_parameter.map(|it| it.saturating_sub(1)); | 469 | active_parameter = active_parameter.map(|it| it.saturating_sub(1)); |
@@ -610,13 +603,24 @@ pub fn handle_formatting( | |||
610 | let file_line_index = world.analysis().file_line_index(file_id)?; | 603 | let file_line_index = world.analysis().file_line_index(file_id)?; |
611 | let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); | 604 | let end_position = TextUnit::of_str(&file).conv_with(&file_line_index); |
612 | 605 | ||
613 | let mut rustfmt = process::Command::new("rustfmt"); | 606 | let mut rustfmt = match &world.config.rustfmt { |
614 | rustfmt.args(&world.config.rustfmt_args); | 607 | RustfmtConfig::Rustfmt { extra_args } => { |
615 | if let Some(&crate_id) = crate_ids.first() { | 608 | let mut cmd = process::Command::new("rustfmt"); |
616 | // Assume all crates are in the same edition | 609 | cmd.args(extra_args); |
617 | let edition = world.analysis().crate_edition(crate_id)?; | 610 | if let Some(&crate_id) = crate_ids.first() { |
618 | rustfmt.args(&["--edition", &edition.to_string()]); | 611 | // Assume all crates are in the same edition |
619 | } | 612 | let edition = world.analysis().crate_edition(crate_id)?; |
613 | cmd.arg("--edition"); | ||
614 | cmd.arg(edition.to_string()); | ||
615 | } | ||
616 | cmd | ||
617 | } | ||
618 | RustfmtConfig::CustomCommand { command, args } => { | ||
619 | let mut cmd = process::Command::new(command); | ||
620 | cmd.args(args); | ||
621 | cmd | ||
622 | } | ||
623 | }; | ||
620 | 624 | ||
621 | if let Ok(path) = params.text_document.uri.to_file_path() { | 625 | if let Ok(path) = params.text_document.uri.to_file_path() { |
622 | if let Some(parent) = path.parent() { | 626 | if let Some(parent) = path.parent() { |
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index 2db058eb1..5674f42ef 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs | |||
@@ -13,8 +13,7 @@ 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, FlycheckConfig}; | 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, InlayHintsConfig, LibraryData, | 16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, LibraryData, SourceRootId, |
17 | SourceRootId, | ||
18 | }; | 17 | }; |
19 | use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; | 18 | use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; |
20 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; | 19 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; |
@@ -22,8 +21,8 @@ use relative_path::RelativePathBuf; | |||
22 | use stdx::format_to; | 21 | use stdx::format_to; |
23 | 22 | ||
24 | use crate::{ | 23 | use crate::{ |
24 | config::Config, | ||
25 | diagnostics::{CheckFixes, DiagnosticCollection}, | 25 | diagnostics::{CheckFixes, DiagnosticCollection}, |
26 | feature_flags::FeatureFlags, | ||
27 | main_loop::pending_requests::{CompletedRequest, LatestRequests}, | 26 | main_loop::pending_requests::{CompletedRequest, LatestRequests}, |
28 | vfs_glob::{Glob, RustPackageFilterBuilder}, | 27 | vfs_glob::{Glob, RustPackageFilterBuilder}, |
29 | LspError, Result, | 28 | LspError, Result, |
@@ -31,9 +30,7 @@ use crate::{ | |||
31 | use ra_db::ExternSourceId; | 30 | use ra_db::ExternSourceId; |
32 | use rustc_hash::{FxHashMap, FxHashSet}; | 31 | use rustc_hash::{FxHashMap, FxHashSet}; |
33 | 32 | ||
34 | fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<Flycheck> { | 33 | fn create_flycheck(workspaces: &[ProjectWorkspace], config: &FlycheckConfig) -> Option<Flycheck> { |
35 | let check_config = config.check.as_ref()?; | ||
36 | |||
37 | // FIXME: Figure out the multi-workspace situation | 34 | // FIXME: Figure out the multi-workspace situation |
38 | workspaces | 35 | workspaces |
39 | .iter() | 36 | .iter() |
@@ -43,7 +40,7 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<F | |||
43 | }) | 40 | }) |
44 | .map(|cargo| { | 41 | .map(|cargo| { |
45 | let cargo_project_root = cargo.workspace_root().to_path_buf(); | 42 | let cargo_project_root = cargo.workspace_root().to_path_buf(); |
46 | Some(Flycheck::new(check_config.clone(), cargo_project_root)) | 43 | Some(Flycheck::new(config.clone(), cargo_project_root)) |
47 | }) | 44 | }) |
48 | .unwrap_or_else(|| { | 45 | .unwrap_or_else(|| { |
49 | log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); | 46 | log::warn!("Cargo check watching only supported for cargo workspaces, disabling"); |
@@ -51,18 +48,6 @@ fn create_flycheck(workspaces: &[ProjectWorkspace], config: &Config) -> Option<F | |||
51 | }) | 48 | }) |
52 | } | 49 | } |
53 | 50 | ||
54 | #[derive(Debug, Clone)] | ||
55 | pub struct Config { | ||
56 | pub publish_decorations: bool, | ||
57 | pub supports_location_link: bool, | ||
58 | pub line_folding_only: bool, | ||
59 | pub inlay_hints: InlayHintsConfig, | ||
60 | pub rustfmt_args: Vec<String>, | ||
61 | pub check: Option<FlycheckConfig>, | ||
62 | pub vscode_lldb: bool, | ||
63 | pub proc_macro_srv: Option<String>, | ||
64 | } | ||
65 | |||
66 | /// `WorldState` is the primary mutable state of the language server | 51 | /// `WorldState` is the primary mutable state of the language server |
67 | /// | 52 | /// |
68 | /// The most interesting components are `vfs`, which stores a consistent | 53 | /// The most interesting components are `vfs`, which stores a consistent |
@@ -71,7 +56,6 @@ pub struct Config { | |||
71 | #[derive(Debug)] | 56 | #[derive(Debug)] |
72 | pub struct WorldState { | 57 | pub struct WorldState { |
73 | pub config: Config, | 58 | pub config: Config, |
74 | pub feature_flags: Arc<FeatureFlags>, | ||
75 | pub roots: Vec<PathBuf>, | 59 | pub roots: Vec<PathBuf>, |
76 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | 60 | pub workspaces: Arc<Vec<ProjectWorkspace>>, |
77 | pub analysis_host: AnalysisHost, | 61 | pub analysis_host: AnalysisHost, |
@@ -85,7 +69,6 @@ pub struct WorldState { | |||
85 | /// An immutable snapshot of the world's state at a point in time. | 69 | /// An immutable snapshot of the world's state at a point in time. |
86 | pub struct WorldSnapshot { | 70 | pub struct WorldSnapshot { |
87 | pub config: Config, | 71 | pub config: Config, |
88 | pub feature_flags: Arc<FeatureFlags>, | ||
89 | pub workspaces: Arc<Vec<ProjectWorkspace>>, | 72 | pub workspaces: Arc<Vec<ProjectWorkspace>>, |
90 | pub analysis: Analysis, | 73 | pub analysis: Analysis, |
91 | pub latest_requests: Arc<RwLock<LatestRequests>>, | 74 | pub latest_requests: Arc<RwLock<LatestRequests>>, |
@@ -101,7 +84,6 @@ impl WorldState { | |||
101 | exclude_globs: &[Glob], | 84 | exclude_globs: &[Glob], |
102 | watch: Watch, | 85 | watch: Watch, |
103 | config: Config, | 86 | config: Config, |
104 | feature_flags: FeatureFlags, | ||
105 | ) -> WorldState { | 87 | ) -> WorldState { |
106 | let mut change = AnalysisChange::new(); | 88 | let mut change = AnalysisChange::new(); |
107 | 89 | ||
@@ -203,13 +185,12 @@ impl WorldState { | |||
203 | }); | 185 | }); |
204 | change.set_crate_graph(crate_graph); | 186 | change.set_crate_graph(crate_graph); |
205 | 187 | ||
206 | let flycheck = create_flycheck(&workspaces, &config); | 188 | let flycheck = config.check.as_ref().and_then(|c| create_flycheck(&workspaces, c)); |
207 | 189 | ||
208 | let mut analysis_host = AnalysisHost::new(lru_capacity); | 190 | let mut analysis_host = AnalysisHost::new(lru_capacity); |
209 | analysis_host.apply_change(change); | 191 | analysis_host.apply_change(change); |
210 | WorldState { | 192 | WorldState { |
211 | config: config, | 193 | config: config, |
212 | feature_flags: Arc::new(feature_flags), | ||
213 | roots: folder_roots, | 194 | roots: folder_roots, |
214 | workspaces: Arc::new(workspaces), | 195 | workspaces: Arc::new(workspaces), |
215 | analysis_host, | 196 | analysis_host, |
@@ -221,15 +202,13 @@ impl WorldState { | |||
221 | } | 202 | } |
222 | } | 203 | } |
223 | 204 | ||
224 | pub fn update_configuration( | 205 | pub fn update_configuration(&mut self, config: Config) { |
225 | &mut self, | 206 | self.analysis_host.update_lru_capacity(config.lru_capacity); |
226 | lru_capacity: Option<usize>, | 207 | if config.check != self.config.check { |
227 | config: Config, | 208 | self.flycheck = |
228 | feature_flags: FeatureFlags, | 209 | config.check.as_ref().and_then(|it| create_flycheck(&self.workspaces, it)); |
229 | ) { | 210 | } |
230 | self.feature_flags = Arc::new(feature_flags); | 211 | |
231 | self.analysis_host.update_lru_capacity(lru_capacity); | ||
232 | self.flycheck = create_flycheck(&self.workspaces, &config); | ||
233 | self.config = config; | 212 | self.config = config; |
234 | } | 213 | } |
235 | 214 | ||
@@ -287,7 +266,6 @@ impl WorldState { | |||
287 | pub fn snapshot(&self) -> WorldSnapshot { | 266 | pub fn snapshot(&self) -> WorldSnapshot { |
288 | WorldSnapshot { | 267 | WorldSnapshot { |
289 | config: self.config.clone(), | 268 | config: self.config.clone(), |
290 | feature_flags: Arc::clone(&self.feature_flags), | ||
291 | workspaces: Arc::clone(&self.workspaces), | 269 | workspaces: Arc::clone(&self.workspaces), |
292 | analysis: self.analysis_host.analysis(), | 270 | analysis: self.analysis_host.analysis(), |
293 | vfs: Arc::clone(&self.vfs), | 271 | vfs: Arc::clone(&self.vfs), |
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..7eebedff7 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,17 @@ 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::{ |
23 | config::{ClientCapsConfig, Config}, | ||
24 | main_loop, req, | ||
25 | }; | ||
24 | 26 | ||
25 | pub struct Project<'a> { | 27 | pub struct Project<'a> { |
26 | fixture: &'a str, | 28 | fixture: &'a str, |
27 | with_sysroot: bool, | 29 | with_sysroot: bool, |
28 | tmp_dir: Option<TempDir>, | 30 | tmp_dir: Option<TempDir>, |
29 | roots: Vec<PathBuf>, | 31 | roots: Vec<PathBuf>, |
30 | config: Option<Box<dyn Fn(&mut ServerConfig)>>, | 32 | config: Option<Box<dyn Fn(&mut Config)>>, |
31 | } | 33 | } |
32 | 34 | ||
33 | impl<'a> Project<'a> { | 35 | impl<'a> Project<'a> { |
@@ -50,7 +52,7 @@ impl<'a> Project<'a> { | |||
50 | self | 52 | self |
51 | } | 53 | } |
52 | 54 | ||
53 | pub fn with_config(mut self, config: impl Fn(&mut ServerConfig) + 'static) -> Project<'a> { | 55 | pub fn with_config(mut self, config: impl Fn(&mut Config) + 'static) -> Project<'a> { |
54 | self.config = Some(Box::new(config)); | 56 | self.config = Some(Box::new(config)); |
55 | self | 57 | self |
56 | } | 58 | } |
@@ -78,8 +80,11 @@ impl<'a> Project<'a> { | |||
78 | 80 | ||
79 | let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); | 81 | let roots = self.roots.into_iter().map(|root| tmp_dir.path().join(root)).collect(); |
80 | 82 | ||
81 | let mut config = | 83 | let mut config = Config { |
82 | ServerConfig { with_sysroot: self.with_sysroot, ..ServerConfig::default() }; | 84 | client_caps: ClientCapsConfig { location_link: true, ..Default::default() }, |
85 | with_sysroot: self.with_sysroot, | ||
86 | ..Config::default() | ||
87 | }; | ||
83 | 88 | ||
84 | if let Some(f) = &self.config { | 89 | if let Some(f) = &self.config { |
85 | f(&mut config) | 90 | f(&mut config) |
@@ -105,7 +110,7 @@ pub struct Server { | |||
105 | impl Server { | 110 | impl Server { |
106 | fn new( | 111 | fn new( |
107 | dir: TempDir, | 112 | dir: TempDir, |
108 | config: ServerConfig, | 113 | config: Config, |
109 | roots: Vec<PathBuf>, | 114 | roots: Vec<PathBuf>, |
110 | files: Vec<(PathBuf, String)>, | 115 | files: Vec<(PathBuf, String)>, |
111 | ) -> Server { | 116 | ) -> Server { |
@@ -116,26 +121,7 @@ impl Server { | |||
116 | 121 | ||
117 | let _thread = jod_thread::Builder::new() | 122 | let _thread = jod_thread::Builder::new() |
118 | .name("test server".to_string()) | 123 | .name("test server".to_string()) |
119 | .spawn(move || { | 124 | .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"); | 125 | .expect("failed to spawn a thread"); |
140 | 126 | ||
141 | let res = | 127 | let res = |