]> git.lizzy.rs Git - rust.git/commitdiff
Improve inlay hinting for types
authorKirill Bulatov <mail4score@gmail.com>
Fri, 26 Jul 2019 11:10:29 +0000 (14:10 +0300)
committerKirill Bulatov <mail4score@gmail.com>
Fri, 26 Jul 2019 15:06:31 +0000 (18:06 +0300)
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
crates/ra_lsp_server/src/main_loop/handlers.rs
crates/ra_lsp_server/src/req.rs

index 174662beb57b5fe095ea0c3e609f577b41f38190..95289f01663a306a4a0307c633a68b6b7e83453b 100644 (file)
@@ -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 @@
     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<Vec<InlayHint>> {
     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<InlayHint> {
+    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<Pat> {
+    let mut pats_to_process = std::collections::VecDeque::<Pat>::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<Ty> {
-    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::<Vec<_>>();
-    let mut test = 33;
-    let _ = 22;
-    let test: Vec<_> = (0..3).collect();
+    let test = test.into_iter().map(|i| i * i).collect::<Vec<u128>>();
 
     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 @@ struct InnerStruct {}
         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",
     },
 ]"#
         );
index 14619ede2231df65227d8dc316df32f34e44a1c7..0b4493ce2f509c0f737f04914864fb27f9771997 100644 (file)
@@ -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())
index 916185f99d17d99ef6b18133d0a144ee334b86e1..96f6b1bc5ff9a552f0b7e183e0157430be5d9681 100644 (file)
@@ -215,6 +215,7 @@ pub struct InlayHintsParams {
 pub enum InlayKind {
     LetBindingType,
     ClosureParameterType,
+    ForExpressionBindingType,
 }
 
 #[derive(Debug, Deserialize, Serialize)]