aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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
-rw-r--r--docs/dev/README.md10
-rw-r--r--docs/user/features.md6
-rw-r--r--editors/code/package-lock.json38
-rw-r--r--editors/code/package.json370
-rw-r--r--editors/code/ra_syntax_tree.tmGrammar.json31
-rw-r--r--editors/code/src/client.ts27
-rw-r--r--editors/code/src/commands/runnables.ts4
-rw-r--r--editors/code/src/commands/syntax_tree.ts111
-rw-r--r--editors/code/src/config.ts32
-rw-r--r--editors/code/src/ctx.ts3
-rw-r--r--editors/code/src/highlighting.ts255
-rw-r--r--editors/code/src/main.ts4
-rw-r--r--editors/code/src/status_display.ts2
32 files changed, 906 insertions, 840 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));
diff --git a/docs/dev/README.md b/docs/dev/README.md
index 8d7e18010..f230dc1db 100644
--- a/docs/dev/README.md
+++ b/docs/dev/README.md
@@ -155,6 +155,16 @@ There's also two VS Code commands which might be of interest:
155 155
156* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection. 156* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection.
157 157
158 You can hover over syntax nodes in the opened text file to see the appropriate
159 rust code that it refers to and the rust editor will also highlight the proper
160 text range.
161
162 If you press <kbd>Ctrl</kbd> (i.e. trigger goto definition) in the inspected
163 Rust source file the syntax tree read-only editor should scroll to and select the
164 appropriate syntax node token.
165
166 ![demo](https://user-images.githubusercontent.com/36276403/78225773-6636a480-74d3-11ea-9d9f-1c9d42da03b0.png)
167
158# Profiling 168# Profiling
159 169
160We have a built-in hierarchical profiler, you can enable it by using `RA_PROFILE` env-var: 170We have a built-in hierarchical profiler, you can enable it by using `RA_PROFILE` env-var:
diff --git a/docs/user/features.md b/docs/user/features.md
index 8aeec2e81..56d2969fd 100644
--- a/docs/user/features.md
+++ b/docs/user/features.md
@@ -81,12 +81,6 @@ Join selected lines into one, smartly fixing up whitespace and trailing commas.
81Shows the parse tree of the current file. It exists mostly for debugging 81Shows the parse tree of the current file. It exists mostly for debugging
82rust-analyzer itself. 82rust-analyzer itself.
83 83
84You can hover over syntax nodes in the opened text file to see the appropriate
85rust code that it refers to and the rust editor will also highlight the proper
86text range.
87
88<img src="https://user-images.githubusercontent.com/36276403/78043783-7425e180-737c-11ea-8653-b02b773c5aa1.png" alt="demo" height="200px" >
89
90#### Expand Macro Recursively 84#### Expand Macro Recursively
91 85
92Shows the full macro expansion of the macro at current cursor. 86Shows the full macro expansion of the macro at current cursor.
diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json
index 2d8b432a7..d5dec8fc5 100644
--- a/editors/code/package-lock.json
+++ b/editors/code/package-lock.json
@@ -459,12 +459,6 @@
459 "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=", 459 "integrity": "sha1-OjYof1A05pnnV3kBBSwubJQlFjE=",
460 "dev": true 460 "dev": true
461 }, 461 },
462 "didyoumean": {
463 "version": "1.2.1",
464 "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.1.tgz",
465 "integrity": "sha1-6S7f2tplN9SE1zwBcv0eugxJdv8=",
466 "dev": true
467 },
468 "doctrine": { 462 "doctrine": {
469 "version": "3.0.0", 463 "version": "3.0.0",
470 "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", 464 "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -1043,6 +1037,12 @@
1043 "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", 1037 "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz",
1044 "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==" 1038 "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w=="
1045 }, 1039 },
1040 "leven": {
1041 "version": "3.1.0",
1042 "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
1043 "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
1044 "dev": true
1045 },
1046 "levn": { 1046 "levn": {
1047 "version": "0.3.0", 1047 "version": "0.3.0",
1048 "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 1048 "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@@ -1088,16 +1088,24 @@
1088 } 1088 }
1089 }, 1089 },
1090 "markdown-it": { 1090 "markdown-it": {
1091 "version": "8.4.2", 1091 "version": "10.0.0",
1092 "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", 1092 "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
1093 "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", 1093 "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
1094 "dev": true, 1094 "dev": true,
1095 "requires": { 1095 "requires": {
1096 "argparse": "^1.0.7", 1096 "argparse": "^1.0.7",
1097 "entities": "~1.1.1", 1097 "entities": "~2.0.0",
1098 "linkify-it": "^2.0.0", 1098 "linkify-it": "^2.0.0",
1099 "mdurl": "^1.0.1", 1099 "mdurl": "^1.0.1",
1100 "uc.micro": "^1.0.5" 1100 "uc.micro": "^1.0.5"
1101 },
1102 "dependencies": {
1103 "entities": {
1104 "version": "2.0.0",
1105 "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
1106 "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==",
1107 "dev": true
1108 }
1101 } 1109 }
1102 }, 1110 },
1103 "mdurl": { 1111 "mdurl": {
@@ -1723,9 +1731,9 @@
1723 "dev": true 1731 "dev": true
1724 }, 1732 },
1725 "vsce": { 1733 "vsce": {
1726 "version": "1.74.0", 1734 "version": "1.75.0",
1727 "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.74.0.tgz", 1735 "resolved": "https://registry.npmjs.org/vsce/-/vsce-1.75.0.tgz",
1728 "integrity": "sha512-8zWM9bZBNn9my40kkxAxdY4nhb9ADfazXsyDgx1thbRaLPbmPTlmqQ55vCAyWYFEi6XbJv8w599vzVUqsU1gHg==", 1736 "integrity": "sha512-qyAQTmolxKWc9bV1z0yBTSH4WEIWhDueBJMKB0GUFD6lM4MiaU1zJ9BtzekUORZu094YeNSKz0RmVVuxfqPq0g==",
1729 "dev": true, 1737 "dev": true,
1730 "requires": { 1738 "requires": {
1731 "azure-devops-node-api": "^7.2.0", 1739 "azure-devops-node-api": "^7.2.0",
@@ -1733,10 +1741,10 @@
1733 "cheerio": "^1.0.0-rc.1", 1741 "cheerio": "^1.0.0-rc.1",
1734 "commander": "^2.8.1", 1742 "commander": "^2.8.1",
1735 "denodeify": "^1.2.1", 1743 "denodeify": "^1.2.1",
1736 "didyoumean": "^1.2.1",
1737 "glob": "^7.0.6", 1744 "glob": "^7.0.6",
1745 "leven": "^3.1.0",
1738 "lodash": "^4.17.15", 1746 "lodash": "^4.17.15",
1739 "markdown-it": "^8.3.1", 1747 "markdown-it": "^10.0.0",
1740 "mime": "^1.3.4", 1748 "mime": "^1.3.4",
1741 "minimatch": "^3.0.3", 1749 "minimatch": "^3.0.3",
1742 "osenv": "^0.1.3", 1750 "osenv": "^0.1.3",
diff --git a/editors/code/package.json b/editors/code/package.json
index 146b696e9..88b7ee86a 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -49,7 +49,7 @@
49 "tslib": "^1.11.1", 49 "tslib": "^1.11.1",
50 "typescript": "^3.8.3", 50 "typescript": "^3.8.3",
51 "typescript-formatter": "^7.2.2", 51 "typescript-formatter": "^7.2.2",
52 "vsce": "^1.74.0" 52 "vsce": "^1.75.0"
53 }, 53 },
54 "activationEvents": [ 54 "activationEvents": [
55 "onLanguage:rust", 55 "onLanguage:rust",
@@ -177,91 +177,70 @@
177 "type": "object", 177 "type": "object",
178 "title": "Rust Analyzer", 178 "title": "Rust Analyzer",
179 "properties": { 179 "properties": {
180 "rust-analyzer.highlighting.semanticTokens": { 180 "rust-analyzer.diagnostics.enable": {
181 "type": "boolean", 181 "type": "boolean",
182 "default": false, 182 "default": true,
183 "description": "Use proposed semantic tokens API for syntax highlighting" 183 "markdownDescription": "Whether to show native rust-analyzer diagnostics."
184 },
185 "rust-analyzer.highlightingOn": {
186 "type": "boolean",
187 "default": false,
188 "description": "Highlight Rust code (overrides built-in syntax highlighting)"
189 },
190 "rust-analyzer.rainbowHighlightingOn": {
191 "type": "boolean",
192 "default": false,
193 "description": "When highlighting Rust code, use a unique color per identifier"
194 }, 184 },
195 "rust-analyzer.featureFlags": { 185 "rust-analyzer.lruCapacity": {
196 "type": "object", 186 "type": [
197 "default": {}, 187 "null",
198 "description": "Fine grained feature flags to disable annoying features", 188 "integer"
199 "properties": { 189 ],
200 "lsp.diagnostics": { 190 "default": null,
201 "type": "boolean", 191 "minimum": 0,
202 "markdownDescription": "Whether to show diagnostics from `cargo check`" 192 "exclusiveMinimum": true,
203 }, 193 "description": "Number of syntax trees rust-analyzer keeps in memory."
204 "completion.insertion.add-call-parenthesis": {
205 "type": "boolean",
206 "description": "Whether to add parenthesis when completing functions"
207 },
208 "completion.insertion.add-argument-snippets": {
209 "type": "boolean",
210 "description": "Whether to add argument snippets when completing functions"
211 },
212 "completion.enable-postfix": {
213 "type": "boolean",
214 "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc."
215 },
216 "call-info.full": {
217 "type": "boolean",
218 "description": "Show function name and docs in parameter hints"
219 },
220 "notifications.workspace-loaded": {
221 "type": "boolean",
222 "markdownDescription": "Whether to show `workspace loaded` message"
223 },
224 "notifications.cargo-toml-not-found": {
225 "type": "boolean",
226 "markdownDescription": "Whether to show `can't find Cargo.toml` error message"
227 }
228 }
229 }, 194 },
230 "rust-analyzer.updates.channel": { 195 "rust-analyzer.files.watcher": {
231 "type": "string", 196 "type": "string",
232 "enum": [ 197 "enum": [
233 "stable", 198 "client",
234 "nightly" 199 "notify"
235 ],
236 "default": "stable",
237 "markdownEnumDescriptions": [
238 "`\"stable\"` updates are shipped weekly, they don't contain cutting-edge features from VSCode proposed APIs but have less bugs in general",
239 "`\"nightly\"` updates are shipped daily (extension updates automatically by downloading artifacts directly from GitHub), they contain cutting-edge features and latest bug fixes. These releases help us get your feedback very quickly and speed up rust-analyzer development **drastically**"
240 ], 200 ],
241 "markdownDescription": "Choose `\"nightly\"` updates to get the latest features and bug fixes every day. While `\"stable\"` releases occur weekly and don't contain cutting-edge features from VSCode proposed APIs" 201 "default": "client",
202 "description": "Controls file watching implementation."
242 }, 203 },
243 "rust-analyzer.updates.askBeforeDownload": { 204 "rust-analyzer.files.exclude": {
205 "type": "array",
206 "items": {
207 "type": "string"
208 },
209 "default": [],
210 "description": "Paths to exclude from analysis."
211 },
212 "rust-analyzer.notifications.workspaceLoaded": {
244 "type": "boolean", 213 "type": "boolean",
245 "default": true, 214 "markdownDescription": "Whether to show `workspace loaded` message."
246 "description": "Whether to ask for permission before downloading any files from the Internet"
247 }, 215 },
248 "rust-analyzer.serverPath": { 216 "rust-analyzer.notifications.cargoTomlNotFound": {
249 "type": [ 217 "type": "boolean",
250 "null", 218 "markdownDescription": "Whether to show `can't find Cargo.toml` error message"
251 "string" 219 },
252 ], 220 "rust-analyzer.cargo.noDefaultFeatures": {
253 "default": null, 221 "type": "boolean",
254 "description": "Path to rust-analyzer executable (points to bundled binary by default). If this is set, then \"rust-analyzer.updates.channel\" setting is not used" 222 "default": false,
223 "markdownDescription": "Do not activate the `default` feature"
224 },
225 "rust-analyzer.cargo.allFeatures": {
226 "type": "boolean",
227 "default": true,
228 "description": "Activate all available features"
255 }, 229 },
256 "rust-analyzer.excludeGlobs": { 230 "rust-analyzer.cargo.features": {
257 "type": "array", 231 "type": "array",
258 "items": { 232 "items": {
259 "type": "string" 233 "type": "string"
260 }, 234 },
261 "default": [], 235 "default": [],
262 "description": "Paths to exclude from analysis" 236 "description": "List of features to activate"
237 },
238 "rust-analyzer.cargo.loadOutDirsFromCheck": {
239 "type": "boolean",
240 "default": false,
241 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs"
263 }, 242 },
264 "rust-analyzer.rustfmtArgs": { 243 "rust-analyzer.rustfmt.extraArgs": {
265 "type": "array", 244 "type": "array",
266 "items": { 245 "items": {
267 "type": "string" 246 "type": "string"
@@ -269,64 +248,42 @@
269 "default": [], 248 "default": [],
270 "description": "Additional arguments to rustfmt" 249 "description": "Additional arguments to rustfmt"
271 }, 250 },
272 "rust-analyzer.useClientWatching": { 251 "rust-analyzer.rustfmt.overrideCommand": {
273 "type": "boolean", 252 "type": "array",
274 "default": true, 253 "items": {
275 "description": "client provided file watching instead of notify watching." 254 "type": "string"
255 },
256 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for formatting."
276 }, 257 },
277 "rust-analyzer.cargo-watch.enable": { 258 "rust-analyzer.checkOnSave.enable": {
278 "type": "boolean", 259 "type": "boolean",
279 "default": true, 260 "default": true,
280 "markdownDescription": "Run specified `cargo-watch` command for diagnostics on save" 261 "markdownDescription": "Run specified `cargo check` command for diagnostics on save"
281 }, 262 },
282 "rust-analyzer.cargo-watch.arguments": { 263 "rust-analyzer.checkOnSave.extraArgs": {
283 "type": "array", 264 "type": "array",
284 "items": { 265 "items": {
285 "type": "string" 266 "type": "string"
286 }, 267 },
287 "markdownDescription": "`cargo-watch` arguments. (e.g: `--features=\"shumway,pdf\"` will run as `cargo watch -x \"check --features=\"shumway,pdf\"\"` )", 268 "markdownDescription": "Extra arguments for `cargo check`",
288 "default": [] 269 "default": []
289 }, 270 },
290 "rust-analyzer.cargo-watch.command": { 271 "rust-analyzer.checkOnSave.command": {
291 "type": "string", 272 "type": "string",
292 "markdownDescription": "`cargo-watch` command. (e.g: `clippy` will run as `cargo watch -x clippy` )", 273 "default": "check",
293 "default": "check" 274 "markdownDescription": "Cargo command to use for `cargo check`"
294 }, 275 },
295 "rust-analyzer.cargo-watch.allTargets": { 276 "rust-analyzer.checkOnSave.overrideCommand": {
296 "type": "boolean", 277 "type": "array",
297 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)", 278 "items": {
298 "default": true 279 "type": "string"
299 }, 280 },
300 "rust-analyzer.trace.server": { 281 "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for checking. The command should include `--message=format=json` or similar option."
301 "type": "string",
302 "scope": "window",
303 "enum": [
304 "off",
305 "messages",
306 "verbose"
307 ],
308 "enumDescriptions": [
309 "No traces",
310 "Error only",
311 "Full log"
312 ],
313 "default": "off",
314 "description": "Trace requests to the rust-analyzer"
315 }, 282 },
316 "rust-analyzer.trace.extension": { 283 "rust-analyzer.checkOnSave.allTargets": {
317 "description": "Enable logging of VS Code extensions itself",
318 "type": "boolean", 284 "type": "boolean",
319 "default": false 285 "default": true,
320 }, 286 "markdownDescription": "Check all targets and tests (will be passed as `--all-targets`)"
321 "rust-analyzer.lruCapacity": {
322 "type": [
323 "null",
324 "integer"
325 ],
326 "default": null,
327 "minimum": 0,
328 "exclusiveMinimum": true,
329 "description": "Number of syntax trees rust-analyzer keeps in memory"
330 }, 287 },
331 "rust-analyzer.inlayHints.typeHints": { 288 "rust-analyzer.inlayHints.typeHints": {
332 "type": "boolean", 289 "type": "boolean",
@@ -353,28 +310,76 @@
353 "exclusiveMinimum": true, 310 "exclusiveMinimum": true,
354 "description": "Maximum length for inlay hints" 311 "description": "Maximum length for inlay hints"
355 }, 312 },
356 "rust-analyzer.cargoFeatures.noDefaultFeatures": { 313 "rust-analyzer.completion.addCallParenthesis": {
357 "type": "boolean", 314 "type": "boolean",
358 "default": false, 315 "default": true,
359 "markdownDescription": "Do not activate the `default` feature" 316 "description": "Whether to add parenthesis when completing functions"
360 }, 317 },
361 "rust-analyzer.cargoFeatures.allFeatures": { 318 "rust-analyzer.completion.addCallArgumentSnippets": {
362 "type": "boolean", 319 "type": "boolean",
363 "default": true, 320 "default": true,
364 "description": "Activate all available features" 321 "description": "Whether to add argument snippets when completing functions"
365 }, 322 },
366 "rust-analyzer.cargoFeatures.features": { 323 "rust-analyzer.completion.postfix.enable": {
367 "type": "array", 324 "type": "boolean",
368 "items": { 325 "default": true,
369 "type": "string" 326 "markdownDescription": "Whether to show postfix snippets like `dbg`, `if`, `not`, etc."
370 },
371 "default": [],
372 "description": "List of features to activate"
373 }, 327 },
374 "rust-analyzer.cargoFeatures.loadOutDirsFromCheck": { 328 "rust-analyzer.callInfo.full": {
329 "type": "boolean",
330 "description": "Show function name and docs in parameter hints"
331 },
332 "rust-analyzer.highlighting.semanticTokens": {
375 "type": "boolean", 333 "type": "boolean",
376 "default": false, 334 "default": false,
377 "markdownDescription": "Run `cargo check` on startup to get the correct value for package OUT_DIRs" 335 "description": "Use proposed semantic tokens API for syntax highlighting"
336 },
337 "rust-analyzer.updates.channel": {
338 "type": "string",
339 "enum": [
340 "stable",
341 "nightly"
342 ],
343 "default": "stable",
344 "markdownEnumDescriptions": [
345 "`\"stable\"` updates are shipped weekly, they don't contain cutting-edge features from VSCode proposed APIs but have less bugs in general",
346 "`\"nightly\"` updates are shipped daily (extension updates automatically by downloading artifacts directly from GitHub), they contain cutting-edge features and latest bug fixes. These releases help us get your feedback very quickly and speed up rust-analyzer development **drastically**"
347 ],
348 "markdownDescription": "Choose `\"nightly\"` updates to get the latest features and bug fixes every day. While `\"stable\"` releases occur weekly and don't contain cutting-edge features from VSCode proposed APIs"
349 },
350 "rust-analyzer.updates.askBeforeDownload": {
351 "type": "boolean",
352 "default": true,
353 "description": "Whether to ask for permission before downloading any files from the Internet"
354 },
355 "rust-analyzer.serverPath": {
356 "type": [
357 "null",
358 "string"
359 ],
360 "default": null,
361 "description": "Path to rust-analyzer executable (points to bundled binary by default). If this is set, then \"rust-analyzer.updates.channel\" setting is not used"
362 },
363 "rust-analyzer.trace.server": {
364 "type": "string",
365 "scope": "window",
366 "enum": [
367 "off",
368 "messages",
369 "verbose"
370 ],
371 "enumDescriptions": [
372 "No traces",
373 "Error only",
374 "Full log"
375 ],
376 "default": "off",
377 "description": "Trace requests to the rust-analyzer"
378 },
379 "rust-analyzer.trace.extension": {
380 "description": "Enable logging of VS Code extensions itself",
381 "type": "boolean",
382 "default": false
378 } 383 }
379 } 384 }
380 }, 385 },
@@ -411,6 +416,21 @@
411 ] 416 ]
412 } 417 }
413 ], 418 ],
419 "languages": [
420 {
421 "id": "ra_syntax_tree",
422 "extensions": [
423 ".rast"
424 ]
425 }
426 ],
427 "grammars": [
428 {
429 "language": "ra_syntax_tree",
430 "scopeName": "source.ra_syntax_tree",
431 "path": "ra_syntax_tree.tmGrammar.json"
432 }
433 ],
414 "problemMatchers": [ 434 "problemMatchers": [
415 { 435 {
416 "name": "rustc", 436 "name": "rustc",
@@ -450,6 +470,15 @@
450 "light": "#747474", 470 "light": "#747474",
451 "highContrast": "#BEBEBE" 471 "highContrast": "#BEBEBE"
452 } 472 }
473 },
474 {
475 "id": "rust_analyzer.syntaxTreeBorder",
476 "description": "Color of the border displayed in the Rust source code for the selected syntax node (see \"Show Syntax Tree\" command)",
477 "defaults": {
478 "dark": "#ffffff",
479 "light": "#b700ff",
480 "highContrast": "#b700ff"
481 }
453 } 482 }
454 ], 483 ],
455 "semanticTokenTypes": [ 484 "semanticTokenTypes": [
@@ -492,65 +521,34 @@
492 "description": "Style for unsafe operations" 521 "description": "Style for unsafe operations"
493 } 522 }
494 ], 523 ],
495 "semanticTokenStyleDefaults": [ 524 "semanticTokenScopes": [
496 { 525 {
497 "selector": "attribute", 526 "language": "rust",
498 "scope": [ 527 "scopes": {
499 "meta.attribute" 528 "attribute": [
500 ] 529 "meta.attribute"
501 }, 530 ],
502 { 531 "builtinType": [
503 "selector": "builtinType", 532 "support.type.primitive"
504 "scope": [ 533 ],
505 "support.type.primitive" 534 "lifetime": [
506 ] 535 "entity.name.lifetime.rust"
507 }, 536 ],
508 { 537 "typeAlias": [
509 "selector": "lifetime", 538 "entity.name.typeAlias"
510 "scope": [ 539 ],
511 "entity.name.lifetime.rust" 540 "union": [
512 ] 541 "entity.name.union"
513 }, 542 ],
514 { 543 "keyword.unsafe": [
515 "selector": "typeAlias", 544 "keyword.other.unsafe"
516 "scope": [ 545 ],
517 "entity.name.typeAlias" 546 "keyword.control": [
518 ] 547 "keyword.control"
519 }, 548 ],
520 { 549 "variable.constant": [
521 "selector": "union", 550 "entity.name.constant"
522 "scope": [ 551 ]
523 "entity.name.union"
524 ]
525 },
526 {
527 "selector": "keyword.unsafe",
528 "scope": [
529 "keyword.other.unsafe"
530 ]
531 },
532 {
533 "selector": "keyword.control",
534 "scope": [
535 "keyword.control"
536 ]
537 },
538 {
539 "selector": "variable.constant",
540 "scope": [
541 "entity.name.constant"
542 ]
543 },
544 {
545 "selector": "*.mutable",
546 "light": {
547 "fontStyle": "underline"
548 },
549 "dark": {
550 "fontStyle": "underline"
551 },
552 "highContrast": {
553 "fontStyle": "underline"
554 } 552 }
555 } 553 }
556 ] 554 ]
diff --git a/editors/code/ra_syntax_tree.tmGrammar.json b/editors/code/ra_syntax_tree.tmGrammar.json
new file mode 100644
index 000000000..0d72a3e36
--- /dev/null
+++ b/editors/code/ra_syntax_tree.tmGrammar.json
@@ -0,0 +1,31 @@
1{
2 "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
3
4 "scopeName": "source.ra_syntax_tree",
5 "patterns": [
6 { "include": "#node_type" },
7 { "include": "#node_range_index" },
8 { "include": "#token_text" }
9 ],
10 "repository": {
11 "node_type": {
12 "match": "^\\s*([A-Z_]+?)@",
13 "captures": {
14 "1": {
15 "name": "entity.name.class"
16 }
17 }
18 },
19 "node_range_index": {
20 "match": "\\d+",
21 "name": "constant.numeric"
22 },
23 "token_text": {
24 "match": "\".+\"",
25 "name": "string"
26 }
27 },
28 "fileTypes": [
29 "rast"
30 ]
31}
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index f909f8db2..3b1d00bca 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -5,31 +5,6 @@ import { Config } from './config';
5import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed'; 5import { CallHierarchyFeature } from 'vscode-languageclient/lib/callHierarchy.proposed';
6import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed'; 6import { SemanticTokensFeature, DocumentSemanticsTokensSignature } from 'vscode-languageclient/lib/semanticTokens.proposed';
7 7
8export function configToServerOptions(config: Config) {
9 return {
10 publishDecorations: !config.highlightingSemanticTokens,
11 lruCapacity: config.lruCapacity,
12
13 inlayHintsType: config.inlayHints.typeHints,
14 inlayHintsParameter: config.inlayHints.parameterHints,
15 inlayHintsChaining: config.inlayHints.chainingHints,
16 inlayHintsMaxLength: config.inlayHints.maxLength,
17
18 cargoWatchEnable: config.cargoWatchOptions.enable,
19 cargoWatchArgs: config.cargoWatchOptions.arguments,
20 cargoWatchCommand: config.cargoWatchOptions.command,
21 cargoWatchAllTargets: config.cargoWatchOptions.allTargets,
22
23 excludeGlobs: config.excludeGlobs,
24 useClientWatching: config.useClientWatching,
25 featureFlags: config.featureFlags,
26 withSysroot: config.withSysroot,
27 cargoFeatures: config.cargoFeatures,
28 rustfmtArgs: config.rustfmtArgs,
29 vscodeLldb: vscode.extensions.getExtension("vadimcn.vscode-lldb") != null,
30 };
31}
32
33export async function createClient(config: Config, serverPath: string, cwd: string): Promise<lc.LanguageClient> { 8export async function createClient(config: Config, serverPath: string, cwd: string): Promise<lc.LanguageClient> {
34 // '.' Is the fallback if no folder is open 9 // '.' Is the fallback if no folder is open
35 // TODO?: Workspace folders support Uri's (eg: file://test.txt). 10 // TODO?: Workspace folders support Uri's (eg: file://test.txt).
@@ -49,7 +24,7 @@ export async function createClient(config: Config, serverPath: string, cwd: stri
49 24
50 const clientOptions: lc.LanguageClientOptions = { 25 const clientOptions: lc.LanguageClientOptions = {
51 documentSelector: [{ scheme: 'file', language: 'rust' }], 26 documentSelector: [{ scheme: 'file', language: 'rust' }],
52 initializationOptions: configToServerOptions(config), 27 initializationOptions: vscode.workspace.getConfiguration("rust-analyzer"),
53 traceOutputChannel, 28 traceOutputChannel,
54 middleware: { 29 middleware: {
55 // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576 30 // Workaround for https://github.com/microsoft/vscode-languageserver-node/issues/576
diff --git a/editors/code/src/commands/runnables.ts b/editors/code/src/commands/runnables.ts
index 357155163..2635a1440 100644
--- a/editors/code/src/commands/runnables.ts
+++ b/editors/code/src/commands/runnables.ts
@@ -66,6 +66,10 @@ export function debugSingle(ctx: Ctx): Cmd {
66 return async (config: ra.Runnable) => { 66 return async (config: ra.Runnable) => {
67 const editor = ctx.activeRustEditor; 67 const editor = ctx.activeRustEditor;
68 if (!editor) return; 68 if (!editor) return;
69 if (!vscode.extensions.getExtension("vadimcn.vscode-lldb")) {
70 vscode.window.showErrorMessage("Install `vadimcn.vscode-lldb` extension for debugging");
71 return;
72 }
69 73
70 const debugConfig = { 74 const debugConfig = {
71 type: "lldb", 75 type: "lldb",
diff --git a/editors/code/src/commands/syntax_tree.ts b/editors/code/src/commands/syntax_tree.ts
index 996c7a716..b7a397414 100644
--- a/editors/code/src/commands/syntax_tree.ts
+++ b/editors/code/src/commands/syntax_tree.ts
@@ -15,6 +15,9 @@ export function syntaxTree(ctx: Ctx): Cmd {
15 void new AstInspector(ctx); 15 void new AstInspector(ctx);
16 16
17 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp)); 17 ctx.pushCleanup(vscode.workspace.registerTextDocumentContentProvider(AST_FILE_SCHEME, tdcp));
18 ctx.pushCleanup(vscode.languages.setLanguageConfiguration("ra_syntax_tree", {
19 brackets: [["[", ")"]],
20 }));
18 21
19 return async () => { 22 return async () => {
20 const editor = vscode.window.activeTextEditor; 23 const editor = vscode.window.activeTextEditor;
@@ -36,7 +39,7 @@ export function syntaxTree(ctx: Ctx): Cmd {
36} 39}
37 40
38class TextDocumentContentProvider implements vscode.TextDocumentContentProvider { 41class TextDocumentContentProvider implements vscode.TextDocumentContentProvider {
39 readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree'); 42 readonly uri = vscode.Uri.parse('rust-analyzer://syntaxtree/tree.rast');
40 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>(); 43 readonly eventEmitter = new vscode.EventEmitter<vscode.Uri>();
41 44
42 45
@@ -79,16 +82,41 @@ class TextDocumentContentProvider implements vscode.TextDocumentContentProvider
79 82
80// FIXME: consider implementing this via the Tree View API? 83// FIXME: consider implementing this via the Tree View API?
81// https://code.visualstudio.com/api/extension-guides/tree-view 84// https://code.visualstudio.com/api/extension-guides/tree-view
82class AstInspector implements vscode.HoverProvider, Disposable { 85class AstInspector implements vscode.HoverProvider, vscode.DefinitionProvider, Disposable {
83 private static readonly astDecorationType = vscode.window.createTextEditorDecorationType({ 86 private readonly astDecorationType = vscode.window.createTextEditorDecorationType({
84 fontStyle: "normal", 87 borderColor: new vscode.ThemeColor('rust_analyzer.syntaxTreeBorder'),
85 border: "#ffffff 1px solid", 88 borderStyle: "solid",
89 borderWidth: "2px",
90
86 }); 91 });
87 private rustEditor: undefined | RustEditor; 92 private rustEditor: undefined | RustEditor;
88 93
94 // Lazy rust token range -> syntax tree file range.
95 private readonly rust2Ast = new Lazy(() => {
96 const astEditor = this.findAstTextEditor();
97 if (!this.rustEditor || !astEditor) return undefined;
98
99 const buf: [vscode.Range, vscode.Range][] = [];
100 for (let i = 0; i < astEditor.document.lineCount; ++i) {
101 const astLine = astEditor.document.lineAt(i);
102
103 // Heuristically look for nodes with quoted text (which are token nodes)
104 const isTokenNode = astLine.text.lastIndexOf('"') >= 0;
105 if (!isTokenNode) continue;
106
107 const rustRange = this.parseRustTextRange(this.rustEditor.document, astLine.text);
108 if (!rustRange) continue;
109
110 buf.push([rustRange, this.findAstNodeRange(astLine)]);
111 }
112 return buf;
113 });
114
89 constructor(ctx: Ctx) { 115 constructor(ctx: Ctx) {
90 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this)); 116 ctx.pushCleanup(vscode.languages.registerHoverProvider({ scheme: AST_FILE_SCHEME }, this));
117 ctx.pushCleanup(vscode.languages.registerDefinitionProvider({ language: "rust" }, this));
91 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions); 118 vscode.workspace.onDidCloseTextDocument(this.onDidCloseTextDocument, this, ctx.subscriptions);
119 vscode.workspace.onDidChangeTextDocument(this.onDidChangeTextDocument, this, ctx.subscriptions);
92 vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions); 120 vscode.window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, ctx.subscriptions);
93 121
94 ctx.pushCleanup(this); 122 ctx.pushCleanup(this);
@@ -97,6 +125,12 @@ class AstInspector implements vscode.HoverProvider, Disposable {
97 this.setRustEditor(undefined); 125 this.setRustEditor(undefined);
98 } 126 }
99 127
128 private onDidChangeTextDocument(event: vscode.TextDocumentChangeEvent) {
129 if (this.rustEditor && event.document.uri.toString() === this.rustEditor.document.uri.toString()) {
130 this.rust2Ast.reset();
131 }
132 }
133
100 private onDidCloseTextDocument(doc: vscode.TextDocument) { 134 private onDidCloseTextDocument(doc: vscode.TextDocument) {
101 if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) { 135 if (this.rustEditor && doc.uri.toString() === this.rustEditor.document.uri.toString()) {
102 this.setRustEditor(undefined); 136 this.setRustEditor(undefined);
@@ -104,38 +138,67 @@ class AstInspector implements vscode.HoverProvider, Disposable {
104 } 138 }
105 139
106 private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) { 140 private onDidChangeVisibleTextEditors(editors: vscode.TextEditor[]) {
107 if (editors.every(suspect => suspect.document.uri.scheme !== AST_FILE_SCHEME)) { 141 if (!this.findAstTextEditor()) {
108 this.setRustEditor(undefined); 142 this.setRustEditor(undefined);
109 return; 143 return;
110 } 144 }
111 this.setRustEditor(editors.find(isRustEditor)); 145 this.setRustEditor(editors.find(isRustEditor));
112 } 146 }
113 147
148 private findAstTextEditor(): undefined | vscode.TextEditor {
149 return vscode.window.visibleTextEditors.find(it => it.document.uri.scheme === AST_FILE_SCHEME);
150 }
151
114 private setRustEditor(newRustEditor: undefined | RustEditor) { 152 private setRustEditor(newRustEditor: undefined | RustEditor) {
115 if (newRustEditor !== this.rustEditor) { 153 if (this.rustEditor && this.rustEditor !== newRustEditor) {
116 this.rustEditor?.setDecorations(AstInspector.astDecorationType, []); 154 this.rustEditor.setDecorations(this.astDecorationType, []);
155 this.rust2Ast.reset();
117 } 156 }
118 this.rustEditor = newRustEditor; 157 this.rustEditor = newRustEditor;
119 } 158 }
120 159
160 // additional positional params are omitted
161 provideDefinition(doc: vscode.TextDocument, pos: vscode.Position): vscode.ProviderResult<vscode.DefinitionLink[]> {
162 if (!this.rustEditor || doc.uri.toString() !== this.rustEditor.document.uri.toString()) return;
163
164 const astEditor = this.findAstTextEditor();
165 if (!astEditor) return;
166
167 const rust2AstRanges = this.rust2Ast.get()?.find(([rustRange, _]) => rustRange.contains(pos));
168 if (!rust2AstRanges) return;
169
170 const [rustFileRange, astFileRange] = rust2AstRanges;
171
172 astEditor.revealRange(astFileRange);
173 astEditor.selection = new vscode.Selection(astFileRange.start, astFileRange.end);
174
175 return [{
176 targetRange: astFileRange,
177 targetUri: astEditor.document.uri,
178 originSelectionRange: rustFileRange,
179 targetSelectionRange: astFileRange,
180 }];
181 }
182
183 // additional positional params are omitted
121 provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> { 184 provideHover(doc: vscode.TextDocument, hoverPosition: vscode.Position): vscode.ProviderResult<vscode.Hover> {
122 if (!this.rustEditor) return; 185 if (!this.rustEditor) return;
123 186
124 const astTextLine = doc.lineAt(hoverPosition.line); 187 const astFileLine = doc.lineAt(hoverPosition.line);
125 188
126 const rustTextRange = this.parseRustTextRange(this.rustEditor.document, astTextLine.text); 189 const rustFileRange = this.parseRustTextRange(this.rustEditor.document, astFileLine.text);
127 if (!rustTextRange) return; 190 if (!rustFileRange) return;
128 191
129 this.rustEditor.setDecorations(AstInspector.astDecorationType, [rustTextRange]); 192 this.rustEditor.setDecorations(this.astDecorationType, [rustFileRange]);
130 this.rustEditor.revealRange(rustTextRange); 193 this.rustEditor.revealRange(rustFileRange);
131 194
132 const rustSourceCode = this.rustEditor.document.getText(rustTextRange); 195 const rustSourceCode = this.rustEditor.document.getText(rustFileRange);
133 const astTextRange = this.findAstRange(astTextLine); 196 const astFileRange = this.findAstNodeRange(astFileLine);
134 197
135 return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astTextRange); 198 return new vscode.Hover(["```rust\n" + rustSourceCode + "\n```"], astFileRange);
136 } 199 }
137 200
138 private findAstRange(astLine: vscode.TextLine) { 201 private findAstNodeRange(astLine: vscode.TextLine) {
139 const lineOffset = astLine.range.start; 202 const lineOffset = astLine.range.start;
140 const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex); 203 const begin = lineOffset.translate(undefined, astLine.firstNonWhitespaceCharacterIndex);
141 const end = lineOffset.translate(undefined, astLine.text.trimEnd().length); 204 const end = lineOffset.translate(undefined, astLine.text.trimEnd().length);
@@ -151,3 +214,17 @@ class AstInspector implements vscode.HoverProvider, Disposable {
151 return new vscode.Range(begin, end); 214 return new vscode.Range(begin, end);
152 } 215 }
153} 216}
217
218class Lazy<T> {
219 val: undefined | T;
220
221 constructor(private readonly compute: () => undefined | T) { }
222
223 get() {
224 return this.val ?? (this.val = this.compute());
225 }
226
227 reset() {
228 this.val = undefined;
229 }
230}
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts
index 501997fef..1f45f1de0 100644
--- a/editors/code/src/config.ts
+++ b/editors/code/src/config.ts
@@ -11,9 +11,8 @@ export class Config {
11 private readonly rootSection = "rust-analyzer"; 11 private readonly rootSection = "rust-analyzer";
12 private readonly requiresReloadOpts = [ 12 private readonly requiresReloadOpts = [
13 "serverPath", 13 "serverPath",
14 "cargoFeatures", 14 "cargo",
15 "excludeGlobs", 15 "files",
16 "useClientWatching",
17 "highlighting", 16 "highlighting",
18 "updates.channel", 17 "updates.channel",
19 ] 18 ]
@@ -71,19 +70,8 @@ export class Config {
71 get channel() { return this.cfg.get<UpdatesChannel>("updates.channel")!; } 70 get channel() { return this.cfg.get<UpdatesChannel>("updates.channel")!; }
72 get askBeforeDownload() { return this.cfg.get<boolean>("updates.askBeforeDownload")!; } 71 get askBeforeDownload() { return this.cfg.get<boolean>("updates.askBeforeDownload")!; }
73 get highlightingSemanticTokens() { return this.cfg.get<boolean>("highlighting.semanticTokens")!; } 72 get highlightingSemanticTokens() { return this.cfg.get<boolean>("highlighting.semanticTokens")!; }
74 get highlightingOn() { return this.cfg.get<boolean>("highlightingOn")!; }
75 get rainbowHighlightingOn() { return this.cfg.get<boolean>("rainbowHighlightingOn")!; }
76 get lruCapacity() { return this.cfg.get<null | number>("lruCapacity")!; }
77 get excludeGlobs() { return this.cfg.get<string[]>("excludeGlobs")!; }
78 get useClientWatching() { return this.cfg.get<boolean>("useClientWatching")!; }
79 get featureFlags() { return this.cfg.get<Record<string, boolean>>("featureFlags")!; }
80 get rustfmtArgs() { return this.cfg.get<string[]>("rustfmtArgs")!; }
81 get loadOutDirsFromCheck() { return this.cfg.get<boolean>("loadOutDirsFromCheck")!; }
82 get traceExtension() { return this.cfg.get<boolean>("trace.extension")!; } 73 get traceExtension() { return this.cfg.get<boolean>("trace.extension")!; }
83 74
84 // for internal use
85 get withSysroot() { return this.cfg.get<boolean>("withSysroot", true)!; }
86
87 get inlayHints() { 75 get inlayHints() {
88 return { 76 return {
89 typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!, 77 typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!,
@@ -93,21 +81,9 @@ export class Config {
93 }; 81 };
94 } 82 }
95 83
96 get cargoWatchOptions() { 84 get checkOnSave() {
97 return {
98 enable: this.cfg.get<boolean>("cargo-watch.enable")!,
99 arguments: this.cfg.get<string[]>("cargo-watch.arguments")!,
100 allTargets: this.cfg.get<boolean>("cargo-watch.allTargets")!,
101 command: this.cfg.get<string>("cargo-watch.command")!,
102 };
103 }
104
105 get cargoFeatures() {
106 return { 85 return {
107 noDefaultFeatures: this.cfg.get<boolean>("cargoFeatures.noDefaultFeatures")!, 86 command: this.cfg.get<string>("checkOnSave.command")!,
108 allFeatures: this.cfg.get<boolean>("cargoFeatures.allFeatures")!,
109 features: this.cfg.get<string[]>("cargoFeatures.features")!,
110 loadOutDirsFromCheck: this.cfg.get<boolean>("cargoFeatures.loadOutDirsFromCheck")!,
111 }; 87 };
112 } 88 }
113} 89}
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 86b5f3629..bd1c3de07 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -2,7 +2,7 @@ import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3 3
4import { Config } from './config'; 4import { Config } from './config';
5import { createClient, configToServerOptions } from './client'; 5import { createClient } from './client';
6import { isRustEditor, RustEditor } from './util'; 6import { isRustEditor, RustEditor } from './util';
7 7
8export class Ctx { 8export class Ctx {
@@ -25,7 +25,6 @@ export class Ctx {
25 const res = new Ctx(config, extCtx, client, serverPath); 25 const res = new Ctx(config, extCtx, client, serverPath);
26 res.pushCleanup(client.start()); 26 res.pushCleanup(client.start());
27 await client.onReady(); 27 await client.onReady();
28 client.onRequest('workspace/configuration', _ => [configToServerOptions(config)]);
29 return res; 28 return res;
30 } 29 }
31 30
diff --git a/editors/code/src/highlighting.ts b/editors/code/src/highlighting.ts
deleted file mode 100644
index ea2dfc0e3..000000000
--- a/editors/code/src/highlighting.ts
+++ /dev/null
@@ -1,255 +0,0 @@
1import * as vscode from 'vscode';
2import * as ra from './rust-analyzer-api';
3
4import { ColorTheme, TextMateRuleSettings } from './color_theme';
5
6import { Ctx } from './ctx';
7import { sendRequestWithRetry, isRustDocument } from './util';
8
9export function activateHighlighting(ctx: Ctx) {
10 const highlighter = new Highlighter(ctx);
11
12 ctx.client.onNotification(ra.publishDecorations, params => {
13 if (!ctx.config.highlightingOn) return;
14
15 const targetEditor = vscode.window.visibleTextEditors.find(
16 editor => {
17 const unescapedUri = unescape(
18 editor.document.uri.toString(),
19 );
20 // Unescaped URI looks like:
21 // file:///c:/Workspace/ra-test/src/main.rs
22 return unescapedUri === params.uri;
23 },
24 );
25 if (!targetEditor) return;
26
27 highlighter.setHighlights(targetEditor, params.decorations);
28 });
29
30
31 vscode.workspace.onDidChangeConfiguration(
32 _ => highlighter.removeHighlights(),
33 null,
34 ctx.subscriptions,
35 );
36
37 vscode.window.onDidChangeActiveTextEditor(
38 async (editor: vscode.TextEditor | undefined) => {
39 if (!editor || !isRustDocument(editor.document)) return;
40 if (!ctx.config.highlightingOn) return;
41 const client = ctx.client;
42 if (!client) return;
43
44 const decorations = await sendRequestWithRetry(
45 client,
46 ra.decorationsRequest,
47 { uri: editor.document.uri.toString() },
48 );
49 highlighter.setHighlights(editor, decorations);
50 },
51 null,
52 ctx.subscriptions,
53 );
54}
55
56// Based on this HSL-based color generator: https://gist.github.com/bendc/76c48ce53299e6078a76
57function fancify(seed: string, shade: 'light' | 'dark') {
58 const random = randomU32Numbers(hashString(seed));
59 const randomInt = (min: number, max: number) => {
60 return Math.abs(random()) % (max - min + 1) + min;
61 };
62
63 const h = randomInt(0, 360);
64 const s = randomInt(42, 98);
65 const l = shade === 'light' ? randomInt(15, 40) : randomInt(40, 90);
66 return `hsl(${h},${s}%,${l}%)`;
67}
68
69class Highlighter {
70 private ctx: Ctx;
71 private decorations: Map<
72 string,
73 vscode.TextEditorDecorationType
74 > | null = null;
75
76 constructor(ctx: Ctx) {
77 this.ctx = ctx;
78 }
79
80 public removeHighlights() {
81 if (this.decorations == null) {
82 return;
83 }
84
85 // Decorations are removed when the object is disposed
86 for (const decoration of this.decorations.values()) {
87 decoration.dispose();
88 }
89
90 this.decorations = null;
91 }
92
93 public setHighlights(editor: vscode.TextEditor, highlights: ra.Decoration[]) {
94 const client = this.ctx.client;
95 if (!client) return;
96 // Initialize decorations if necessary
97 //
98 // Note: decoration objects need to be kept around so we can dispose them
99 // if the user disables syntax highlighting
100 if (this.decorations == null) {
101 this.decorations = initDecorations();
102 }
103
104 const byTag: Map<string, vscode.Range[]> = new Map();
105 const colorfulIdents: Map<
106 string,
107 [vscode.Range[], boolean]
108 > = new Map();
109 const rainbowTime = this.ctx.config.rainbowHighlightingOn;
110
111 for (const tag of this.decorations.keys()) {
112 byTag.set(tag, []);
113 }
114
115 for (const d of highlights) {
116 if (!byTag.get(d.tag)) {
117 continue;
118 }
119
120 if (rainbowTime && d.bindingHash) {
121 if (!colorfulIdents.has(d.bindingHash)) {
122 const mut = d.tag.endsWith('.mut');
123 colorfulIdents.set(d.bindingHash, [[], mut]);
124 }
125 colorfulIdents
126 .get(d.bindingHash)![0]
127 .push(
128 client.protocol2CodeConverter.asRange(d.range),
129 );
130 } else {
131 byTag
132 .get(d.tag)!
133 .push(
134 client.protocol2CodeConverter.asRange(d.range),
135 );
136 }
137 }
138
139 for (const tag of byTag.keys()) {
140 const dec = this.decorations.get(
141 tag,
142 ) as vscode.TextEditorDecorationType;
143 const ranges = byTag.get(tag)!;
144 editor.setDecorations(dec, ranges);
145 }
146
147 for (const [hash, [ranges, mut]] of colorfulIdents.entries()) {
148 const textDecoration = mut ? 'underline' : undefined;
149 const dec = vscode.window.createTextEditorDecorationType({
150 light: { color: fancify(hash, 'light'), textDecoration },
151 dark: { color: fancify(hash, 'dark'), textDecoration },
152 });
153 editor.setDecorations(dec, ranges);
154 }
155 }
156}
157
158function initDecorations(): Map<string, vscode.TextEditorDecorationType> {
159 const theme = ColorTheme.load();
160 const res = new Map();
161 TAG_TO_SCOPES.forEach((scopes, tag) => {
162 // We are going to axe this soon, so don't try to detect unknown tags.
163 // Users should switch to the new semantic tokens implementation.
164 if (!scopes) return;
165 const rule = theme.lookup(scopes);
166 const decor = createDecorationFromTextmate(rule);
167 res.set(tag, decor);
168 });
169 return res;
170}
171
172function createDecorationFromTextmate(
173 themeStyle: TextMateRuleSettings,
174): vscode.TextEditorDecorationType {
175 const decorationOptions: vscode.DecorationRenderOptions = {};
176 decorationOptions.rangeBehavior = vscode.DecorationRangeBehavior.OpenOpen;
177
178 if (themeStyle.foreground) {
179 decorationOptions.color = themeStyle.foreground;
180 }
181
182 if (themeStyle.background) {
183 decorationOptions.backgroundColor = themeStyle.background;
184 }
185
186 if (themeStyle.fontStyle) {
187 const parts: string[] = themeStyle.fontStyle.split(' ');
188 parts.forEach(part => {
189 switch (part) {
190 case 'italic':
191 decorationOptions.fontStyle = 'italic';
192 break;
193 case 'bold':
194 decorationOptions.fontWeight = 'bold';
195 break;
196 case 'underline':
197 decorationOptions.textDecoration = 'underline';
198 break;
199 default:
200 break;
201 }
202 });
203 }
204 return vscode.window.createTextEditorDecorationType(decorationOptions);
205}
206
207// sync with tags from `syntax_highlighting.rs`.
208const TAG_TO_SCOPES = new Map<string, string[]>([
209 ["field", ["entity.name.field"]],
210 ["function", ["entity.name.function"]],
211 ["module", ["entity.name.module"]],
212 ["constant", ["entity.name.constant"]],
213 ["macro", ["entity.name.macro"]],
214
215 ["variable", ["variable"]],
216 ["variable.mutable", ["variable", "meta.mutable"]],
217
218 ["type", ["entity.name.type"]],
219 ["type.builtin", ["entity.name.type", "support.type.primitive"]],
220 ["type.self", ["entity.name.type.parameter.self"]],
221 ["type.param", ["entity.name.type.parameter", "entity.name.type.param.rust"]],
222 ["type.lifetime", ["entity.name.type.lifetime", "entity.name.lifetime.rust"]],
223
224 ["literal.byte", ["constant.character.byte"]],
225 ["literal.char", ["constant.character.rust"]],
226 ["numeric_literal", ["constant.numeric"]],
227
228 ["comment", ["comment"]],
229 ["string_literal", ["string.quoted"]],
230 ["attribute", ["meta.attribute.rust"]],
231
232 ["keyword", ["keyword"]],
233 ["keyword.unsafe", ["keyword.other.unsafe"]],
234 ["keyword.control", ["keyword.control"]],
235]);
236
237function randomU32Numbers(seed: number) {
238 let random = seed | 0;
239 return () => {
240 random ^= random << 13;
241 random ^= random >> 17;
242 random ^= random << 5;
243 random |= 0;
244 return random;
245 };
246}
247
248function hashString(str: string): number {
249 let res = 0;
250 for (let i = 0; i < str.length; ++i) {
251 const c = str.codePointAt(i)!;
252 res = (res * 31 + c) & ~0;
253 }
254 return res;
255}
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts
index 7ba16120c..4f3b89f44 100644
--- a/editors/code/src/main.ts
+++ b/editors/code/src/main.ts
@@ -7,7 +7,6 @@ import * as commands from './commands';
7import { activateInlayHints } from './inlay_hints'; 7import { activateInlayHints } from './inlay_hints';
8import { activateStatusDisplay } from './status_display'; 8import { activateStatusDisplay } from './status_display';
9import { Ctx } from './ctx'; 9import { Ctx } from './ctx';
10import { activateHighlighting } from './highlighting';
11import { Config, NIGHTLY_TAG } from './config'; 10import { Config, NIGHTLY_TAG } from './config';
12import { log, assert } from './util'; 11import { log, assert } from './util';
13import { PersistentState } from './persistent_state'; 12import { PersistentState } from './persistent_state';
@@ -97,9 +96,6 @@ export async function activate(context: vscode.ExtensionContext) {
97 96
98 activateStatusDisplay(ctx); 97 activateStatusDisplay(ctx);
99 98
100 if (!ctx.config.highlightingSemanticTokens) {
101 activateHighlighting(ctx);
102 }
103 activateInlayHints(ctx); 99 activateInlayHints(ctx);
104 100
105 vscode.workspace.onDidChangeConfiguration( 101 vscode.workspace.onDidChangeConfiguration(
diff --git a/editors/code/src/status_display.ts b/editors/code/src/status_display.ts
index 0f5f6ef99..f9cadc8a2 100644
--- a/editors/code/src/status_display.ts
+++ b/editors/code/src/status_display.ts
@@ -7,7 +7,7 @@ import { Ctx } from './ctx';
7const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; 7const spinnerFrames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
8 8
9export function activateStatusDisplay(ctx: Ctx) { 9export function activateStatusDisplay(ctx: Ctx) {
10 const statusDisplay = new StatusDisplay(ctx.config.cargoWatchOptions.command); 10 const statusDisplay = new StatusDisplay(ctx.config.checkOnSave.command);
11 ctx.pushCleanup(statusDisplay); 11 ctx.pushCleanup(statusDisplay);
12 const client = ctx.client; 12 const client = ctx.client;
13 if (client != null) { 13 if (client != null) {