diff options
author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2020-03-24 23:28:57 +0000 |
---|---|---|
committer | GitHub <[email protected]> | 2020-03-24 23:28:57 +0000 |
commit | 6ad1a0711631d8017791a6dfe85bbe205d6c7414 (patch) | |
tree | ce2d6448ff8770c8fe73086eefb85dab38d298d8 | |
parent | fae627174aecae0b4f4d2c087a856eda1a97a1ac (diff) | |
parent | 7b35da04bf56a5461321a6dca515dcd29f44b57f (diff) |
Merge #3710
3710: Inlay hints for method chaining pattern r=matklad a=M-J-Hooper
This PR adds inlay hints on method call chains:
![image](https://user-images.githubusercontent.com/13765376/77472008-8dc2a880-6e13-11ea-9c18-2c2e2b809799.png)
It is not only explicit `MethodCall`s where this can be helpful. The heuristic used here is that whenever any expression is followed by a new line and then a dot, it resembles a call chain and type information can be #useful.
Changes:
- A new `InlayKind` for chaining.
- New option for disabling this type of hints.
- Tree traversal rules for identifying the chaining hints.
- VSCode decorators in the extension layer (and associated types).
Notes:
- IntelliJ has additional rules and configuration on this topic. Eg. minimum length of chain to start displaying hints and only displaying distinct types in the chain.
- I am checking for chaining on every `ast::Expr` in the tree; Are there performance concerns there?
This is my first contribution (to RA and to Rust in general) so would appreciate any feedback.
The only issue I can find the references this feature is #2741.
Co-authored-by: Matt Hooper <[email protected]>
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 176 | ||||
-rw-r--r-- | crates/rust-analyzer/src/config.rs | 3 | ||||
-rw-r--r-- | crates/rust-analyzer/src/conv.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 1 | ||||
-rw-r--r-- | crates/rust-analyzer/src/req.rs | 1 | ||||
-rw-r--r-- | docs/user/features.md | 2 | ||||
-rw-r--r-- | editors/code/package.json | 5 | ||||
-rw-r--r-- | editors/code/src/client.ts | 1 | ||||
-rw-r--r-- | editors/code/src/config.ts | 1 | ||||
-rw-r--r-- | editors/code/src/inlay_hints.ts | 32 | ||||
-rw-r--r-- | editors/code/src/rust-analyzer-api.ts | 4 |
11 files changed, 218 insertions, 9 deletions
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index ecd615cf4..f4f0751c0 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -5,7 +5,7 @@ use ra_ide_db::RootDatabase; | |||
5 | use ra_prof::profile; | 5 | use ra_prof::profile; |
6 | use ra_syntax::{ | 6 | use ra_syntax::{ |
7 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, | 7 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, |
8 | match_ast, SmolStr, TextRange, | 8 | match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{FileId, FunctionSignature}; | 11 | use crate::{FileId, FunctionSignature}; |
@@ -14,12 +14,13 @@ use crate::{FileId, FunctionSignature}; | |||
14 | pub struct InlayHintsOptions { | 14 | pub struct InlayHintsOptions { |
15 | pub type_hints: bool, | 15 | pub type_hints: bool, |
16 | pub parameter_hints: bool, | 16 | pub parameter_hints: bool, |
17 | pub chaining_hints: bool, | ||
17 | pub max_length: Option<usize>, | 18 | pub max_length: Option<usize>, |
18 | } | 19 | } |
19 | 20 | ||
20 | impl Default for InlayHintsOptions { | 21 | impl Default for InlayHintsOptions { |
21 | fn default() -> Self { | 22 | fn default() -> Self { |
22 | Self { type_hints: true, parameter_hints: true, max_length: None } | 23 | Self { type_hints: true, parameter_hints: true, chaining_hints: true, max_length: None } |
23 | } | 24 | } |
24 | } | 25 | } |
25 | 26 | ||
@@ -27,6 +28,7 @@ impl Default for InlayHintsOptions { | |||
27 | pub enum InlayKind { | 28 | pub enum InlayKind { |
28 | TypeHint, | 29 | TypeHint, |
29 | ParameterHint, | 30 | ParameterHint, |
31 | ChainingHint, | ||
30 | } | 32 | } |
31 | 33 | ||
32 | #[derive(Debug)] | 34 | #[derive(Debug)] |
@@ -47,6 +49,10 @@ pub(crate) fn inlay_hints( | |||
47 | 49 | ||
48 | let mut res = Vec::new(); | 50 | let mut res = Vec::new(); |
49 | for node in file.syntax().descendants() { | 51 | for node in file.syntax().descendants() { |
52 | if let Some(expr) = ast::Expr::cast(node.clone()) { | ||
53 | get_chaining_hints(&mut res, &sema, options, expr); | ||
54 | } | ||
55 | |||
50 | match_ast! { | 56 | match_ast! { |
51 | match node { | 57 | match node { |
52 | ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, | 58 | ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, |
@@ -59,6 +65,46 @@ pub(crate) fn inlay_hints( | |||
59 | res | 65 | res |
60 | } | 66 | } |
61 | 67 | ||
68 | fn get_chaining_hints( | ||
69 | acc: &mut Vec<InlayHint>, | ||
70 | sema: &Semantics<RootDatabase>, | ||
71 | options: &InlayHintsOptions, | ||
72 | expr: ast::Expr, | ||
73 | ) -> Option<()> { | ||
74 | if !options.chaining_hints { | ||
75 | return None; | ||
76 | } | ||
77 | |||
78 | let ty = sema.type_of_expr(&expr)?; | ||
79 | if ty.is_unknown() { | ||
80 | return None; | ||
81 | } | ||
82 | |||
83 | let mut tokens = expr | ||
84 | .syntax() | ||
85 | .siblings_with_tokens(Direction::Next) | ||
86 | .filter_map(NodeOrToken::into_token) | ||
87 | .filter(|t| match t.kind() { | ||
88 | SyntaxKind::WHITESPACE if !t.text().contains('\n') => false, | ||
89 | SyntaxKind::COMMENT => false, | ||
90 | _ => true, | ||
91 | }); | ||
92 | |||
93 | // Chaining can be defined as an expression whose next sibling tokens are newline and dot | ||
94 | // Ignoring extra whitespace and comments | ||
95 | let next = tokens.next()?.kind(); | ||
96 | let next_next = tokens.next()?.kind(); | ||
97 | if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { | ||
98 | let label = ty.display_truncated(sema.db, options.max_length).to_string(); | ||
99 | acc.push(InlayHint { | ||
100 | range: expr.syntax().text_range(), | ||
101 | kind: InlayKind::ChainingHint, | ||
102 | label: label.into(), | ||
103 | }); | ||
104 | } | ||
105 | Some(()) | ||
106 | } | ||
107 | |||
62 | fn get_param_name_hints( | 108 | fn get_param_name_hints( |
63 | acc: &mut Vec<InlayHint>, | 109 | acc: &mut Vec<InlayHint>, |
64 | sema: &Semantics<RootDatabase>, | 110 | sema: &Semantics<RootDatabase>, |
@@ -238,7 +284,7 @@ mod tests { | |||
238 | let _x = foo(4, 4); | 284 | let _x = foo(4, 4); |
239 | }"#, | 285 | }"#, |
240 | ); | 286 | ); |
241 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, max_length: None}).unwrap(), @r###" | 287 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" |
242 | [ | 288 | [ |
243 | InlayHint { | 289 | InlayHint { |
244 | range: [106; 107), | 290 | range: [106; 107), |
@@ -262,7 +308,7 @@ mod tests { | |||
262 | let _x = foo(4, 4); | 308 | let _x = foo(4, 4); |
263 | }"#, | 309 | }"#, |
264 | ); | 310 | ); |
265 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, max_length: None}).unwrap(), @r###"[]"###); | 311 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###); |
266 | } | 312 | } |
267 | 313 | ||
268 | #[test] | 314 | #[test] |
@@ -274,7 +320,7 @@ mod tests { | |||
274 | let _x = foo(4, 4); | 320 | let _x = foo(4, 4); |
275 | }"#, | 321 | }"#, |
276 | ); | 322 | ); |
277 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, max_length: None}).unwrap(), @r###" | 323 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" |
278 | [ | 324 | [ |
279 | InlayHint { | 325 | InlayHint { |
280 | range: [97; 99), | 326 | range: [97; 99), |
@@ -1052,4 +1098,124 @@ fn main() { | |||
1052 | "### | 1098 | "### |
1053 | ); | 1099 | ); |
1054 | } | 1100 | } |
1101 | |||
1102 | #[test] | ||
1103 | fn chaining_hints_ignore_comments() { | ||
1104 | let (analysis, file_id) = single_file( | ||
1105 | r#" | ||
1106 | struct A(B); | ||
1107 | impl A { fn into_b(self) -> B { self.0 } } | ||
1108 | struct B(C); | ||
1109 | impl B { fn into_c(self) -> C { self.0 } } | ||
1110 | struct C; | ||
1111 | |||
1112 | fn main() { | ||
1113 | let c = A(B(C)) | ||
1114 | .into_b() // This is a comment | ||
1115 | .into_c(); | ||
1116 | }"#, | ||
1117 | ); | ||
1118 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" | ||
1119 | [ | ||
1120 | InlayHint { | ||
1121 | range: [232; 269), | ||
1122 | kind: ChainingHint, | ||
1123 | label: "B", | ||
1124 | }, | ||
1125 | InlayHint { | ||
1126 | range: [232; 239), | ||
1127 | kind: ChainingHint, | ||
1128 | label: "A", | ||
1129 | }, | ||
1130 | ]"###); | ||
1131 | } | ||
1132 | |||
1133 | #[test] | ||
1134 | fn chaining_hints_without_newlines() { | ||
1135 | let (analysis, file_id) = single_file( | ||
1136 | r#" | ||
1137 | struct A(B); | ||
1138 | impl A { fn into_b(self) -> B { self.0 } } | ||
1139 | struct B(C); | ||
1140 | impl B { fn into_c(self) -> C { self.0 } } | ||
1141 | struct C; | ||
1142 | |||
1143 | fn main() { | ||
1144 | let c = A(B(C)).into_b().into_c(); | ||
1145 | }"#, | ||
1146 | ); | ||
1147 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###); | ||
1148 | } | ||
1149 | |||
1150 | #[test] | ||
1151 | fn struct_access_chaining_hints() { | ||
1152 | let (analysis, file_id) = single_file( | ||
1153 | r#" | ||
1154 | struct A { pub b: B } | ||
1155 | struct B { pub c: C } | ||
1156 | struct C(pub bool); | ||
1157 | |||
1158 | fn main() { | ||
1159 | let x = A { b: B { c: C(true) } } | ||
1160 | .b | ||
1161 | .c | ||
1162 | .0; | ||
1163 | }"#, | ||
1164 | ); | ||
1165 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" | ||
1166 | [ | ||
1167 | InlayHint { | ||
1168 | range: [150; 221), | ||
1169 | kind: ChainingHint, | ||
1170 | label: "C", | ||
1171 | }, | ||
1172 | InlayHint { | ||
1173 | range: [150; 198), | ||
1174 | kind: ChainingHint, | ||
1175 | label: "B", | ||
1176 | }, | ||
1177 | InlayHint { | ||
1178 | range: [150; 175), | ||
1179 | kind: ChainingHint, | ||
1180 | label: "A", | ||
1181 | }, | ||
1182 | ]"###); | ||
1183 | } | ||
1184 | |||
1185 | #[test] | ||
1186 | fn generic_chaining_hints() { | ||
1187 | let (analysis, file_id) = single_file( | ||
1188 | r#" | ||
1189 | struct A<T>(T); | ||
1190 | struct B<T>(T); | ||
1191 | struct C<T>(T); | ||
1192 | struct X<T,R>(T, R); | ||
1193 | |||
1194 | impl<T> A<T> { | ||
1195 | fn new(t: T) -> Self { A(t) } | ||
1196 | fn into_b(self) -> B<T> { B(self.0) } | ||
1197 | } | ||
1198 | impl<T> B<T> { | ||
1199 | fn into_c(self) -> C<T> { C(self.0) } | ||
1200 | } | ||
1201 | fn main() { | ||
1202 | let c = A::new(X(42, true)) | ||
1203 | .into_b() | ||
1204 | .into_c(); | ||
1205 | }"#, | ||
1206 | ); | ||
1207 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" | ||
1208 | [ | ||
1209 | InlayHint { | ||
1210 | range: [403; 452), | ||
1211 | kind: ChainingHint, | ||
1212 | label: "B<X<i32, bool>>", | ||
1213 | }, | ||
1214 | InlayHint { | ||
1215 | range: [403; 422), | ||
1216 | kind: ChainingHint, | ||
1217 | label: "A<X<i32, bool>>", | ||
1218 | }, | ||
1219 | ]"###); | ||
1220 | } | ||
1055 | } | 1221 | } |
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 103b2b53c..628ed107e 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs | |||
@@ -34,6 +34,8 @@ pub struct ServerConfig { | |||
34 | pub inlay_hints_type: bool, | 34 | pub inlay_hints_type: bool, |
35 | #[serde(deserialize_with = "nullable_bool_true")] | 35 | #[serde(deserialize_with = "nullable_bool_true")] |
36 | pub inlay_hints_parameter: bool, | 36 | pub inlay_hints_parameter: bool, |
37 | #[serde(deserialize_with = "nullable_bool_true")] | ||
38 | pub inlay_hints_chaining: bool, | ||
37 | pub inlay_hints_max_length: Option<usize>, | 39 | pub inlay_hints_max_length: Option<usize>, |
38 | 40 | ||
39 | pub cargo_watch_enable: bool, | 41 | pub cargo_watch_enable: bool, |
@@ -66,6 +68,7 @@ impl Default for ServerConfig { | |||
66 | lru_capacity: None, | 68 | lru_capacity: None, |
67 | inlay_hints_type: true, | 69 | inlay_hints_type: true, |
68 | inlay_hints_parameter: true, | 70 | inlay_hints_parameter: true, |
71 | inlay_hints_chaining: true, | ||
69 | inlay_hints_max_length: None, | 72 | inlay_hints_max_length: None, |
70 | cargo_watch_enable: true, | 73 | cargo_watch_enable: true, |
71 | cargo_watch_args: Vec::new(), | 74 | cargo_watch_args: Vec::new(), |
diff --git a/crates/rust-analyzer/src/conv.rs b/crates/rust-analyzer/src/conv.rs index fd4657d7e..6edc03fe0 100644 --- a/crates/rust-analyzer/src/conv.rs +++ b/crates/rust-analyzer/src/conv.rs | |||
@@ -332,6 +332,7 @@ impl ConvWith<&LineIndex> for InlayHint { | |||
332 | kind: match self.kind { | 332 | kind: match self.kind { |
333 | InlayKind::ParameterHint => req::InlayKind::ParameterHint, | 333 | InlayKind::ParameterHint => req::InlayKind::ParameterHint, |
334 | InlayKind::TypeHint => req::InlayKind::TypeHint, | 334 | InlayKind::TypeHint => req::InlayKind::TypeHint, |
335 | InlayKind::ChainingHint => req::InlayKind::ChainingHint, | ||
335 | }, | 336 | }, |
336 | } | 337 | } |
337 | } | 338 | } |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index a8a5894d2..7825b0077 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -183,6 +183,7 @@ pub fn main_loop( | |||
183 | inlay_hints: InlayHintsOptions { | 183 | inlay_hints: InlayHintsOptions { |
184 | type_hints: config.inlay_hints_type, | 184 | type_hints: config.inlay_hints_type, |
185 | parameter_hints: config.inlay_hints_parameter, | 185 | parameter_hints: config.inlay_hints_parameter, |
186 | chaining_hints: config.inlay_hints_chaining, | ||
186 | max_length: config.inlay_hints_max_length, | 187 | max_length: config.inlay_hints_max_length, |
187 | }, | 188 | }, |
188 | cargo_watch: CheckOptions { | 189 | cargo_watch: CheckOptions { |
diff --git a/crates/rust-analyzer/src/req.rs b/crates/rust-analyzer/src/req.rs index 9e27d3f1c..8557294f6 100644 --- a/crates/rust-analyzer/src/req.rs +++ b/crates/rust-analyzer/src/req.rs | |||
@@ -200,6 +200,7 @@ pub struct InlayHintsParams { | |||
200 | pub enum InlayKind { | 200 | pub enum InlayKind { |
201 | TypeHint, | 201 | TypeHint, |
202 | ParameterHint, | 202 | ParameterHint, |
203 | ChainingHint, | ||
203 | } | 204 | } |
204 | 205 | ||
205 | #[derive(Debug, Deserialize, Serialize)] | 206 | #[derive(Debug, Deserialize, Serialize)] |
diff --git a/docs/user/features.md b/docs/user/features.md index 45360c633..56d2969fd 100644 --- a/docs/user/features.md +++ b/docs/user/features.md | |||
@@ -185,6 +185,7 @@ These contain extended information on the hovered language item. | |||
185 | Two types of inlay hints are displayed currently: | 185 | Two types of inlay hints are displayed currently: |
186 | 186 | ||
187 | * type hints, displaying the minimal information on the type of the expression (if the information is available) | 187 | * type hints, displaying the minimal information on the type of the expression (if the information is available) |
188 | * method chaining hints, type information for multi-line method chains | ||
188 | * parameter name hints, displaying the names of the parameters in the corresponding methods | 189 | * parameter name hints, displaying the names of the parameters in the corresponding methods |
189 | 190 | ||
190 | #### VS Code | 191 | #### VS Code |
@@ -192,6 +193,7 @@ Two types of inlay hints are displayed currently: | |||
192 | In VS Code, the following settings can be used to configure the inlay hints: | 193 | In VS Code, the following settings can be used to configure the inlay hints: |
193 | 194 | ||
194 | * `rust-analyzer.inlayHints.typeHints` - enable hints for inferred types. | 195 | * `rust-analyzer.inlayHints.typeHints` - enable hints for inferred types. |
196 | * `rust-analyzer.inlayHints.chainingHints` - enable hints for inferred types on method chains. | ||
195 | * `rust-analyzer.inlayHints.parameterHints` - enable hints for function parameters. | 197 | * `rust-analyzer.inlayHints.parameterHints` - enable hints for function parameters. |
196 | * `rust-analyzer.inlayHints.maxLength` — shortens the hints if their length exceeds the value specified. If no value is specified (`null`), no shortening is applied. | 198 | * `rust-analyzer.inlayHints.maxLength` — shortens the hints if their length exceeds the value specified. If no value is specified (`null`), no shortening is applied. |
197 | 199 | ||
diff --git a/editors/code/package.json b/editors/code/package.json index 1d113ebb6..37e083220 100644 --- a/editors/code/package.json +++ b/editors/code/package.json | |||
@@ -333,6 +333,11 @@ | |||
333 | "default": true, | 333 | "default": true, |
334 | "description": "Whether to show inlay type hints" | 334 | "description": "Whether to show inlay type hints" |
335 | }, | 335 | }, |
336 | "rust-analyzer.inlayHints.chainingHints": { | ||
337 | "type": "boolean", | ||
338 | "default": true, | ||
339 | "description": "Whether to show inlay type hints for method chains" | ||
340 | }, | ||
336 | "rust-analyzer.inlayHints.parameterHints": { | 341 | "rust-analyzer.inlayHints.parameterHints": { |
337 | "type": "boolean", | 342 | "type": "boolean", |
338 | "default": true, | 343 | "default": true, |
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 82ca749f3..98f2f232f 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts | |||
@@ -32,6 +32,7 @@ export async function createClient(config: Config, serverPath: string): Promise< | |||
32 | 32 | ||
33 | inlayHintsType: config.inlayHints.typeHints, | 33 | inlayHintsType: config.inlayHints.typeHints, |
34 | inlayHintsParameter: config.inlayHints.parameterHints, | 34 | inlayHintsParameter: config.inlayHints.parameterHints, |
35 | inlayHintsChaining: config.inlayHints.chainingHints, | ||
35 | inlayHintsMaxLength: config.inlayHints.maxLength, | 36 | inlayHintsMaxLength: config.inlayHints.maxLength, |
36 | 37 | ||
37 | cargoWatchEnable: cargoWatchOpts.enable, | 38 | cargoWatchEnable: cargoWatchOpts.enable, |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 7668c20b7..637aea27d 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -88,6 +88,7 @@ export class Config { | |||
88 | return { | 88 | return { |
89 | typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!, | 89 | typeHints: this.cfg.get<boolean>("inlayHints.typeHints")!, |
90 | parameterHints: this.cfg.get<boolean>("inlayHints.parameterHints")!, | 90 | parameterHints: this.cfg.get<boolean>("inlayHints.parameterHints")!, |
91 | chainingHints: this.cfg.get<boolean>("inlayHints.chainingHints")!, | ||
91 | maxLength: this.cfg.get<null | number>("inlayHints.maxLength")!, | 92 | maxLength: this.cfg.get<null | number>("inlayHints.maxLength")!, |
92 | }; | 93 | }; |
93 | } | 94 | } |
diff --git a/editors/code/src/inlay_hints.ts b/editors/code/src/inlay_hints.ts index 17d0dfa33..542d1f367 100644 --- a/editors/code/src/inlay_hints.ts +++ b/editors/code/src/inlay_hints.ts | |||
@@ -10,7 +10,11 @@ export function activateInlayHints(ctx: Ctx) { | |||
10 | const maybeUpdater = { | 10 | const maybeUpdater = { |
11 | updater: null as null | HintsUpdater, | 11 | updater: null as null | HintsUpdater, |
12 | onConfigChange() { | 12 | onConfigChange() { |
13 | if (!ctx.config.inlayHints.typeHints && !ctx.config.inlayHints.parameterHints) { | 13 | if ( |
14 | !ctx.config.inlayHints.typeHints && | ||
15 | !ctx.config.inlayHints.parameterHints && | ||
16 | !ctx.config.inlayHints.chainingHints | ||
17 | ) { | ||
14 | return this.dispose(); | 18 | return this.dispose(); |
15 | } | 19 | } |
16 | if (!this.updater) this.updater = new HintsUpdater(ctx); | 20 | if (!this.updater) this.updater = new HintsUpdater(ctx); |
@@ -63,6 +67,22 @@ const paramHints = { | |||
63 | } | 67 | } |
64 | }; | 68 | }; |
65 | 69 | ||
70 | const chainingHints = { | ||
71 | decorationType: vscode.window.createTextEditorDecorationType({ | ||
72 | after: { | ||
73 | color: new vscode.ThemeColor('rust_analyzer.inlayHint'), | ||
74 | fontStyle: "normal", | ||
75 | } | ||
76 | }), | ||
77 | |||
78 | toDecoration(hint: ra.InlayHint.ChainingHint, conv: lc.Protocol2CodeConverter): vscode.DecorationOptions { | ||
79 | return { | ||
80 | range: conv.asRange(hint.range), | ||
81 | renderOptions: { after: { contentText: ` ${hint.label}` } } | ||
82 | }; | ||
83 | } | ||
84 | }; | ||
85 | |||
66 | class HintsUpdater implements Disposable { | 86 | class HintsUpdater implements Disposable { |
67 | private sourceFiles = new Map<string, RustSourceFile>(); // map Uri -> RustSourceFile | 87 | private sourceFiles = new Map<string, RustSourceFile>(); // map Uri -> RustSourceFile |
68 | private readonly disposables: Disposable[] = []; | 88 | private readonly disposables: Disposable[] = []; |
@@ -95,7 +115,7 @@ class HintsUpdater implements Disposable { | |||
95 | 115 | ||
96 | dispose() { | 116 | dispose() { |
97 | this.sourceFiles.forEach(file => file.inlaysRequest?.cancel()); | 117 | this.sourceFiles.forEach(file => file.inlaysRequest?.cancel()); |
98 | this.ctx.visibleRustEditors.forEach(editor => this.renderDecorations(editor, { param: [], type: [] })); | 118 | this.ctx.visibleRustEditors.forEach(editor => this.renderDecorations(editor, { param: [], type: [], chaining: [] })); |
99 | this.disposables.forEach(d => d.dispose()); | 119 | this.disposables.forEach(d => d.dispose()); |
100 | } | 120 | } |
101 | 121 | ||
@@ -154,10 +174,11 @@ class HintsUpdater implements Disposable { | |||
154 | private renderDecorations(editor: RustEditor, decorations: InlaysDecorations) { | 174 | private renderDecorations(editor: RustEditor, decorations: InlaysDecorations) { |
155 | editor.setDecorations(typeHints.decorationType, decorations.type); | 175 | editor.setDecorations(typeHints.decorationType, decorations.type); |
156 | editor.setDecorations(paramHints.decorationType, decorations.param); | 176 | editor.setDecorations(paramHints.decorationType, decorations.param); |
177 | editor.setDecorations(chainingHints.decorationType, decorations.chaining); | ||
157 | } | 178 | } |
158 | 179 | ||
159 | private hintsToDecorations(hints: ra.InlayHint[]): InlaysDecorations { | 180 | private hintsToDecorations(hints: ra.InlayHint[]): InlaysDecorations { |
160 | const decorations: InlaysDecorations = { type: [], param: [] }; | 181 | const decorations: InlaysDecorations = { type: [], param: [], chaining: [] }; |
161 | const conv = this.ctx.client.protocol2CodeConverter; | 182 | const conv = this.ctx.client.protocol2CodeConverter; |
162 | 183 | ||
163 | for (const hint of hints) { | 184 | for (const hint of hints) { |
@@ -170,6 +191,10 @@ class HintsUpdater implements Disposable { | |||
170 | decorations.param.push(paramHints.toDecoration(hint, conv)); | 191 | decorations.param.push(paramHints.toDecoration(hint, conv)); |
171 | continue; | 192 | continue; |
172 | } | 193 | } |
194 | case ra.InlayHint.Kind.ChainingHint: { | ||
195 | decorations.chaining.push(chainingHints.toDecoration(hint, conv)); | ||
196 | continue; | ||
197 | } | ||
173 | } | 198 | } |
174 | } | 199 | } |
175 | return decorations; | 200 | return decorations; |
@@ -196,6 +221,7 @@ class HintsUpdater implements Disposable { | |||
196 | interface InlaysDecorations { | 221 | interface InlaysDecorations { |
197 | type: vscode.DecorationOptions[]; | 222 | type: vscode.DecorationOptions[]; |
198 | param: vscode.DecorationOptions[]; | 223 | param: vscode.DecorationOptions[]; |
224 | chaining: vscode.DecorationOptions[]; | ||
199 | } | 225 | } |
200 | 226 | ||
201 | interface RustSourceFile { | 227 | interface RustSourceFile { |
diff --git a/editors/code/src/rust-analyzer-api.ts b/editors/code/src/rust-analyzer-api.ts index 9846f7343..400ac3714 100644 --- a/editors/code/src/rust-analyzer-api.ts +++ b/editors/code/src/rust-analyzer-api.ts | |||
@@ -86,12 +86,13 @@ export interface Runnable { | |||
86 | } | 86 | } |
87 | export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables"); | 87 | export const runnables = request<RunnablesParams, Vec<Runnable>>("runnables"); |
88 | 88 | ||
89 | export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint; | 89 | export type InlayHint = InlayHint.TypeHint | InlayHint.ParamHint | InlayHint.ChainingHint; |
90 | 90 | ||
91 | export namespace InlayHint { | 91 | export namespace InlayHint { |
92 | export const enum Kind { | 92 | export const enum Kind { |
93 | TypeHint = "TypeHint", | 93 | TypeHint = "TypeHint", |
94 | ParamHint = "ParameterHint", | 94 | ParamHint = "ParameterHint", |
95 | ChainingHint = "ChainingHint", | ||
95 | } | 96 | } |
96 | interface Common { | 97 | interface Common { |
97 | range: lc.Range; | 98 | range: lc.Range; |
@@ -99,6 +100,7 @@ export namespace InlayHint { | |||
99 | } | 100 | } |
100 | export type TypeHint = Common & { kind: Kind.TypeHint }; | 101 | export type TypeHint = Common & { kind: Kind.TypeHint }; |
101 | export type ParamHint = Common & { kind: Kind.ParamHint }; | 102 | export type ParamHint = Common & { kind: Kind.ParamHint }; |
103 | export type ChainingHint = Common & { kind: Kind.ChainingHint }; | ||
102 | } | 104 | } |
103 | export interface InlayHintsParams { | 105 | export interface InlayHintsParams { |
104 | textDocument: lc.TextDocumentIdentifier; | 106 | textDocument: lc.TextDocumentIdentifier; |