diff options
author | Matt Hooper <[email protected]> | 2020-03-23 19:32:05 +0000 |
---|---|---|
committer | Matt Hooper <[email protected]> | 2020-03-24 22:18:42 +0000 |
commit | a197abbc7aed53c42cac7e9e86787e44a5026291 (patch) | |
tree | b3fc1a2055a2071edfdbb03c9591a91b31c01c32 /crates | |
parent | fae627174aecae0b4f4d2c087a856eda1a97a1ac (diff) |
Added new inlay hint kind and rules for method chaining
Diffstat (limited to 'crates')
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 97 | ||||
-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 |
5 files changed, 98 insertions, 5 deletions
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index ecd615cf4..2353ad71f 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, SmolStr, TextRange, NodeOrToken, SyntaxKind, Direction |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use crate::{FileId, FunctionSignature}; | 11 | use crate::{FileId, FunctionSignature}; |
@@ -14,12 +14,18 @@ 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 { |
24 | type_hints: true, | ||
25 | parameter_hints: true, | ||
26 | chaining_hints: true, | ||
27 | max_length: None | ||
28 | } | ||
23 | } | 29 | } |
24 | } | 30 | } |
25 | 31 | ||
@@ -27,6 +33,7 @@ impl Default for InlayHintsOptions { | |||
27 | pub enum InlayKind { | 33 | pub enum InlayKind { |
28 | TypeHint, | 34 | TypeHint, |
29 | ParameterHint, | 35 | ParameterHint, |
36 | ChainingHint, | ||
30 | } | 37 | } |
31 | 38 | ||
32 | #[derive(Debug)] | 39 | #[derive(Debug)] |
@@ -47,6 +54,10 @@ pub(crate) fn inlay_hints( | |||
47 | 54 | ||
48 | let mut res = Vec::new(); | 55 | let mut res = Vec::new(); |
49 | for node in file.syntax().descendants() { | 56 | for node in file.syntax().descendants() { |
57 | if let Some(expr) = ast::Expr::cast(node.clone()) { | ||
58 | get_chaining_hints(&mut res, &sema, options, expr); | ||
59 | } | ||
60 | |||
50 | match_ast! { | 61 | match_ast! { |
51 | match node { | 62 | match node { |
52 | ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, | 63 | ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, |
@@ -222,6 +233,45 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option< | |||
222 | } | 233 | } |
223 | } | 234 | } |
224 | 235 | ||
236 | fn get_chaining_hints( | ||
237 | acc: &mut Vec<InlayHint>, | ||
238 | sema: &Semantics<RootDatabase>, | ||
239 | options: &InlayHintsOptions, | ||
240 | expr: ast::Expr, | ||
241 | ) -> Option<()> { | ||
242 | if !options.chaining_hints { | ||
243 | return None; | ||
244 | } | ||
245 | |||
246 | let ty = sema.type_of_expr(&expr)?; | ||
247 | let label = ty.display_truncated(sema.db, options.max_length).to_string(); | ||
248 | if ty.is_unknown() { | ||
249 | return None; | ||
250 | } | ||
251 | |||
252 | let mut tokens = expr.syntax() | ||
253 | .siblings_with_tokens(Direction::Next) | ||
254 | .filter_map(NodeOrToken::into_token) | ||
255 | .filter(|t| match t.kind() { | ||
256 | SyntaxKind::WHITESPACE if !t.text().contains('\n') => false, | ||
257 | SyntaxKind::COMMENT => false, | ||
258 | _ => true, | ||
259 | }); | ||
260 | |||
261 | // Chaining can be defined as an expression whose next sibling tokens are newline and dot | ||
262 | // Ignoring extra whitespace and comments | ||
263 | let next = tokens.next()?.kind(); | ||
264 | let next_next = tokens.next()?.kind(); | ||
265 | if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { | ||
266 | acc.push(InlayHint { | ||
267 | range: expr.syntax().text_range(), | ||
268 | kind: InlayKind::ChainingHint, | ||
269 | label: label.into(), | ||
270 | }); | ||
271 | } | ||
272 | Some(()) | ||
273 | } | ||
274 | |||
225 | #[cfg(test)] | 275 | #[cfg(test)] |
226 | mod tests { | 276 | mod tests { |
227 | use crate::inlay_hints::InlayHintsOptions; | 277 | use crate::inlay_hints::InlayHintsOptions; |
@@ -230,6 +280,43 @@ mod tests { | |||
230 | use crate::mock_analysis::single_file; | 280 | use crate::mock_analysis::single_file; |
231 | 281 | ||
232 | #[test] | 282 | #[test] |
283 | fn generic_chaining_hints() { | ||
284 | let (analysis, file_id) = single_file( | ||
285 | r#" | ||
286 | struct A<T>(T); | ||
287 | struct B<T>(T); | ||
288 | struct C<T>(T); | ||
289 | struct X<T,R>(T, R); | ||
290 | |||
291 | impl<T> A<T> { | ||
292 | fn new(t: T) -> Self { A(t) } | ||
293 | fn into_b(self) -> B<T> { B(self.0) } | ||
294 | } | ||
295 | impl<T> B<T> { | ||
296 | fn into_c(self) -> C<T> { C(self.0) } | ||
297 | } | ||
298 | fn test() { | ||
299 | let c = A::new(X(42, true)) | ||
300 | .into_b() // All the from A -> B -> C | ||
301 | .into_c(); | ||
302 | }"#, | ||
303 | ); | ||
304 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" | ||
305 | [ | ||
306 | InlayHint { | ||
307 | range: [416; 465), | ||
308 | kind: ChainingHint, | ||
309 | label: "B<X<i32, bool>>", | ||
310 | }, | ||
311 | InlayHint { | ||
312 | range: [416; 435), | ||
313 | kind: ChainingHint, | ||
314 | label: "A<X<i32, bool>>", | ||
315 | }, | ||
316 | ]"###); | ||
317 | } | ||
318 | |||
319 | #[test] | ||
233 | fn param_hints_only() { | 320 | fn param_hints_only() { |
234 | let (analysis, file_id) = single_file( | 321 | let (analysis, file_id) = single_file( |
235 | r#" | 322 | r#" |
@@ -238,7 +325,7 @@ mod tests { | |||
238 | let _x = foo(4, 4); | 325 | let _x = foo(4, 4); |
239 | }"#, | 326 | }"#, |
240 | ); | 327 | ); |
241 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, max_length: None}).unwrap(), @r###" | 328 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" |
242 | [ | 329 | [ |
243 | InlayHint { | 330 | InlayHint { |
244 | range: [106; 107), | 331 | range: [106; 107), |
@@ -262,7 +349,7 @@ mod tests { | |||
262 | let _x = foo(4, 4); | 349 | let _x = foo(4, 4); |
263 | }"#, | 350 | }"#, |
264 | ); | 351 | ); |
265 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, max_length: None}).unwrap(), @r###"[]"###); | 352 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###); |
266 | } | 353 | } |
267 | 354 | ||
268 | #[test] | 355 | #[test] |
@@ -274,7 +361,7 @@ mod tests { | |||
274 | let _x = foo(4, 4); | 361 | let _x = foo(4, 4); |
275 | }"#, | 362 | }"#, |
276 | ); | 363 | ); |
277 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, max_length: None}).unwrap(), @r###" | 364 | assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" |
278 | [ | 365 | [ |
279 | InlayHint { | 366 | InlayHint { |
280 | range: [97; 99), | 367 | range: [97; 99), |
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)] |