From 201b344f2b0c9e84606115d135cd658d0a955d2c Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Sat, 20 Jul 2019 00:20:09 +0300 Subject: Refactor server api --- crates/ra_ide_api/src/display/structure.rs | 27 ----- crates/ra_ide_api/src/inlay_hints.rs | 110 +++++++++++++++++++++ crates/ra_ide_api/src/lib.rs | 7 ++ .../src/snapshots/tests__inlay_hints.snap | 63 ++++++++++++ crates/ra_lsp_server/src/main_loop/handlers.rs | 70 +++++++------ 5 files changed, 222 insertions(+), 55 deletions(-) create mode 100644 crates/ra_ide_api/src/inlay_hints.rs create mode 100644 crates/ra_ide_api/src/snapshots/tests__inlay_hints.snap diff --git a/crates/ra_ide_api/src/display/structure.rs b/crates/ra_ide_api/src/display/structure.rs index bd2e908da..2ba10b2ef 100644 --- a/crates/ra_ide_api/src/display/structure.rs +++ b/crates/ra_ide_api/src/display/structure.rs @@ -1,6 +1,5 @@ use crate::TextRange; -use ra_syntax::ast::PatKind; use ra_syntax::{ algo::visit::{visitor, Visitor}, ast::{self, AttrsOwner, NameOwner, TypeAscriptionOwner, TypeParamsOwner}, @@ -156,32 +155,6 @@ fn structure_node(node: &SyntaxNode) -> Option { } decl(mc) }) - .visit(|let_statement: ast::LetStmt| { - let let_syntax = let_statement.syntax(); - - let mut label = String::new(); - collapse_ws(let_syntax, &mut label); - - if let_statement.ascribed_type().is_some() { - return None; - } - - let pat_range = match let_statement.pat()?.kind() { - PatKind::BindPat(bind_pat) => bind_pat.syntax().range(), - PatKind::TuplePat(tuple_pat) => tuple_pat.syntax().range(), - _ => return None, - }; - - Some(StructureNode { - parent: None, - label, - navigation_range: pat_range, - node_range: let_syntax.range(), - kind: let_syntax.kind(), - detail: None, - deprecated: false, - }) - }) .accept(&node)? } diff --git a/crates/ra_ide_api/src/inlay_hints.rs b/crates/ra_ide_api/src/inlay_hints.rs new file mode 100644 index 000000000..4d1df65d0 --- /dev/null +++ b/crates/ra_ide_api/src/inlay_hints.rs @@ -0,0 +1,110 @@ +use ra_syntax::{ + algo::visit::{visitor, Visitor}, + ast::{self, PatKind, TypeAscriptionOwner}, + AstNode, SmolStr, SourceFile, SyntaxNode, TextRange, +}; + +#[derive(Debug, PartialEq, Eq)] +pub enum InlayKind { + LetBinding, + ClosureParameter, +} + +#[derive(Debug)] +pub struct InlayHint { + pub range: TextRange, + pub text: SmolStr, + pub inlay_kind: InlayKind, +} + +pub(crate) fn inlay_hints(file: &SourceFile) -> Vec { + file.syntax().descendants().map(|node| get_inlay_hints(&node)).flatten().collect() +} + +fn get_inlay_hints(node: &SyntaxNode) -> Vec { + visitor() + .visit(|let_statement: ast::LetStmt| { + let let_syntax = let_statement.syntax(); + + if let_statement.ascribed_type().is_some() { + return Vec::new(); + } + + 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(), + }; + + vec![InlayHint { + range: pat_range, + text: let_syntax.text().to_smol_string(), + inlay_kind: InlayKind::LetBinding, + }] + }) + .visit(|closure_parameter: ast::LambdaExpr| { + if let Some(param_list) = closure_parameter.param_list() { + param_list + .params() + .filter(|closure_param| closure_param.ascribed_type().is_none()) + .map(|closure_param| { + let closure_param_syntax = closure_param.syntax(); + InlayHint { + range: closure_param_syntax.text_range(), + text: closure_param_syntax.text().to_smol_string(), + inlay_kind: InlayKind::ClosureParameter, + } + }) + .collect() + } else { + Vec::new() + } + }) + .accept(&node) + .unwrap_or_else(Vec::new) +} + +#[cfg(test)] +mod tests { + use super::*; + use insta::assert_debug_snapshot_matches; + + #[test] + fn test_inlay_hints() { + let file = SourceFile::parse( + r#" +struct OuterStruct {} + +fn main() { + struct InnerStruct {} + + let test = 54; + let test = InnerStruct {}; + let test = OuterStruct {}; + let test = vec![222]; + 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 _ = (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'); +} + +"#, + ) + .ok() + .unwrap(); + let hints = inlay_hints(&file); + assert_debug_snapshot_matches!("inlay_hints", hints); + } +} diff --git a/crates/ra_ide_api/src/lib.rs b/crates/ra_ide_api/src/lib.rs index c54d574bc..af163088a 100644 --- a/crates/ra_ide_api/src/lib.rs +++ b/crates/ra_ide_api/src/lib.rs @@ -38,6 +38,7 @@ mod join_lines; mod typing; mod matching_brace; mod display; +mod inlay_hints; #[cfg(test)] mod marks; @@ -64,6 +65,7 @@ pub use crate::{ display::{file_structure, FunctionSignature, NavigationTarget, StructureNode}, folding_ranges::{Fold, FoldKind}, hover::HoverResult, + inlay_hints::{InlayHint, InlayKind}, line_index::{LineCol, LineIndex}, line_index_utils::translate_offset_with_edit, references::ReferenceSearchResult, @@ -396,6 +398,11 @@ impl Analysis { file_structure(&parse.tree()) } + /// Returns a list of the places in the file where type hints can be displayed. + pub fn inlay_hints(&self, file_id: FileId) -> Vec { + inlay_hints::inlay_hints(&self.db.parse(file_id).tree()) + } + /// Returns the set of folding ranges. pub fn folding_ranges(&self, file_id: FileId) -> Vec { let parse = self.db.parse(file_id); diff --git a/crates/ra_ide_api/src/snapshots/tests__inlay_hints.snap b/crates/ra_ide_api/src/snapshots/tests__inlay_hints.snap new file mode 100644 index 000000000..f4d562314 --- /dev/null +++ b/crates/ra_ide_api/src/snapshots/tests__inlay_hints.snap @@ -0,0 +1,63 @@ +--- +created: "2019-07-20T20:13:53.385368Z" +creator: insta@0.8.1 +source: crates/ra_ide_api/src/inlay_hints.rs +expression: hints +--- +[ + InlayHint { + range: [71; 75), + text: "let test = 54;", + inlay_kind: LetBinding, + }, + InlayHint { + range: [90; 94), + text: "let test = InnerStruct {};", + inlay_kind: LetBinding, + }, + 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, + }, + InlayHint { + range: [297; 305), + text: "let mut test = 33;", + inlay_kind: LetBinding, + }, + InlayHint { + range: [417; 426), + text: "let i_squared = i * i;", + inlay_kind: LetBinding, + }, + InlayHint { + range: [500; 506), + text: "let (x, c) = (42, \'a\');", + inlay_kind: LetBinding, + }, + InlayHint { + range: [528; 532), + text: "let test = (42, \'a\');", + inlay_kind: LetBinding, + }, +] diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index ea947417f..1077aafd8 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -9,7 +9,8 @@ use lsp_types::{ TextDocumentIdentifier, TextEdit, WorkspaceEdit, }; use ra_ide_api::{ - AssistId, Cancelable, FileId, FilePosition, FileRange, FoldKind, Query, RunnableKind, Severity, + AssistId, Cancelable, FileId, FilePosition, FileRange, FoldKind, InlayKind, Query, + RunnableKind, Severity, }; use ra_prof::profile; use ra_syntax::{AstNode, SyntaxKind, TextRange, TextUnit}; @@ -685,13 +686,14 @@ pub fn handle_code_lens( params: req::CodeLensParams, ) -> Result>> { let file_id = params.text_document.try_conv_with(&world)?; - let line_index = world.analysis().file_line_index(file_id); + let analysis = world.analysis(); + let line_index = analysis.file_line_index(file_id); let mut lenses: Vec = Default::default(); let workspace_root = world.workspace_root_for(file_id); // Gather runnables - for runnable in world.analysis().runnables(file_id)? { + for runnable in analysis.runnables(file_id)? { let title = match &runnable.kind { RunnableKind::Test { .. } | RunnableKind::TestMod { .. } => Some("▶️Run Test"), RunnableKind::Bench { .. } => Some("Run Bench"), @@ -726,39 +728,51 @@ pub fn handle_code_lens( } } - lenses.extend(world.analysis().file_structure(file_id).into_iter().filter_map(|it| { - match it.kind { - // Handle impls - SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => { + // Handle impls + lenses.extend( + analysis + .file_structure(file_id) + .into_iter() + .filter(|it| match it.kind { + SyntaxKind::TRAIT_DEF | SyntaxKind::STRUCT_DEF | SyntaxKind::ENUM_DEF => true, + _ => false, + }) + .map(|it| { let range = it.node_range.conv_with(&line_index); let pos = range.start; let lens_params = req::TextDocumentPositionParams::new(params.text_document.clone(), pos); - Some(CodeLens { + CodeLens { range, command: None, data: Some(to_value(CodeLensResolveData::Impls(lens_params)).unwrap()), - }) - } - // handle let statements - SyntaxKind::LET_STMT => world - .analysis() - .type_of(FileRange { range: it.navigation_range, file_id }) - .ok() - .and_then(std::convert::identity) - .filter(|resolved_type| "{unknown}" != resolved_type) - .map(|resolved_type| CodeLens { - range: it.node_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() + .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, }), - _ => None, - } - })); + data: None, + }), + ); Ok(Some(lenses)) } -- cgit v1.2.3