From b4af02d110b2515295d8375b40311c630b90d7be Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Tue, 2 Jun 2020 01:29:54 +0200 Subject: Add top level keywords completion --- crates/ra_ide/src/completion/complete_keyword.rs | 187 ++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index fd95bc410..36280b703 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -1,8 +1,9 @@ //! FIXME: write short doc here use ra_syntax::{ + algo::non_trivia_sibling, ast::{self, LoopBodyOwner}, - match_ast, AstNode, + match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind::*, SyntaxToken, }; @@ -52,7 +53,28 @@ fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { .build() } +fn add_top_level_keywords(acc: &mut Completions, ctx: &CompletionContext) { + if let Some(token) = previous_non_triva_element(&ctx.token).and_then(|it| it.into_token()) { + if token.kind() == UNSAFE_KW { + acc.add(keyword(ctx, "impl", "impl $0 {}")); + acc.add(keyword(ctx, "trait", "trait $0 {}")); + acc.add(keyword(ctx, "fn", "fn $0() {}")); + return; + } + } + acc.add(keyword(ctx, "impl", "impl $0 {}")); + acc.add(keyword(ctx, "enum", "enum $0 {}")); + acc.add(keyword(ctx, "struct", "struct $0 {}")); + acc.add(keyword(ctx, "trait", "trait $0 {}")); + acc.add(keyword(ctx, "fn", "fn $0() {}")); + acc.add(keyword(ctx, "unsafe", "unsafe ")); +} + pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { + if ctx.is_new_item { + add_top_level_keywords(acc, ctx); + return; + } if !ctx.is_trivial_path { return; } @@ -82,6 +104,36 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); } +fn previous_non_triva_element(token: &SyntaxToken) -> Option { + // trying to get first non triva sibling if we have one + let token_sibling = non_trivia_sibling(NodeOrToken::Token(token.to_owned()), Direction::Prev); + let mut wrapped = if let Some(sibling) = token_sibling { + sibling + } else { + // if not trying to find first ancestor which has such a sibling + let node = token.parent(); + let range = node.text_range(); + let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?; + let prev_sibling_node = top_node.ancestors().find(|it| { + non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some() + })?; + non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)? + }; + // traversing the tree down to get the last token or node, i.e. the closest one + loop { + if let Some(token) = wrapped.as_token() { + return Some(NodeOrToken::Token(token.clone())); + } else { + let new = wrapped.as_node().and_then(|n| n.last_child_or_token()); + if new.is_some() { + wrapped = new.unwrap().clone(); + } else { + return Some(wrapped); + } + } + } +} + fn is_in_loop_body(leaf: &SyntaxToken) -> bool { // FIXME move this to CompletionContext and make it handle macros for node in leaf.parent().ancestors() { @@ -268,6 +320,139 @@ mod tests { ); } + #[test] + fn completes_unsafe_context_in_item_position_with_non_empty_token() { + assert_debug_snapshot!( + do_keyword_completion( + r" + mod my_mod { + unsafe i<|> + } + ", + ), + @r###" + [ + CompletionItem { + label: "fn", + source_range: 57..58, + delete: 57..58, + insert: "fn $0() {}", + kind: Keyword, + }, + CompletionItem { + label: "impl", + source_range: 57..58, + delete: 57..58, + insert: "impl $0 {}", + kind: Keyword, + }, + CompletionItem { + label: "trait", + source_range: 57..58, + delete: 57..58, + insert: "trait $0 {}", + kind: Keyword, + }, + ] + "### + ); + } + + #[test] + fn completes_unsafe_context_in_item_position_with_empty_token() { + assert_debug_snapshot!( + do_keyword_completion( + r" + mod my_mod { + unsafe <|> + } + ", + ), + @r###" + [ + CompletionItem { + label: "fn", + source_range: 57..57, + delete: 57..57, + insert: "fn $0() {}", + kind: Keyword, + }, + CompletionItem { + label: "impl", + source_range: 57..57, + delete: 57..57, + insert: "impl $0 {}", + kind: Keyword, + }, + CompletionItem { + label: "trait", + source_range: 57..57, + delete: 57..57, + insert: "trait $0 {}", + kind: Keyword, + }, + ] + "### + ); + } + + #[test] + fn completes_keywords_in_item_position_with_empty_token() { + assert_debug_snapshot!( + do_keyword_completion( + r" + <|> + ", + ), + @r###" + [ + CompletionItem { + label: "enum", + source_range: 17..17, + delete: 17..17, + insert: "enum $0 {}", + kind: Keyword, + }, + CompletionItem { + label: "fn", + source_range: 17..17, + delete: 17..17, + insert: "fn $0() {}", + kind: Keyword, + }, + CompletionItem { + label: "impl", + source_range: 17..17, + delete: 17..17, + insert: "impl $0 {}", + kind: Keyword, + }, + CompletionItem { + label: "struct", + source_range: 17..17, + delete: 17..17, + insert: "struct $0 {}", + kind: Keyword, + }, + CompletionItem { + label: "trait", + source_range: 17..17, + delete: 17..17, + insert: "trait $0 {}", + kind: Keyword, + }, + CompletionItem { + label: "unsafe", + source_range: 17..17, + delete: 17..17, + insert: "unsafe ", + kind: Keyword, + }, + ] + "### + ); + } + #[test] fn completes_else_after_if() { assert_debug_snapshot!( -- cgit v1.2.3 From 9f91901f7e55915b359909b5422c03f70e4ab79b Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Thu, 11 Jun 2020 14:16:35 +0200 Subject: Add more keywords --- crates/ra_ide/src/completion.rs | 1 + crates/ra_ide/src/completion/complete_keyword.rs | 271 +++------------------ crates/ra_ide/src/completion/completion_context.rs | 28 ++- crates/ra_ide/src/completion/patterns.rs | 117 +++++++++ 4 files changed, 183 insertions(+), 234 deletions(-) create mode 100644 crates/ra_ide/src/completion/patterns.rs (limited to 'crates') diff --git a/crates/ra_ide/src/completion.rs b/crates/ra_ide/src/completion.rs index d890b69d2..fa37b6955 100644 --- a/crates/ra_ide/src/completion.rs +++ b/crates/ra_ide/src/completion.rs @@ -15,6 +15,7 @@ mod complete_unqualified_path; mod complete_postfix; mod complete_macro_in_item_position; mod complete_trait_impl; +mod patterns; #[cfg(test)] mod test_utils; diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 36280b703..5b56c6275 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -1,12 +1,6 @@ //! FIXME: write short doc here -use ra_syntax::{ - algo::non_trivia_sibling, - ast::{self, LoopBodyOwner}, - match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, - SyntaxKind::*, - SyntaxToken, -}; +use ra_syntax::ast; use crate::completion::{ CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions, @@ -53,110 +47,56 @@ fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { .build() } -fn add_top_level_keywords(acc: &mut Completions, ctx: &CompletionContext) { - if let Some(token) = previous_non_triva_element(&ctx.token).and_then(|it| it.into_token()) { - if token.kind() == UNSAFE_KW { - acc.add(keyword(ctx, "impl", "impl $0 {}")); - acc.add(keyword(ctx, "trait", "trait $0 {}")); - acc.add(keyword(ctx, "fn", "fn $0() {}")); - return; - } +fn add_keyword( + ctx: &CompletionContext, + acc: &mut Completions, + kw: &str, + snippet: &str, + should_add: bool, +) { + if should_add { + acc.add(keyword(ctx, kw, snippet)); } - acc.add(keyword(ctx, "impl", "impl $0 {}")); - acc.add(keyword(ctx, "enum", "enum $0 {}")); - acc.add(keyword(ctx, "struct", "struct $0 {}")); - acc.add(keyword(ctx, "trait", "trait $0 {}")); - acc.add(keyword(ctx, "fn", "fn $0() {}")); - acc.add(keyword(ctx, "unsafe", "unsafe ")); } pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { - if ctx.is_new_item { - add_top_level_keywords(acc, ctx); - return; - } - if !ctx.is_trivial_path { - return; - } + add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); + add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent); + add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); + add_keyword(ctx, acc, "impl", "impl $0 {}", ctx.is_new_item); + add_keyword(ctx, acc, "trait", "impl $0 {}", ctx.is_new_item); + add_keyword(ctx, acc, "enum", "enum $0 {}", ctx.is_new_item && !ctx.after_unsafe); + add_keyword(ctx, acc, "struct", "struct $0 {}", ctx.is_new_item && !ctx.after_unsafe); + add_keyword(ctx, acc, "union", "union $0 {}", ctx.is_new_item && !ctx.after_unsafe); + add_keyword(ctx, acc, "match", "match $0 {}", ctx.block_expr_parent); + add_keyword(ctx, acc, "loop", "loop {$0}", ctx.block_expr_parent); + add_keyword(ctx, acc, "while", "while $0 {}", ctx.block_expr_parent); + add_keyword(ctx, acc, "let", "let ", ctx.after_if || ctx.block_expr_parent); + add_keyword(ctx, acc, "let", "let ", ctx.after_if || ctx.block_expr_parent); + add_keyword(ctx, acc, "else", "else {$0}", ctx.after_if); + add_keyword(ctx, acc, "else if", "else if $0 {}", ctx.after_if); + add_keyword(ctx, acc, "mod", "mod $0 {}", ctx.is_new_item || ctx.block_expr_parent); + add_keyword(ctx, acc, "mut", "mut ", ctx.bind_pat_parent || ctx.ref_pat_parent); + add_keyword(ctx, acc, "true", "true", !ctx.is_new_item); // this should be defined properly + add_keyword(ctx, acc, "false", "false", !ctx.is_new_item); // this should be defined properly + add_keyword(ctx, acc, "const", "const ", ctx.is_new_item || ctx.block_expr_parent); + add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent); + add_keyword(ctx, acc, "static", "static ", ctx.is_new_item || ctx.block_expr_parent); + add_keyword(ctx, acc, "extern", "extern ", ctx.is_new_item || ctx.block_expr_parent); + add_keyword(ctx, acc, "unsafe", "unsafe ", ctx.is_new_item || ctx.block_expr_parent); + add_keyword(ctx, acc, "continue", "continue;", ctx.in_loop_body && ctx.can_be_stmt); + add_keyword(ctx, acc, "break", "break;", ctx.in_loop_body && ctx.can_be_stmt); + add_keyword(ctx, acc, "continue", "continue", ctx.in_loop_body && !ctx.can_be_stmt); + add_keyword(ctx, acc, "break", "break", ctx.in_loop_body && !ctx.can_be_stmt); + complete_use_tree_keyword(acc, ctx); let fn_def = match &ctx.function_syntax { Some(it) => it, None => return, }; - acc.add(keyword(ctx, "if", "if $0 {}")); - acc.add(keyword(ctx, "match", "match $0 {}")); - acc.add(keyword(ctx, "while", "while $0 {}")); - acc.add(keyword(ctx, "loop", "loop {$0}")); - - if ctx.after_if { - acc.add(keyword(ctx, "else", "else {$0}")); - acc.add(keyword(ctx, "else if", "else if $0 {}")); - } - if is_in_loop_body(&ctx.token) { - if ctx.can_be_stmt { - acc.add(keyword(ctx, "continue", "continue;")); - acc.add(keyword(ctx, "break", "break;")); - } else { - acc.add(keyword(ctx, "continue", "continue")); - acc.add(keyword(ctx, "break", "break")); - } - } acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); } -fn previous_non_triva_element(token: &SyntaxToken) -> Option { - // trying to get first non triva sibling if we have one - let token_sibling = non_trivia_sibling(NodeOrToken::Token(token.to_owned()), Direction::Prev); - let mut wrapped = if let Some(sibling) = token_sibling { - sibling - } else { - // if not trying to find first ancestor which has such a sibling - let node = token.parent(); - let range = node.text_range(); - let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?; - let prev_sibling_node = top_node.ancestors().find(|it| { - non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some() - })?; - non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)? - }; - // traversing the tree down to get the last token or node, i.e. the closest one - loop { - if let Some(token) = wrapped.as_token() { - return Some(NodeOrToken::Token(token.clone())); - } else { - let new = wrapped.as_node().and_then(|n| n.last_child_or_token()); - if new.is_some() { - wrapped = new.unwrap().clone(); - } else { - return Some(wrapped); - } - } - } -} - -fn is_in_loop_body(leaf: &SyntaxToken) -> bool { - // FIXME move this to CompletionContext and make it handle macros - for node in leaf.parent().ancestors() { - if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { - break; - } - let loop_body = match_ast! { - match node { - ast::ForExpr(it) => it.loop_body(), - ast::WhileExpr(it) => it.loop_body(), - ast::LoopExpr(it) => it.loop_body(), - _ => None, - } - }; - if let Some(body) = loop_body { - if body.syntax().text_range().contains_range(leaf.text_range()) { - return true; - } - } - } - false -} - fn complete_return( ctx: &CompletionContext, fn_def: &ast::FnDef, @@ -320,139 +260,6 @@ mod tests { ); } - #[test] - fn completes_unsafe_context_in_item_position_with_non_empty_token() { - assert_debug_snapshot!( - do_keyword_completion( - r" - mod my_mod { - unsafe i<|> - } - ", - ), - @r###" - [ - CompletionItem { - label: "fn", - source_range: 57..58, - delete: 57..58, - insert: "fn $0() {}", - kind: Keyword, - }, - CompletionItem { - label: "impl", - source_range: 57..58, - delete: 57..58, - insert: "impl $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "trait", - source_range: 57..58, - delete: 57..58, - insert: "trait $0 {}", - kind: Keyword, - }, - ] - "### - ); - } - - #[test] - fn completes_unsafe_context_in_item_position_with_empty_token() { - assert_debug_snapshot!( - do_keyword_completion( - r" - mod my_mod { - unsafe <|> - } - ", - ), - @r###" - [ - CompletionItem { - label: "fn", - source_range: 57..57, - delete: 57..57, - insert: "fn $0() {}", - kind: Keyword, - }, - CompletionItem { - label: "impl", - source_range: 57..57, - delete: 57..57, - insert: "impl $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "trait", - source_range: 57..57, - delete: 57..57, - insert: "trait $0 {}", - kind: Keyword, - }, - ] - "### - ); - } - - #[test] - fn completes_keywords_in_item_position_with_empty_token() { - assert_debug_snapshot!( - do_keyword_completion( - r" - <|> - ", - ), - @r###" - [ - CompletionItem { - label: "enum", - source_range: 17..17, - delete: 17..17, - insert: "enum $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "fn", - source_range: 17..17, - delete: 17..17, - insert: "fn $0() {}", - kind: Keyword, - }, - CompletionItem { - label: "impl", - source_range: 17..17, - delete: 17..17, - insert: "impl $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "struct", - source_range: 17..17, - delete: 17..17, - insert: "struct $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "trait", - source_range: 17..17, - delete: 17..17, - insert: "trait $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "unsafe", - source_range: 17..17, - delete: 17..17, - insert: "unsafe ", - kind: Keyword, - }, - ] - "### - ); - } - #[test] fn completes_else_after_if() { assert_debug_snapshot!( diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index c4646b727..1ef07d8f4 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -5,12 +5,16 @@ use ra_db::SourceDatabase; use ra_ide_db::RootDatabase; use ra_syntax::{ algo::{find_covering_element, find_node_at_offset}, - ast, match_ast, AstNode, + ast, match_ast, AstNode, NodeOrToken, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextSize, }; use ra_text_edit::Indel; +use super::patterns::{ + goes_after_unsafe, has_bind_pat_parent, has_block_expr_parent, has_ref_pat_parent, + is_in_loop_body, +}; use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; use test_utils::mark; @@ -60,6 +64,11 @@ pub(crate) struct CompletionContext<'a> { pub(super) is_path_type: bool, pub(super) has_type_args: bool, pub(super) attribute_under_caret: Option, + pub(super) after_unsafe: bool, + pub(super) block_expr_parent: bool, + pub(super) bind_pat_parent: bool, + pub(super) ref_pat_parent: bool, + pub(super) in_loop_body: bool, } impl<'a> CompletionContext<'a> { @@ -118,6 +127,11 @@ impl<'a> CompletionContext<'a> { has_type_args: false, dot_receiver_is_ambiguous_float_literal: false, attribute_under_caret: None, + after_unsafe: false, + in_loop_body: false, + ref_pat_parent: false, + bind_pat_parent: false, + block_expr_parent: false, }; let mut original_file = original_file.syntax().clone(); @@ -159,7 +173,7 @@ impl<'a> CompletionContext<'a> { break; } } - + ctx.fill_keyword_patterns(&hypothetical_file, offset); ctx.fill(&original_file, hypothetical_file, offset); Some(ctx) } @@ -188,6 +202,16 @@ impl<'a> CompletionContext<'a> { self.sema.scope_at_offset(&self.token.parent(), self.offset) } + fn fill_keyword_patterns(&mut self, file_with_fake_ident: &SyntaxNode, offset: TextSize) { + let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); + let syntax_element = NodeOrToken::Token(fake_ident_token.clone()); + self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); + self.after_unsafe = goes_after_unsafe(syntax_element.clone()); + self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); + self.ref_pat_parent = has_ref_pat_parent(syntax_element.clone()); + self.in_loop_body = is_in_loop_body(syntax_element.clone()); + } + fn fill( &mut self, original_file: &SyntaxNode, diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs new file mode 100644 index 000000000..b55f23fbe --- /dev/null +++ b/crates/ra_ide/src/completion/patterns.rs @@ -0,0 +1,117 @@ +use ra_syntax::{ + algo::non_trivia_sibling, + ast::{self, LoopBodyOwner}, + match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, + SyntaxKind::*, + SyntaxNode, +}; + +pub(crate) fn inside_impl(element: SyntaxElement) -> bool { + let node = match element { + NodeOrToken::Node(node) => node, + NodeOrToken::Token(token) => token.parent(), + }; + node.ancestors().find(|it| it.kind() == IMPL_DEF).is_some() +} + +pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { + let node = match element { + NodeOrToken::Node(node) => node, + NodeOrToken::Token(token) => token.parent(), + }; + node.ancestors().find(|it| it.kind() == BIND_PAT).is_some() +} + +pub(crate) fn has_ref_pat_parent(element: SyntaxElement) -> bool { + let node = match element { + NodeOrToken::Node(node) => node, + NodeOrToken::Token(token) => token.parent(), + }; + node.ancestors().find(|it| it.kind() == REF_PAT).is_some() +} + +pub(crate) fn goes_after_unsafe(element: SyntaxElement) -> bool { + if let Some(token) = previous_non_triva_element(element).and_then(|it| it.into_token()) { + if token.kind() == UNSAFE_KW { + return true; + } + } + false +} + +pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { + not_same_range_parent(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() +} + +pub(crate) fn has_item_list_parent(element: SyntaxElement) -> bool { + not_same_range_parent(element).filter(|it| it.kind() == ITEM_LIST).is_some() +} + +pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { + let leaf = match element { + NodeOrToken::Node(node) => node, + NodeOrToken::Token(token) => token.parent(), + }; + for node in leaf.ancestors() { + if node.kind() == FN_DEF || node.kind() == LAMBDA_EXPR { + break; + } + let loop_body = match_ast! { + match node { + ast::ForExpr(it) => it.loop_body(), + ast::WhileExpr(it) => it.loop_body(), + ast::LoopExpr(it) => it.loop_body(), + _ => None, + } + }; + if let Some(body) = loop_body { + if body.syntax().text_range().contains_range(leaf.text_range()) { + return true; + } + } + } + false +} + +fn not_same_range_parent(element: SyntaxElement) -> Option { + let node = match element { + NodeOrToken::Node(node) => node, + NodeOrToken::Token(token) => token.parent(), + }; + let range = node.text_range(); + node.ancestors().take_while(|it| it.text_range() == range).last().and_then(|it| it.parent()) +} + +fn previous_non_triva_element(element: SyntaxElement) -> Option { + // trying to get first non triva sibling if we have one + let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev); + let mut wrapped = if let Some(sibling) = token_sibling { + sibling + } else { + // if not trying to find first ancestor which has such a sibling + let node = match element { + NodeOrToken::Node(node) => node, + NodeOrToken::Token(token) => token.parent(), + }; + let range = node.text_range(); + let top_node = node.ancestors().take_while(|it| it.text_range() == range).last()?; + let prev_sibling_node = top_node.ancestors().find(|it| { + non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some() + })?; + non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)? + }; + //I think you can avoid this loop if you use SyntaxToken::prev_token -- unlike prev_sibling_or_token, it works across parents. + // traversing the tree down to get the last token or node, i.e. the closest one + loop { + if let Some(token) = wrapped.as_token() { + return Some(NodeOrToken::Token(token.clone())); + } else { + let new = wrapped.as_node().and_then(|n| n.last_child_or_token()); + if new.is_some() { + wrapped = new.unwrap().clone(); + } else { + return Some(wrapped); + } + } + } +} -- cgit v1.2.3 From eb4004fdb8f4cef602581d1fb35e515748d586cb Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Thu, 11 Jun 2020 14:32:14 +0200 Subject: Add todo --- crates/ra_ide/src/completion/patterns.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs index b55f23fbe..145f01786 100644 --- a/crates/ra_ide/src/completion/patterns.rs +++ b/crates/ra_ide/src/completion/patterns.rs @@ -100,7 +100,8 @@ fn previous_non_triva_element(element: SyntaxElement) -> Option { })?; non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)? }; - //I think you can avoid this loop if you use SyntaxToken::prev_token -- unlike prev_sibling_or_token, it works across parents. + // TODO: Check if this can be simplified + // Matklad: I think you can avoid this loop if you use SyntaxToken::prev_token -- unlike prev_sibling_or_token, it works across parents. // traversing the tree down to get the last token or node, i.e. the closest one loop { if let Some(token) = wrapped.as_token() { -- cgit v1.2.3 From f46bc12199dc88b88bd826f49d5c5af6e9caf727 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Thu, 11 Jun 2020 23:25:58 +0200 Subject: Add more patterns and keywords --- crates/ra_ide/src/completion/complete_keyword.rs | 5 +- crates/ra_ide/src/completion/completion_context.rs | 13 ++- crates/ra_ide/src/completion/patterns.rs | 96 ++++++++++------------ 3 files changed, 59 insertions(+), 55 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 5b56c6275..50f003949 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -62,7 +62,7 @@ fn add_keyword( pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent); - add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); + add_keyword(ctx, acc, "use", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); add_keyword(ctx, acc, "impl", "impl $0 {}", ctx.is_new_item); add_keyword(ctx, acc, "trait", "impl $0 {}", ctx.is_new_item); add_keyword(ctx, acc, "enum", "enum $0 {}", ctx.is_new_item && !ctx.after_unsafe); @@ -72,7 +72,6 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte add_keyword(ctx, acc, "loop", "loop {$0}", ctx.block_expr_parent); add_keyword(ctx, acc, "while", "while $0 {}", ctx.block_expr_parent); add_keyword(ctx, acc, "let", "let ", ctx.after_if || ctx.block_expr_parent); - add_keyword(ctx, acc, "let", "let ", ctx.after_if || ctx.block_expr_parent); add_keyword(ctx, acc, "else", "else {$0}", ctx.after_if); add_keyword(ctx, acc, "else if", "else if $0 {}", ctx.after_if); add_keyword(ctx, acc, "mod", "mod $0 {}", ctx.is_new_item || ctx.block_expr_parent); @@ -88,6 +87,8 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte add_keyword(ctx, acc, "break", "break;", ctx.in_loop_body && ctx.can_be_stmt); add_keyword(ctx, acc, "continue", "continue", ctx.in_loop_body && !ctx.can_be_stmt); add_keyword(ctx, acc, "break", "break", ctx.in_loop_body && !ctx.can_be_stmt); + add_keyword(ctx, acc, "pub", "pub ", ctx.is_new_item && !ctx.inside_trait); + add_keyword(ctx, acc, "where", "where ", ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling); complete_use_tree_keyword(acc, ctx); let fn_def = match &ctx.function_syntax { diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 1ef07d8f4..e579e2ee2 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -12,8 +12,8 @@ use ra_syntax::{ use ra_text_edit::Indel; use super::patterns::{ - goes_after_unsafe, has_bind_pat_parent, has_block_expr_parent, has_ref_pat_parent, - is_in_loop_body, + goes_after_unsafe, has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, + has_ref_pat_parent, has_trait_as_prev_sibling, inside_trait, is_in_loop_body, }; use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; use test_utils::mark; @@ -69,6 +69,9 @@ pub(crate) struct CompletionContext<'a> { pub(super) bind_pat_parent: bool, pub(super) ref_pat_parent: bool, pub(super) in_loop_body: bool, + pub(super) inside_trait: bool, + pub(super) trait_as_prev_sibling: bool, + pub(super) impl_as_prev_sibling: bool, } impl<'a> CompletionContext<'a> { @@ -132,6 +135,9 @@ impl<'a> CompletionContext<'a> { ref_pat_parent: false, bind_pat_parent: false, block_expr_parent: false, + inside_trait: false, + trait_as_prev_sibling: false, + impl_as_prev_sibling: false, }; let mut original_file = original_file.syntax().clone(); @@ -210,6 +216,9 @@ impl<'a> CompletionContext<'a> { self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); self.ref_pat_parent = has_ref_pat_parent(syntax_element.clone()); self.in_loop_body = is_in_loop_body(syntax_element.clone()); + self.inside_trait = inside_trait(syntax_element.clone()); + self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); + self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); } fn fill( diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs index 145f01786..31d32ccd9 100644 --- a/crates/ra_ide/src/completion/patterns.rs +++ b/crates/ra_ide/src/completion/patterns.rs @@ -3,48 +3,47 @@ use ra_syntax::{ ast::{self, LoopBodyOwner}, match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxKind::*, - SyntaxNode, + SyntaxNode, SyntaxToken, }; pub(crate) fn inside_impl(element: SyntaxElement) -> bool { - let node = match element { - NodeOrToken::Node(node) => node, - NodeOrToken::Token(token) => token.parent(), - }; - node.ancestors().find(|it| it.kind() == IMPL_DEF).is_some() + element.ancestors().find(|it| it.kind() == IMPL_DEF).is_some() +} + +pub(crate) fn inside_trait(element: SyntaxElement) -> bool { + element.ancestors().find(|it| it.kind() == TRAIT_DEF).is_some() } pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { - let node = match element { - NodeOrToken::Node(node) => node, - NodeOrToken::Token(token) => token.parent(), - }; - node.ancestors().find(|it| it.kind() == BIND_PAT).is_some() + element.ancestors().find(|it| it.kind() == BIND_PAT).is_some() } pub(crate) fn has_ref_pat_parent(element: SyntaxElement) -> bool { - let node = match element { - NodeOrToken::Node(node) => node, - NodeOrToken::Token(token) => token.parent(), - }; - node.ancestors().find(|it| it.kind() == REF_PAT).is_some() + element.ancestors().find(|it| it.kind() == REF_PAT).is_some() } pub(crate) fn goes_after_unsafe(element: SyntaxElement) -> bool { - if let Some(token) = previous_non_triva_element(element).and_then(|it| it.into_token()) { - if token.kind() == UNSAFE_KW { - return true; - } - } - false + element + .into_token() + .and_then(|it| previous_non_trivia_token(it)) + .filter(|it| it.kind() == UNSAFE_KW) + .is_some() } pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { - not_same_range_parent(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() + not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() } pub(crate) fn has_item_list_parent(element: SyntaxElement) -> bool { - not_same_range_parent(element).filter(|it| it.kind() == ITEM_LIST).is_some() + not_same_range_ancestor(element).filter(|it| it.kind() == ITEM_LIST).is_some() +} + +pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool { + previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some() +} + +pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool { + previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL_DEF).is_some() } pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { @@ -73,20 +72,30 @@ pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { false } -fn not_same_range_parent(element: SyntaxElement) -> Option { - let node = match element { - NodeOrToken::Node(node) => node, - NodeOrToken::Token(token) => token.parent(), - }; - let range = node.text_range(); - node.ancestors().take_while(|it| it.text_range() == range).last().and_then(|it| it.parent()) +fn not_same_range_ancestor(element: SyntaxElement) -> Option { + element + .ancestors() + .take_while(|it| it.text_range() == element.text_range()) + .last() + .and_then(|it| it.parent()) } -fn previous_non_triva_element(element: SyntaxElement) -> Option { - // trying to get first non triva sibling if we have one +fn previous_non_trivia_token(token: SyntaxToken) -> Option { + let mut token = token.prev_token(); + while let Some(inner) = token.clone() { + if !inner.kind().is_trivia() { + return Some(inner); + } else { + token = inner.prev_token(); + } + } + None +} + +fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option { let token_sibling = non_trivia_sibling(element.clone(), Direction::Prev); - let mut wrapped = if let Some(sibling) = token_sibling { - sibling + if let Some(sibling) = token_sibling { + Some(sibling) } else { // if not trying to find first ancestor which has such a sibling let node = match element { @@ -98,21 +107,6 @@ fn previous_non_triva_element(element: SyntaxElement) -> Option { let prev_sibling_node = top_node.ancestors().find(|it| { non_trivia_sibling(NodeOrToken::Node(it.to_owned()), Direction::Prev).is_some() })?; - non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev)? - }; - // TODO: Check if this can be simplified - // Matklad: I think you can avoid this loop if you use SyntaxToken::prev_token -- unlike prev_sibling_or_token, it works across parents. - // traversing the tree down to get the last token or node, i.e. the closest one - loop { - if let Some(token) = wrapped.as_token() { - return Some(NodeOrToken::Token(token.clone())); - } else { - let new = wrapped.as_node().and_then(|n| n.last_child_or_token()); - if new.is_some() { - wrapped = new.unwrap().clone(); - } else { - return Some(wrapped); - } - } + non_trivia_sibling(NodeOrToken::Node(prev_sibling_node), Direction::Prev) } } -- cgit v1.2.3 From a2b4385f161134955fd729087f142d54c3a5e035 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 12 Jun 2020 00:17:30 +0200 Subject: Add few smoke tests for patterns and refactoring --- crates/ra_ide/src/completion/complete_keyword.rs | 8 +- crates/ra_ide/src/completion/completion_context.rs | 13 ++-- crates/ra_ide/src/completion/patterns.rs | 86 +++++++++++++++++++++- crates/ra_ide/src/completion/test_utils.rs | 14 ++++ 4 files changed, 111 insertions(+), 10 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 50f003949..675991154 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -65,13 +65,13 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte add_keyword(ctx, acc, "use", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); add_keyword(ctx, acc, "impl", "impl $0 {}", ctx.is_new_item); add_keyword(ctx, acc, "trait", "impl $0 {}", ctx.is_new_item); - add_keyword(ctx, acc, "enum", "enum $0 {}", ctx.is_new_item && !ctx.after_unsafe); - add_keyword(ctx, acc, "struct", "struct $0 {}", ctx.is_new_item && !ctx.after_unsafe); - add_keyword(ctx, acc, "union", "union $0 {}", ctx.is_new_item && !ctx.after_unsafe); + add_keyword(ctx, acc, "enum", "enum $0 {}", ctx.is_new_item && !ctx.unsafe_is_prev); + add_keyword(ctx, acc, "struct", "struct $0 {}", ctx.is_new_item && !ctx.unsafe_is_prev); + add_keyword(ctx, acc, "union", "union $0 {}", ctx.is_new_item && !ctx.unsafe_is_prev); add_keyword(ctx, acc, "match", "match $0 {}", ctx.block_expr_parent); add_keyword(ctx, acc, "loop", "loop {$0}", ctx.block_expr_parent); add_keyword(ctx, acc, "while", "while $0 {}", ctx.block_expr_parent); - add_keyword(ctx, acc, "let", "let ", ctx.after_if || ctx.block_expr_parent); + add_keyword(ctx, acc, "let", "let ", ctx.if_is_prev || ctx.block_expr_parent); add_keyword(ctx, acc, "else", "else {$0}", ctx.after_if); add_keyword(ctx, acc, "else if", "else if $0 {}", ctx.after_if); add_keyword(ctx, acc, "mod", "mod $0 {}", ctx.is_new_item || ctx.block_expr_parent); diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index e579e2ee2..41aec5686 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -12,8 +12,8 @@ use ra_syntax::{ use ra_text_edit::Indel; use super::patterns::{ - goes_after_unsafe, has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, - has_ref_pat_parent, has_trait_as_prev_sibling, inside_trait, is_in_loop_body, + has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_ref_pat_parent, + has_trait_as_prev_sibling, if_is_prev, inside_trait, is_in_loop_body, unsafe_is_prev, }; use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; use test_utils::mark; @@ -64,7 +64,8 @@ pub(crate) struct CompletionContext<'a> { pub(super) is_path_type: bool, pub(super) has_type_args: bool, pub(super) attribute_under_caret: Option, - pub(super) after_unsafe: bool, + pub(super) unsafe_is_prev: bool, + pub(super) if_is_prev: bool, pub(super) block_expr_parent: bool, pub(super) bind_pat_parent: bool, pub(super) ref_pat_parent: bool, @@ -130,7 +131,7 @@ impl<'a> CompletionContext<'a> { has_type_args: false, dot_receiver_is_ambiguous_float_literal: false, attribute_under_caret: None, - after_unsafe: false, + unsafe_is_prev: false, in_loop_body: false, ref_pat_parent: false, bind_pat_parent: false, @@ -138,6 +139,7 @@ impl<'a> CompletionContext<'a> { inside_trait: false, trait_as_prev_sibling: false, impl_as_prev_sibling: false, + if_is_prev: false, }; let mut original_file = original_file.syntax().clone(); @@ -212,7 +214,8 @@ impl<'a> CompletionContext<'a> { let fake_ident_token = file_with_fake_ident.token_at_offset(offset).right_biased().unwrap(); let syntax_element = NodeOrToken::Token(fake_ident_token.clone()); self.block_expr_parent = has_block_expr_parent(syntax_element.clone()); - self.after_unsafe = goes_after_unsafe(syntax_element.clone()); + self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone()); + self.if_is_prev = if_is_prev(syntax_element.clone()); self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); self.ref_pat_parent = has_ref_pat_parent(syntax_element.clone()); self.in_loop_body = is_in_loop_body(syntax_element.clone()); diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs index 31d32ccd9..df2594152 100644 --- a/crates/ra_ide/src/completion/patterns.rs +++ b/crates/ra_ide/src/completion/patterns.rs @@ -22,7 +22,7 @@ pub(crate) fn has_ref_pat_parent(element: SyntaxElement) -> bool { element.ancestors().find(|it| it.kind() == REF_PAT).is_some() } -pub(crate) fn goes_after_unsafe(element: SyntaxElement) -> bool { +pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool { element .into_token() .and_then(|it| previous_non_trivia_token(it)) @@ -30,6 +30,14 @@ pub(crate) fn goes_after_unsafe(element: SyntaxElement) -> bool { .is_some() } +pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { + element + .into_token() + .and_then(|it| previous_non_trivia_token(it)) + .filter(|it| it.kind() == IF_KW) + .is_some() +} + pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() } @@ -110,3 +118,79 @@ fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option + ", + unsafe_is_prev, + ); + } + + #[test] + fn test_if_is_prev() { + check_pattern_is_applicable( + r" + if l<|> + ", + if_is_prev, + ); + } + + #[test] + fn test_inside_trait() { + check_pattern_is_applicable( + r" + trait A { + fn<|> + } + ", + inside_trait, + ); + } + + #[test] + fn test_has_trait_as_prev_sibling() { + check_pattern_is_applicable( + r" + trait A w<|> { + } + ", + has_trait_as_prev_sibling, + ); + } + + #[test] + fn test_has_impl_as_prev_sibling() { + check_pattern_is_applicable( + r" + impl A w<|> { + } + ", + has_impl_as_prev_sibling, + ); + } + + #[test] + fn test_parent_block_expr() { + check_pattern_is_applicable( + r" + fn my_fn() { + let a = 2; + f<|> + } + ", + has_block_expr_parent, + ); + } +} diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index bf22452a2..e9e89104e 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs @@ -5,6 +5,8 @@ use crate::{ mock_analysis::{analysis_and_position, single_file_with_position}, CompletionItem, }; +use hir::Semantics; +use ra_syntax::{AstNode, NodeOrToken, SyntaxElement, SyntaxToken}; pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec { do_completion_with_options(code, kind, &CompletionConfig::default()) @@ -27,3 +29,15 @@ pub(crate) fn do_completion_with_options( kind_completions.sort_by_key(|c| c.label().to_owned()); kind_completions } + +pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { + let (analysis, pos) = single_file_with_position(code); + analysis + .with_db(|db| { + let sema = Semantics::new(db); + let original_file = sema.parse(pos.file_id); + let token = original_file.syntax().token_at_offset(pos.offset).left_biased().unwrap(); + assert!(check(NodeOrToken::Token(token))); + }) + .unwrap(); +} -- cgit v1.2.3 From 396167eadbea168e0d9858b5f45b7db860873f8b Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 12 Jun 2020 08:49:12 +0200 Subject: New testing approach for keywords --- crates/ra_ide/src/completion/complete_keyword.rs | 43 +++++++++++++++++++++--- crates/ra_ide/src/completion/test_utils.rs | 19 +++++++++-- 2 files changed, 56 insertions(+), 6 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 675991154..7eddf76b9 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -76,8 +76,6 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte add_keyword(ctx, acc, "else if", "else if $0 {}", ctx.after_if); add_keyword(ctx, acc, "mod", "mod $0 {}", ctx.is_new_item || ctx.block_expr_parent); add_keyword(ctx, acc, "mut", "mut ", ctx.bind_pat_parent || ctx.ref_pat_parent); - add_keyword(ctx, acc, "true", "true", !ctx.is_new_item); // this should be defined properly - add_keyword(ctx, acc, "false", "false", !ctx.is_new_item); // this should be defined properly add_keyword(ctx, acc, "const", "const ", ctx.is_new_item || ctx.block_expr_parent); add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent); add_keyword(ctx, acc, "static", "static ", ctx.is_new_item || ctx.block_expr_parent); @@ -89,7 +87,6 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte add_keyword(ctx, acc, "break", "break", ctx.in_loop_body && !ctx.can_be_stmt); add_keyword(ctx, acc, "pub", "pub ", ctx.is_new_item && !ctx.inside_trait); add_keyword(ctx, acc, "where", "where ", ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling); - complete_use_tree_keyword(acc, ctx); let fn_def = match &ctx.function_syntax { Some(it) => it, @@ -114,13 +111,51 @@ fn complete_return( #[cfg(test)] mod tests { - use crate::completion::{test_utils::do_completion, CompletionItem, CompletionKind}; + use crate::{ + completion::{ + test_utils::{do_completion, do_completion_with_position}, + CompletionItem, CompletionKind, + }, + CompletionItemKind, + }; use insta::assert_debug_snapshot; + use rustc_hash::FxHashSet; fn do_keyword_completion(code: &str) -> Vec { do_completion(code, CompletionKind::Keyword) } + fn assert_completion_keyword(code: &str, keywords: &[(&str, &str)]) { + let (position, completion_items) = + do_completion_with_position(code, CompletionKind::Keyword); + let mut set = FxHashSet::<(String, String)>::default(); + for (key, value) in keywords { + set.insert(((*key).to_string(), (*value).to_string())); + } + + for item in completion_items { + assert!(item.text_edit().len() == 1); + assert!(item.kind() == Some(CompletionItemKind::Keyword)); + let atom = item.text_edit().iter().next().unwrap().clone(); + assert!(atom.delete.start() == position.offset); + assert!(atom.delete.end() == position.offset); + let pair = (item.label().to_string(), atom.insert); + assert!(set.contains(&pair)); + set.remove(&pair); + } + assert!(set.is_empty()); + } + + #[test] + fn completes_keywords_in_use_stmt_new_approach() { + assert_completion_keyword( + r" + use <|> + ", + &[("crate", "crate::"), ("self", "self"), ("super", "super::")], + ); + } + #[test] fn completes_keywords_in_use_stmt() { assert_debug_snapshot!( diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index e9e89104e..277d2904d 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs @@ -3,7 +3,7 @@ use crate::{ completion::{completion_item::CompletionKind, CompletionConfig}, mock_analysis::{analysis_and_position, single_file_with_position}, - CompletionItem, + CompletionItem, FilePosition, }; use hir::Semantics; use ra_syntax::{AstNode, NodeOrToken, SyntaxElement, SyntaxToken}; @@ -12,11 +12,26 @@ pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec (FilePosition, Vec) { + do_completion_with_options_and_position(code, kind, &CompletionConfig::default()) +} + pub(crate) fn do_completion_with_options( code: &str, kind: CompletionKind, options: &CompletionConfig, ) -> Vec { + do_completion_with_options_and_position(code, kind, options).1 +} + +pub(crate) fn do_completion_with_options_and_position( + code: &str, + kind: CompletionKind, + options: &CompletionConfig, +) -> (FilePosition, Vec) { let (analysis, position) = if code.contains("//-") { analysis_and_position(code) } else { @@ -27,7 +42,7 @@ pub(crate) fn do_completion_with_options( let mut kind_completions: Vec = completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); kind_completions.sort_by_key(|c| c.label().to_owned()); - kind_completions + (position, kind_completions) } pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { -- cgit v1.2.3 From 4c92f2d19089c28c59aec27ff7e1b620eeef6f8e Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 12 Jun 2020 10:12:15 +0200 Subject: Add more pattern tests --- crates/ra_ide/src/completion/patterns.rs | 59 +++++++++++++++++++++++++----- crates/ra_ide/src/completion/test_utils.rs | 2 +- 2 files changed, 50 insertions(+), 11 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs index df2594152..25be0c307 100644 --- a/crates/ra_ide/src/completion/patterns.rs +++ b/crates/ra_ide/src/completion/patterns.rs @@ -6,10 +6,6 @@ use ra_syntax::{ SyntaxNode, SyntaxToken, }; -pub(crate) fn inside_impl(element: SyntaxElement) -> bool { - element.ancestors().find(|it| it.kind() == IMPL_DEF).is_some() -} - pub(crate) fn inside_trait(element: SyntaxElement) -> bool { element.ancestors().find(|it| it.kind() == TRAIT_DEF).is_some() } @@ -42,10 +38,6 @@ pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() } -pub(crate) fn has_item_list_parent(element: SyntaxElement) -> bool { - not_same_range_ancestor(element).filter(|it| it.kind() == ITEM_LIST).is_some() -} - pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool { previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some() } @@ -122,8 +114,8 @@ fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option) { + let a = 2; + } + ", + has_ref_pat_parent, + ); + } + + #[test] + fn test_has_ref_pat_parent_in_let_statement() { + check_pattern_is_applicable( + r" + fn my_fn() { + let &<|> + } + ", + has_ref_pat_parent, + ); + } + + #[test] + fn test_has_bind_pat_parent_in_func_parameters() { + check_pattern_is_applicable( + r" + fn my_fn(m<|>) { + } + ", + has_bind_pat_parent, + ); + } + + #[test] + fn test_has_bind_pat_parent_in_let_statement() { + check_pattern_is_applicable( + r" + fn my_fn() { + let m<|> + } + ", + has_bind_pat_parent, + ); + } } diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index 277d2904d..f439f974d 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs @@ -6,7 +6,7 @@ use crate::{ CompletionItem, FilePosition, }; use hir::Semantics; -use ra_syntax::{AstNode, NodeOrToken, SyntaxElement, SyntaxToken}; +use ra_syntax::{AstNode, NodeOrToken, SyntaxElement}; pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec { do_completion_with_options(code, kind, &CompletionConfig::default()) -- cgit v1.2.3 From f123539ad20c6c082a5e6aa69816429c3f73935a Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 12 Jun 2020 12:15:53 +0200 Subject: More assert refactoring --- crates/ra_ide/src/completion/complete_keyword.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 7eddf76b9..2e76f8592 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -118,7 +118,7 @@ mod tests { }, CompletionItemKind, }; - use insta::assert_debug_snapshot; + use insta::{assert_snapshot, assert_debug_snapshot}; use rustc_hash::FxHashSet; fn do_keyword_completion(code: &str) -> Vec { @@ -128,11 +128,12 @@ mod tests { fn assert_completion_keyword(code: &str, keywords: &[(&str, &str)]) { let (position, completion_items) = do_completion_with_position(code, CompletionKind::Keyword); - let mut set = FxHashSet::<(String, String)>::default(); + let mut expected_keywords = FxHashSet::<(String, String)>::default(); for (key, value) in keywords { - set.insert(((*key).to_string(), (*value).to_string())); + expected_keywords.insert(((*key).to_string(), (*value).to_string())); } - + let mut returned_keywords = FxHashSet::<(String, String)>::default(); + for item in completion_items { assert!(item.text_edit().len() == 1); assert!(item.kind() == Some(CompletionItemKind::Keyword)); @@ -140,10 +141,10 @@ mod tests { assert!(atom.delete.start() == position.offset); assert!(atom.delete.end() == position.offset); let pair = (item.label().to_string(), atom.insert); - assert!(set.contains(&pair)); - set.remove(&pair); + returned_keywords.insert(pair); } - assert!(set.is_empty()); + let assert_failed_message = format!("Expected keywords: {:#?}\nReceived keywords: {:#?}", expected_keywords, returned_keywords); + debug_assert!(returned_keywords == expected_keywords, assert_failed_message); } #[test] -- cgit v1.2.3 From d38bf1624d335651dc297fdd92672cdb0ee2ef4f Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 12 Jun 2020 13:06:26 +0200 Subject: Return snapshots to tests --- crates/ra_ide/src/completion/complete_keyword.rs | 52 ++++++++++++++++-------- 1 file changed, 34 insertions(+), 18 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 2e76f8592..feaa438af 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -118,43 +118,59 @@ mod tests { }, CompletionItemKind, }; - use insta::{assert_snapshot, assert_debug_snapshot}; - use rustc_hash::FxHashSet; + use insta::assert_debug_snapshot; fn do_keyword_completion(code: &str) -> Vec { do_completion(code, CompletionKind::Keyword) } - fn assert_completion_keyword(code: &str, keywords: &[(&str, &str)]) { + fn get_completion_text_and_assert_positions(code: &str) -> Vec<(String, String)> { let (position, completion_items) = do_completion_with_position(code, CompletionKind::Keyword); - let mut expected_keywords = FxHashSet::<(String, String)>::default(); - for (key, value) in keywords { - expected_keywords.insert(((*key).to_string(), (*value).to_string())); - } - let mut returned_keywords = FxHashSet::<(String, String)>::default(); - + let mut returned_keywords = Vec::<(String, String)>::new(); + for item in completion_items { - assert!(item.text_edit().len() == 1); + debug_assert!(item.text_edit().len() == 1); assert!(item.kind() == Some(CompletionItemKind::Keyword)); let atom = item.text_edit().iter().next().unwrap().clone(); assert!(atom.delete.start() == position.offset); assert!(atom.delete.end() == position.offset); let pair = (item.label().to_string(), atom.insert); - returned_keywords.insert(pair); + returned_keywords.push(pair); } - let assert_failed_message = format!("Expected keywords: {:#?}\nReceived keywords: {:#?}", expected_keywords, returned_keywords); - debug_assert!(returned_keywords == expected_keywords, assert_failed_message); + returned_keywords.sort(); + returned_keywords } #[test] fn completes_keywords_in_use_stmt_new_approach() { - assert_completion_keyword( - r" - use <|> - ", - &[("crate", "crate::"), ("self", "self"), ("super", "super::")], + assert_debug_snapshot!( + get_completion_text_and_assert_positions(r" + use <|> + "), + @r###" + [ + ( + "crate", + "crate::", + ), + ( + "self", + "self", + ), + ( + "super", + "super::", + ), + ] + "### ); + // assert_completion_keyword( + // r" + // use <|> + // ", + // &[("crate", "crate::"), ("self", "self"), ("super", "super::")], + // ); } #[test] -- cgit v1.2.3 From 42a719ad255c62933f58c2b2a5e767fb3b3c5a26 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 12 Jun 2020 13:14:53 +0200 Subject: Remove comment and incorrect assert --- crates/ra_ide/src/completion/complete_keyword.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index feaa438af..8ea51c7aa 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -130,7 +130,7 @@ mod tests { let mut returned_keywords = Vec::<(String, String)>::new(); for item in completion_items { - debug_assert!(item.text_edit().len() == 1); + assert!(item.text_edit().len() == 1); assert!(item.kind() == Some(CompletionItemKind::Keyword)); let atom = item.text_edit().iter().next().unwrap().clone(); assert!(atom.delete.start() == position.offset); @@ -165,12 +165,6 @@ mod tests { ] "### ); - // assert_completion_keyword( - // r" - // use <|> - // ", - // &[("crate", "crate::"), ("self", "self"), ("super", "super::")], - // ); } #[test] -- cgit v1.2.3 From 357667104371d446cc029267e8095365c17ba085 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Fri, 12 Jun 2020 20:30:57 +0200 Subject: Rewrite snapshot checks --- crates/ra_ide/src/completion/complete_keyword.rs | 183 ++++------------------- crates/ra_ide/src/completion/completion_item.rs | 26 ++++ crates/ra_ide/src/completion/patterns.rs | 84 ++--------- crates/ra_ide/src/completion/test_utils.rs | 52 ++++--- 4 files changed, 100 insertions(+), 245 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 8ea51c7aa..432793de2 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -61,7 +61,6 @@ fn add_keyword( pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); - add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent); add_keyword(ctx, acc, "use", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); add_keyword(ctx, acc, "impl", "impl $0 {}", ctx.is_new_item); add_keyword(ctx, acc, "trait", "impl $0 {}", ctx.is_new_item); @@ -111,12 +110,9 @@ fn complete_return( #[cfg(test)] mod tests { - use crate::{ - completion::{ - test_utils::{do_completion, do_completion_with_position}, - CompletionItem, CompletionKind, - }, - CompletionItemKind, + use crate::completion::{ + test_utils::{do_completion, get_completions}, + CompletionItem, CompletionKind, }; use insta::assert_debug_snapshot; @@ -124,132 +120,39 @@ mod tests { do_completion(code, CompletionKind::Keyword) } - fn get_completion_text_and_assert_positions(code: &str) -> Vec<(String, String)> { - let (position, completion_items) = - do_completion_with_position(code, CompletionKind::Keyword); - let mut returned_keywords = Vec::<(String, String)>::new(); - - for item in completion_items { - assert!(item.text_edit().len() == 1); - assert!(item.kind() == Some(CompletionItemKind::Keyword)); - let atom = item.text_edit().iter().next().unwrap().clone(); - assert!(atom.delete.start() == position.offset); - assert!(atom.delete.end() == position.offset); - let pair = (item.label().to_string(), atom.insert); - returned_keywords.push(pair); - } - returned_keywords.sort(); - returned_keywords - } - - #[test] - fn completes_keywords_in_use_stmt_new_approach() { - assert_debug_snapshot!( - get_completion_text_and_assert_positions(r" - use <|> - "), - @r###" - [ - ( - "crate", - "crate::", - ), - ( - "self", - "self", - ), - ( - "super", - "super::", - ), - ] - "### - ); + fn get_keyword_completions(code: &str) -> Vec { + get_completions(code, CompletionKind::Keyword) } #[test] fn completes_keywords_in_use_stmt() { assert_debug_snapshot!( - do_keyword_completion( - r" - use <|> - ", - ), + get_keyword_completions(r"use <|>"), @r###" [ - CompletionItem { - label: "crate", - source_range: 21..21, - delete: 21..21, - insert: "crate::", - kind: Keyword, - }, - CompletionItem { - label: "self", - source_range: 21..21, - delete: 21..21, - insert: "self", - kind: Keyword, - }, - CompletionItem { - label: "super", - source_range: 21..21, - delete: 21..21, - insert: "super::", - kind: Keyword, - }, + "kw crate", + "kw self", + "kw super", ] "### ); assert_debug_snapshot!( - do_keyword_completion( - r" - use a::<|> - ", - ), + get_keyword_completions(r"use a::<|>"), @r###" [ - CompletionItem { - label: "self", - source_range: 24..24, - delete: 24..24, - insert: "self", - kind: Keyword, - }, - CompletionItem { - label: "super", - source_range: 24..24, - delete: 24..24, - insert: "super::", - kind: Keyword, - }, + "kw self", + "kw super", ] "### ); assert_debug_snapshot!( - do_keyword_completion( - r" - use a::{b, <|>} - ", - ), + get_keyword_completions(r"use a::{b, <|>}"), @r###" [ - CompletionItem { - label: "self", - source_range: 28..28, - delete: 28..28, - insert: "self", - kind: Keyword, - }, - CompletionItem { - label: "super", - source_range: 28..28, - delete: 28..28, - insert: "super::", - kind: Keyword, - }, + "kw self", + "kw super", ] "### ); @@ -258,50 +161,22 @@ mod tests { #[test] fn completes_various_keywords_in_function() { assert_debug_snapshot!( - do_keyword_completion( - r" - fn quux() { - <|> - } - ", - ), + get_keyword_completions(r"fn quux() { <|> }"), @r###" [ - CompletionItem { - label: "if", - source_range: 49..49, - delete: 49..49, - insert: "if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "loop", - source_range: 49..49, - delete: 49..49, - insert: "loop {$0}", - kind: Keyword, - }, - CompletionItem { - label: "match", - source_range: 49..49, - delete: 49..49, - insert: "match $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "return", - source_range: 49..49, - delete: 49..49, - insert: "return;", - kind: Keyword, - }, - CompletionItem { - label: "while", - source_range: 49..49, - delete: 49..49, - insert: "while $0 {}", - kind: Keyword, - }, + "kw const", + "kw extern", + "kw fn", + "kw let", + "kw loop", + "kw match", + "kw mod", + "kw return", + "kw static", + "kw type", + "kw unsafe", + "kw use", + "kw while", ] "### ); diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index cfb7c1e38..d1a4dd881 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs @@ -125,6 +125,32 @@ pub enum CompletionItemKind { Attribute, } +impl CompletionItemKind { + pub fn tag(&self) -> String { + let tag = match self { + CompletionItemKind::Snippet => "sn", + CompletionItemKind::Keyword => "kw", + CompletionItemKind::Module => "md", + CompletionItemKind::Function => "fn", + CompletionItemKind::BuiltinType => "bt", + CompletionItemKind::Struct => "st", + CompletionItemKind::Enum => "en", + CompletionItemKind::EnumVariant => "ev", + CompletionItemKind::Binding => "bn", + CompletionItemKind::Field => "fd", + CompletionItemKind::Static => "sc", + CompletionItemKind::Const => "ct", + CompletionItemKind::Trait => "tt", + CompletionItemKind::TypeAlias => "ta", + CompletionItemKind::Method => "me", + CompletionItemKind::TypeParam => "tp", + CompletionItemKind::Macro => "ma", + CompletionItemKind::Attribute => "at", + }; + tag.to_owned() + } +} + #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub(crate) enum CompletionKind { /// Parser-based keyword completion. diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs index 25be0c307..bc39689ab 100644 --- a/crates/ra_ide/src/completion/patterns.rs +++ b/crates/ra_ide/src/completion/patterns.rs @@ -121,115 +121,51 @@ mod tests { #[test] fn test_unsafe_is_prev() { - check_pattern_is_applicable( - r" - unsafe i<|> - ", - unsafe_is_prev, - ); + check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev); } #[test] fn test_if_is_prev() { - check_pattern_is_applicable( - r" - if l<|> - ", - if_is_prev, - ); + check_pattern_is_applicable(r"if l<|>", if_is_prev); } #[test] fn test_inside_trait() { - check_pattern_is_applicable( - r" - trait A { - fn<|> - } - ", - inside_trait, - ); + check_pattern_is_applicable(r"trait A { fn<|> }", inside_trait); } #[test] fn test_has_trait_as_prev_sibling() { - check_pattern_is_applicable( - r" - trait A w<|> { - } - ", - has_trait_as_prev_sibling, - ); + check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling); } #[test] fn test_has_impl_as_prev_sibling() { - check_pattern_is_applicable( - r" - impl A w<|> { - } - ", - has_impl_as_prev_sibling, - ); + check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling); } #[test] fn test_parent_block_expr() { - check_pattern_is_applicable( - r" - fn my_fn() { - let a = 2; - f<|> - } - ", - has_block_expr_parent, - ); + check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent); } #[test] fn test_has_ref_pat_parent_in_func_parameters() { - check_pattern_is_applicable( - r" - fn my_fn(&<|>) { - let a = 2; - } - ", - has_ref_pat_parent, - ); + check_pattern_is_applicable(r"fn my_fn(&<|>) {}", has_ref_pat_parent); } #[test] fn test_has_ref_pat_parent_in_let_statement() { - check_pattern_is_applicable( - r" - fn my_fn() { - let &<|> - } - ", - has_ref_pat_parent, - ); + check_pattern_is_applicable(r"fn my_fn() { let &<|> }", has_ref_pat_parent); } #[test] fn test_has_bind_pat_parent_in_func_parameters() { - check_pattern_is_applicable( - r" - fn my_fn(m<|>) { - } - ", - has_bind_pat_parent, - ); + check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent); } #[test] fn test_has_bind_pat_parent_in_let_statement() { - check_pattern_is_applicable( - r" - fn my_fn() { - let m<|> - } - ", - has_bind_pat_parent, - ); + check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent); } } diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index f439f974d..c660de7b1 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs @@ -3,7 +3,7 @@ use crate::{ completion::{completion_item::CompletionKind, CompletionConfig}, mock_analysis::{analysis_and_position, single_file_with_position}, - CompletionItem, FilePosition, + CompletionItem, }; use hir::Semantics; use ra_syntax::{AstNode, NodeOrToken, SyntaxElement}; @@ -12,11 +12,8 @@ pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec (FilePosition, Vec) { - do_completion_with_options_and_position(code, kind, &CompletionConfig::default()) +pub(crate) fn get_completions(code: &str, kind: CompletionKind) -> Vec { + get_completions_with_options(code, kind, &CompletionConfig::default()) } pub(crate) fn do_completion_with_options( @@ -24,25 +21,46 @@ pub(crate) fn do_completion_with_options( kind: CompletionKind, options: &CompletionConfig, ) -> Vec { - do_completion_with_options_and_position(code, kind, options).1 + let mut kind_completions: Vec = get_all_completion_items(code, options) + .into_iter() + .filter(|c| c.completion_kind == kind) + .collect(); + kind_completions.sort_by_key(|c| c.label().to_owned()); + kind_completions } -pub(crate) fn do_completion_with_options_and_position( - code: &str, - kind: CompletionKind, - options: &CompletionConfig, -) -> (FilePosition, Vec) { +fn get_all_completion_items(code: &str, options: &CompletionConfig) -> Vec { let (analysis, position) = if code.contains("//-") { analysis_and_position(code) } else { single_file_with_position(code) }; - let completions = analysis.completions(options, position).unwrap().unwrap(); - let completion_items: Vec = completions.into(); - let mut kind_completions: Vec = - completion_items.into_iter().filter(|c| c.completion_kind == kind).collect(); + analysis.completions(options, position).unwrap().unwrap().into() +} + +pub(crate) fn get_all_completions(code: &str, options: &CompletionConfig) -> Vec { + let mut kind_completions = get_all_completion_items(code, options); + kind_completions.sort_by_key(|c| c.label().to_owned()); + kind_completions + .into_iter() + .map(|it| format!("{} {}", it.kind().unwrap().tag(), it.label())) + .collect() +} + +pub(crate) fn get_completions_with_options( + code: &str, + kind: CompletionKind, + options: &CompletionConfig, +) -> Vec { + let mut kind_completions: Vec = get_all_completion_items(code, options) + .into_iter() + .filter(|c| c.completion_kind == kind) + .collect(); kind_completions.sort_by_key(|c| c.label().to_owned()); - (position, kind_completions) + kind_completions + .into_iter() + .map(|it| format!("{} {}", it.kind().unwrap().tag(), it.label())) + .collect() } pub(crate) fn check_pattern_is_applicable(code: &str, check: fn(SyntaxElement) -> bool) { -- cgit v1.2.3 From 6feb52c12accbf0ef54475cf66a03e035b922749 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Sat, 13 Jun 2020 00:55:21 +0200 Subject: Add more patterns, tests and fix keywords --- crates/ra_ide/src/completion/complete_keyword.rs | 690 +++++++-------------- crates/ra_ide/src/completion/completion_context.rs | 19 +- crates/ra_ide/src/completion/patterns.rs | 61 +- 3 files changed, 297 insertions(+), 473 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 432793de2..79432113c 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -60,32 +60,104 @@ fn add_keyword( } pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { + let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; + if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { + add_keyword(ctx, acc, "where", "where ", true); + return; + } + if ctx.unsafe_is_prev { + add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); + add_keyword( + ctx, + acc, + "trait", + "trait $0 {}", + (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + ); + add_keyword( + ctx, + acc, + "impl", + "impl $0 {}", + (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + ); + return; + } add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); - add_keyword(ctx, acc, "use", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); - add_keyword(ctx, acc, "impl", "impl $0 {}", ctx.is_new_item); - add_keyword(ctx, acc, "trait", "impl $0 {}", ctx.is_new_item); - add_keyword(ctx, acc, "enum", "enum $0 {}", ctx.is_new_item && !ctx.unsafe_is_prev); - add_keyword(ctx, acc, "struct", "struct $0 {}", ctx.is_new_item && !ctx.unsafe_is_prev); - add_keyword(ctx, acc, "union", "union $0 {}", ctx.is_new_item && !ctx.unsafe_is_prev); - add_keyword(ctx, acc, "match", "match $0 {}", ctx.block_expr_parent); - add_keyword(ctx, acc, "loop", "loop {$0}", ctx.block_expr_parent); + add_keyword( + ctx, + acc, + "use", + "use ", + (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + ); + add_keyword( + ctx, + acc, + "impl", + "impl $0 {}", + (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + ); + add_keyword( + ctx, + acc, + "trait", + "trait $0 {}", + (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + ); + add_keyword(ctx, acc, "enum", "enum $0 {}", ctx.is_new_item && !has_trait_or_impl_parent); + add_keyword(ctx, acc, "struct", "struct $0 {}", ctx.is_new_item && !has_trait_or_impl_parent); + add_keyword(ctx, acc, "union", "union $0 {}", ctx.is_new_item && !has_trait_or_impl_parent); + add_keyword(ctx, acc, "match", "match $0 {}", ctx.block_expr_parent || ctx.is_match_arm); + add_keyword(ctx, acc, "loop", "loop {$0}", ctx.block_expr_parent || ctx.is_match_arm); add_keyword(ctx, acc, "while", "while $0 {}", ctx.block_expr_parent); add_keyword(ctx, acc, "let", "let ", ctx.if_is_prev || ctx.block_expr_parent); + add_keyword(ctx, acc, "if", "if ", ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm); + add_keyword( + ctx, + acc, + "if let", + "if let ", + ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm, + ); add_keyword(ctx, acc, "else", "else {$0}", ctx.after_if); add_keyword(ctx, acc, "else if", "else if $0 {}", ctx.after_if); - add_keyword(ctx, acc, "mod", "mod $0 {}", ctx.is_new_item || ctx.block_expr_parent); + add_keyword( + ctx, + acc, + "mod", + "mod $0 {}", + (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + ); add_keyword(ctx, acc, "mut", "mut ", ctx.bind_pat_parent || ctx.ref_pat_parent); add_keyword(ctx, acc, "const", "const ", ctx.is_new_item || ctx.block_expr_parent); add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent); - add_keyword(ctx, acc, "static", "static ", ctx.is_new_item || ctx.block_expr_parent); - add_keyword(ctx, acc, "extern", "extern ", ctx.is_new_item || ctx.block_expr_parent); - add_keyword(ctx, acc, "unsafe", "unsafe ", ctx.is_new_item || ctx.block_expr_parent); + add_keyword( + ctx, + acc, + "static", + "static ", + (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + ); + add_keyword( + ctx, + acc, + "extern", + "extern ", + (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + ); + add_keyword( + ctx, + acc, + "unsafe", + "unsafe ", + ctx.is_new_item || ctx.block_expr_parent || ctx.is_match_arm, + ); add_keyword(ctx, acc, "continue", "continue;", ctx.in_loop_body && ctx.can_be_stmt); add_keyword(ctx, acc, "break", "break;", ctx.in_loop_body && ctx.can_be_stmt); add_keyword(ctx, acc, "continue", "continue", ctx.in_loop_body && !ctx.can_be_stmt); add_keyword(ctx, acc, "break", "break", ctx.in_loop_body && !ctx.can_be_stmt); - add_keyword(ctx, acc, "pub", "pub ", ctx.is_new_item && !ctx.inside_trait); - add_keyword(ctx, acc, "where", "where ", ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling); + add_keyword(ctx, acc, "pub", "pub ", ctx.is_new_item && !ctx.has_trait_parent); let fn_def = match &ctx.function_syntax { Some(it) => it, @@ -111,21 +183,17 @@ fn complete_return( #[cfg(test)] mod tests { use crate::completion::{ - test_utils::{do_completion, get_completions}, - CompletionItem, CompletionKind, + test_utils::get_completions, + CompletionKind, }; use insta::assert_debug_snapshot; - fn do_keyword_completion(code: &str) -> Vec { - do_completion(code, CompletionKind::Keyword) - } - fn get_keyword_completions(code: &str) -> Vec { get_completions(code, CompletionKind::Keyword) } #[test] - fn completes_keywords_in_use_stmt() { + fn test_keywords_in_use_stmt() { assert_debug_snapshot!( get_keyword_completions(r"use <|>"), @r###" @@ -159,7 +227,7 @@ mod tests { } #[test] - fn completes_various_keywords_in_function() { + fn test_keywords_in_function() { assert_debug_snapshot!( get_keyword_completions(r"fn quux() { <|> }"), @r###" @@ -167,12 +235,16 @@ mod tests { "kw const", "kw extern", "kw fn", + "kw if", + "kw if let", + "kw impl", "kw let", "kw loop", "kw match", "kw mod", "kw return", "kw static", + "kw trait", "kw type", "kw unsafe", "kw use", @@ -183,9 +255,37 @@ mod tests { } #[test] - fn completes_else_after_if() { + fn test_keywords_inside_block() { assert_debug_snapshot!( - do_keyword_completion( + get_keyword_completions(r"fn quux() { if true { <|> } }"), + @r###" + [ + "kw const", + "kw extern", + "kw fn", + "kw if", + "kw if let", + "kw impl", + "kw let", + "kw loop", + "kw match", + "kw mod", + "kw return", + "kw static", + "kw trait", + "kw type", + "kw unsafe", + "kw use", + "kw while", + ] + "### + ); + } + + #[test] + fn test_keywords_after_if() { + assert_debug_snapshot!( + get_keyword_completions( r" fn quux() { if true { @@ -196,505 +296,189 @@ mod tests { ), @r###" [ - CompletionItem { - label: "else", - source_range: 108..108, - delete: 108..108, - insert: "else {$0}", - kind: Keyword, - }, - CompletionItem { - label: "else if", - source_range: 108..108, - delete: 108..108, - insert: "else if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "if", - source_range: 108..108, - delete: 108..108, - insert: "if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "loop", - source_range: 108..108, - delete: 108..108, - insert: "loop {$0}", - kind: Keyword, - }, - CompletionItem { - label: "match", - source_range: 108..108, - delete: 108..108, - insert: "match $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "return", - source_range: 108..108, - delete: 108..108, - insert: "return;", - kind: Keyword, - }, - CompletionItem { - label: "while", - source_range: 108..108, - delete: 108..108, - insert: "while $0 {}", - kind: Keyword, - }, + "kw const", + "kw else", + "kw else if", + "kw extern", + "kw fn", + "kw if", + "kw if let", + "kw impl", + "kw let", + "kw loop", + "kw match", + "kw mod", + "kw return", + "kw static", + "kw trait", + "kw type", + "kw unsafe", + "kw use", + "kw while", ] "### ); } #[test] - fn test_completion_return_value() { + fn test_keywords_in_match_arm() { assert_debug_snapshot!( - do_keyword_completion( + get_keyword_completions( r" fn quux() -> i32 { - <|> - 92 + match () { + () => <|> + } } ", ), @r###" [ - CompletionItem { - label: "if", - source_range: 56..56, - delete: 56..56, - insert: "if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "loop", - source_range: 56..56, - delete: 56..56, - insert: "loop {$0}", - kind: Keyword, - }, - CompletionItem { - label: "match", - source_range: 56..56, - delete: 56..56, - insert: "match $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "return", - source_range: 56..56, - delete: 56..56, - insert: "return $0;", - kind: Keyword, - }, - CompletionItem { - label: "while", - source_range: 56..56, - delete: 56..56, - insert: "while $0 {}", - kind: Keyword, - }, + "kw if", + "kw if let", + "kw loop", + "kw match", + "kw return", + "kw unsafe", ] "### ); + } + + #[test] + fn test_keywords_in_trait_def() { assert_debug_snapshot!( - do_keyword_completion( - r" - fn quux() { - <|> - 92 - } - ", - ), + get_keyword_completions(r"trait My { <|> }"), @r###" [ - CompletionItem { - label: "if", - source_range: 49..49, - delete: 49..49, - insert: "if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "loop", - source_range: 49..49, - delete: 49..49, - insert: "loop {$0}", - kind: Keyword, - }, - CompletionItem { - label: "match", - source_range: 49..49, - delete: 49..49, - insert: "match $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "return", - source_range: 49..49, - delete: 49..49, - insert: "return;", - kind: Keyword, - }, - CompletionItem { - label: "while", - source_range: 49..49, - delete: 49..49, - insert: "while $0 {}", - kind: Keyword, - }, + "kw const", + "kw fn", + "kw type", + "kw unsafe", ] "### ); } #[test] - fn dont_add_semi_after_return_if_not_a_statement() { + fn test_keywords_in_impl_def() { assert_debug_snapshot!( - do_keyword_completion( - r" - fn quux() -> i32 { - match () { - () => <|> - } - } - ", - ), + get_keyword_completions(r"impl My { <|> }"), @r###" [ - CompletionItem { - label: "if", - source_range: 97..97, - delete: 97..97, - insert: "if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "loop", - source_range: 97..97, - delete: 97..97, - insert: "loop {$0}", - kind: Keyword, - }, - CompletionItem { - label: "match", - source_range: 97..97, - delete: 97..97, - insert: "match $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "return", - source_range: 97..97, - delete: 97..97, - insert: "return $0", - kind: Keyword, - }, - CompletionItem { - label: "while", - source_range: 97..97, - delete: 97..97, - insert: "while $0 {}", - kind: Keyword, - }, + "kw const", + "kw fn", + "kw pub", + "kw type", + "kw unsafe", ] "### ); } #[test] - fn last_return_in_block_has_semi() { + fn test_keywords_in_loop() { assert_debug_snapshot!( - do_keyword_completion( - r" - fn quux() -> i32 { - if condition { - <|> - } - } - ", - ), + get_keyword_completions(r"fn my() { loop { <|> } }"), @r###" [ - CompletionItem { - label: "if", - source_range: 95..95, - delete: 95..95, - insert: "if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "loop", - source_range: 95..95, - delete: 95..95, - insert: "loop {$0}", - kind: Keyword, - }, - CompletionItem { - label: "match", - source_range: 95..95, - delete: 95..95, - insert: "match $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "return", - source_range: 95..95, - delete: 95..95, - insert: "return $0;", - kind: Keyword, - }, - CompletionItem { - label: "while", - source_range: 95..95, - delete: 95..95, - insert: "while $0 {}", - kind: Keyword, - }, + "kw break", + "kw const", + "kw continue", + "kw extern", + "kw fn", + "kw if", + "kw if let", + "kw impl", + "kw let", + "kw loop", + "kw match", + "kw mod", + "kw return", + "kw static", + "kw trait", + "kw type", + "kw unsafe", + "kw use", + "kw while", ] "### ); + } + + #[test] + fn test_keywords_after_unsafe_in_item_list() { assert_debug_snapshot!( - do_keyword_completion( - r" - fn quux() -> i32 { - if condition { - <|> - } - let x = 92; - x - } - ", - ), + get_keyword_completions(r"unsafe <|>"), @r###" [ - CompletionItem { - label: "if", - source_range: 95..95, - delete: 95..95, - insert: "if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "loop", - source_range: 95..95, - delete: 95..95, - insert: "loop {$0}", - kind: Keyword, - }, - CompletionItem { - label: "match", - source_range: 95..95, - delete: 95..95, - insert: "match $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "return", - source_range: 95..95, - delete: 95..95, - insert: "return $0;", - kind: Keyword, - }, - CompletionItem { - label: "while", - source_range: 95..95, - delete: 95..95, - insert: "while $0 {}", - kind: Keyword, - }, + "kw fn", + "kw impl", + "kw trait", ] "### ); } #[test] - fn completes_break_and_continue_in_loops() { + fn test_keywords_after_unsafe_in_block_expr() { assert_debug_snapshot!( - do_keyword_completion( - r" - fn quux() -> i32 { - loop { <|> } - } - ", - ), + get_keyword_completions(r"fn my_fn() { unsafe <|> }"), @r###" [ - CompletionItem { - label: "break", - source_range: 63..63, - delete: 63..63, - insert: "break;", - kind: Keyword, - }, - CompletionItem { - label: "continue", - source_range: 63..63, - delete: 63..63, - insert: "continue;", - kind: Keyword, - }, - CompletionItem { - label: "if", - source_range: 63..63, - delete: 63..63, - insert: "if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "loop", - source_range: 63..63, - delete: 63..63, - insert: "loop {$0}", - kind: Keyword, - }, - CompletionItem { - label: "match", - source_range: 63..63, - delete: 63..63, - insert: "match $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "return", - source_range: 63..63, - delete: 63..63, - insert: "return $0;", - kind: Keyword, - }, - CompletionItem { - label: "while", - source_range: 63..63, - delete: 63..63, - insert: "while $0 {}", - kind: Keyword, - }, + "kw fn", + "kw impl", + "kw trait", ] "### ); + } - // No completion: lambda isolates control flow + #[test] + fn test_mut_in_ref_and_in_fn_parameters_list() { assert_debug_snapshot!( - do_keyword_completion( - r" - fn quux() -> i32 { - loop { || { <|> } } - } - ", - ), + get_keyword_completions(r"fn my_fn(&<|>) {}"), + @r###" + [ + "kw mut", + ] + "### + ); + assert_debug_snapshot!( + get_keyword_completions(r"fn my_fn(<|>) {}"), + @r###" + [ + "kw mut", + ] + "### + ); + assert_debug_snapshot!( + get_keyword_completions(r"fn my_fn() { let &<|> }"), @r###" [ - CompletionItem { - label: "if", - source_range: 68..68, - delete: 68..68, - insert: "if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "loop", - source_range: 68..68, - delete: 68..68, - insert: "loop {$0}", - kind: Keyword, - }, - CompletionItem { - label: "match", - source_range: 68..68, - delete: 68..68, - insert: "match $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "return", - source_range: 68..68, - delete: 68..68, - insert: "return $0;", - kind: Keyword, - }, - CompletionItem { - label: "while", - source_range: 68..68, - delete: 68..68, - insert: "while $0 {}", - kind: Keyword, - }, + "kw mut", ] "### ); } #[test] - fn no_semi_after_break_continue_in_expr() { + fn test_where_keyword() { assert_debug_snapshot!( - do_keyword_completion( - r" - fn f() { - loop { - match () { - () => br<|> - } - } - } - ", - ), + get_keyword_completions(r"trait A <|>"), + @r###" + [ + "kw where", + ] + "### + ); + assert_debug_snapshot!( + get_keyword_completions(r"impl A <|>"), @r###" [ - CompletionItem { - label: "break", - source_range: 122..124, - delete: 122..124, - insert: "break", - kind: Keyword, - }, - CompletionItem { - label: "continue", - source_range: 122..124, - delete: 122..124, - insert: "continue", - kind: Keyword, - }, - CompletionItem { - label: "if", - source_range: 122..124, - delete: 122..124, - insert: "if $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "loop", - source_range: 122..124, - delete: 122..124, - insert: "loop {$0}", - kind: Keyword, - }, - CompletionItem { - label: "match", - source_range: 122..124, - delete: 122..124, - insert: "match $0 {}", - kind: Keyword, - }, - CompletionItem { - label: "return", - source_range: 122..124, - delete: 122..124, - insert: "return", - kind: Keyword, - }, - CompletionItem { - label: "while", - source_range: 122..124, - delete: 122..124, - insert: "while $0 {}", - kind: Keyword, - }, + "kw where", ] "### - ) + ); } } diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 41aec5686..2f96861ca 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -12,8 +12,9 @@ use ra_syntax::{ use ra_text_edit::Indel; use super::patterns::{ - has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_ref_pat_parent, - has_trait_as_prev_sibling, if_is_prev, inside_trait, is_in_loop_body, unsafe_is_prev, + has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent, + has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev, is_in_loop_body, + is_match_arm, unsafe_is_prev, }; use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; use test_utils::mark; @@ -70,9 +71,11 @@ pub(crate) struct CompletionContext<'a> { pub(super) bind_pat_parent: bool, pub(super) ref_pat_parent: bool, pub(super) in_loop_body: bool, - pub(super) inside_trait: bool, + pub(super) has_trait_parent: bool, + pub(super) has_impl_parent: bool, pub(super) trait_as_prev_sibling: bool, pub(super) impl_as_prev_sibling: bool, + pub(super) is_match_arm: bool, } impl<'a> CompletionContext<'a> { @@ -136,10 +139,12 @@ impl<'a> CompletionContext<'a> { ref_pat_parent: false, bind_pat_parent: false, block_expr_parent: false, - inside_trait: false, + has_trait_parent: false, + has_impl_parent: false, trait_as_prev_sibling: false, impl_as_prev_sibling: false, if_is_prev: false, + is_match_arm: false, }; let mut original_file = original_file.syntax().clone(); @@ -217,11 +222,13 @@ impl<'a> CompletionContext<'a> { self.unsafe_is_prev = unsafe_is_prev(syntax_element.clone()); self.if_is_prev = if_is_prev(syntax_element.clone()); self.bind_pat_parent = has_bind_pat_parent(syntax_element.clone()); - self.ref_pat_parent = has_ref_pat_parent(syntax_element.clone()); + self.ref_pat_parent = has_ref_parent(syntax_element.clone()); self.in_loop_body = is_in_loop_body(syntax_element.clone()); - self.inside_trait = inside_trait(syntax_element.clone()); + self.has_trait_parent = has_trait_parent(syntax_element.clone()); + self.has_impl_parent = has_impl_parent(syntax_element.clone()); self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); + self.is_match_arm = is_match_arm(syntax_element.clone()); } fn fill( diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs index bc39689ab..bc37196d5 100644 --- a/crates/ra_ide/src/completion/patterns.rs +++ b/crates/ra_ide/src/completion/patterns.rs @@ -6,16 +6,42 @@ use ra_syntax::{ SyntaxNode, SyntaxToken, }; -pub(crate) fn inside_trait(element: SyntaxElement) -> bool { - element.ancestors().find(|it| it.kind() == TRAIT_DEF).is_some() +pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { + not_same_range_ancestor(element) + .filter(|it| it.kind() == ITEM_LIST) + .and_then(|it| it.parent()) + .filter(|it| it.kind() == TRAIT_DEF) + .is_some() +} + +pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { + not_same_range_ancestor(element) + .filter(|it| it.kind() == ITEM_LIST) + .and_then(|it| it.parent()) + .filter(|it| it.kind() == IMPL_DEF) + .is_some() +} + +pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { + not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() } pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { element.ancestors().find(|it| it.kind() == BIND_PAT).is_some() } -pub(crate) fn has_ref_pat_parent(element: SyntaxElement) -> bool { - element.ancestors().find(|it| it.kind() == REF_PAT).is_some() +pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool { + not_same_range_ancestor(element) + .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR) + .is_some() +} + +pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { + not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some() + && previous_sibling_or_ancestor_sibling(element) + .and_then(|it| it.into_token()) + .filter(|it| it.kind() == FAT_ARROW) + .is_some() } pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool { @@ -34,10 +60,6 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { .is_some() } -pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { - not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() -} - pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool { previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some() } @@ -114,8 +136,9 @@ fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option }", inside_trait); + fn test_has_trait_parent() { + check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent); + } + + #[test] + fn test_has_impl_parent() { + check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent); } #[test] @@ -151,12 +179,12 @@ mod tests { #[test] fn test_has_ref_pat_parent_in_func_parameters() { - check_pattern_is_applicable(r"fn my_fn(&<|>) {}", has_ref_pat_parent); + check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent); } #[test] fn test_has_ref_pat_parent_in_let_statement() { - check_pattern_is_applicable(r"fn my_fn() { let &<|> }", has_ref_pat_parent); + check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent); } #[test] @@ -168,4 +196,9 @@ mod tests { fn test_has_bind_pat_parent_in_let_statement() { check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent); } + + #[test] + fn test_is_match_arm() { + check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm); + } } -- cgit v1.2.3 From eeb8b9e236796da8734ba81a49164864497f7226 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Sat, 13 Jun 2020 01:21:48 +0200 Subject: Fix tests and remove unused methods --- crates/ra_ide/src/completion/complete_keyword.rs | 8 ++++---- crates/ra_ide/src/completion/patterns.rs | 2 ++ crates/ra_ide/src/completion/test_utils.rs | 9 --------- 3 files changed, 6 insertions(+), 13 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 79432113c..025097e49 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -159,6 +159,9 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte add_keyword(ctx, acc, "break", "break", ctx.in_loop_body && !ctx.can_be_stmt); add_keyword(ctx, acc, "pub", "pub ", ctx.is_new_item && !ctx.has_trait_parent); + if !ctx.is_trivial_path { + return; + } let fn_def = match &ctx.function_syntax { Some(it) => it, None => return, @@ -182,10 +185,7 @@ fn complete_return( #[cfg(test)] mod tests { - use crate::completion::{ - test_utils::get_completions, - CompletionKind, - }; + use crate::completion::{test_utils::get_completions, CompletionKind}; use insta::assert_debug_snapshot; fn get_keyword_completions(code: &str) -> Vec { diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs index bc37196d5..464032cb4 100644 --- a/crates/ra_ide/src/completion/patterns.rs +++ b/crates/ra_ide/src/completion/patterns.rs @@ -1,3 +1,5 @@ +//! Patterns telling us certain facts about current syntax element, they are used in completion context + use ra_syntax::{ algo::non_trivia_sibling, ast::{self, LoopBodyOwner}, diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index c660de7b1..8b838a0a5 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs @@ -38,15 +38,6 @@ fn get_all_completion_items(code: &str, options: &CompletionConfig) -> Vec Vec { - let mut kind_completions = get_all_completion_items(code, options); - kind_completions.sort_by_key(|c| c.label().to_owned()); - kind_completions - .into_iter() - .map(|it| format!("{} {}", it.kind().unwrap().tag(), it.label())) - .collect() -} - pub(crate) fn get_completions_with_options( code: &str, kind: CompletionKind, -- cgit v1.2.3 From 912f38200fb6c8c6cea750a84c04de810992dec7 Mon Sep 17 00:00:00 2001 From: Mikhail Rakhmanov Date: Sat, 13 Jun 2020 10:43:39 +0200 Subject: Add keywords completions on source file position --- crates/ra_ide/src/completion/complete_keyword.rs | 115 ++++++++++++++++++--- crates/ra_ide/src/completion/completion_context.rs | 8 +- crates/ra_ide/src/completion/patterns.rs | 22 +++- 3 files changed, 124 insertions(+), 21 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 025097e49..d016f0c7c 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -66,48 +66,83 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte return; } if ctx.unsafe_is_prev { - add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); + add_keyword( + ctx, + acc, + "fn", + "fn $0() {}", + ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent, + ); add_keyword( ctx, acc, "trait", "trait $0 {}", - (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent, ); add_keyword( ctx, acc, "impl", "impl $0 {}", - (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent, ); return; } - add_keyword(ctx, acc, "fn", "fn $0() {}", ctx.is_new_item || ctx.block_expr_parent); + add_keyword( + ctx, + acc, + "fn", + "fn $0() {}", + ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent, + ); add_keyword( ctx, acc, "use", "use ", - (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent, ); add_keyword( ctx, acc, "impl", "impl $0 {}", - (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent, ); add_keyword( ctx, acc, "trait", "trait $0 {}", - (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent, + ); + add_keyword( + ctx, + acc, + "enum", + "enum $0 {}", + ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent, + ); + add_keyword( + ctx, + acc, + "struct", + "struct $0 {}", + ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent, + ); + add_keyword( + ctx, + acc, + "union", + "union $0 {}", + ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent, ); - add_keyword(ctx, acc, "enum", "enum $0 {}", ctx.is_new_item && !has_trait_or_impl_parent); - add_keyword(ctx, acc, "struct", "struct $0 {}", ctx.is_new_item && !has_trait_or_impl_parent); - add_keyword(ctx, acc, "union", "union $0 {}", ctx.is_new_item && !has_trait_or_impl_parent); add_keyword(ctx, acc, "match", "match $0 {}", ctx.block_expr_parent || ctx.is_match_arm); add_keyword(ctx, acc, "loop", "loop {$0}", ctx.block_expr_parent || ctx.is_match_arm); add_keyword(ctx, acc, "while", "while $0 {}", ctx.block_expr_parent); @@ -127,37 +162,58 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte acc, "mod", "mod $0 {}", - (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent, ); add_keyword(ctx, acc, "mut", "mut ", ctx.bind_pat_parent || ctx.ref_pat_parent); - add_keyword(ctx, acc, "const", "const ", ctx.is_new_item || ctx.block_expr_parent); - add_keyword(ctx, acc, "type", "type ", ctx.is_new_item || ctx.block_expr_parent); + add_keyword( + ctx, + acc, + "const", + "const ", + ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent, + ); + add_keyword( + ctx, + acc, + "type", + "type ", + ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent, + ); add_keyword( ctx, acc, "static", "static ", - (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent, ); add_keyword( ctx, acc, "extern", "extern ", - (ctx.is_new_item && !has_trait_or_impl_parent) || ctx.block_expr_parent, + (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent, ); add_keyword( ctx, acc, "unsafe", "unsafe ", - ctx.is_new_item || ctx.block_expr_parent || ctx.is_match_arm, + ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm, ); add_keyword(ctx, acc, "continue", "continue;", ctx.in_loop_body && ctx.can_be_stmt); add_keyword(ctx, acc, "break", "break;", ctx.in_loop_body && ctx.can_be_stmt); add_keyword(ctx, acc, "continue", "continue", ctx.in_loop_body && !ctx.can_be_stmt); add_keyword(ctx, acc, "break", "break", ctx.in_loop_body && !ctx.can_be_stmt); - add_keyword(ctx, acc, "pub", "pub ", ctx.is_new_item && !ctx.has_trait_parent); + add_keyword( + ctx, + acc, + "pub", + "pub ", + ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent, + ); if !ctx.is_trivial_path { return; @@ -226,6 +282,31 @@ mod tests { ); } + #[test] + fn test_keywords_at_source_file_level() { + assert_debug_snapshot!( + get_keyword_completions(r"m<|>"), + @r###" + [ + "kw const", + "kw enum", + "kw extern", + "kw fn", + "kw impl", + "kw mod", + "kw pub", + "kw static", + "kw struct", + "kw trait", + "kw type", + "kw union", + "kw unsafe", + "kw use", + ] + "### + ); + } + #[test] fn test_keywords_in_function() { assert_debug_snapshot!( diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 2f96861ca..9f4c582d0 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -13,8 +13,8 @@ use ra_text_edit::Indel; use super::patterns::{ has_bind_pat_parent, has_block_expr_parent, has_impl_as_prev_sibling, has_impl_parent, - has_ref_parent, has_trait_as_prev_sibling, has_trait_parent, if_is_prev, is_in_loop_body, - is_match_arm, unsafe_is_prev, + has_item_list_or_source_file_parent, has_ref_parent, has_trait_as_prev_sibling, + has_trait_parent, if_is_prev, is_in_loop_body, is_match_arm, unsafe_is_prev, }; use crate::{call_info::ActiveParameter, completion::CompletionConfig, FilePosition}; use test_utils::mark; @@ -76,6 +76,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) trait_as_prev_sibling: bool, pub(super) impl_as_prev_sibling: bool, pub(super) is_match_arm: bool, + pub(super) has_item_list_or_source_file_parent: bool, } impl<'a> CompletionContext<'a> { @@ -145,6 +146,7 @@ impl<'a> CompletionContext<'a> { impl_as_prev_sibling: false, if_is_prev: false, is_match_arm: false, + has_item_list_or_source_file_parent: false, }; let mut original_file = original_file.syntax().clone(); @@ -229,6 +231,8 @@ impl<'a> CompletionContext<'a> { self.impl_as_prev_sibling = has_impl_as_prev_sibling(syntax_element.clone()); self.trait_as_prev_sibling = has_trait_as_prev_sibling(syntax_element.clone()); self.is_match_arm = is_match_arm(syntax_element.clone()); + self.has_item_list_or_source_file_parent = + has_item_list_or_source_file_parent(syntax_element.clone()); } fn fill( diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs index 464032cb4..fdcd3faa5 100644 --- a/crates/ra_ide/src/completion/patterns.rs +++ b/crates/ra_ide/src/completion/patterns.rs @@ -38,6 +38,14 @@ pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool { .is_some() } +pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool { + let ancestor = not_same_range_ancestor(element); + if !ancestor.is_some() { + return true; + } + ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some() +} + pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some() && previous_sibling_or_ancestor_sibling(element) @@ -139,8 +147,8 @@ fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option m<|> } }", is_match_arm); } + + #[test] + fn test_has_source_file_parent() { + check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent); + } + + #[test] + fn test_has_item_list_parent() { + check_pattern_is_applicable(r"impl { f<|> }", has_item_list_or_source_file_parent); + } } -- cgit v1.2.3 From ef70076f1da391be8697aaf3c534e4e770561429 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Jun 2020 13:47:30 +0200 Subject: Cleanup --- crates/ra_ide/src/completion/complete_keyword.rs | 269 +++++++++-------------- crates/ra_ide/src/completion/completion_item.rs | 8 +- crates/ra_ide/src/completion/patterns.rs | 130 +++++------ crates/ra_ide/src/completion/test_utils.rs | 2 +- 4 files changed, 154 insertions(+), 255 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index d016f0c7c..0ab330b73 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -36,184 +36,98 @@ pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionC } } -fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { - let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) - .kind(CompletionItemKind::Keyword); - - match ctx.config.snippet_cap { - Some(cap) => res.insert_snippet(cap, snippet), - _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }), - } - .build() -} - -fn add_keyword( - ctx: &CompletionContext, - acc: &mut Completions, - kw: &str, - snippet: &str, - should_add: bool, -) { - if should_add { - acc.add(keyword(ctx, kw, snippet)); - } -} - pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) { let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent; if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling { - add_keyword(ctx, acc, "where", "where ", true); + add_keyword(ctx, acc, "where", "where "); return; } if ctx.unsafe_is_prev { - add_keyword( - ctx, - acc, - "fn", - "fn $0() {}", - ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent, - ); - add_keyword( - ctx, - acc, - "trait", - "trait $0 {}", - (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) - || ctx.block_expr_parent, - ); - add_keyword( - ctx, - acc, - "impl", - "impl $0 {}", - (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) - || ctx.block_expr_parent, - ); + if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { + add_keyword(ctx, acc, "fn", "fn $0() {}") + } + + if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent + { + add_keyword(ctx, acc, "trait", "trait $0 {}"); + add_keyword(ctx, acc, "impl", "impl $0 {}"); + } + return; } - add_keyword( - ctx, - acc, - "fn", - "fn $0() {}", - ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent, - ); - add_keyword( - ctx, - acc, - "use", - "use ", - (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) - || ctx.block_expr_parent, - ); - add_keyword( - ctx, - acc, - "impl", - "impl $0 {}", - (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) - || ctx.block_expr_parent, - ); - add_keyword( - ctx, - acc, - "trait", - "trait $0 {}", - (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) - || ctx.block_expr_parent, - ); - add_keyword( - ctx, - acc, - "enum", - "enum $0 {}", - ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent, - ); - add_keyword( - ctx, - acc, - "struct", - "struct $0 {}", - ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent, - ); - add_keyword( - ctx, - acc, - "union", - "union $0 {}", - ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent, - ); - add_keyword(ctx, acc, "match", "match $0 {}", ctx.block_expr_parent || ctx.is_match_arm); - add_keyword(ctx, acc, "loop", "loop {$0}", ctx.block_expr_parent || ctx.is_match_arm); - add_keyword(ctx, acc, "while", "while $0 {}", ctx.block_expr_parent); - add_keyword(ctx, acc, "let", "let ", ctx.if_is_prev || ctx.block_expr_parent); - add_keyword(ctx, acc, "if", "if ", ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm); - add_keyword( - ctx, - acc, - "if let", - "if let ", - ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm, - ); - add_keyword(ctx, acc, "else", "else {$0}", ctx.after_if); - add_keyword(ctx, acc, "else if", "else if $0 {}", ctx.after_if); - add_keyword( - ctx, - acc, - "mod", - "mod $0 {}", - (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) - || ctx.block_expr_parent, - ); - add_keyword(ctx, acc, "mut", "mut ", ctx.bind_pat_parent || ctx.ref_pat_parent); - add_keyword( - ctx, - acc, - "const", - "const ", - ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent, - ); - add_keyword( - ctx, - acc, - "type", - "type ", - ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent, - ); - add_keyword( - ctx, - acc, - "static", - "static ", - (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) - || ctx.block_expr_parent, - ); - add_keyword( - ctx, - acc, - "extern", - "extern ", - (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) - || ctx.block_expr_parent, - ); - add_keyword( - ctx, - acc, - "unsafe", - "unsafe ", - ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm, - ); - add_keyword(ctx, acc, "continue", "continue;", ctx.in_loop_body && ctx.can_be_stmt); - add_keyword(ctx, acc, "break", "break;", ctx.in_loop_body && ctx.can_be_stmt); - add_keyword(ctx, acc, "continue", "continue", ctx.in_loop_body && !ctx.can_be_stmt); - add_keyword(ctx, acc, "break", "break", ctx.in_loop_body && !ctx.can_be_stmt); - add_keyword( - ctx, - acc, - "pub", - "pub ", - ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent, - ); + if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { + add_keyword(ctx, acc, "fn", "fn $0() {}"); + } + if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent + { + add_keyword(ctx, acc, "use", "use "); + add_keyword(ctx, acc, "impl", "impl $0 {}"); + add_keyword(ctx, acc, "trait", "trait $0 {}"); + } + + if ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent { + add_keyword(ctx, acc, "enum", "enum $0 {}"); + add_keyword(ctx, acc, "struct", "struct $0 {}"); + add_keyword(ctx, acc, "union", "union $0 {}"); + } + + if ctx.block_expr_parent || ctx.is_match_arm { + add_keyword(ctx, acc, "match", "match $0 {}"); + add_keyword(ctx, acc, "loop", "loop {$0}"); + } + if ctx.block_expr_parent { + add_keyword(ctx, acc, "while", "while $0 {}"); + } + if ctx.if_is_prev || ctx.block_expr_parent { + add_keyword(ctx, acc, "let", "let "); + } + if ctx.if_is_prev || ctx.block_expr_parent || ctx.is_match_arm { + add_keyword(ctx, acc, "if", "if "); + add_keyword(ctx, acc, "if let", "if let "); + } + if ctx.after_if { + add_keyword(ctx, acc, "else", "else {$0}"); + add_keyword(ctx, acc, "else if", "else if $0 {}"); + } + if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent + { + add_keyword(ctx, acc, "mod", "mod $0 {}"); + } + if ctx.bind_pat_parent || ctx.ref_pat_parent { + add_keyword(ctx, acc, "mut", "mut "); + } + if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent { + add_keyword(ctx, acc, "const", "const "); + add_keyword(ctx, acc, "type", "type "); + } + if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent + { + add_keyword(ctx, acc, "static", "static "); + }; + if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent) + || ctx.block_expr_parent + { + add_keyword(ctx, acc, "extern", "extern "); + } + if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm { + add_keyword(ctx, acc, "unsafe", "unsafe "); + } + if ctx.in_loop_body { + if ctx.can_be_stmt { + add_keyword(ctx, acc, "continue", "continue;"); + add_keyword(ctx, acc, "break", "break;"); + } else { + add_keyword(ctx, acc, "continue", "continue"); + add_keyword(ctx, acc, "break", "break"); + } + } + if ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent { + add_keyword(ctx, acc, "pub", "pub ") + } if !ctx.is_trivial_path { return; @@ -225,6 +139,21 @@ pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionConte acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt)); } +fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem { + let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw) + .kind(CompletionItemKind::Keyword); + + match ctx.config.snippet_cap { + Some(cap) => res.insert_snippet(cap, snippet), + _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }), + } + .build() +} + +fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) { + acc.add(keyword(ctx, kw, snippet)); +} + fn complete_return( ctx: &CompletionContext, fn_def: &ast::FnDef, diff --git a/crates/ra_ide/src/completion/completion_item.rs b/crates/ra_ide/src/completion/completion_item.rs index d1a4dd881..98348b349 100644 --- a/crates/ra_ide/src/completion/completion_item.rs +++ b/crates/ra_ide/src/completion/completion_item.rs @@ -126,8 +126,9 @@ pub enum CompletionItemKind { } impl CompletionItemKind { - pub fn tag(&self) -> String { - let tag = match self { + #[cfg(test)] + pub(crate) fn tag(&self) -> &'static str { + match self { CompletionItemKind::Snippet => "sn", CompletionItemKind::Keyword => "kw", CompletionItemKind::Module => "md", @@ -146,8 +147,7 @@ impl CompletionItemKind { CompletionItemKind::TypeParam => "tp", CompletionItemKind::Macro => "ma", CompletionItemKind::Attribute => "at", - }; - tag.to_owned() + } } } diff --git a/crates/ra_ide/src/completion/patterns.rs b/crates/ra_ide/src/completion/patterns.rs index fdcd3faa5..b2fe13280 100644 --- a/crates/ra_ide/src/completion/patterns.rs +++ b/crates/ra_ide/src/completion/patterns.rs @@ -8,6 +8,9 @@ use ra_syntax::{ SyntaxNode, SyntaxToken, }; +#[cfg(test)] +use crate::completion::test_utils::check_pattern_is_applicable; + pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { not_same_range_ancestor(element) .filter(|it| it.kind() == ITEM_LIST) @@ -15,6 +18,10 @@ pub(crate) fn has_trait_parent(element: SyntaxElement) -> bool { .filter(|it| it.kind() == TRAIT_DEF) .is_some() } +#[test] +fn test_has_trait_parent() { + check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent); +} pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { not_same_range_ancestor(element) @@ -23,20 +30,38 @@ pub(crate) fn has_impl_parent(element: SyntaxElement) -> bool { .filter(|it| it.kind() == IMPL_DEF) .is_some() } +#[test] +fn test_has_impl_parent() { + check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent); +} pub(crate) fn has_block_expr_parent(element: SyntaxElement) -> bool { not_same_range_ancestor(element).filter(|it| it.kind() == BLOCK_EXPR).is_some() } +#[test] +fn test_has_block_expr_parent() { + check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent); +} pub(crate) fn has_bind_pat_parent(element: SyntaxElement) -> bool { element.ancestors().find(|it| it.kind() == BIND_PAT).is_some() } +#[test] +fn test_has_bind_pat_parent() { + check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent); + check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent); +} pub(crate) fn has_ref_parent(element: SyntaxElement) -> bool { not_same_range_ancestor(element) .filter(|it| it.kind() == REF_PAT || it.kind() == REF_EXPR) .is_some() } +#[test] +fn test_has_ref_parent() { + check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent); + check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent); +} pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> bool { let ancestor = not_same_range_ancestor(element); @@ -45,6 +70,11 @@ pub(crate) fn has_item_list_or_source_file_parent(element: SyntaxElement) -> boo } ancestor.filter(|it| it.kind() == SOURCE_FILE || it.kind() == ITEM_LIST).is_some() } +#[test] +fn test_has_item_list_or_source_file_parent() { + check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent); + check_pattern_is_applicable(r"impl { f<|> }", has_item_list_or_source_file_parent); +} pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { not_same_range_ancestor(element.clone()).filter(|it| it.kind() == MATCH_ARM).is_some() @@ -53,6 +83,10 @@ pub(crate) fn is_match_arm(element: SyntaxElement) -> bool { .filter(|it| it.kind() == FAT_ARROW) .is_some() } +#[test] +fn test_is_match_arm() { + check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm); +} pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool { element @@ -61,6 +95,10 @@ pub(crate) fn unsafe_is_prev(element: SyntaxElement) -> bool { .filter(|it| it.kind() == UNSAFE_KW) .is_some() } +#[test] +fn test_unsafe_is_prev() { + check_pattern_is_applicable(r"unsafe i<|>", unsafe_is_prev); +} pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { element @@ -69,14 +107,26 @@ pub(crate) fn if_is_prev(element: SyntaxElement) -> bool { .filter(|it| it.kind() == IF_KW) .is_some() } +#[test] +fn test_if_is_prev() { + check_pattern_is_applicable(r"if l<|>", if_is_prev); +} pub(crate) fn has_trait_as_prev_sibling(element: SyntaxElement) -> bool { previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == TRAIT_DEF).is_some() } +#[test] +fn test_has_trait_as_prev_sibling() { + check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling); +} pub(crate) fn has_impl_as_prev_sibling(element: SyntaxElement) -> bool { previous_sibling_or_ancestor_sibling(element).filter(|it| it.kind() == IMPL_DEF).is_some() } +#[test] +fn test_has_impl_as_prev_sibling() { + check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling); +} pub(crate) fn is_in_loop_body(element: SyntaxElement) -> bool { let leaf = match element { @@ -142,83 +192,3 @@ fn previous_sibling_or_ancestor_sibling(element: SyntaxElement) -> Option", unsafe_is_prev); - } - - #[test] - fn test_if_is_prev() { - check_pattern_is_applicable(r"if l<|>", if_is_prev); - } - - #[test] - fn test_has_trait_parent() { - check_pattern_is_applicable(r"trait A { f<|> }", has_trait_parent); - } - - #[test] - fn test_has_impl_parent() { - check_pattern_is_applicable(r"impl A { f<|> }", has_impl_parent); - } - - #[test] - fn test_has_trait_as_prev_sibling() { - check_pattern_is_applicable(r"trait A w<|> {}", has_trait_as_prev_sibling); - } - - #[test] - fn test_has_impl_as_prev_sibling() { - check_pattern_is_applicable(r"impl A w<|> {}", has_impl_as_prev_sibling); - } - - #[test] - fn test_parent_block_expr() { - check_pattern_is_applicable(r"fn my_fn() { let a = 2; f<|> }", has_block_expr_parent); - } - - #[test] - fn test_has_ref_pat_parent_in_func_parameters() { - check_pattern_is_applicable(r"fn my_fn(&m<|>) {}", has_ref_parent); - } - - #[test] - fn test_has_ref_pat_parent_in_let_statement() { - check_pattern_is_applicable(r"fn my() { let &m<|> }", has_ref_parent); - } - - #[test] - fn test_has_bind_pat_parent_in_func_parameters() { - check_pattern_is_applicable(r"fn my_fn(m<|>) {}", has_bind_pat_parent); - } - - #[test] - fn test_has_bind_pat_parent_in_let_statement() { - check_pattern_is_applicable(r"fn my_fn() { let m<|> }", has_bind_pat_parent); - } - - #[test] - fn test_is_match_arm() { - check_pattern_is_applicable(r"fn my_fn() { match () { () => m<|> } }", is_match_arm); - } - - #[test] - fn test_has_source_file_parent() { - check_pattern_is_applicable(r"i<|>", has_item_list_or_source_file_parent); - } - - #[test] - fn test_has_item_list_parent() { - check_pattern_is_applicable(r"impl { f<|> }", has_item_list_or_source_file_parent); - } -} diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index 8b838a0a5..ba724116d 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs @@ -25,7 +25,7 @@ pub(crate) fn do_completion_with_options( .into_iter() .filter(|c| c.completion_kind == kind) .collect(); - kind_completions.sort_by_key(|c| c.label().to_owned()); + kind_completions.sort_by(|l, r| l.label().cmp(r.label())); kind_completions } -- cgit v1.2.3 From b99b4953c9a4791f7f39ab208a1842086ad0f04c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 13 Jun 2020 13:57:18 +0200 Subject: More concise completion tests --- crates/ra_ide/src/completion/complete_keyword.rs | 318 ++++++++++------------- crates/ra_ide/src/completion/test_utils.rs | 10 +- 2 files changed, 146 insertions(+), 182 deletions(-) (limited to 'crates') diff --git a/crates/ra_ide/src/completion/complete_keyword.rs b/crates/ra_ide/src/completion/complete_keyword.rs index 0ab330b73..b2f621a11 100644 --- a/crates/ra_ide/src/completion/complete_keyword.rs +++ b/crates/ra_ide/src/completion/complete_keyword.rs @@ -170,131 +170,119 @@ fn complete_return( #[cfg(test)] mod tests { - use crate::completion::{test_utils::get_completions, CompletionKind}; - use insta::assert_debug_snapshot; + use crate::completion::{test_utils::completion_list, CompletionKind}; + use insta::assert_snapshot; - fn get_keyword_completions(code: &str) -> Vec { - get_completions(code, CompletionKind::Keyword) + fn get_keyword_completions(code: &str) -> String { + completion_list(code, CompletionKind::Keyword) } #[test] fn test_keywords_in_use_stmt() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"use <|>"), @r###" - [ - "kw crate", - "kw self", - "kw super", - ] + kw crate + kw self + kw super "### ); - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"use a::<|>"), @r###" - [ - "kw self", - "kw super", - ] + kw self + kw super "### ); - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"use a::{b, <|>}"), @r###" - [ - "kw self", - "kw super", - ] + kw self + kw super "### ); } #[test] fn test_keywords_at_source_file_level() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"m<|>"), @r###" - [ - "kw const", - "kw enum", - "kw extern", - "kw fn", - "kw impl", - "kw mod", - "kw pub", - "kw static", - "kw struct", - "kw trait", - "kw type", - "kw union", - "kw unsafe", - "kw use", - ] + kw const + kw enum + kw extern + kw fn + kw impl + kw mod + kw pub + kw static + kw struct + kw trait + kw type + kw union + kw unsafe + kw use "### ); } #[test] fn test_keywords_in_function() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"fn quux() { <|> }"), @r###" - [ - "kw const", - "kw extern", - "kw fn", - "kw if", - "kw if let", - "kw impl", - "kw let", - "kw loop", - "kw match", - "kw mod", - "kw return", - "kw static", - "kw trait", - "kw type", - "kw unsafe", - "kw use", - "kw while", - ] + kw const + kw extern + kw fn + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw static + kw trait + kw type + kw unsafe + kw use + kw while "### ); } #[test] fn test_keywords_inside_block() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"fn quux() { if true { <|> } }"), @r###" - [ - "kw const", - "kw extern", - "kw fn", - "kw if", - "kw if let", - "kw impl", - "kw let", - "kw loop", - "kw match", - "kw mod", - "kw return", - "kw static", - "kw trait", - "kw type", - "kw unsafe", - "kw use", - "kw while", - ] + kw const + kw extern + kw fn + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw static + kw trait + kw type + kw unsafe + kw use + kw while "### ); } #[test] fn test_keywords_after_if() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions( r" fn quux() { @@ -305,34 +293,32 @@ mod tests { ", ), @r###" - [ - "kw const", - "kw else", - "kw else if", - "kw extern", - "kw fn", - "kw if", - "kw if let", - "kw impl", - "kw let", - "kw loop", - "kw match", - "kw mod", - "kw return", - "kw static", - "kw trait", - "kw type", - "kw unsafe", - "kw use", - "kw while", - ] + kw const + kw else + kw else if + kw extern + kw fn + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw static + kw trait + kw type + kw unsafe + kw use + kw while "### ); } #[test] fn test_keywords_in_match_arm() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions( r" fn quux() -> i32 { @@ -343,151 +329,129 @@ mod tests { ", ), @r###" - [ - "kw if", - "kw if let", - "kw loop", - "kw match", - "kw return", - "kw unsafe", - ] + kw if + kw if let + kw loop + kw match + kw return + kw unsafe "### ); } #[test] fn test_keywords_in_trait_def() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"trait My { <|> }"), @r###" - [ - "kw const", - "kw fn", - "kw type", - "kw unsafe", - ] + kw const + kw fn + kw type + kw unsafe "### ); } #[test] fn test_keywords_in_impl_def() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"impl My { <|> }"), @r###" - [ - "kw const", - "kw fn", - "kw pub", - "kw type", - "kw unsafe", - ] + kw const + kw fn + kw pub + kw type + kw unsafe "### ); } #[test] fn test_keywords_in_loop() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"fn my() { loop { <|> } }"), @r###" - [ - "kw break", - "kw const", - "kw continue", - "kw extern", - "kw fn", - "kw if", - "kw if let", - "kw impl", - "kw let", - "kw loop", - "kw match", - "kw mod", - "kw return", - "kw static", - "kw trait", - "kw type", - "kw unsafe", - "kw use", - "kw while", - ] + kw break + kw const + kw continue + kw extern + kw fn + kw if + kw if let + kw impl + kw let + kw loop + kw match + kw mod + kw return + kw static + kw trait + kw type + kw unsafe + kw use + kw while "### ); } #[test] fn test_keywords_after_unsafe_in_item_list() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"unsafe <|>"), @r###" - [ - "kw fn", - "kw impl", - "kw trait", - ] + kw fn + kw impl + kw trait "### ); } #[test] fn test_keywords_after_unsafe_in_block_expr() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"fn my_fn() { unsafe <|> }"), @r###" - [ - "kw fn", - "kw impl", - "kw trait", - ] + kw fn + kw impl + kw trait "### ); } #[test] fn test_mut_in_ref_and_in_fn_parameters_list() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"fn my_fn(&<|>) {}"), @r###" - [ - "kw mut", - ] + kw mut "### ); - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"fn my_fn(<|>) {}"), @r###" - [ - "kw mut", - ] + kw mut "### ); - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"fn my_fn() { let &<|> }"), @r###" - [ - "kw mut", - ] + kw mut "### ); } #[test] fn test_where_keyword() { - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"trait A <|>"), @r###" - [ - "kw where", - ] + kw where "### ); - assert_debug_snapshot!( + assert_snapshot!( get_keyword_completions(r"impl A <|>"), @r###" - [ - "kw where", - ] + kw where "### ); } diff --git a/crates/ra_ide/src/completion/test_utils.rs b/crates/ra_ide/src/completion/test_utils.rs index ba724116d..1e16a43ca 100644 --- a/crates/ra_ide/src/completion/test_utils.rs +++ b/crates/ra_ide/src/completion/test_utils.rs @@ -12,8 +12,8 @@ pub(crate) fn do_completion(code: &str, kind: CompletionKind) -> Vec Vec { - get_completions_with_options(code, kind, &CompletionConfig::default()) +pub(crate) fn completion_list(code: &str, kind: CompletionKind) -> String { + completion_list_with_options(code, kind, &CompletionConfig::default()) } pub(crate) fn do_completion_with_options( @@ -38,11 +38,11 @@ fn get_all_completion_items(code: &str, options: &CompletionConfig) -> Vec Vec { +) -> String { let mut kind_completions: Vec = get_all_completion_items(code, options) .into_iter() .filter(|c| c.completion_kind == kind) @@ -50,7 +50,7 @@ pub(crate) fn get_completions_with_options( kind_completions.sort_by_key(|c| c.label().to_owned()); kind_completions .into_iter() - .map(|it| format!("{} {}", it.kind().unwrap().tag(), it.label())) + .map(|it| format!("{} {}\n", it.kind().unwrap().tag(), it.label())) .collect() } -- cgit v1.2.3