From 2257c08cb159a30492bf2aec172539b1dd504700 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 28 Aug 2018 21:11:17 +0300 Subject: Add ret type --- crates/libeditor/src/completion.rs | 123 +++++++++++++++++++++++++++++++------ 1 file changed, 105 insertions(+), 18 deletions(-) (limited to 'crates/libeditor/src/completion.rs') diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs index 7e8669822..65527db62 100644 --- a/crates/libeditor/src/completion.rs +++ b/crates/libeditor/src/completion.rs @@ -12,7 +12,8 @@ use { }; #[derive(Debug)] -pub struct CompletionItem { +pub struct + CompletionItem { pub name: String, pub snippet: Option } @@ -25,10 +26,17 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option(file.syntax(), offset)?; + if !is_ident_expr(name_ref) { + return None; + } + let mut res = Vec::new(); if let Some(fn_def) = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next() { + complete_keywords(&file, Some(fn_def), name_ref, &mut res); let scopes = FnScopes::new(fn_def); complete_fn(name_ref, &scopes, &mut res); + } else { + complete_keywords(&file, None, name_ref, &mut res); } if let Some(root) = ancestors(name_ref.syntax()).filter_map(ast::Root::cast).next() { let scope = ModuleScope::new(root); @@ -43,6 +51,42 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option bool { + match ancestors(name_ref.syntax()).filter_map(ast::Expr::cast).next() { + None => false, + Some(expr) => { + expr.syntax().range() == name_ref.syntax().range() + } + } +} + +fn complete_keywords(file: &File, fn_def: Option, name_ref: ast::NameRef, acc: &mut Vec) { + acc.push(keyword("if", "if $0 { }")); + acc.push(keyword("match", "match $0 { }")); + acc.push(keyword("while", "while $0 { }")); + acc.push(keyword("loop", "loop {$0}")); + + if let Some(off) = name_ref.syntax().range().start().checked_sub(2.into()) { + if let Some(if_expr) = find_node_at_offset::(file.syntax(), off) { + if if_expr.syntax().range().end() < name_ref.syntax().range().start() { + acc.push(keyword("else", "else {$0}")); + acc.push(keyword("else if", "else if $0 { }")); + } + } + } + + // if let Some(fn_def) = fn_def { + // acc.push(keyword("return", "")) + // } + + fn keyword(kw: &str, snip: &str) -> CompletionItem { + CompletionItem { + name: kw.to_string(), + snippet: Some(snip.to_string()), + } + } +} + fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec) { acc.extend( scopes.scope_chain(name_ref.syntax()) @@ -59,29 +103,44 @@ mod tests { use super::*; use test_utils::{assert_eq_dbg, extract_offset}; - fn do_check(code: &str, expected_completions: &str) { + fn check_scope_completion(code: &str, expected_completions: &str) { let (off, code) = extract_offset(&code); let file = File::parse(&code); - let completions = scope_completion(&file, off).unwrap(); + let completions = scope_completion(&file, off) + .unwrap() + .into_iter() + .filter(|c| c.snippet.is_none()) + .collect::>(); + assert_eq_dbg(expected_completions, &completions); + } + + fn check_snippet_completion(code: &str, expected_completions: &str) { + let (off, code) = extract_offset(&code); + let file = File::parse(&code); + let completions = scope_completion(&file, off) + .unwrap() + .into_iter() + .filter(|c| c.snippet.is_some()) + .collect::>(); assert_eq_dbg(expected_completions, &completions); } #[test] fn test_completion_let_scope() { - do_check(r" + check_scope_completion(r" fn quux(x: i32) { let y = 92; 1 + <|>; let z = (); } - ", r#"[CompletionItem { name: "y" }, - CompletionItem { name: "x" }, - CompletionItem { name: "quux" }]"#); + ", r#"[CompletionItem { name: "y", snippet: None }, + CompletionItem { name: "x", snippet: None }, + CompletionItem { name: "quux", snippet: None }]"#); } #[test] fn test_completion_if_let_scope() { - do_check(r" + check_scope_completion(r" fn quux() { if let Some(x) = foo() { let y = 92; @@ -91,33 +150,61 @@ mod tests { 1 + <|> } } - ", r#"[CompletionItem { name: "b" }, - CompletionItem { name: "a" }, - CompletionItem { name: "quux" }]"#); + ", r#"[CompletionItem { name: "b", snippet: None }, + CompletionItem { name: "a", snippet: None }, + CompletionItem { name: "quux", snippet: None }]"#); } #[test] fn test_completion_for_scope() { - do_check(r" + check_scope_completion(r" fn quux() { for x in &[1, 2, 3] { <|> } } - ", r#"[CompletionItem { name: "x" }, - CompletionItem { name: "quux" }]"#); + ", r#"[CompletionItem { name: "x", snippet: None }, + CompletionItem { name: "quux", snippet: None }]"#); } #[test] fn test_completion_mod_scope() { - do_check(r" + check_scope_completion(r" struct Foo; enum Baz {} fn quux() { <|> } - ", r#"[CompletionItem { name: "Foo" }, - CompletionItem { name: "Baz" }, - CompletionItem { name: "quux" }]"#); + ", r#"[CompletionItem { name: "Foo", snippet: None }, + CompletionItem { name: "Baz", snippet: None }, + CompletionItem { name: "quux", snippet: None }]"#); + } + + #[test] + fn test_completion_kewords() { + check_snippet_completion(r" + fn quux() { + <|> + } + ", r#"[CompletionItem { name: "if", snippet: Some("if $0 { }") }, + CompletionItem { name: "match", snippet: Some("match $0 { }") }, + CompletionItem { name: "while", snippet: Some("while $0 { }") }, + CompletionItem { name: "loop", snippet: Some("loop {$0}") }]"#); + } + + #[test] + fn test_completion_else() { + check_snippet_completion(r" + fn quux() { + if true { + () + } <|> + } + ", r#"[CompletionItem { name: "if", snippet: Some("if $0 { }") }, + CompletionItem { name: "match", snippet: Some("match $0 { }") }, + CompletionItem { name: "while", snippet: Some("while $0 { }") }, + CompletionItem { name: "loop", snippet: Some("loop {$0}") }, + CompletionItem { name: "else", snippet: Some("else {$0}") }, + CompletionItem { name: "else if", snippet: Some("else if $0 { }") }]"#); } } -- cgit v1.2.3