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 ++++++++++++++++++--- crates/libsyntax2/Cargo.toml | 2 +- crates/libsyntax2/src/ast/generated.rs | 22 ++++ crates/libsyntax2/src/grammar.ron | 3 + crates/libsyntax2/src/grammar/mod.rs | 2 + crates/libsyntax2/src/syntax_kinds/generated.rs | 2 + .../data/parser/err/0010_unsafe_lambda_block.txt | 15 +-- .../inline/0030_fn_pointer_type_with_ret.txt | 11 +- .../tests/data/parser/inline/0031_for_type.txt | 11 +- .../data/parser/inline/0038_function_ret_type.txt | 11 +- .../tests/data/parser/inline/0066_lambda_expr.txt | 15 +-- .../data/parser/inline/0093_path_fn_trait_args.txt | 11 +- .../parser/inline/0095_path_type_with_bounds.txt | 41 +++---- crates/server/src/main_loop/handlers.rs | 16 ++- 14 files changed, 208 insertions(+), 77 deletions(-) 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 { }") }]"#); } } diff --git a/crates/libsyntax2/Cargo.toml b/crates/libsyntax2/Cargo.toml index 918c02775..3e5a83290 100644 --- a/crates/libsyntax2/Cargo.toml +++ b/crates/libsyntax2/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] unicode-xid = "0.1.0" -text_unit = "0.1.3" +text_unit = "0.1.4" itertools = "0.7.8" drop_bomb = "0.1.4" parking_lot = "0.6.0" diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index 999023e3d..e8743caa8 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs @@ -523,6 +523,10 @@ impl<'a> FnDef<'a> { pub fn body(self) -> Option> { super::child_opt(self) } + + pub fn ret_type(self) -> Option> { + super::child_opt(self) + } } // FnPointerType @@ -1412,6 +1416,24 @@ impl<'a> AstNode<'a> for ReferenceType<'a> { impl<'a> ReferenceType<'a> {} +// RetType +#[derive(Debug, Clone, Copy)] +pub struct RetType<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for RetType<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + RET_TYPE => Some(RetType { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> RetType<'a> {} + // ReturnExpr #[derive(Debug, Clone, Copy)] pub struct ReturnExpr<'a> { diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index f8fa7b694..f1907d1ce 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron @@ -119,6 +119,7 @@ Grammar( "STRUCT_DEF", "ENUM_DEF", "FN_DEF", + "RET_TYPE", "EXTERN_CRATE_ITEM", "MODULE", "USE_ITEM", @@ -252,8 +253,10 @@ Grammar( options: [ ["param_list", "ParamList"], ["body", "Block"], + ["ret_type", "RetType"] ], ), + "RetType": (), "StructDef": ( traits: [ "NameOwner", diff --git a/crates/libsyntax2/src/grammar/mod.rs b/crates/libsyntax2/src/grammar/mod.rs index 496d28349..1acecac41 100644 --- a/crates/libsyntax2/src/grammar/mod.rs +++ b/crates/libsyntax2/src/grammar/mod.rs @@ -119,8 +119,10 @@ fn abi(p: &mut Parser) { fn opt_fn_ret_type(p: &mut Parser) -> bool { if p.at(THIN_ARROW) { + let m = p.start(); p.bump(); types::type_(p); + m.complete(p, RET_TYPE); true } else { false diff --git a/crates/libsyntax2/src/syntax_kinds/generated.rs b/crates/libsyntax2/src/syntax_kinds/generated.rs index 0a22b11c2..7882bded9 100644 --- a/crates/libsyntax2/src/syntax_kinds/generated.rs +++ b/crates/libsyntax2/src/syntax_kinds/generated.rs @@ -119,6 +119,7 @@ pub enum SyntaxKind { STRUCT_DEF, ENUM_DEF, FN_DEF, + RET_TYPE, EXTERN_CRATE_ITEM, MODULE, USE_ITEM, @@ -380,6 +381,7 @@ impl SyntaxKind { STRUCT_DEF => &SyntaxInfo { name: "STRUCT_DEF" }, ENUM_DEF => &SyntaxInfo { name: "ENUM_DEF" }, FN_DEF => &SyntaxInfo { name: "FN_DEF" }, + RET_TYPE => &SyntaxInfo { name: "RET_TYPE" }, EXTERN_CRATE_ITEM => &SyntaxInfo { name: "EXTERN_CRATE_ITEM" }, MODULE => &SyntaxInfo { name: "MODULE" }, USE_ITEM => &SyntaxInfo { name: "USE_ITEM" }, diff --git a/crates/libsyntax2/tests/data/parser/err/0010_unsafe_lambda_block.txt b/crates/libsyntax2/tests/data/parser/err/0010_unsafe_lambda_block.txt index 400442c51..edd801599 100644 --- a/crates/libsyntax2/tests/data/parser/err/0010_unsafe_lambda_block.txt +++ b/crates/libsyntax2/tests/data/parser/err/0010_unsafe_lambda_block.txt @@ -17,13 +17,14 @@ ROOT@[0; 42) PIPE@[16; 17) PIPE@[17; 18) WHITESPACE@[18; 19) - THIN_ARROW@[19; 21) - WHITESPACE@[21; 22) - TUPLE_TYPE@[22; 24) - L_PAREN@[22; 23) - R_PAREN@[23; 24) - err: `expected a block` - err: `expected SEMI` + RET_TYPE@[19; 24) + THIN_ARROW@[19; 21) + WHITESPACE@[21; 22) + TUPLE_TYPE@[22; 24) + L_PAREN@[22; 23) + R_PAREN@[23; 24) + err: `expected a block` + err: `expected SEMI` WHITESPACE@[24; 25) EXPR_STMT@[25; 39) BLOCK_EXPR@[25; 38) diff --git a/crates/libsyntax2/tests/data/parser/inline/0030_fn_pointer_type_with_ret.txt b/crates/libsyntax2/tests/data/parser/inline/0030_fn_pointer_type_with_ret.txt index 7d0ecfbd1..203839636 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0030_fn_pointer_type_with_ret.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0030_fn_pointer_type_with_ret.txt @@ -13,10 +13,11 @@ ROOT@[0; 21) L_PAREN@[11; 12) R_PAREN@[12; 13) WHITESPACE@[13; 14) - THIN_ARROW@[14; 16) - WHITESPACE@[16; 17) - TUPLE_TYPE@[17; 19) - L_PAREN@[17; 18) - R_PAREN@[18; 19) + RET_TYPE@[14; 19) + THIN_ARROW@[14; 16) + WHITESPACE@[16; 17) + TUPLE_TYPE@[17; 19) + L_PAREN@[17; 18) + R_PAREN@[18; 19) SEMI@[19; 20) WHITESPACE@[20; 21) diff --git a/crates/libsyntax2/tests/data/parser/inline/0031_for_type.txt b/crates/libsyntax2/tests/data/parser/inline/0031_for_type.txt index 41461f2a6..f6b962b2c 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0031_for_type.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0031_for_type.txt @@ -21,10 +21,11 @@ ROOT@[0; 29) L_PAREN@[19; 20) R_PAREN@[20; 21) WHITESPACE@[21; 22) - THIN_ARROW@[22; 24) - WHITESPACE@[24; 25) - TUPLE_TYPE@[25; 27) - L_PAREN@[25; 26) - R_PAREN@[26; 27) + RET_TYPE@[22; 27) + THIN_ARROW@[22; 24) + WHITESPACE@[24; 25) + TUPLE_TYPE@[25; 27) + L_PAREN@[25; 26) + R_PAREN@[26; 27) SEMI@[27; 28) WHITESPACE@[28; 29) diff --git a/crates/libsyntax2/tests/data/parser/inline/0038_function_ret_type.txt b/crates/libsyntax2/tests/data/parser/inline/0038_function_ret_type.txt index 0df6c8f51..a3d235ce1 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0038_function_ret_type.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0038_function_ret_type.txt @@ -21,11 +21,12 @@ ROOT@[0; 30) L_PAREN@[18; 19) R_PAREN@[19; 20) WHITESPACE@[20; 21) - THIN_ARROW@[21; 23) - WHITESPACE@[23; 24) - TUPLE_TYPE@[24; 26) - L_PAREN@[24; 25) - R_PAREN@[25; 26) + RET_TYPE@[21; 26) + THIN_ARROW@[21; 23) + WHITESPACE@[23; 24) + TUPLE_TYPE@[24; 26) + L_PAREN@[24; 25) + R_PAREN@[25; 26) WHITESPACE@[26; 27) BLOCK@[27; 29) L_CURLY@[27; 28) diff --git a/crates/libsyntax2/tests/data/parser/inline/0066_lambda_expr.txt b/crates/libsyntax2/tests/data/parser/inline/0066_lambda_expr.txt index 6b34bc302..6f8304db3 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0066_lambda_expr.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0066_lambda_expr.txt @@ -28,13 +28,14 @@ ROOT@[0; 79) PIPE@[26; 27) PIPE@[27; 28) WHITESPACE@[28; 29) - THIN_ARROW@[29; 31) - WHITESPACE@[31; 32) - PATH_TYPE@[32; 35) - PATH@[32; 35) - PATH_SEGMENT@[32; 35) - NAME_REF@[32; 35) - IDENT@[32; 35) "i32" + RET_TYPE@[29; 35) + THIN_ARROW@[29; 31) + WHITESPACE@[31; 32) + PATH_TYPE@[32; 35) + PATH@[32; 35) + PATH_SEGMENT@[32; 35) + NAME_REF@[32; 35) + IDENT@[32; 35) "i32" WHITESPACE@[35; 36) BLOCK@[36; 42) L_CURLY@[36; 37) diff --git a/crates/libsyntax2/tests/data/parser/inline/0093_path_fn_trait_args.txt b/crates/libsyntax2/tests/data/parser/inline/0093_path_fn_trait_args.txt index 1b1a8a9e2..26a690d3a 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0093_path_fn_trait_args.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0093_path_fn_trait_args.txt @@ -35,11 +35,12 @@ ROOT@[0; 32) IDENT@[19; 22) "i32" R_PAREN@[22; 23) WHITESPACE@[23; 24) - THIN_ARROW@[24; 26) - WHITESPACE@[26; 27) - TUPLE_TYPE@[27; 29) - L_PAREN@[27; 28) - R_PAREN@[28; 29) + RET_TYPE@[24; 29) + THIN_ARROW@[24; 26) + WHITESPACE@[26; 27) + TUPLE_TYPE@[27; 29) + L_PAREN@[27; 28) + R_PAREN@[28; 29) R_ANGLE@[29; 30) SEMI@[30; 31) WHITESPACE@[31; 32) diff --git a/crates/libsyntax2/tests/data/parser/inline/0095_path_type_with_bounds.txt b/crates/libsyntax2/tests/data/parser/inline/0095_path_type_with_bounds.txt index 0703d1da2..4b864f741 100644 --- a/crates/libsyntax2/tests/data/parser/inline/0095_path_type_with_bounds.txt +++ b/crates/libsyntax2/tests/data/parser/inline/0095_path_type_with_bounds.txt @@ -8,26 +8,27 @@ ROOT@[0; 27) L_PAREN@[6; 7) R_PAREN@[7; 8) WHITESPACE@[8; 9) - THIN_ARROW@[9; 11) - WHITESPACE@[11; 12) - PATH_TYPE@[12; 23) - PATH@[12; 23) - PATH_SEGMENT@[12; 23) - NAME_REF@[12; 15) - IDENT@[12; 15) "Box" - TYPE_ARG_LIST@[15; 23) - L_ANGLE@[15; 16) - TYPE_ARG@[16; 22) - PATH_TYPE@[16; 22) - PATH@[16; 17) - PATH_SEGMENT@[16; 17) - NAME_REF@[16; 17) - IDENT@[16; 17) "T" - WHITESPACE@[17; 18) - PLUS@[18; 19) - WHITESPACE@[19; 20) - LIFETIME@[20; 22) "'f" - R_ANGLE@[22; 23) + RET_TYPE@[9; 23) + THIN_ARROW@[9; 11) + WHITESPACE@[11; 12) + PATH_TYPE@[12; 23) + PATH@[12; 23) + PATH_SEGMENT@[12; 23) + NAME_REF@[12; 15) + IDENT@[12; 15) "Box" + TYPE_ARG_LIST@[15; 23) + L_ANGLE@[15; 16) + TYPE_ARG@[16; 22) + PATH_TYPE@[16; 22) + PATH@[16; 17) + PATH_SEGMENT@[16; 17) + NAME_REF@[16; 17) + IDENT@[16; 17) "T" + WHITESPACE@[17; 18) + PLUS@[18; 19) + WHITESPACE@[19; 20) + LIFETIME@[20; 22) "'f" + R_ANGLE@[22; 23) WHITESPACE@[23; 24) BLOCK@[24; 26) L_CURLY@[24; 25) diff --git a/crates/server/src/main_loop/handlers.rs b/crates/server/src/main_loop/handlers.rs index 92ffb30c3..3ee0873f4 100644 --- a/crates/server/src/main_loop/handlers.rs +++ b/crates/server/src/main_loop/handlers.rs @@ -4,7 +4,7 @@ use languageserver_types::{ Diagnostic, DiagnosticSeverity, Url, DocumentSymbol, Command, TextDocumentIdentifier, WorkspaceEdit, SymbolInformation, Position, Location, TextEdit, - CompletionItem, + CompletionItem, InsertTextFormat, CompletionItemKind, }; use serde_json::{to_value, from_value}; use url_serde; @@ -331,9 +331,17 @@ pub fn handle_completion( Some(items) => items, }; let items = items.into_iter() - .map(|item| CompletionItem { - label: item.name, - .. Default::default() + .map(|item| { + let mut res = CompletionItem { + label: item.name, + .. Default::default() + }; + if let Some(snip) = item.snippet { + res.insert_text = Some(snip); + res.insert_text_format = Some(InsertTextFormat::Snippet); + res.kind = Some(CompletionItemKind::Keyword); + }; + res }) .collect(); -- cgit v1.2.3