diff --git a/crates/ra_analysis/src/completion.rs b/crates/ra_analysis/src/completion.rs index 39066d51f69..883b3e8510d 100644 --- a/crates/ra_analysis/src/completion.rs +++ b/crates/ra_analysis/src/completion.rs @@ -2,7 +2,8 @@ mod completion_item; mod reference_completion; mod complete_fn_param; -mod complete_keywords; +mod complete_keyword; +mod complete_snippet; use ra_editor::find_node_at_offset; use ra_text_edit::AtomTextEdit; @@ -49,7 +50,9 @@ pub(crate) fn completions( let ctx = ctry!(SyntaxContext::new(&original_file, position.offset)); complete_fn_param::complete_fn_param(&mut acc, &ctx); - complete_keywords::complete_expr_keyword(&mut acc, &ctx); + complete_keyword::complete_expr_keyword(&mut acc, &ctx); + complete_snippet::complete_expr_snippet(&mut acc, &ctx); + complete_snippet::complete_item_snippet(&mut acc, &ctx); Ok(Some(acc)) } @@ -61,10 +64,12 @@ pub(super) struct SyntaxContext<'a> { leaf: SyntaxNodeRef<'a>, enclosing_fn: Option>, is_param: bool, - /// a single-indent path, like `foo`. + /// A single-indent path, like `foo`. is_trivial_path: bool, after_if: bool, is_stmt: bool, + /// Something is typed at the "top" level, in module or impl/trait. + is_new_item: bool, } impl SyntaxContext<'_> { @@ -77,6 +82,7 @@ impl SyntaxContext<'_> { is_trivial_path: false, after_if: false, is_stmt: false, + is_new_item: false, }; ctx.fill(original_file, offset); Some(ctx) @@ -112,17 +118,22 @@ impl SyntaxContext<'_> { } } fn classify_name_ref(&mut self, file: &SourceFileNode, name_ref: ast::NameRef) { - // let name_range = name_ref.syntax().range(); - // let top_node = name_ref - // .syntax() - // .ancestors() - // .take_while(|it| it.range() == name_range) - // .last() - // .unwrap(); - // match top_node.parent().map(|it| it.kind()) { - // Some(SOURCE_FILE) | Some(ITEM_LIST) => return Some(NameRefKind::BareIdentInMod), - // _ => (), - // } + let name_range = name_ref.syntax().range(); + let top_node = name_ref + .syntax() + .ancestors() + .take_while(|it| it.range() == name_range) + .last() + .unwrap(); + + match top_node.parent().map(|it| it.kind()) { + Some(SOURCE_FILE) | Some(ITEM_LIST) => { + self.is_new_item = true; + return; + } + _ => (), + } + let parent = match name_ref.syntax().parent() { Some(it) => it, None => return, diff --git a/crates/ra_analysis/src/completion/complete_keywords.rs b/crates/ra_analysis/src/completion/complete_keyword.rs similarity index 100% rename from crates/ra_analysis/src/completion/complete_keywords.rs rename to crates/ra_analysis/src/completion/complete_keyword.rs diff --git a/crates/ra_analysis/src/completion/complete_snippet.rs b/crates/ra_analysis/src/completion/complete_snippet.rs new file mode 100644 index 00000000000..5d6cc5dc9a7 --- /dev/null +++ b/crates/ra_analysis/src/completion/complete_snippet.rs @@ -0,0 +1,78 @@ +use crate::{ + completion::{CompletionItem, Completions, CompletionKind::*, SyntaxContext}, +}; + +pub(super) fn complete_expr_snippet(acc: &mut Completions, ctx: &SyntaxContext) { + if !(ctx.is_trivial_path && ctx.enclosing_fn.is_some()) { + return; + } + CompletionItem::new("pd") + .snippet("eprintln!(\"$0 = {:?}\", $0);") + .kind(Snippet) + .add_to(acc); + CompletionItem::new("ppd") + .snippet("eprintln!(\"$0 = {:#?}\", $0);") + .kind(Snippet) + .add_to(acc); +} + +pub(super) fn complete_item_snippet(acc: &mut Completions, ctx: &SyntaxContext) { + if !ctx.is_new_item { + return; + } + CompletionItem::new("Test function") + .lookup_by("tfn") + .snippet( + "\ +#[test] +fn ${1:feature}() { + $0 +}", + ) + .kind(Snippet) + .add_to(acc); + CompletionItem::new("pub(crate)") + .snippet("pub(crate) $0") + .kind(Snippet) + .add_to(acc); +} + +#[cfg(test)] +mod tests { + use crate::completion::{CompletionKind, check_completion}; + fn check_snippet_completion(code: &str, expected_completions: &str) { + check_completion(code, expected_completions, CompletionKind::Snippet); + } + + #[test] + fn completes_snippets_in_expressions() { + check_snippet_completion( + r"fn foo(x: i32) { <|> }", + r##" + pd "eprintln!(\"$0 = {:?}\", $0);" + ppd "eprintln!(\"$0 = {:#?}\", $0);" + "##, + ); + } + + #[test] + fn completes_snippets_in_items() { + // check_snippet_completion(r" + // <|> + // ", + // r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##, + // ); + check_snippet_completion( + r" + #[cfg(test)] + mod tests { + <|> + } + ", + r##" + tfn "Test function" "#[test]\nfn ${1:feature}() {\n $0\n}" + pub(crate) "pub(crate) $0" + "##, + ); + } +} diff --git a/crates/ra_analysis/src/completion/reference_completion.rs b/crates/ra_analysis/src/completion/reference_completion.rs index 15ff4c5dd43..46d3819277f 100644 --- a/crates/ra_analysis/src/completion/reference_completion.rs +++ b/crates/ra_analysis/src/completion/reference_completion.rs @@ -32,8 +32,6 @@ pub(super) fn completions( if let Some(fn_def) = enclosing_fn { let scopes = FnScopes::new(fn_def); complete_fn(name_ref, &scopes, acc); - // complete_expr_keywords(&file, fn_def, name_ref, acc); - complete_expr_snippets(acc); } let module_scope = module.scope(db)?; @@ -56,19 +54,7 @@ pub(super) fn completions( }); } NameRefKind::Path(path) => complete_path(acc, db, module, path)?, - NameRefKind::BareIdentInMod => { - let name_range = name_ref.syntax().range(); - let top_node = name_ref - .syntax() - .ancestors() - .take_while(|it| it.range() == name_range) - .last() - .unwrap(); - match top_node.parent().map(|it| it.kind()) { - Some(SOURCE_FILE) | Some(ITEM_LIST) => complete_mod_item_snippets(acc), - _ => (), - } - } + NameRefKind::BareIdentInMod => (), } Ok(()) } @@ -162,35 +148,6 @@ fn complete_path( Ok(()) } -fn complete_mod_item_snippets(acc: &mut Completions) { - CompletionItem::new("Test function") - .lookup_by("tfn") - .snippet( - "\ -#[test] -fn ${1:feature}() { - $0 -}", - ) - .kind(Snippet) - .add_to(acc); - CompletionItem::new("pub(crate)") - .snippet("pub(crate) $0") - .kind(Snippet) - .add_to(acc); -} - -fn complete_expr_snippets(acc: &mut Completions) { - CompletionItem::new("pd") - .snippet("eprintln!(\"$0 = {:?}\", $0);") - .kind(Snippet) - .add_to(acc); - CompletionItem::new("ppd") - .snippet("eprintln!(\"$0 = {:#?}\", $0);") - .kind(Snippet) - .add_to(acc); -} - #[cfg(test)] mod tests { use crate::completion::{CompletionKind, check_completion}; @@ -199,10 +156,6 @@ mod tests { check_completion(code, expected_completions, CompletionKind::Reference); } - fn check_snippet_completion(code: &str, expected_completions: &str) { - check_completion(code, expected_completions, CompletionKind::Snippet); - } - #[test] fn test_completion_let_scope() { check_reference_completion( @@ -378,37 +331,4 @@ mod tests { "Spam", ); } - - #[test] - fn completes_snippets_in_expressions() { - check_snippet_completion( - r"fn foo(x: i32) { <|> }", - r##" - pd "eprintln!(\"$0 = {:?}\", $0);" - ppd "eprintln!(\"$0 = {:#?}\", $0);" - "##, - ); - } - - #[test] - fn completes_snippets_in_items() { - // check_snippet_completion(r" - // <|> - // ", - // r##"[CompletionItem { label: "Test function", lookup: None, snippet: Some("#[test]\nfn test_${1:feature}() {\n$0\n}"##, - // ); - check_snippet_completion( - r" - #[cfg(test)] - mod tests { - <|> - } - ", - r##" - tfn "Test function" "#[test]\nfn ${1:feature}() {\n $0\n}" - pub(crate) "pub(crate) $0" - "##, - ); - } - }