diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs index 739a44b195e..7d49ebcf461 100644 --- a/crates/ra_ide_api/src/inlay_hints.rs +++ b/crates/ra_ide_api/src/inlay_hints.rs @@ -1,3 +1,6 @@ +use crate::{db::RootDatabase, FileId}; +use hir::{HirDisplay, Ty}; +use ra_syntax::ast::Pat; use ra_syntax::{ algo::visit::{visitor, Visitor}, ast::{self, PatKind, TypeAscriptionOwner}, @@ -15,63 +18,101 @@ pub struct InlayHint { pub range: TextRange, pub text: SmolStr, pub inlay_kind: InlayKind, + pub inlay_type_string: String, } -pub(crate) fn inlay_hints(file: &SourceFile) -> Vec { - file.syntax().descendants().map(|node| get_inlay_hints(&node)).flatten().collect() +pub(crate) fn inlay_hints(db: &RootDatabase, file_id: FileId, file: &SourceFile) -> Vec { + file.syntax() + .descendants() + .map(|node| get_inlay_hints(db, file_id, &node).unwrap_or_default()) + .flatten() + .collect() } -fn get_inlay_hints(node: &SyntaxNode) -> Vec { +fn get_inlay_hints( + db: &RootDatabase, + file_id: FileId, + node: &SyntaxNode, +) -> Option> { visitor() .visit(|let_statement: ast::LetStmt| { let let_syntax = let_statement.syntax(); if let_statement.ascribed_type().is_some() { - return Vec::new(); + return None; } - let pat_range = match let_statement.pat().map(|pat| pat.kind()) { - Some(PatKind::BindPat(bind_pat)) => bind_pat.syntax().text_range(), - Some(PatKind::TuplePat(tuple_pat)) => tuple_pat.syntax().text_range(), - _ => return Vec::new(), + 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();; + + 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, }; - vec![InlayHint { + Some(vec![InlayHint { range: pat_range, text: let_syntax.text().to_smol_string(), inlay_kind: InlayKind::LetBinding, - }] + inlay_type_string, + }]) }) - .visit(|closure_parameter: ast::LambdaExpr| { - if let Some(param_list) = closure_parameter.param_list() { + .visit(|closure_parameter: ast::LambdaExpr| match closure_parameter.param_list() { + Some(param_list) => Some( param_list .params() .filter(|closure_param| closure_param.ascribed_type().is_none()) - .map(|closure_param| { + .filter_map(|closure_param| { let closure_param_syntax = closure_param.syntax(); - InlayHint { + let inlay_type_string = get_node_displayable_type( + db, + file_id, + closure_param_syntax, + &closure_param.pat()?, + )? + .display(db) + .to_string(); + Some(InlayHint { range: closure_param_syntax.text_range(), text: closure_param_syntax.text().to_smol_string(), inlay_kind: InlayKind::ClosureParameter, - } + inlay_type_string, + }) }) - .collect() - } else { - Vec::new() - } + .collect(), + ), + None => None, }) - .accept(&node) - .unwrap_or_default() + .accept(&node)? +} + +fn get_node_displayable_type( + db: &RootDatabase, + file_id: FileId, + node_syntax: &SyntaxNode, + 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) + } else { + None + } + }) } #[cfg(test)] mod tests { - use super::*; + use crate::mock_analysis::single_file; use insta::assert_debug_snapshot_matches; #[test] fn test_inlay_hints() { - let file = SourceFile::parse( + let (analysis, file_id) = single_file( r#" struct OuterStruct {} @@ -99,66 +140,45 @@ fn main() { let (x, c) = (42, 'a'); let test = (42, 'a'); } - "#, - ) - .ok() - .unwrap(); - assert_debug_snapshot_matches!(inlay_hints(&file), @r#"[ + ); + + assert_debug_snapshot_matches!(analysis.inlay_hints(file_id).unwrap(), @r#"[ InlayHint { range: [71; 75), text: "let test = 54;", inlay_kind: LetBinding, - }, - InlayHint { - range: [90; 94), - text: "let test = InnerStruct {};", - inlay_kind: LetBinding, + inlay_type_string: "i32", }, InlayHint { range: [121; 125), text: "let test = OuterStruct {};", inlay_kind: LetBinding, - }, - InlayHint { - range: [152; 156), - text: "let test = vec![222];", - inlay_kind: LetBinding, - }, - InlayHint { - range: [178; 186), - text: "let mut test = Vec::new();", - inlay_kind: LetBinding, - }, - InlayHint { - range: [229; 233), - text: "let test = test.into_iter().map(|i| i * i).collect::>();", - inlay_kind: LetBinding, - }, - InlayHint { - range: [258; 259), - text: "i", - inlay_kind: ClosureParameter, + inlay_type_string: "OuterStruct", }, InlayHint { range: [297; 305), text: "let mut test = 33;", inlay_kind: LetBinding, + inlay_type_string: "i32", }, InlayHint { range: [417; 426), text: "let i_squared = i * i;", inlay_kind: LetBinding, + inlay_type_string: "u32", }, InlayHint { range: [496; 502), text: "let (x, c) = (42, \'a\');", inlay_kind: LetBinding, + inlay_type_string: "(i32, char)", }, InlayHint { range: [524; 528), text: "let test = (42, \'a\');", inlay_kind: LetBinding, + inlay_type_string: "(i32, char)", }, ]"# ); diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index 32168f12d76..16ffb03ce92 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -400,7 +400,7 @@ impl Analysis { /// Returns a list of the places in the file where type hints can be displayed. pub fn inlay_hints(&self, file_id: FileId) -> Cancelable> { - self.with_db(|db| inlay_hints::inlay_hints(&db.parse(file_id).tree())) + self.with_db(|db| inlay_hints::inlay_hints(db, file_id, &db.parse(file_id).tree())) } /// Returns the set of folding ranges. diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 1077aafd8a4..e5d2ff832b0 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -9,8 +9,7 @@ use lsp_types::{ TextDocumentIdentifier, TextEdit, WorkspaceEdit, }; use ra_ide_api::{ - AssistId, Cancelable, FileId, FilePosition, FileRange, FoldKind, InlayKind, Query, - RunnableKind, Severity, + AssistId, Cancelable, FileId, FilePosition, FileRange, FoldKind, Query, RunnableKind, Severity, }; use ra_prof::profile; use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; @@ -750,29 +749,15 @@ pub fn handle_code_lens( }), ); - lenses.extend( - analysis - .inlay_hints(file_id) - .into_iter() - .filter(|hint| hint.inlay_kind == InlayKind::LetBinding) - .filter_map(|inlay_hint| { - let resolved_type = analysis - .type_of(FileRange { range: inlay_hint.range, file_id }) - .ok() - .and_then(std::convert::identity) - .filter(|resolved_type| "{unknown}" != resolved_type); - resolved_type.map(|resolved_type| (resolved_type, inlay_hint.range)) - }) - .map(|(resolved_type, range)| CodeLens { - range: range.conv_with(&line_index), - command: Some(Command { - title: resolved_type, - command: String::new(), - arguments: None, - }), - data: None, - }), - ); + lenses.extend(analysis.inlay_hints(file_id)?.into_iter().map(|inlay_hint| CodeLens { + range: inlay_hint.range.conv_with(&line_index), + command: Some(Command { + title: inlay_hint.inlay_type_string, + command: String::new(), + arguments: None, + }), + data: None, + })); Ok(Some(lenses)) }