From 5169a9d498c82ffd0a24a4cc640eb8232a3f7e21 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Fri, 26 Jul 2019 14:10:29 +0300 Subject: Improve inlay hinting for types Add hints for types in for loop expressions. Resolve types for every tuple parameter. Refactor the code. --- crates/ra_ide_api/src/inlay_hints.rs | 182 ++++++++++++++++--------- crates/ra_lsp_server/src/main_loop/handlers.rs | 3 + crates/ra_lsp_server/src/req.rs | 1 + 3 files changed, 125 insertions(+), 61 deletions(-) diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs index 174662beb..95289f016 100644 --- a/crates/ra_ide_api/src/inlay_hints.rs +++ b/crates/ra_ide_api/src/inlay_hints.rs @@ -1,5 +1,5 @@ use crate::{db::RootDatabase, FileId}; -use hir::{HirDisplay, Ty}; +use hir::{HirDisplay, SourceAnalyzer, Ty}; use ra_syntax::ast::Pat; use ra_syntax::{ algo::visit::{visitor, Visitor}, @@ -7,10 +7,11 @@ use ra_syntax::{ AstNode, SmolStr, SourceFile, SyntaxNode, TextRange, }; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum InlayKind { LetBindingType, ClosureParameterType, + ForExpressionBindingType, } #[derive(Debug)] @@ -35,67 +36,89 @@ fn get_inlay_hints( ) -> Option> { visitor() .visit(|let_statement: ast::LetStmt| { - let let_syntax = let_statement.syntax(); - if let_statement.ascribed_type().is_some() { return None; } - - let let_pat = let_statement.pat()?; - let inlay_type_string = get_node_displayable_type(db, file_id, let_syntax, &let_pat)? - .display(db) - .to_string() - .into(); - - let pat_range = match let_pat.kind() { - PatKind::BindPat(bind_pat) => bind_pat.syntax().text_range(), - PatKind::TuplePat(tuple_pat) => tuple_pat.syntax().text_range(), - _ => return None, - }; - - Some(vec![InlayHint { - range: pat_range, - kind: InlayKind::LetBindingType, - label: inlay_type_string, - }]) + let analyzer = SourceAnalyzer::new(db, file_id, let_statement.syntax(), None); + Some(get_pat_hints(db, &analyzer, let_statement.pat()?, InlayKind::LetBindingType)) }) - .visit(|closure_parameter: ast::LambdaExpr| match closure_parameter.param_list() { - Some(param_list) => Some( + .visit(|closure_parameter: ast::LambdaExpr| { + let analyzer = SourceAnalyzer::new(db, file_id, closure_parameter.syntax(), None); + closure_parameter.param_list().map(|param_list| { param_list .params() .filter(|closure_param| closure_param.ascribed_type().is_none()) - .filter_map(|closure_param| { - let closure_param_syntax = closure_param.syntax(); - let inlay_type_string = get_node_displayable_type( - db, - file_id, - closure_param_syntax, - &closure_param.pat()?, - )? - .display(db) - .to_string() - .into(); - - Some(InlayHint { - range: closure_param_syntax.text_range(), - kind: InlayKind::ClosureParameterType, - label: inlay_type_string, - }) + .filter_map(|closure_param| closure_param.pat()) + .map(|root_pat| { + get_pat_hints(db, &analyzer, root_pat, InlayKind::ClosureParameterType) }) - .collect(), - ), - None => None, + .flatten() + .collect() + }) + }) + .visit(|for_expression: ast::ForExpr| { + let analyzer = SourceAnalyzer::new(db, file_id, for_expression.syntax(), None); + Some(get_pat_hints( + db, + &analyzer, + for_expression.pat()?, + InlayKind::ForExpressionBindingType, + )) }) .accept(&node)? } +fn get_pat_hints( + db: &RootDatabase, + analyzer: &SourceAnalyzer, + root_pat: Pat, + kind: InlayKind, +) -> Vec { + get_leaf_pats(root_pat) + .into_iter() + .filter_map(|pat| { + get_node_displayable_type(db, &analyzer, &pat) + .map(|pat_type| (pat.syntax().text_range(), pat_type)) + }) + .map(|(range, pat_type)| InlayHint { + range, + kind: kind.clone(), + label: pat_type.display(db).to_string().into(), + }) + .collect() +} + +fn get_leaf_pats(root_pat: Pat) -> Vec { + let mut pats_to_process = std::collections::VecDeque::::new(); + pats_to_process.push_back(root_pat); + + let mut leaf_pats = Vec::new(); + + while let Some(maybe_leaf_pat) = pats_to_process.pop_front() { + match maybe_leaf_pat.kind() { + PatKind::BindPat(bind_pat) => { + if let Some(pat) = bind_pat.pat() { + pats_to_process.push_back(pat); + } else { + leaf_pats.push(maybe_leaf_pat); + } + } + PatKind::TuplePat(tuple_pat) => { + for arg_pat in tuple_pat.args() { + pats_to_process.push_back(arg_pat); + } + } + _ => (), + } + } + leaf_pats +} + fn get_node_displayable_type( db: &RootDatabase, - file_id: FileId, - node_syntax: &SyntaxNode, + analyzer: &SourceAnalyzer, node_pat: &Pat, ) -> Option { - let analyzer = hir::SourceAnalyzer::new(db, file_id, node_syntax, None); analyzer.type_of_pat(db, node_pat).and_then(|resolved_type| { if let Ty::Apply(_) = resolved_type { Some(resolved_type) @@ -120,25 +143,32 @@ fn main() { struct InnerStruct {} let test = 54; + let test: i32 = 33; + let mut test = 33; + let _ = 22; + let test = "test"; let test = InnerStruct {}; let test = OuterStruct {}; + let test = vec![222]; + let test: Vec<_> = (0..3).collect(); + let mut test = Vec::new(); test.push(333); + let test = test.into_iter().map(|i| i * i).collect::>(); - let mut test = 33; - let _ = 22; - let test: Vec<_> = (0..3).collect(); + let test = test.into_iter().map(|i| i * i).collect::>(); let _ = (0..23).map(|i: u32| { let i_squared = i * i; i_squared }); - let test: i32 = 33; - - let (x, c) = (42, 'a'); let test = (42, 'a'); + let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); + + let test = Some((2, 3)); + for (i, j) in test {} } "#, ); @@ -150,29 +180,59 @@ fn main() { label: "i32", }, InlayHint { - range: [121; 125), + range: [114; 122), kind: LetBindingType, - label: "OuterStruct", + label: "i32", }, InlayHint { - range: [297; 305), + range: [153; 157), kind: LetBindingType, - label: "i32", + label: "&str", }, InlayHint { - range: [417; 426), + range: [207; 211), + kind: LetBindingType, + label: "OuterStruct", + }, + InlayHint { + range: [538; 547), kind: LetBindingType, label: "u32", }, InlayHint { - range: [496; 502), + range: [592; 596), kind: LetBindingType, label: "(i32, char)", }, InlayHint { - range: [524; 528), + range: [619; 620), kind: LetBindingType, - label: "(i32, char)", + label: "i32", + }, + InlayHint { + range: [623; 624), + kind: LetBindingType, + label: "i32", + }, + InlayHint { + range: [626; 627), + kind: LetBindingType, + label: "i32", + }, + InlayHint { + range: [637; 638), + kind: LetBindingType, + label: "i32", + }, + InlayHint { + range: [630; 631), + kind: LetBindingType, + label: "f64", + }, + InlayHint { + range: [633; 634), + kind: LetBindingType, + label: "f64", }, ]"# ); diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 14619ede2..0b4493ce2 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -897,6 +897,9 @@ pub fn handle_inlay_hints( kind: match api_type.kind { ra_ide_api::InlayKind::LetBindingType => InlayKind::LetBindingType, ra_ide_api::InlayKind::ClosureParameterType => InlayKind::ClosureParameterType, + ra_ide_api::InlayKind::ForExpressionBindingType => { + InlayKind::ForExpressionBindingType + } }, }) .collect()) diff --git a/crates/ra_lsp_server/src/req.rs b/crates/ra_lsp_server/src/req.rs index 916185f99..96f6b1bc5 100644 --- a/crates/ra_lsp_server/src/req.rs +++ b/crates/ra_lsp_server/src/req.rs @@ -215,6 +215,7 @@ pub struct InlayHintsParams { pub enum InlayKind { LetBindingType, ClosureParameterType, + ForExpressionBindingType, } #[derive(Debug, Deserialize, Serialize)] -- cgit v1.2.3