aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_assists/src/handlers/add_from_impl_for_enum.rs206
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--crates/ra_hir/src/code_model.rs20
-rw-r--r--crates/ra_hir_ty/src/infer/pat.rs11
-rw-r--r--crates/ra_hir_ty/src/tests/coercion.rs6
-rw-r--r--crates/ra_hir_ty/src/tests/patterns.rs85
-rw-r--r--crates/ra_hir_ty/src/tests/regression.rs3
-rw-r--r--crates/ra_hir_ty/src/tests/simple.rs2
-rw-r--r--crates/ra_project_model/src/cargo_workspace.rs9
-rw-r--r--crates/ra_project_model/src/lib.rs129
-rw-r--r--crates/ra_syntax/src/ast/make.rs3
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs11
-rw-r--r--crates/rust-analyzer/src/config.rs143
-rw-r--r--crates/rust-analyzer/src/main_loop.rs53
-rw-r--r--crates/rust-analyzer/src/main_loop/handlers.rs65
-rw-r--r--crates/rust-analyzer/src/main_loop/subscriptions.rs2
-rw-r--r--crates/rust-analyzer/src/req.rs36
-rw-r--r--crates/rust-analyzer/src/vfs_glob.rs10
-rw-r--r--crates/rust-analyzer/src/world.rs57
19 files changed, 553 insertions, 300 deletions
diff --git a/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
new file mode 100644
index 000000000..864373aa5
--- /dev/null
+++ b/crates/ra_assists/src/handlers/add_from_impl_for_enum.rs
@@ -0,0 +1,206 @@
1use ra_syntax::{
2 ast::{self, AstNode, NameOwner},
3 TextUnit,
4};
5use stdx::format_to;
6
7use crate::{Assist, AssistCtx, AssistId};
8use ra_ide_db::RootDatabase;
9
10// Assist add_from_impl_for_enum
11//
12// Adds a From impl for an enum variant with one tuple field
13//
14// ```
15// enum A { <|>One(u32) }
16// ```
17// ->
18// ```
19// enum A { One(u32) }
20//
21// impl From<u32> for A {
22// fn from(v: u32) -> Self {
23// A::One(v)
24// }
25// }
26// ```
27pub(crate) fn add_from_impl_for_enum(ctx: AssistCtx) -> Option<Assist> {
28 let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
29 let variant_name = variant.name()?;
30 let enum_name = variant.parent_enum().name()?;
31 let field_list = match variant.kind() {
32 ast::StructKind::Tuple(field_list) => field_list,
33 _ => return None,
34 };
35 if field_list.fields().count() != 1 {
36 return None;
37 }
38 let field_type = field_list.fields().next()?.type_ref()?;
39 let path = match field_type {
40 ast::TypeRef::PathType(p) => p,
41 _ => return None,
42 };
43
44 if already_has_from_impl(ctx.sema, &variant) {
45 return None;
46 }
47
48 ctx.add_assist(
49 AssistId("add_from_impl_for_enum"),
50 "Add From impl for this enum variant",
51 |edit| {
52 let start_offset = variant.parent_enum().syntax().text_range().end();
53 let mut buf = String::new();
54 format_to!(
55 buf,
56 r#"
57
58impl From<{0}> for {1} {{
59 fn from(v: {0}) -> Self {{
60 {1}::{2}(v)
61 }}
62}}"#,
63 path.syntax(),
64 enum_name,
65 variant_name
66 );
67 edit.insert(start_offset, buf);
68 edit.set_cursor(start_offset + TextUnit::of_str("\n\n"));
69 },
70 )
71}
72
73fn already_has_from_impl(
74 sema: &'_ hir::Semantics<'_, RootDatabase>,
75 variant: &ast::EnumVariant,
76) -> bool {
77 let scope = sema.scope(&variant.syntax());
78
79 let from_path = ast::make::path_from_text("From");
80 let from_hir_path = match hir::Path::from_ast(from_path) {
81 Some(p) => p,
82 None => return false,
83 };
84 let from_trait = match scope.resolve_hir_path(&from_hir_path) {
85 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(t))) => t,
86 _ => return false,
87 };
88
89 let e: hir::Enum = match sema.to_def(&variant.parent_enum()) {
90 Some(e) => e,
91 None => return false,
92 };
93 let e_ty = e.ty(sema.db);
94
95 let hir_enum_var: hir::EnumVariant = match sema.to_def(variant) {
96 Some(ev) => ev,
97 None => return false,
98 };
99 let var_ty = hir_enum_var.fields(sema.db)[0].signature_ty(sema.db);
100
101 e_ty.impls_trait(sema.db, from_trait, &[var_ty.clone()])
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 use crate::helpers::{check_assist, check_assist_not_applicable};
108
109 #[test]
110 fn test_add_from_impl_for_enum() {
111 check_assist(
112 add_from_impl_for_enum,
113 "enum A { <|>One(u32) }",
114 r#"enum A { One(u32) }
115
116<|>impl From<u32> for A {
117 fn from(v: u32) -> Self {
118 A::One(v)
119 }
120}"#,
121 );
122 }
123
124 #[test]
125 fn test_add_from_impl_for_enum_complicated_path() {
126 check_assist(
127 add_from_impl_for_enum,
128 "enum A { <|>One(foo::bar::baz::Boo) }",
129 r#"enum A { One(foo::bar::baz::Boo) }
130
131<|>impl From<foo::bar::baz::Boo> for A {
132 fn from(v: foo::bar::baz::Boo) -> Self {
133 A::One(v)
134 }
135}"#,
136 );
137 }
138
139 #[test]
140 fn test_add_from_impl_no_element() {
141 check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One }");
142 }
143
144 #[test]
145 fn test_add_from_impl_more_than_one_element_in_tuple() {
146 check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One(u32, String) }");
147 }
148
149 #[test]
150 fn test_add_from_impl_struct_variant() {
151 check_assist_not_applicable(add_from_impl_for_enum, "enum A { <|>One { x: u32 } }");
152 }
153
154 #[test]
155 fn test_add_from_impl_already_exists() {
156 check_assist_not_applicable(
157 add_from_impl_for_enum,
158 r#"enum A { <|>One(u32), }
159
160impl From<u32> for A {
161 fn from(v: u32) -> Self {
162 A::One(v)
163 }
164}
165
166pub trait From<T> {
167 fn from(T) -> Self;
168}"#,
169 );
170 }
171
172 #[test]
173 fn test_add_from_impl_different_variant_impl_exists() {
174 check_assist(
175 add_from_impl_for_enum,
176 r#"enum A { <|>One(u32), Two(String), }
177
178impl From<String> for A {
179 fn from(v: String) -> Self {
180 A::Two(v)
181 }
182}
183
184pub trait From<T> {
185 fn from(T) -> Self;
186}"#,
187 r#"enum A { One(u32), Two(String), }
188
189<|>impl From<u32> for A {
190 fn from(v: u32) -> Self {
191 A::One(v)
192 }
193}
194
195impl From<String> for A {
196 fn from(v: String) -> Self {
197 A::Two(v)
198 }
199}
200
201pub trait From<T> {
202 fn from(T) -> Self;
203}"#,
204 );
205 }
206}
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs
index 66854cb5a..fa3d3913f 100644
--- a/crates/ra_assists/src/lib.rs
+++ b/crates/ra_assists/src/lib.rs
@@ -123,6 +123,7 @@ mod handlers {
123 mod replace_qualified_name_with_use; 123 mod replace_qualified_name_with_use;
124 mod replace_unwrap_with_match; 124 mod replace_unwrap_with_match;
125 mod split_import; 125 mod split_import;
126 mod add_from_impl_for_enum;
126 127
127 pub(crate) fn all() -> &'static [AssistHandler] { 128 pub(crate) fn all() -> &'static [AssistHandler] {
128 &[ 129 &[
@@ -161,6 +162,7 @@ mod handlers {
161 replace_qualified_name_with_use::replace_qualified_name_with_use, 162 replace_qualified_name_with_use::replace_qualified_name_with_use,
162 replace_unwrap_with_match::replace_unwrap_with_match, 163 replace_unwrap_with_match::replace_unwrap_with_match,
163 split_import::split_import, 164 split_import::split_import,
165 add_from_impl_for_enum::add_from_impl_for_enum,
164 ] 166 ]
165 } 167 }
166} 168}
diff --git a/crates/ra_hir/src/code_model.rs b/crates/ra_hir/src/code_model.rs
index cd2a8fc62..c6f3bdb8e 100644
--- a/crates/ra_hir/src/code_model.rs
+++ b/crates/ra_hir/src/code_model.rs
@@ -1084,6 +1084,26 @@ impl Type {
1084 ) 1084 )
1085 } 1085 }
1086 1086
1087 pub fn impls_trait(&self, db: &dyn HirDatabase, trait_: Trait, args: &[Type]) -> bool {
1088 let trait_ref = hir_ty::TraitRef {
1089 trait_: trait_.id,
1090 substs: Substs::build_for_def(db, trait_.id)
1091 .push(self.ty.value.clone())
1092 .fill(args.iter().map(|t| t.ty.value.clone()))
1093 .build(),
1094 };
1095
1096 let goal = Canonical {
1097 value: hir_ty::InEnvironment::new(
1098 self.ty.environment.clone(),
1099 hir_ty::Obligation::Trait(trait_ref),
1100 ),
1101 num_vars: 0,
1102 };
1103
1104 db.trait_solve(self.krate, goal).is_some()
1105 }
1106
1087 // FIXME: this method is broken, as it doesn't take closures into account. 1107 // FIXME: this method is broken, as it doesn't take closures into account.
1088 pub fn as_callable(&self) -> Option<CallableDef> { 1108 pub fn as_callable(&self) -> Option<CallableDef> {
1089 Some(self.ty.value.as_callable()?.0) 1109 Some(self.ty.value.as_callable()?.0)
diff --git a/crates/ra_hir_ty/src/infer/pat.rs b/crates/ra_hir_ty/src/infer/pat.rs
index baed6225b..86acd27f8 100644
--- a/crates/ra_hir_ty/src/infer/pat.rs
+++ b/crates/ra_hir_ty/src/infer/pat.rs
@@ -11,7 +11,7 @@ use hir_def::{
11use hir_expand::name::Name; 11use hir_expand::name::Name;
12use test_utils::tested_by; 12use test_utils::tested_by;
13 13
14use super::{BindingMode, InferenceContext}; 14use super::{BindingMode, Expectation, InferenceContext};
15use crate::{utils::variant_data, Substs, Ty, TypeCtor}; 15use crate::{utils::variant_data, Substs, Ty, TypeCtor};
16 16
17impl<'a> InferenceContext<'a> { 17impl<'a> InferenceContext<'a> {
@@ -198,7 +198,14 @@ impl<'a> InferenceContext<'a> {
198 198
199 Ty::apply_one(container_ty, elem_ty) 199 Ty::apply_one(container_ty, elem_ty)
200 } 200 }
201 _ => Ty::Unknown, 201 Pat::Wild => expected.clone(),
202 Pat::Range { start, end } => {
203 let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone()));
204 let end_ty = self.infer_expr(*end, &Expectation::has_type(start_ty));
205 end_ty
206 }
207 Pat::Lit(expr) => self.infer_expr(*expr, &Expectation::has_type(expected.clone())),
208 Pat::Missing => Ty::Unknown,
202 }; 209 };
203 // use a new type variable if we got Ty::Unknown here 210 // use a new type variable if we got Ty::Unknown here
204 let ty = self.insert_type_vars_shallow(ty); 211 let ty = self.insert_type_vars_shallow(ty);
diff --git a/crates/ra_hir_ty/src/tests/coercion.rs b/crates/ra_hir_ty/src/tests/coercion.rs
index 1e303f5ce..3e3d55c04 100644
--- a/crates/ra_hir_ty/src/tests/coercion.rs
+++ b/crates/ra_hir_ty/src/tests/coercion.rs
@@ -275,12 +275,14 @@ fn test(i: i32) {
275 [70; 147) 'match ... }': &[i32] 275 [70; 147) 'match ... }': &[i32]
276 [76; 77) 'i': i32 276 [76; 77) 'i': i32
277 [88; 89) '2': i32 277 [88; 89) '2': i32
278 [88; 89) '2': i32
278 [93; 96) 'foo': fn foo<i32>(&[i32]) -> &[i32] 279 [93; 96) 'foo': fn foo<i32>(&[i32]) -> &[i32]
279 [93; 102) 'foo(&[2])': &[i32] 280 [93; 102) 'foo(&[2])': &[i32]
280 [97; 101) '&[2]': &[i32; _] 281 [97; 101) '&[2]': &[i32; _]
281 [98; 101) '[2]': [i32; _] 282 [98; 101) '[2]': [i32; _]
282 [99; 100) '2': i32 283 [99; 100) '2': i32
283 [112; 113) '1': i32 284 [112; 113) '1': i32
285 [112; 113) '1': i32
284 [117; 121) '&[1]': &[i32; _] 286 [117; 121) '&[1]': &[i32; _]
285 [118; 121) '[1]': [i32; _] 287 [118; 121) '[1]': [i32; _]
286 [119; 120) '1': i32 288 [119; 120) '1': i32
@@ -316,10 +318,12 @@ fn test(i: i32) {
316 [70; 147) 'match ... }': &[i32] 318 [70; 147) 'match ... }': &[i32]
317 [76; 77) 'i': i32 319 [76; 77) 'i': i32
318 [88; 89) '1': i32 320 [88; 89) '1': i32
321 [88; 89) '1': i32
319 [93; 97) '&[1]': &[i32; _] 322 [93; 97) '&[1]': &[i32; _]
320 [94; 97) '[1]': [i32; _] 323 [94; 97) '[1]': [i32; _]
321 [95; 96) '1': i32 324 [95; 96) '1': i32
322 [107; 108) '2': i32 325 [107; 108) '2': i32
326 [107; 108) '2': i32
323 [112; 115) 'foo': fn foo<i32>(&[i32]) -> &[i32] 327 [112; 115) 'foo': fn foo<i32>(&[i32]) -> &[i32]
324 [112; 121) 'foo(&[2])': &[i32] 328 [112; 121) 'foo(&[2])': &[i32]
325 [116; 120) '&[2]': &[i32; _] 329 [116; 120) '&[2]': &[i32; _]
@@ -357,9 +361,11 @@ fn test() {
357 [45; 142) 'match ... }': *const i32 361 [45; 142) 'match ... }': *const i32
358 [51; 52) '1': i32 362 [51; 52) '1': i32
359 [63; 64) '1': i32 363 [63; 64) '1': i32
364 [63; 64) '1': i32
360 [68; 69) 't': &mut i32 365 [68; 69) 't': &mut i32
361 [68; 81) 't as *mut i32': *mut i32 366 [68; 81) 't as *mut i32': *mut i32
362 [91; 92) '2': i32 367 [91; 92) '2': i32
368 [91; 92) '2': i32
363 [96; 97) 't': &mut i32 369 [96; 97) 't': &mut i32
364 [96; 105) 't as &i32': &i32 370 [96; 105) 't as &i32': &i32
365 [115; 116) '_': i32 371 [115; 116) '_': i32
diff --git a/crates/ra_hir_ty/src/tests/patterns.rs b/crates/ra_hir_ty/src/tests/patterns.rs
index 76aa32024..6e5d2247c 100644
--- a/crates/ra_hir_ty/src/tests/patterns.rs
+++ b/crates/ra_hir_ty/src/tests/patterns.rs
@@ -82,6 +82,90 @@ fn test(x: &i32) {
82} 82}
83 83
84#[test] 84#[test]
85fn infer_literal_pattern() {
86 assert_snapshot!(
87 infer_with_mismatches(r#"
88fn any<T>() -> T { loop {} }
89fn test(x: &i32) {
90 if let "foo" = any() {}
91 if let 1 = any() {}
92 if let 1u32 = any() {}
93 if let 1f32 = any() {}
94 if let 1.0 = any() {}
95 if let true = any() {}
96}
97"#, true),
98 @r###"
99 [18; 29) '{ loop {} }': T
100 [20; 27) 'loop {}': !
101 [25; 27) '{}': ()
102 [38; 39) 'x': &i32
103 [47; 209) '{ ...) {} }': ()
104 [53; 76) 'if let...y() {}': ()
105 [60; 65) '"foo"': &str
106 [60; 65) '"foo"': &str
107 [68; 71) 'any': fn any<&str>() -> &str
108 [68; 73) 'any()': &str
109 [74; 76) '{}': ()
110 [81; 100) 'if let...y() {}': ()
111 [88; 89) '1': i32
112 [88; 89) '1': i32
113 [92; 95) 'any': fn any<i32>() -> i32
114 [92; 97) 'any()': i32
115 [98; 100) '{}': ()
116 [105; 127) 'if let...y() {}': ()
117 [112; 116) '1u32': u32
118 [112; 116) '1u32': u32
119 [119; 122) 'any': fn any<u32>() -> u32
120 [119; 124) 'any()': u32
121 [125; 127) '{}': ()
122 [132; 154) 'if let...y() {}': ()
123 [139; 143) '1f32': f32
124 [139; 143) '1f32': f32
125 [146; 149) 'any': fn any<f32>() -> f32
126 [146; 151) 'any()': f32
127 [152; 154) '{}': ()
128 [159; 180) 'if let...y() {}': ()
129 [166; 169) '1.0': f64
130 [166; 169) '1.0': f64
131 [172; 175) 'any': fn any<f64>() -> f64
132 [172; 177) 'any()': f64
133 [178; 180) '{}': ()
134 [185; 207) 'if let...y() {}': ()
135 [192; 196) 'true': bool
136 [192; 196) 'true': bool
137 [199; 202) 'any': fn any<bool>() -> bool
138 [199; 204) 'any()': bool
139 [205; 207) '{}': ()
140 "###
141 );
142}
143
144#[test]
145fn infer_range_pattern() {
146 assert_snapshot!(
147 infer_with_mismatches(r#"
148fn test(x: &i32) {
149 if let 1..76 = 2u32 {}
150 if let 1..=76 = 2u32 {}
151}
152"#, true),
153 @r###"
154 [9; 10) 'x': &i32
155 [18; 76) '{ ...2 {} }': ()
156 [24; 46) 'if let...u32 {}': ()
157 [31; 36) '1..76': u32
158 [39; 43) '2u32': u32
159 [44; 46) '{}': ()
160 [51; 74) 'if let...u32 {}': ()
161 [58; 64) '1..=76': u32
162 [67; 71) '2u32': u32
163 [72; 74) '{}': ()
164 "###
165 );
166}
167
168#[test]
85fn infer_pattern_match_ergonomics() { 169fn infer_pattern_match_ergonomics() {
86 assert_snapshot!( 170 assert_snapshot!(
87 infer(r#" 171 infer(r#"
@@ -212,6 +296,7 @@ fn test() {
212 [59; 62) 'arr': [f64; _] 296 [59; 62) 'arr': [f64; _]
213 [73; 81) '[1.0, a]': [f64; _] 297 [73; 81) '[1.0, a]': [f64; _]
214 [74; 77) '1.0': f64 298 [74; 77) '1.0': f64
299 [74; 77) '1.0': f64
215 [79; 80) 'a': f64 300 [79; 80) 'a': f64
216 [85; 111) '{ ... }': () 301 [85; 111) '{ ... }': ()
217 [99; 100) 'a': f64 302 [99; 100) 'a': f64
diff --git a/crates/ra_hir_ty/src/tests/regression.rs b/crates/ra_hir_ty/src/tests/regression.rs
index a02e3ee05..2ee9b8f10 100644
--- a/crates/ra_hir_ty/src/tests/regression.rs
+++ b/crates/ra_hir_ty/src/tests/regression.rs
@@ -206,7 +206,8 @@ pub fn compute() {
206 [24; 106) 'match ... }': () 206 [24; 106) 'match ... }': ()
207 [30; 37) 'nope!()': {unknown} 207 [30; 37) 'nope!()': {unknown}
208 [48; 94) 'SizeSk...tail }': {unknown} 208 [48; 94) 'SizeSk...tail }': {unknown}
209 [82; 86) 'true': {unknown} 209 [82; 86) 'true': bool
210 [82; 86) 'true': bool
210 [88; 92) 'tail': {unknown} 211 [88; 92) 'tail': {unknown}
211 [98; 100) '{}': () 212 [98; 100) '{}': ()
212 "### 213 "###
diff --git a/crates/ra_hir_ty/src/tests/simple.rs b/crates/ra_hir_ty/src/tests/simple.rs
index c140bd513..a600b947d 100644
--- a/crates/ra_hir_ty/src/tests/simple.rs
+++ b/crates/ra_hir_ty/src/tests/simple.rs
@@ -948,6 +948,7 @@ fn foo() {
948 [165; 247) 'match ... }': i32 948 [165; 247) 'match ... }': i32
949 [171; 175) 'true': bool 949 [171; 175) 'true': bool
950 [186; 190) 'true': bool 950 [186; 190) 'true': bool
951 [186; 190) 'true': bool
951 [194; 195) '3': i32 952 [194; 195) '3': i32
952 [205; 206) '_': bool 953 [205; 206) '_': bool
953 [210; 241) '{ ... }': ! 954 [210; 241) '{ ... }': !
@@ -956,6 +957,7 @@ fn foo() {
956 [263; 320) 'match ... }': i32 957 [263; 320) 'match ... }': i32
957 [269; 273) 'true': bool 958 [269; 273) 'true': bool
958 [284; 288) 'true': bool 959 [284; 288) 'true': bool
960 [284; 288) 'true': bool
959 [292; 293) '4': i32 961 [292; 293) '4': i32
960 [303; 304) '_': bool 962 [303; 304) '_': bool
961 [308; 314) 'return': ! 963 [308; 314) 'return': !
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs
index c1b6e1ddc..b50cda06f 100644
--- a/crates/ra_project_model/src/cargo_workspace.rs
+++ b/crates/ra_project_model/src/cargo_workspace.rs
@@ -75,7 +75,7 @@ pub type Target = Idx<TargetData>;
75 75
76#[derive(Debug, Clone)] 76#[derive(Debug, Clone)]
77pub struct PackageData { 77pub struct PackageData {
78 pub id: String, 78 pub version: String,
79 pub name: String, 79 pub name: String,
80 pub manifest: PathBuf, 80 pub manifest: PathBuf,
81 pub targets: Vec<Target>, 81 pub targets: Vec<Target>,
@@ -174,14 +174,15 @@ impl CargoWorkspace {
174 let ws_members = &meta.workspace_members; 174 let ws_members = &meta.workspace_members;
175 175
176 for meta_pkg in meta.packages { 176 for meta_pkg in meta.packages {
177 let cargo_metadata::Package { id, edition, name, manifest_path, .. } = meta_pkg; 177 let cargo_metadata::Package { id, edition, name, manifest_path, version, .. } =
178 meta_pkg;
178 let is_member = ws_members.contains(&id); 179 let is_member = ws_members.contains(&id);
179 let edition = edition 180 let edition = edition
180 .parse::<Edition>() 181 .parse::<Edition>()
181 .with_context(|| format!("Failed to parse edition {}", edition))?; 182 .with_context(|| format!("Failed to parse edition {}", edition))?;
182 let pkg = packages.alloc(PackageData { 183 let pkg = packages.alloc(PackageData {
183 name, 184 name,
184 id: id.to_string(), 185 version: version.to_string(),
185 manifest: manifest_path, 186 manifest: manifest_path,
186 targets: Vec::new(), 187 targets: Vec::new(),
187 is_member, 188 is_member,
@@ -256,7 +257,7 @@ impl CargoWorkspace {
256 if self.is_unique(&*package.name) { 257 if self.is_unique(&*package.name) {
257 package.name.clone() 258 package.name.clone()
258 } else { 259 } else {
259 package.id.clone() 260 format!("{}:{}", package.name, package.version)
260 } 261 }
261 } 262 }
262 263
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index dd9c80691..0ab64a1e0 100644
--- a/crates/ra_project_model/src/lib.rs
+++ b/crates/ra_project_model/src/lib.rs
@@ -62,16 +62,16 @@ pub struct PackageRoot {
62 /// Is a member of the current workspace 62 /// Is a member of the current workspace
63 is_member: bool, 63 is_member: bool,
64} 64}
65
66impl PackageRoot { 65impl PackageRoot {
67 pub fn new(path: PathBuf, is_member: bool) -> PackageRoot { 66 pub fn new_member(path: PathBuf) -> PackageRoot {
68 PackageRoot { path, is_member } 67 Self { path, is_member: true }
69 } 68 }
70 69 pub fn new_non_member(path: PathBuf) -> PackageRoot {
71 pub fn path(&self) -> &PathBuf { 70 Self { path, is_member: false }
71 }
72 pub fn path(&self) -> &Path {
72 &self.path 73 &self.path
73 } 74 }
74
75 pub fn is_member(&self) -> bool { 75 pub fn is_member(&self) -> bool {
76 self.is_member 76 self.is_member
77 } 77 }
@@ -130,70 +130,45 @@ impl ProjectWorkspace {
130 pub fn to_roots(&self) -> Vec<PackageRoot> { 130 pub fn to_roots(&self) -> Vec<PackageRoot> {
131 match self { 131 match self {
132 ProjectWorkspace::Json { project } => { 132 ProjectWorkspace::Json { project } => {
133 let mut roots = Vec::with_capacity(project.roots.len()); 133 project.roots.iter().map(|r| PackageRoot::new_member(r.path.clone())).collect()
134 for root in &project.roots {
135 roots.push(PackageRoot::new(root.path.clone(), true));
136 }
137 roots
138 }
139 ProjectWorkspace::Cargo { cargo, sysroot } => {
140 let mut roots = Vec::with_capacity(cargo.packages().len() + sysroot.crates().len());
141 for pkg in cargo.packages() {
142 let root = cargo[pkg].root().to_path_buf();
143 let member = cargo[pkg].is_member;
144 roots.push(PackageRoot::new(root, member));
145 }
146 for krate in sysroot.crates() {
147 roots.push(PackageRoot::new(sysroot[krate].root_dir().to_path_buf(), false))
148 }
149 roots
150 } 134 }
135 ProjectWorkspace::Cargo { cargo, sysroot } => cargo
136 .packages()
137 .map(|pkg| PackageRoot {
138 path: cargo[pkg].root().to_path_buf(),
139 is_member: cargo[pkg].is_member,
140 })
141 .chain(sysroot.crates().map(|krate| {
142 PackageRoot::new_non_member(sysroot[krate].root_dir().to_path_buf())
143 }))
144 .collect(),
151 } 145 }
152 } 146 }
153 147
154 pub fn out_dirs(&self) -> Vec<PathBuf> { 148 pub fn out_dirs(&self) -> Vec<PathBuf> {
155 match self { 149 match self {
156 ProjectWorkspace::Json { project } => { 150 ProjectWorkspace::Json { project } => {
157 let mut out_dirs = Vec::with_capacity(project.crates.len()); 151 project.crates.iter().filter_map(|krate| krate.out_dir.as_ref()).cloned().collect()
158 for krate in &project.crates {
159 if let Some(out_dir) = &krate.out_dir {
160 out_dirs.push(out_dir.to_path_buf());
161 }
162 }
163 out_dirs
164 } 152 }
165 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => { 153 ProjectWorkspace::Cargo { cargo, sysroot: _ } => {
166 let mut out_dirs = Vec::with_capacity(cargo.packages().len()); 154 cargo.packages().filter_map(|pkg| cargo[pkg].out_dir.as_ref()).cloned().collect()
167 for pkg in cargo.packages() {
168 if let Some(out_dir) = &cargo[pkg].out_dir {
169 out_dirs.push(out_dir.to_path_buf());
170 }
171 }
172 out_dirs
173 } 155 }
174 } 156 }
175 } 157 }
176 158
177 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> { 159 pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> {
178 match self { 160 match self {
179 ProjectWorkspace::Json { project } => { 161 ProjectWorkspace::Json { project } => project
180 let mut proc_macro_dylib_paths = Vec::with_capacity(project.crates.len()); 162 .crates
181 for krate in &project.crates { 163 .iter()
182 if let Some(out_dir) = &krate.proc_macro_dylib_path { 164 .filter_map(|krate| krate.proc_macro_dylib_path.as_ref())
183 proc_macro_dylib_paths.push(out_dir.to_path_buf()); 165 .cloned()
184 } 166 .collect(),
185 } 167 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => cargo
186 proc_macro_dylib_paths 168 .packages()
187 } 169 .filter_map(|pkg| cargo[pkg].proc_macro_dylib_path.as_ref())
188 ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => { 170 .cloned()
189 let mut proc_macro_dylib_paths = Vec::with_capacity(cargo.packages().len()); 171 .collect(),
190 for pkg in cargo.packages() {
191 if let Some(dylib_path) = &cargo[pkg].proc_macro_dylib_path {
192 proc_macro_dylib_paths.push(dylib_path.to_path_buf());
193 }
194 }
195 proc_macro_dylib_paths
196 }
197 } 172 }
198 } 173 }
199 174
@@ -216,10 +191,12 @@ impl ProjectWorkspace {
216 let mut crate_graph = CrateGraph::default(); 191 let mut crate_graph = CrateGraph::default();
217 match self { 192 match self {
218 ProjectWorkspace::Json { project } => { 193 ProjectWorkspace::Json { project } => {
219 let mut crates = FxHashMap::default(); 194 let crates: FxHashMap<_, _> = project
220 for (id, krate) in project.crates.iter().enumerate() { 195 .crates
221 let crate_id = json_project::CrateId(id); 196 .iter()
222 if let Some(file_id) = load(&krate.root_module) { 197 .enumerate()
198 .filter_map(|(seq_index, krate)| {
199 let file_id = load(&krate.root_module)?;
223 let edition = match krate.edition { 200 let edition = match krate.edition {
224 json_project::Edition::Edition2015 => Edition::Edition2015, 201 json_project::Edition::Edition2015 => Edition::Edition2015,
225 json_project::Edition::Edition2018 => Edition::Edition2018, 202 json_project::Edition::Edition2018 => Edition::Edition2018,
@@ -249,8 +226,8 @@ impl ProjectWorkspace {
249 .clone() 226 .clone()
250 .map(|it| proc_macro_client.by_dylib_path(&it)); 227 .map(|it| proc_macro_client.by_dylib_path(&it));
251 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env 228 // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env
252 crates.insert( 229 Some((
253 crate_id, 230 json_project::CrateId(seq_index),
254 crate_graph.add_crate_root( 231 crate_graph.add_crate_root(
255 file_id, 232 file_id,
256 edition, 233 edition,
@@ -261,9 +238,9 @@ impl ProjectWorkspace {
261 extern_source, 238 extern_source,
262 proc_macro.unwrap_or_default(), 239 proc_macro.unwrap_or_default(),
263 ), 240 ),
264 ); 241 ))
265 } 242 })
266 } 243 .collect();
267 244
268 for (id, krate) in project.crates.iter().enumerate() { 245 for (id, krate) in project.crates.iter().enumerate() {
269 for dep in &krate.deps { 246 for dep in &krate.deps {
@@ -287,9 +264,11 @@ impl ProjectWorkspace {
287 } 264 }
288 } 265 }
289 ProjectWorkspace::Cargo { cargo, sysroot } => { 266 ProjectWorkspace::Cargo { cargo, sysroot } => {
290 let mut sysroot_crates = FxHashMap::default(); 267 let sysroot_crates: FxHashMap<_, _> = sysroot
291 for krate in sysroot.crates() { 268 .crates()
292 if let Some(file_id) = load(&sysroot[krate].root) { 269 .filter_map(|krate| {
270 let file_id = load(&sysroot[krate].root)?;
271
293 // Crates from sysroot have `cfg(test)` disabled 272 // Crates from sysroot have `cfg(test)` disabled
294 let cfg_options = { 273 let cfg_options = {
295 let mut opts = default_cfg_options.clone(); 274 let mut opts = default_cfg_options.clone();
@@ -300,22 +279,22 @@ impl ProjectWorkspace {
300 let env = Env::default(); 279 let env = Env::default();
301 let extern_source = ExternSource::default(); 280 let extern_source = ExternSource::default();
302 let proc_macro = vec![]; 281 let proc_macro = vec![];
282 let crate_name = CrateName::new(&sysroot[krate].name)
283 .expect("Sysroot crate names should not contain dashes");
303 284
304 let crate_id = crate_graph.add_crate_root( 285 let crate_id = crate_graph.add_crate_root(
305 file_id, 286 file_id,
306 Edition::Edition2018, 287 Edition::Edition2018,
307 Some( 288 Some(crate_name),
308 CrateName::new(&sysroot[krate].name)
309 .expect("Sysroot crate names should not contain dashes"),
310 ),
311 cfg_options, 289 cfg_options,
312 env, 290 env,
313 extern_source, 291 extern_source,
314 proc_macro, 292 proc_macro,
315 ); 293 );
316 sysroot_crates.insert(krate, crate_id); 294 Some((krate, crate_id))
317 } 295 })
318 } 296 .collect();
297
319 for from in sysroot.crates() { 298 for from in sysroot.crates() {
320 for &to in sysroot[from].deps.iter() { 299 for &to in sysroot[from].deps.iter() {
321 let name = &sysroot[to].name; 300 let name = &sysroot[to].name;
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs
index dbb9c8a9b..f39559e9e 100644
--- a/crates/ra_syntax/src/ast/make.rs
+++ b/crates/ra_syntax/src/ast/make.rs
@@ -22,7 +22,8 @@ pub fn path_unqualified(segment: ast::PathSegment) -> ast::Path {
22pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path { 22pub fn path_qualified(qual: ast::Path, segment: ast::PathSegment) -> ast::Path {
23 path_from_text(&format!("{}::{}", qual, segment)) 23 path_from_text(&format!("{}::{}", qual, segment))
24} 24}
25fn path_from_text(text: &str) -> ast::Path { 25
26pub fn path_from_text(text: &str) -> ast::Path {
26 ast_from_text(text) 27 ast_from_text(text)
27} 28}
28 29
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 2c0bde920..69133e4e4 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -36,8 +36,7 @@ pub(crate) fn load_cargo(
36 extern_dirs.extend(ws.out_dirs()); 36 extern_dirs.extend(ws.out_dirs());
37 37
38 let mut project_roots = ws.to_roots(); 38 let mut project_roots = ws.to_roots();
39 project_roots 39 project_roots.extend(extern_dirs.iter().cloned().map(PackageRoot::new_non_member));
40 .extend(extern_dirs.iter().map(|path| PackageRoot::new(path.to_path_buf(), false)));
41 40
42 let (sender, receiver) = unbounded(); 41 let (sender, receiver) = unbounded();
43 let sender = Box::new(move |t| sender.send(t).unwrap()); 42 let sender = Box::new(move |t| sender.send(t).unwrap());
@@ -46,7 +45,7 @@ pub(crate) fn load_cargo(
46 .iter() 45 .iter()
47 .map(|pkg_root| { 46 .map(|pkg_root| {
48 RootEntry::new( 47 RootEntry::new(
49 pkg_root.path().clone(), 48 pkg_root.path().to_owned(),
50 RustPackageFilterBuilder::default() 49 RustPackageFilterBuilder::default()
51 .set_member(pkg_root.is_member()) 50 .set_member(pkg_root.is_member())
52 .into_vfs_filter(), 51 .into_vfs_filter(),
@@ -58,12 +57,12 @@ pub(crate) fn load_cargo(
58 ); 57 );
59 58
60 let source_roots = roots 59 let source_roots = roots
61 .iter() 60 .into_iter()
62 .map(|&vfs_root| { 61 .map(|vfs_root| {
63 let source_root_id = vfs_root_to_id(vfs_root); 62 let source_root_id = vfs_root_to_id(vfs_root);
64 let project_root = project_roots 63 let project_root = project_roots
65 .iter() 64 .iter()
66 .find(|it| it.path() == &vfs.root2path(vfs_root)) 65 .find(|it| it.path() == vfs.root2path(vfs_root))
67 .unwrap() 66 .unwrap()
68 .clone(); 67 .clone();
69 (source_root_id, project_root) 68 (source_root_id, project_root)
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3c8f55f1e..04f5bb473 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -16,21 +16,33 @@ use serde::Deserialize;
16#[derive(Debug, Clone)] 16#[derive(Debug, Clone)]
17pub struct Config { 17pub struct Config {
18 pub client_caps: ClientCapsConfig, 18 pub client_caps: ClientCapsConfig,
19 pub publish_decorations: bool, 19
20 pub with_sysroot: bool,
20 pub publish_diagnostics: bool, 21 pub publish_diagnostics: bool,
22 pub lru_capacity: Option<usize>,
23 pub proc_macro_srv: Option<String>,
24 pub files: FilesConfig,
21 pub notifications: NotificationsConfig, 25 pub notifications: NotificationsConfig,
26
27 pub cargo: CargoConfig,
28 pub rustfmt: RustfmtConfig,
29 pub check: Option<FlycheckConfig>,
30
22 pub inlay_hints: InlayHintsConfig, 31 pub inlay_hints: InlayHintsConfig,
23 pub completion: CompletionConfig, 32 pub completion: CompletionConfig,
24 pub call_info_full: bool, 33 pub call_info_full: bool,
25 pub rustfmt: RustfmtConfig, 34}
26 pub check: Option<FlycheckConfig>, 35
27 pub vscode_lldb: bool, 36#[derive(Debug, Clone)]
28 pub proc_macro_srv: Option<String>, 37pub struct FilesConfig {
29 pub lru_capacity: Option<usize>, 38 pub watcher: FilesWatcher,
30 pub use_client_watching: bool, 39 pub exclude: Vec<String>,
31 pub exclude_globs: Vec<String>, 40}
32 pub cargo: CargoConfig, 41
33 pub with_sysroot: bool, 42#[derive(Debug, Clone)]
43pub enum FilesWatcher {
44 Client,
45 Notify,
34} 46}
35 47
36#[derive(Debug, Clone)] 48#[derive(Debug, Clone)]
@@ -60,13 +72,26 @@ pub struct ClientCapsConfig {
60impl Default for Config { 72impl Default for Config {
61 fn default() -> Self { 73 fn default() -> Self {
62 Config { 74 Config {
63 publish_decorations: false, 75 client_caps: ClientCapsConfig::default(),
76
77 with_sysroot: true,
64 publish_diagnostics: true, 78 publish_diagnostics: true,
79 lru_capacity: None,
80 proc_macro_srv: None,
81 files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
65 notifications: NotificationsConfig { 82 notifications: NotificationsConfig {
66 workspace_loaded: true, 83 workspace_loaded: true,
67 cargo_toml_not_found: true, 84 cargo_toml_not_found: true,
68 }, 85 },
69 client_caps: ClientCapsConfig::default(), 86
87 cargo: CargoConfig::default(),
88 rustfmt: RustfmtConfig::Rustfmt { extra_args: Vec::new() },
89 check: Some(FlycheckConfig::CargoCommand {
90 command: "check".to_string(),
91 all_targets: true,
92 extra_args: Vec::new(),
93 }),
94
70 inlay_hints: InlayHintsConfig { 95 inlay_hints: InlayHintsConfig {
71 type_hints: true, 96 type_hints: true,
72 parameter_hints: true, 97 parameter_hints: true,
@@ -79,19 +104,6 @@ impl Default for Config {
79 add_call_argument_snippets: true, 104 add_call_argument_snippets: true,
80 }, 105 },
81 call_info_full: true, 106 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,
90 lru_capacity: None,
91 use_client_watching: false,
92 exclude_globs: Vec::new(),
93 cargo: CargoConfig::default(),
94 with_sysroot: true,
95 } 107 }
96 } 108 }
97} 109}
@@ -105,46 +117,61 @@ impl Config {
105 *self = Default::default(); 117 *self = Default::default();
106 self.client_caps = client_caps; 118 self.client_caps = client_caps;
107 119
108 set(value, "/publishDecorations", &mut self.publish_decorations); 120 set(value, "/withSysroot", &mut self.with_sysroot);
109 set(value, "/excludeGlobs", &mut self.exclude_globs); 121 set(value, "/featureFlags/lsp.diagnostics", &mut self.publish_diagnostics);
110 set(value, "/useClientWatching", &mut self.use_client_watching);
111 set(value, "/lruCapacity", &mut self.lru_capacity); 122 set(value, "/lruCapacity", &mut self.lru_capacity);
112 123 if let Some(watcher) = get::<String>(value, "/files/watcher") {
113 set(value, "/inlayHintsType", &mut self.inlay_hints.type_hints); 124 self.files.watcher = match watcher.as_str() {
114 set(value, "/inlayHintsParameter", &mut self.inlay_hints.parameter_hints); 125 "client" => FilesWatcher::Client,
115 set(value, "/inlayHintsChaining", &mut self.inlay_hints.chaining_hints); 126 "notify"| _ => FilesWatcher::Notify,
116 set(value, "/inlayHintsMaxLength", &mut self.inlay_hints.max_length); 127 }
117 128 }
118 if let Some(false) = get(value, "cargo_watch_enable") { 129 set(value, "/notifications/workspaceLoaded", &mut self.notifications.workspace_loaded);
130 set(value, "/notifications/cargoTomlNotFound", &mut self.notifications.cargo_toml_not_found);
131
132 set(value, "/cargo/noDefaultFeatures", &mut self.cargo.no_default_features);
133 set(value, "/cargo/allFeatures", &mut self.cargo.all_features);
134 set(value, "/cargo/features", &mut self.cargo.features);
135 set(value, "/cargo/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
136 if let Some(mut args) = get::<Vec<String>>(value, "/rustfmt/overrideCommand") {
137 if !args.is_empty() {
138 let command = args.remove(0);
139 self.rustfmt = RustfmtConfig::CustomCommand {
140 command,
141 args,
142 }
143 }
144 } else if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt {
145 set(value, "/rustfmt/extraArgs", extra_args);
146 }
147 if let Some(false) = get(value, "/checkOnSave/enable") {
119 self.check = None 148 self.check = None
120 } else { 149 } else {
121 if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) = &mut self.check 150 if let Some(mut args) = get::<Vec<String>>(value, "/checkOnSave/overrideCommand") {
151 if !args.is_empty() {
152 let command = args.remove(0);
153 self.check = Some(FlycheckConfig::CustomCommand {
154 command,
155 args,
156 })
157 }
158
159 } else if let Some(FlycheckConfig::CargoCommand { command, extra_args, all_targets }) = &mut self.check
122 { 160 {
123 set(value, "/cargoWatchArgs", extra_args); 161 set(value, "/checkOnSave/extraArgs", extra_args);
124 set(value, "/cargoWatchCommand", command); 162 set(value, "/checkOnSave/command", command);
125 set(value, "/cargoWatchAllTargets", all_targets); 163 set(value, "/checkOnSave/allTargets", all_targets);
126 } 164 }
127 }; 165 };
128 166
129 set(value, "/withSysroot", &mut self.with_sysroot); 167 set(value, "/inlayHints/typeHints", &mut self.inlay_hints.type_hints);
130 if let RustfmtConfig::Rustfmt { extra_args } = &mut self.rustfmt { 168 set(value, "/inlayHints/parameterHints", &mut self.inlay_hints.parameter_hints);
131 set(value, "/rustfmtArgs", extra_args); 169 set(value, "/inlayHints/chainingHints", &mut self.inlay_hints.chaining_hints);
132 } 170 set(value, "/inlayHints/maxLength", &mut self.inlay_hints.max_length);
133 171 set(value, "/completion/postfix/enable", &mut self.completion.enable_postfix_completions);
134 set(value, "/cargoFeatures/noDefaultFeatures", &mut self.cargo.no_default_features); 172 set(value, "/completion/addCallParenthesis", &mut self.completion.add_call_parenthesis);
135 set(value, "/cargoFeatures/allFeatures", &mut self.cargo.all_features); 173 set(value, "/completion/addCallArgumentSnippets", &mut self.completion.add_call_argument_snippets);
136 set(value, "/cargoFeatures/features", &mut self.cargo.features); 174 set(value, "/callInfo/full", &mut self.call_info_full);
137 set(value, "/cargoFeatures/loadOutDirsFromCheck", &mut self.cargo.load_out_dirs_from_check);
138
139 set(value, "/vscodeLldb", &mut self.vscode_lldb);
140
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);
148 175
149 log::info!("Config::update() = {:#?}", self); 176 log::info!("Config::update() = {:#?}", self);
150 177
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 45ae0ad9d..8d1429196 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -23,6 +23,7 @@ use lsp_types::{
23use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask}; 23use ra_flycheck::{url_from_path_with_drive_lowercasing, CheckTask};
24use ra_ide::{Canceled, FileId, LibraryData, SourceRootId}; 24use ra_ide::{Canceled, FileId, LibraryData, SourceRootId};
25use ra_prof::profile; 25use ra_prof::profile;
26use ra_project_model::{PackageRoot, ProjectWorkspace};
26use ra_vfs::{VfsFile, VfsTask, Watch}; 27use ra_vfs::{VfsFile, VfsTask, Watch};
27use relative_path::RelativePathBuf; 28use relative_path::RelativePathBuf;
28use rustc_hash::FxHashSet; 29use rustc_hash::FxHashSet;
@@ -30,7 +31,7 @@ use serde::{de::DeserializeOwned, Serialize};
30use threadpool::ThreadPool; 31use threadpool::ThreadPool;
31 32
32use crate::{ 33use crate::{
33 config::Config, 34 config::{Config, FilesWatcher},
34 diagnostics::DiagnosticTask, 35 diagnostics::DiagnosticTask,
35 main_loop::{ 36 main_loop::{
36 pending_requests::{PendingRequest, PendingRequests}, 37 pending_requests::{PendingRequest, PendingRequests},
@@ -40,7 +41,6 @@ use crate::{
40 world::{WorldSnapshot, WorldState}, 41 world::{WorldSnapshot, WorldState},
41 Result, 42 Result,
42}; 43};
43use req::ConfigurationParams;
44 44
45#[derive(Debug)] 45#[derive(Debug)]
46pub struct LspError { 46pub struct LspError {
@@ -122,17 +122,18 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
122 }; 122 };
123 123
124 let globs = config 124 let globs = config
125 .exclude_globs 125 .files
126 .exclude
126 .iter() 127 .iter()
127 .map(|glob| crate::vfs_glob::Glob::new(glob)) 128 .map(|glob| crate::vfs_glob::Glob::new(glob))
128 .collect::<std::result::Result<Vec<_>, _>>()?; 129 .collect::<std::result::Result<Vec<_>, _>>()?;
129 130
130 if config.use_client_watching { 131 if let FilesWatcher::Client = config.files.watcher {
131 let registration_options = req::DidChangeWatchedFilesRegistrationOptions { 132 let registration_options = req::DidChangeWatchedFilesRegistrationOptions {
132 watchers: workspaces 133 watchers: workspaces
133 .iter() 134 .iter()
134 .flat_map(|ws| ws.to_roots()) 135 .flat_map(ProjectWorkspace::to_roots)
135 .filter(|root| root.is_member()) 136 .filter(PackageRoot::is_member)
136 .map(|root| format!("{}/**/*.rs", root.path().display())) 137 .map(|root| format!("{}/**/*.rs", root.path().display()))
137 .map(|glob_pattern| req::FileSystemWatcher { glob_pattern, kind: None }) 138 .map(|glob_pattern| req::FileSystemWatcher { glob_pattern, kind: None })
138 .collect(), 139 .collect(),
@@ -153,7 +154,7 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
153 workspaces, 154 workspaces,
154 config.lru_capacity, 155 config.lru_capacity,
155 &globs, 156 &globs,
156 Watch(!config.use_client_watching), 157 Watch(matches!(config.files.watcher, FilesWatcher::Notify)),
157 config, 158 config,
158 ) 159 )
159 }; 160 };
@@ -250,9 +251,7 @@ impl fmt::Debug for Event {
250 } 251 }
251 } 252 }
252 Event::Task(Task::Notify(not)) => { 253 Event::Task(Task::Notify(not)) => {
253 if notification_is::<req::PublishDecorations>(not) 254 if notification_is::<req::PublishDiagnostics>(not) {
254 || notification_is::<req::PublishDiagnostics>(not)
255 {
256 return debug_verbose_not(not, f); 255 return debug_verbose_not(not, f);
257 } 256 }
258 } 257 }
@@ -427,7 +426,6 @@ fn loop_turn(
427 update_file_notifications_on_threadpool( 426 update_file_notifications_on_threadpool(
428 pool, 427 pool,
429 world_state.snapshot(), 428 world_state.snapshot(),
430 world_state.config.publish_decorations,
431 task_sender.clone(), 429 task_sender.clone(),
432 loop_state.subscriptions.subscriptions(), 430 loop_state.subscriptions.subscriptions(),
433 ) 431 )
@@ -508,7 +506,6 @@ fn on_request(
508 .on::<req::GotoTypeDefinition>(handlers::handle_goto_type_definition)? 506 .on::<req::GotoTypeDefinition>(handlers::handle_goto_type_definition)?
509 .on::<req::ParentModule>(handlers::handle_parent_module)? 507 .on::<req::ParentModule>(handlers::handle_parent_module)?
510 .on::<req::Runnables>(handlers::handle_runnables)? 508 .on::<req::Runnables>(handlers::handle_runnables)?
511 .on::<req::DecorationsRequest>(handlers::handle_decorations)?
512 .on::<req::Completion>(handlers::handle_completion)? 509 .on::<req::Completion>(handlers::handle_completion)?
513 .on::<req::CodeActionRequest>(handlers::handle_code_action)? 510 .on::<req::CodeActionRequest>(handlers::handle_code_action)?
514 .on::<req::CodeLensRequest>(handlers::handle_code_lens)? 511 .on::<req::CodeLensRequest>(handlers::handle_code_lens)?
@@ -611,7 +608,12 @@ fn on_notification(
611 let request_id = loop_state.next_request_id(); 608 let request_id = loop_state.next_request_id();
612 let request = request_new::<req::WorkspaceConfiguration>( 609 let request = request_new::<req::WorkspaceConfiguration>(
613 request_id.clone(), 610 request_id.clone(),
614 ConfigurationParams::default(), 611 req::ConfigurationParams {
612 items: vec![req::ConfigurationItem {
613 scope_uri: None,
614 section: Some("rust-analyzer".to_string()),
615 }],
616 },
615 ); 617 );
616 msg_sender.send(request.into())?; 618 msg_sender.send(request.into())?;
617 loop_state.configuration_request_id = Some(request_id); 619 loop_state.configuration_request_id = Some(request_id);
@@ -884,15 +886,13 @@ where
884fn update_file_notifications_on_threadpool( 886fn update_file_notifications_on_threadpool(
885 pool: &ThreadPool, 887 pool: &ThreadPool,
886 world: WorldSnapshot, 888 world: WorldSnapshot,
887 publish_decorations: bool,
888 task_sender: Sender<Task>, 889 task_sender: Sender<Task>,
889 subscriptions: Vec<FileId>, 890 subscriptions: Vec<FileId>,
890) { 891) {
891 log::trace!("updating notifications for {:?}", subscriptions); 892 log::trace!("updating notifications for {:?}", subscriptions);
892 let publish_diagnostics = world.config.publish_diagnostics; 893 if world.config.publish_diagnostics {
893 pool.execute(move || { 894 pool.execute(move || {
894 for file_id in subscriptions { 895 for file_id in subscriptions {
895 if publish_diagnostics {
896 match handlers::publish_diagnostics(&world, file_id) { 896 match handlers::publish_diagnostics(&world, file_id) {
897 Err(e) => { 897 Err(e) => {
898 if !is_canceled(&e) { 898 if !is_canceled(&e) {
@@ -904,21 +904,8 @@ fn update_file_notifications_on_threadpool(
904 } 904 }
905 } 905 }
906 } 906 }
907 if publish_decorations { 907 })
908 match handlers::publish_decorations(&world, file_id) { 908 }
909 Err(e) => {
910 if !is_canceled(&e) {
911 log::error!("failed to compute decorations: {:?}", e);
912 }
913 }
914 Ok(params) => {
915 let not = notification_new::<req::PublishDecorations>(params);
916 task_sender.send(Task::Notify(not)).unwrap();
917 }
918 }
919 }
920 }
921 });
922} 909}
923 910
924pub fn show_message(typ: req::MessageType, message: impl Into<String>, sender: &Sender<Message>) { 911pub fn show_message(typ: req::MessageType, message: impl Into<String>, sender: &Sender<Message>) {
diff --git a/crates/rust-analyzer/src/main_loop/handlers.rs b/crates/rust-analyzer/src/main_loop/handlers.rs
index 23e48c089..b207f0764 100644
--- a/crates/rust-analyzer/src/main_loop/handlers.rs
+++ b/crates/rust-analyzer/src/main_loop/handlers.rs
@@ -38,7 +38,7 @@ use crate::{
38 }, 38 },
39 diagnostics::DiagnosticTask, 39 diagnostics::DiagnosticTask,
40 from_json, 40 from_json,
41 req::{self, Decoration, InlayHint, InlayHintsParams}, 41 req::{self, InlayHint, InlayHintsParams},
42 semantic_tokens::SemanticTokensBuilder, 42 semantic_tokens::SemanticTokensBuilder,
43 world::WorldSnapshot, 43 world::WorldSnapshot,
44 LspError, Result, 44 LspError, Result,
@@ -389,15 +389,6 @@ pub fn handle_runnables(
389 Ok(res) 389 Ok(res)
390} 390}
391 391
392pub fn handle_decorations(
393 world: WorldSnapshot,
394 params: TextDocumentIdentifier,
395) -> Result<Vec<Decoration>> {
396 let _p = profile("handle_decorations");
397 let file_id = params.try_conv_with(&world)?;
398 highlight(&world, file_id)
399}
400
401pub fn handle_completion( 392pub fn handle_completion(
402 world: WorldSnapshot, 393 world: WorldSnapshot,
403 params: req::CompletionParams, 394 params: req::CompletionParams,
@@ -819,23 +810,21 @@ pub fn handle_code_lens(
819 }; 810 };
820 lenses.push(lens); 811 lenses.push(lens);
821 812
822 if world.config.vscode_lldb { 813 if r.args[0] == "run" {
823 if r.args[0] == "run" { 814 r.args[0] = "build".into();
824 r.args[0] = "build".into(); 815 } else {
825 } else { 816 r.args.push("--no-run".into());
826 r.args.push("--no-run".into());
827 }
828 let debug_lens = CodeLens {
829 range: r.range,
830 command: Some(Command {
831 title: "Debug".into(),
832 command: "rust-analyzer.debugSingle".into(),
833 arguments: Some(vec![to_value(r).unwrap()]),
834 }),
835 data: None,
836 };
837 lenses.push(debug_lens);
838 } 817 }
818 let debug_lens = CodeLens {
819 range: r.range,
820 command: Some(Command {
821 title: "Debug".into(),
822 command: "rust-analyzer.debugSingle".into(),
823 arguments: Some(vec![to_value(r).unwrap()]),
824 }),
825 data: None,
826 };
827 lenses.push(debug_lens);
839 } 828 }
840 829
841 // Handle impls 830 // Handle impls
@@ -970,15 +959,6 @@ pub fn publish_diagnostics(world: &WorldSnapshot, file_id: FileId) -> Result<Dia
970 Ok(DiagnosticTask::SetNative(file_id, diagnostics)) 959 Ok(DiagnosticTask::SetNative(file_id, diagnostics))
971} 960}
972 961
973pub fn publish_decorations(
974 world: &WorldSnapshot,
975 file_id: FileId,
976) -> Result<req::PublishDecorationsParams> {
977 let _p = profile("publish_decorations");
978 let uri = world.file_id_to_uri(file_id)?;
979 Ok(req::PublishDecorationsParams { uri, decorations: highlight(&world, file_id)? })
980}
981
982fn to_lsp_runnable( 962fn to_lsp_runnable(
983 world: &WorldSnapshot, 963 world: &WorldSnapshot,
984 file_id: FileId, 964 file_id: FileId,
@@ -1008,21 +988,6 @@ fn to_lsp_runnable(
1008 }) 988 })
1009} 989}
1010 990
1011fn highlight(world: &WorldSnapshot, file_id: FileId) -> Result<Vec<Decoration>> {
1012 let line_index = world.analysis().file_line_index(file_id)?;
1013 let res = world
1014 .analysis()
1015 .highlight(file_id)?
1016 .into_iter()
1017 .map(|h| Decoration {
1018 range: h.range.conv_with(&line_index),
1019 tag: h.highlight.to_string(),
1020 binding_hash: h.binding_hash.map(|x| x.to_string()),
1021 })
1022 .collect();
1023 Ok(res)
1024}
1025
1026pub fn handle_inlay_hints( 991pub fn handle_inlay_hints(
1027 world: WorldSnapshot, 992 world: WorldSnapshot,
1028 params: InlayHintsParams, 993 params: InlayHintsParams,
diff --git a/crates/rust-analyzer/src/main_loop/subscriptions.rs b/crates/rust-analyzer/src/main_loop/subscriptions.rs
index bee6437cf..2c76418be 100644
--- a/crates/rust-analyzer/src/main_loop/subscriptions.rs
+++ b/crates/rust-analyzer/src/main_loop/subscriptions.rs
@@ -17,6 +17,6 @@ impl Subscriptions {
17 self.subs.remove(&file_id); 17 self.subs.remove(&file_id);
18 } 18 }
19 pub(crate) fn subscriptions(&self) -> Vec<FileId> { 19 pub(crate) fn subscriptions(&self) -> Vec<FileId> {
20 self.subs.iter().cloned().collect() 20 self.subs.iter().copied().collect()
21 } 21 }
22} 22}
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs
index 994f0ed61..b8b627e28 100644
--- a/crates/rust-analyzer/src/req.rs
+++ b/crates/rust-analyzer/src/req.rs
@@ -1,13 +1,13 @@
1//! Defines `rust-analyzer` specific custom messages. 1//! Defines `rust-analyzer` specific custom messages.
2 2
3use lsp_types::{Location, Position, Range, TextDocumentIdentifier, Url}; 3use lsp_types::{Location, Position, Range, TextDocumentIdentifier};
4use rustc_hash::FxHashMap; 4use rustc_hash::FxHashMap;
5use serde::{Deserialize, Serialize}; 5use serde::{Deserialize, Serialize};
6 6
7pub use lsp_types::{ 7pub use lsp_types::{
8 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens, 8 notification::*, request::*, ApplyWorkspaceEditParams, CodeActionParams, CodeLens,
9 CodeLensParams, CompletionParams, CompletionResponse, ConfigurationParams, DiagnosticTag, 9 CodeLensParams, CompletionParams, CompletionResponse, ConfigurationItem, ConfigurationParams,
10 DidChangeConfigurationParams, DidChangeWatchedFilesParams, 10 DiagnosticTag, DidChangeConfigurationParams, DidChangeWatchedFilesParams,
11 DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams, 11 DidChangeWatchedFilesRegistrationOptions, DocumentOnTypeFormattingParams, DocumentSymbolParams,
12 DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType, 12 DocumentSymbolResponse, FileSystemWatcher, Hover, InitializeResult, MessageType,
13 PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken, 13 PartialResultParams, ProgressParams, ProgressParamsValue, ProgressToken,
@@ -86,36 +86,6 @@ pub struct FindMatchingBraceParams {
86 pub offsets: Vec<Position>, 86 pub offsets: Vec<Position>,
87} 87}
88 88
89pub enum DecorationsRequest {}
90
91impl Request for DecorationsRequest {
92 type Params = TextDocumentIdentifier;
93 type Result = Vec<Decoration>;
94 const METHOD: &'static str = "rust-analyzer/decorationsRequest";
95}
96
97pub enum PublishDecorations {}
98
99impl Notification for PublishDecorations {
100 type Params = PublishDecorationsParams;
101 const METHOD: &'static str = "rust-analyzer/publishDecorations";
102}
103
104#[derive(Deserialize, Serialize, Debug)]
105#[serde(rename_all = "camelCase")]
106pub struct PublishDecorationsParams {
107 pub uri: Url,
108 pub decorations: Vec<Decoration>,
109}
110
111#[derive(Deserialize, Serialize, Debug)]
112#[serde(rename_all = "camelCase")]
113pub struct Decoration {
114 pub range: Range,
115 pub tag: String,
116 pub binding_hash: Option<String>,
117}
118
119pub enum ParentModule {} 89pub enum ParentModule {}
120 90
121impl Request for ParentModule { 91impl Request for ParentModule {
diff --git a/crates/rust-analyzer/src/vfs_glob.rs b/crates/rust-analyzer/src/vfs_glob.rs
index 91b33f94e..ff37a7008 100644
--- a/crates/rust-analyzer/src/vfs_glob.rs
+++ b/crates/rust-analyzer/src/vfs_glob.rs
@@ -29,10 +29,14 @@ impl RustPackageFilterBuilder {
29 self.is_member = is_member; 29 self.is_member = is_member;
30 self 30 self
31 } 31 }
32 pub fn exclude(mut self, glob: Glob) -> RustPackageFilterBuilder { 32
33 self.exclude.add(glob); 33 pub fn exclude(mut self, globs: impl IntoIterator<Item = Glob>) -> RustPackageFilterBuilder {
34 for glob in globs.into_iter() {
35 self.exclude.add(glob);
36 }
34 self 37 self
35 } 38 }
39
36 pub fn into_vfs_filter(self) -> Box<dyn Filter> { 40 pub fn into_vfs_filter(self) -> Box<dyn Filter> {
37 let RustPackageFilterBuilder { is_member, mut exclude } = self; 41 let RustPackageFilterBuilder { is_member, mut exclude } = self;
38 for &glob in ALWAYS_IGNORED { 42 for &glob in ALWAYS_IGNORED {
@@ -87,7 +91,7 @@ fn test_globs() {
87 91
88 let filter = RustPackageFilterBuilder::default() 92 let filter = RustPackageFilterBuilder::default()
89 .set_member(true) 93 .set_member(true)
90 .exclude(Glob::new("src/llvm-project/**").unwrap()) 94 .exclude(std::iter::once(Glob::new("src/llvm-project/**").unwrap()))
91 .into_vfs_filter(); 95 .into_vfs_filter();
92 96
93 assert!(!filter.include_dir(RelativePath::new("src/llvm-project/clang"))); 97 assert!(!filter.include_dir(RelativePath::new("src/llvm-project/clang")));
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 5674f42ef..365f57d8c 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -87,44 +87,35 @@ impl WorldState {
87 ) -> WorldState { 87 ) -> WorldState {
88 let mut change = AnalysisChange::new(); 88 let mut change = AnalysisChange::new();
89 89
90 let mut roots = Vec::new(); 90 let extern_dirs: FxHashSet<_> =
91 roots.extend(folder_roots.iter().map(|path| { 91 workspaces.iter().flat_map(ProjectWorkspace::out_dirs).collect();
92 let mut filter = RustPackageFilterBuilder::default().set_member(true); 92
93 for glob in exclude_globs.iter() { 93 let roots: Vec<_> = {
94 filter = filter.exclude(glob.clone()); 94 let create_filter = |is_member| {
95 } 95 RustPackageFilterBuilder::default()
96 RootEntry::new(path.clone(), filter.into_vfs_filter()) 96 .set_member(is_member)
97 })); 97 .exclude(exclude_globs.iter().cloned())
98 for ws in workspaces.iter() { 98 .into_vfs_filter()
99 roots.extend(ws.to_roots().into_iter().map(|pkg_root| { 99 };
100 let mut filter = 100 folder_roots
101 RustPackageFilterBuilder::default().set_member(pkg_root.is_member()); 101 .iter()
102 for glob in exclude_globs.iter() { 102 .map(|path| RootEntry::new(path.clone(), create_filter(true)))
103 filter = filter.exclude(glob.clone()); 103 .chain(workspaces.iter().flat_map(ProjectWorkspace::to_roots).map(|pkg_root| {
104 } 104 RootEntry::new(pkg_root.path().to_owned(), create_filter(pkg_root.is_member()))
105 RootEntry::new(pkg_root.path().clone(), filter.into_vfs_filter()) 105 }))
106 })); 106 .chain(
107 } 107 extern_dirs
108 108 .iter()
109 let mut extern_dirs = FxHashSet::default(); 109 .map(|path| RootEntry::new(path.to_owned(), create_filter(false))),
110 for ws in workspaces.iter() { 110 )
111 extern_dirs.extend(ws.out_dirs()); 111 .collect()
112 } 112 };
113
114 let mut extern_source_roots = FxHashMap::default();
115
116 roots.extend(extern_dirs.iter().map(|path| {
117 let mut filter = RustPackageFilterBuilder::default().set_member(false);
118 for glob in exclude_globs.iter() {
119 filter = filter.exclude(glob.clone());
120 }
121 RootEntry::new(PathBuf::from(&path), filter.into_vfs_filter())
122 }));
123 113
124 let (task_sender, task_receiver) = unbounded(); 114 let (task_sender, task_receiver) = unbounded();
125 let task_sender = Box::new(move |t| task_sender.send(t).unwrap()); 115 let task_sender = Box::new(move |t| task_sender.send(t).unwrap());
126 let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch); 116 let (mut vfs, vfs_roots) = Vfs::new(roots, task_sender, watch);
127 117
118 let mut extern_source_roots = FxHashMap::default();
128 for r in vfs_roots { 119 for r in vfs_roots {
129 let vfs_root_path = vfs.root2path(r); 120 let vfs_root_path = vfs.root2path(r);
130 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it)); 121 let is_local = folder_roots.iter().any(|it| vfs_root_path.starts_with(it));