From a197abbc7aed53c42cac7e9e86787e44a5026291 Mon Sep 17 00:00:00 2001 From: Matt Hooper Date: Mon, 23 Mar 2020 20:32:05 +0100 Subject: Added new inlay hint kind and rules for method chaining --- crates/ra_ide/src/inlay_hints.rs | 97 +++++++++++++++++++++++++++++++++-- crates/rust-analyzer/src/config.rs | 3 ++ crates/rust-analyzer/src/conv.rs | 1 + crates/rust-analyzer/src/main_loop.rs | 1 + 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; use ra_prof::profile; use ra_syntax::{ ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, - match_ast, SmolStr, TextRange, + match_ast, SmolStr, TextRange, NodeOrToken, SyntaxKind, Direction }; use crate::{FileId, FunctionSignature}; @@ -14,12 +14,18 @@ use crate::{FileId, FunctionSignature}; pub struct InlayHintsOptions { pub type_hints: bool, pub parameter_hints: bool, + pub chaining_hints: bool, pub max_length: Option, } impl Default for InlayHintsOptions { fn default() -> Self { - Self { type_hints: true, parameter_hints: true, max_length: None } + Self { + type_hints: true, + parameter_hints: true, + chaining_hints: true, + max_length: None + } } } @@ -27,6 +33,7 @@ impl Default for InlayHintsOptions { pub enum InlayKind { TypeHint, ParameterHint, + ChainingHint, } #[derive(Debug)] @@ -47,6 +54,10 @@ pub(crate) fn inlay_hints( let mut res = Vec::new(); for node in file.syntax().descendants() { + if let Some(expr) = ast::Expr::cast(node.clone()) { + get_chaining_hints(&mut res, &sema, options, expr); + } + match_ast! { match node { 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, expr: &ast::Expr) -> Option< } } +fn get_chaining_hints( + acc: &mut Vec, + sema: &Semantics, + options: &InlayHintsOptions, + expr: ast::Expr, +) -> Option<()> { + if !options.chaining_hints { + return None; + } + + let ty = sema.type_of_expr(&expr)?; + let label = ty.display_truncated(sema.db, options.max_length).to_string(); + if ty.is_unknown() { + return None; + } + + let mut tokens = expr.syntax() + .siblings_with_tokens(Direction::Next) + .filter_map(NodeOrToken::into_token) + .filter(|t| match t.kind() { + SyntaxKind::WHITESPACE if !t.text().contains('\n') => false, + SyntaxKind::COMMENT => false, + _ => true, + }); + + // Chaining can be defined as an expression whose next sibling tokens are newline and dot + // Ignoring extra whitespace and comments + let next = tokens.next()?.kind(); + let next_next = tokens.next()?.kind(); + if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::ChainingHint, + label: label.into(), + }); + } + Some(()) +} + #[cfg(test)] mod tests { use crate::inlay_hints::InlayHintsOptions; @@ -229,6 +279,43 @@ mod tests { use crate::mock_analysis::single_file; + #[test] + fn generic_chaining_hints() { + let (analysis, file_id) = single_file( + r#" + struct A(T); + struct B(T); + struct C(T); + struct X(T, R); + + impl A { + fn new(t: T) -> Self { A(t) } + fn into_b(self) -> B { B(self.0) } + } + impl B { + fn into_c(self) -> C { C(self.0) } + } + fn test() { + let c = A::new(X(42, true)) + .into_b() // All the from A -> B -> C + .into_c(); + }"#, + ); + assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" + [ + InlayHint { + range: [416; 465), + kind: ChainingHint, + label: "B>", + }, + InlayHint { + range: [416; 435), + kind: ChainingHint, + label: "A>", + }, + ]"###); + } + #[test] fn param_hints_only() { let (analysis, file_id) = single_file( @@ -238,7 +325,7 @@ mod tests { let _x = foo(4, 4); }"#, ); - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, max_length: None}).unwrap(), @r###" + assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" [ InlayHint { range: [106; 107), @@ -262,7 +349,7 @@ mod tests { let _x = foo(4, 4); }"#, ); - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, max_length: None}).unwrap(), @r###"[]"###); + assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###); } #[test] @@ -274,7 +361,7 @@ mod tests { let _x = foo(4, 4); }"#, ); - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, max_length: None}).unwrap(), @r###" + assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" [ InlayHint { 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 { pub inlay_hints_type: bool, #[serde(deserialize_with = "nullable_bool_true")] pub inlay_hints_parameter: bool, + #[serde(deserialize_with = "nullable_bool_true")] + pub inlay_hints_chaining: bool, pub inlay_hints_max_length: Option, pub cargo_watch_enable: bool, @@ -66,6 +68,7 @@ impl Default for ServerConfig { lru_capacity: None, inlay_hints_type: true, inlay_hints_parameter: true, + inlay_hints_chaining: true, inlay_hints_max_length: None, cargo_watch_enable: true, 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 { kind: match self.kind { InlayKind::ParameterHint => req::InlayKind::ParameterHint, InlayKind::TypeHint => req::InlayKind::TypeHint, + InlayKind::ChainingHint => req::InlayKind::ChainingHint, }, } } 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( inlay_hints: InlayHintsOptions { type_hints: config.inlay_hints_type, parameter_hints: config.inlay_hints_parameter, + chaining_hints: config.inlay_hints_chaining, max_length: config.inlay_hints_max_length, }, 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 { pub enum InlayKind { TypeHint, ParameterHint, + ChainingHint, } #[derive(Debug, Deserialize, Serialize)] -- cgit v1.2.3