Add ret type
This commit is contained in:
parent
b00a4d43ec
commit
2257c08cb1
14 changed files with 208 additions and 77 deletions
|
@ -12,7 +12,8 @@ use {
|
|||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct CompletionItem {
|
||||
pub struct
|
||||
CompletionItem {
|
||||
pub name: String,
|
||||
pub snippet: Option<String>
|
||||
}
|
||||
|
@ -25,10 +26,17 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI
|
|||
file.incremental_reparse(&edit)?
|
||||
};
|
||||
let name_ref = find_node_at_offset::<ast::NameRef>(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<Vec<CompletionI
|
|||
Some(res)
|
||||
}
|
||||
|
||||
fn is_ident_expr(name_ref: ast::NameRef) -> 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<ast::FnDef>, name_ref: ast::NameRef, acc: &mut Vec<CompletionItem>) {
|
||||
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::<ast::IfExpr>(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<CompletionItem>) {
|
||||
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::<Vec<_>>();
|
||||
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::<Vec<_>>();
|
||||
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 { }") }]"#);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -523,6 +523,10 @@ impl<'a> FnDef<'a> {
|
|||
pub fn body(self) -> Option<Block<'a>> {
|
||||
super::child_opt(self)
|
||||
}
|
||||
|
||||
pub fn ret_type(self) -> Option<RetType<'a>> {
|
||||
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<Self> {
|
||||
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> {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue