From ac226021cfd26a9332b5971f3e05118d77822af5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sun, 26 Aug 2018 12:09:28 +0300 Subject: scope based comletion --- crates/libeditor/src/code_actions.rs | 2 +- crates/libeditor/src/completion.rs | 42 +++- crates/libeditor/tests/test.rs | 34 ++-- crates/libsyntax2/src/algo/visit.rs | 58 ++++++ crates/libsyntax2/src/algo/walk.rs | 10 +- crates/libsyntax2/src/ast/generated.rs | 308 ++++++++++++++++++++++++++++- crates/libsyntax2/src/grammar.ron | 58 +++++- crates/libsyntax2/src/grammar/items/mod.rs | 4 +- 8 files changed, 475 insertions(+), 41 deletions(-) (limited to 'crates') diff --git a/crates/libeditor/src/code_actions.rs b/crates/libeditor/src/code_actions.rs index f53a8f9c6..e6ba83d2e 100644 --- a/crates/libeditor/src/code_actions.rs +++ b/crates/libeditor/src/code_actions.rs @@ -9,7 +9,7 @@ use libsyntax2::{ SyntaxNodeRef, algo::{ Direction, siblings, - find_leaf_at_offset, ancestors, + find_leaf_at_offset, }, }; diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs index cf61ec784..16c9ead74 100644 --- a/crates/libeditor/src/completion.rs +++ b/crates/libeditor/src/completion.rs @@ -1,7 +1,11 @@ use libsyntax2::{ - File, TextUnit, - ast, - algo::find_leaf_at_offset, + File, TextUnit, AstNode, SyntaxNodeRef, + ast::{self, NameOwner}, + algo::{ + ancestors, + visit::{visitor_ctx, VisitorCtx}, + walk::preorder, + }, }; use { @@ -25,7 +29,33 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option Vec { - vec![CompletionItem { - name: "foo".to_string() - }] + let mut res = Vec::new(); + for node in ancestors(name_ref.syntax()) { + process_scope(node, &mut res); + } + res +} + +fn process_scope(node: SyntaxNodeRef, sink: &mut Vec) { + let _ = visitor_ctx(sink) + .visit::(|block, sink| { + block.let_stmts() + .filter_map(|it| it.pat()) + .for_each(move |it| process_pat(it, sink)) + }) + .visit::(|fn_def, sink| { + fn_def.param_list().into_iter() + .flat_map(|it| it.params()) + .filter_map(|it| it.pat()) + .for_each(move |it| process_pat(it, sink)) + }) + .accept(node); + + fn process_pat(pat: ast::Pat, sink: &mut Vec) { + let items = preorder(pat.syntax()) + .filter_map(ast::BindPat::cast) + .filter_map(ast::BindPat::name) + .map(|name| CompletionItem { name: name.text().to_string() }); + sink.extend(items); + } } diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index 20de2f240..ecdc149c7 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs @@ -256,25 +256,23 @@ struct Foo { f: u32 } "); } -// #[test] -// fn test_completion() { -// fn do_check(code: &str, expected_completions: &str) { -// let (off, code) = extract_offset(&code); -// let file = file(&code); -// let completions = scope_completion(&file, off).unwrap(); -// assert_eq_dbg(expected_completions, &completions); -// } +#[test] +fn test_completion() { + fn do_check(code: &str, expected_completions: &str) { + let (off, code) = extract_offset(&code); + let file = file(&code); + let completions = scope_completion(&file, off).unwrap(); + assert_eq_dbg(expected_completions, &completions); + } -// do_check(r" -// fn foo(foo: i32) { -// let bar = 92; -// 1 + <|> -// } -// ", r#" -// CompletionItem { name: "bar" }, -// CompletionItem { name: "foo" }, -// "#); -// } + do_check(r" +fn quux(x: i32) { + let y = 92; + 1 + <|> +} +", r#"[CompletionItem { name: "y" }, + CompletionItem { name: "x" }]"#); +} fn file(text: &str) -> File { File::parse(text) diff --git a/crates/libsyntax2/src/algo/visit.rs b/crates/libsyntax2/src/algo/visit.rs index a36c8f45e..9f1c127c7 100644 --- a/crates/libsyntax2/src/algo/visit.rs +++ b/crates/libsyntax2/src/algo/visit.rs @@ -6,6 +6,10 @@ pub fn visitor<'a, T>() -> impl Visitor<'a, Output=T> { EmptyVisitor { ph: PhantomData } } +pub fn visitor_ctx<'a, T, C>(ctx: C) -> impl VisitorCtx<'a, Output=T, Ctx=C> { + EmptyVisitorCtx { ph: PhantomData, ctx } +} + pub trait Visitor<'a>: Sized { type Output; fn accept(self, node: SyntaxNodeRef<'a>) -> Option; @@ -17,6 +21,18 @@ pub trait Visitor<'a>: Sized { } } +pub trait VisitorCtx<'a>: Sized { + type Output; + type Ctx; + fn accept(self, node: SyntaxNodeRef<'a>) -> Result; + fn visit(self, f: F) -> VisCtx + where N: AstNode<'a>, + F: FnOnce(N, Self::Ctx) -> Self::Output, + { + VisCtx { inner: self, f, ph: PhantomData } + } +} + #[derive(Debug)] struct EmptyVisitor { ph: PhantomData T> @@ -30,6 +46,21 @@ impl<'a, T> Visitor<'a> for EmptyVisitor { } } +#[derive(Debug)] +struct EmptyVisitorCtx { + ctx: C, + ph: PhantomData T>, +} + +impl<'a, T, C> VisitorCtx<'a> for EmptyVisitorCtx { + type Output = T; + type Ctx = C; + + fn accept(self, _node: SyntaxNodeRef<'a>) -> Result { + Err(self.ctx) + } +} + #[derive(Debug)] pub struct Vis { inner: V, @@ -50,3 +81,30 @@ impl<'a, V, N, F> Visitor<'a> for Vis inner.accept(node).or_else(|| N::cast(node).map(f)) } } + +#[derive(Debug)] +pub struct VisCtx { + inner: V, + f: F, + ph: PhantomData, +} + +impl<'a, V, N, F> VisitorCtx<'a> for VisCtx + where + V: VisitorCtx<'a>, + N: AstNode<'a>, + F: FnOnce(N, >::Ctx) -> >::Output, +{ + type Output = >::Output; + type Ctx = >::Ctx; + + fn accept(self, node: SyntaxNodeRef<'a>) -> Result { + let VisCtx { inner, f, .. } = self; + inner.accept(node).or_else(|ctx| + match N::cast(node) { + None => Err(ctx), + Some(node) => Ok(f(node, ctx)) + } + ) + } +} diff --git a/crates/libsyntax2/src/algo/walk.rs b/crates/libsyntax2/src/algo/walk.rs index d8797c453..536ee705f 100644 --- a/crates/libsyntax2/src/algo/walk.rs +++ b/crates/libsyntax2/src/algo/walk.rs @@ -17,19 +17,19 @@ pub enum WalkEvent<'a> { } pub fn walk<'a>(root: SyntaxNodeRef<'a>) -> impl Iterator> { - generate(Some(WalkEvent::Enter(root)), |pos| { + generate(Some(WalkEvent::Enter(root)), move |pos| { let next = match *pos { WalkEvent::Enter(node) => match node.first_child() { Some(child) => WalkEvent::Enter(child), None => WalkEvent::Exit(node), }, WalkEvent::Exit(node) => { + if node == root { + return None; + } match node.next_sibling() { Some(sibling) => WalkEvent::Enter(sibling), - None => match node.parent() { - Some(node) => WalkEvent::Exit(node), - None => return None, - }, + None => WalkEvent::Exit(node.parent().unwrap()), } } }; diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index 7d746a85b..6926c0535 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs @@ -80,6 +80,25 @@ impl<'a> AstNode<'a> for BinExpr<'a> { impl<'a> BinExpr<'a> {} +// BindPat +#[derive(Debug, Clone, Copy)] +pub struct BindPat<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for BindPat<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + BIND_PAT => Some(BindPat { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> ast::NameOwner<'a> for BindPat<'a> {} +impl<'a> BindPat<'a> {} + // Block #[derive(Debug, Clone, Copy)] pub struct Block<'a> { @@ -96,7 +115,11 @@ impl<'a> AstNode<'a> for Block<'a> { fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } } -impl<'a> Block<'a> {} +impl<'a> Block<'a> { + pub fn let_stmts(self) -> impl Iterator> + 'a { + super::children(self) + } +} // BlockExpr #[derive(Debug, Clone, Copy)] @@ -378,6 +401,24 @@ impl<'a> AstNode<'a> for FieldExpr<'a> { impl<'a> FieldExpr<'a> {} +// FieldPatList +#[derive(Debug, Clone, Copy)] +pub struct FieldPatList<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for FieldPatList<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + FIELD_PAT_LIST => Some(FieldPatList { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> FieldPatList<'a> {} + // FnDef #[derive(Debug, Clone, Copy)] pub struct FnDef<'a> { @@ -397,7 +438,11 @@ impl<'a> AstNode<'a> for FnDef<'a> { impl<'a> ast::NameOwner<'a> for FnDef<'a> {} impl<'a> ast::TypeParamsOwner<'a> for FnDef<'a> {} impl<'a> ast::AttrsOwner<'a> for FnDef<'a> {} -impl<'a> FnDef<'a> {} +impl<'a> FnDef<'a> { + pub fn param_list(self) -> Option> { + super::child_opt(self) + } +} // FnPointerType #[derive(Debug, Clone, Copy)] @@ -561,6 +606,28 @@ impl<'a> AstNode<'a> for LambdaExpr<'a> { impl<'a> LambdaExpr<'a> {} +// LetStmt +#[derive(Debug, Clone, Copy)] +pub struct LetStmt<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for LetStmt<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + LET_STMT => Some(LetStmt { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> LetStmt<'a> { + pub fn pat(self) -> Option> { + super::child_opt(self) + } +} + // LoopExpr #[derive(Debug, Clone, Copy)] pub struct LoopExpr<'a> { @@ -831,6 +898,50 @@ impl<'a> ast::TypeParamsOwner<'a> for NominalDef<'a> {} impl<'a> ast::AttrsOwner<'a> for NominalDef<'a> {} impl<'a> NominalDef<'a> {} +// Param +#[derive(Debug, Clone, Copy)] +pub struct Param<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for Param<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + PARAM => Some(Param { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> Param<'a> { + pub fn pat(self) -> Option> { + super::child_opt(self) + } +} + +// ParamList +#[derive(Debug, Clone, Copy)] +pub struct ParamList<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for ParamList<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + PARAM_LIST => Some(ParamList { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> ParamList<'a> { + pub fn params(self) -> impl Iterator> + 'a { + super::children(self) + } +} + // ParenExpr #[derive(Debug, Clone, Copy)] pub struct ParenExpr<'a> { @@ -867,6 +978,55 @@ impl<'a> AstNode<'a> for ParenType<'a> { impl<'a> ParenType<'a> {} +// Pat +#[derive(Debug, Clone, Copy)] +pub enum Pat<'a> { + RefPat(RefPat<'a>), + BindPat(BindPat<'a>), + PlaceholderPat(PlaceholderPat<'a>), + PathPat(PathPat<'a>), + StructPat(StructPat<'a>), + FieldPatList(FieldPatList<'a>), + TupleStructPat(TupleStructPat<'a>), + TuplePat(TuplePat<'a>), + SlicePat(SlicePat<'a>), + RangePat(RangePat<'a>), +} + +impl<'a> AstNode<'a> for Pat<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + REF_PAT => Some(Pat::RefPat(RefPat { syntax })), + BIND_PAT => Some(Pat::BindPat(BindPat { syntax })), + PLACEHOLDER_PAT => Some(Pat::PlaceholderPat(PlaceholderPat { syntax })), + PATH_PAT => Some(Pat::PathPat(PathPat { syntax })), + STRUCT_PAT => Some(Pat::StructPat(StructPat { syntax })), + FIELD_PAT_LIST => Some(Pat::FieldPatList(FieldPatList { syntax })), + TUPLE_STRUCT_PAT => Some(Pat::TupleStructPat(TupleStructPat { syntax })), + TUPLE_PAT => Some(Pat::TuplePat(TuplePat { syntax })), + SLICE_PAT => Some(Pat::SlicePat(SlicePat { syntax })), + RANGE_PAT => Some(Pat::RangePat(RangePat { syntax })), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { + match self { + Pat::RefPat(inner) => inner.syntax(), + Pat::BindPat(inner) => inner.syntax(), + Pat::PlaceholderPat(inner) => inner.syntax(), + Pat::PathPat(inner) => inner.syntax(), + Pat::StructPat(inner) => inner.syntax(), + Pat::FieldPatList(inner) => inner.syntax(), + Pat::TupleStructPat(inner) => inner.syntax(), + Pat::TuplePat(inner) => inner.syntax(), + Pat::SlicePat(inner) => inner.syntax(), + Pat::RangePat(inner) => inner.syntax(), + } + } +} + +impl<'a> Pat<'a> {} + // PathExpr #[derive(Debug, Clone, Copy)] pub struct PathExpr<'a> { @@ -885,6 +1045,24 @@ impl<'a> AstNode<'a> for PathExpr<'a> { impl<'a> PathExpr<'a> {} +// PathPat +#[derive(Debug, Clone, Copy)] +pub struct PathPat<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for PathPat<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + PATH_PAT => Some(PathPat { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> PathPat<'a> {} + // PathType #[derive(Debug, Clone, Copy)] pub struct PathType<'a> { @@ -903,6 +1081,24 @@ impl<'a> AstNode<'a> for PathType<'a> { impl<'a> PathType<'a> {} +// PlaceholderPat +#[derive(Debug, Clone, Copy)] +pub struct PlaceholderPat<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for PlaceholderPat<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + PLACEHOLDER_PAT => Some(PlaceholderPat { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> PlaceholderPat<'a> {} + // PlaceholderType #[derive(Debug, Clone, Copy)] pub struct PlaceholderType<'a> { @@ -975,6 +1171,24 @@ impl<'a> AstNode<'a> for RangeExpr<'a> { impl<'a> RangeExpr<'a> {} +// RangePat +#[derive(Debug, Clone, Copy)] +pub struct RangePat<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for RangePat<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + RANGE_PAT => Some(RangePat { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> RangePat<'a> {} + // RefExpr #[derive(Debug, Clone, Copy)] pub struct RefExpr<'a> { @@ -993,6 +1207,24 @@ impl<'a> AstNode<'a> for RefExpr<'a> { impl<'a> RefExpr<'a> {} +// RefPat +#[derive(Debug, Clone, Copy)] +pub struct RefPat<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for RefPat<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + REF_PAT => Some(RefPat { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> RefPat<'a> {} + // ReferenceType #[derive(Debug, Clone, Copy)] pub struct ReferenceType<'a> { @@ -1055,6 +1287,24 @@ impl<'a> Root<'a> { } } +// SlicePat +#[derive(Debug, Clone, Copy)] +pub struct SlicePat<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for SlicePat<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + SLICE_PAT => Some(SlicePat { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> SlicePat<'a> {} + // SliceType #[derive(Debug, Clone, Copy)] pub struct SliceType<'a> { @@ -1137,6 +1387,24 @@ impl<'a> AstNode<'a> for StructLit<'a> { impl<'a> StructLit<'a> {} +// StructPat +#[derive(Debug, Clone, Copy)] +pub struct StructPat<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for StructPat<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + STRUCT_PAT => Some(StructPat { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> StructPat<'a> {} + // TokenTree #[derive(Debug, Clone, Copy)] pub struct TokenTree<'a> { @@ -1211,6 +1479,42 @@ impl<'a> AstNode<'a> for TupleExpr<'a> { impl<'a> TupleExpr<'a> {} +// TuplePat +#[derive(Debug, Clone, Copy)] +pub struct TuplePat<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for TuplePat<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + TUPLE_PAT => Some(TuplePat { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> TuplePat<'a> {} + +// TupleStructPat +#[derive(Debug, Clone, Copy)] +pub struct TupleStructPat<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for TupleStructPat<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + TUPLE_STRUCT_PAT => Some(TupleStructPat { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> TupleStructPat<'a> {} + // TupleType #[derive(Debug, Clone, Copy)] pub struct TupleType<'a> { diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index a753d5e48..3a125ace6 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron @@ -241,11 +241,16 @@ Grammar( ["modules", "Module"], ] ), - "FnDef": ( traits: [ - "NameOwner", - "TypeParamsOwner", - "AttrsOwner", - ] ), + "FnDef": ( + traits: [ + "NameOwner", + "TypeParamsOwner", + "AttrsOwner", + ], + options: [ + ["param_list", "ParamList"] + ], + ), "StructDef": ( traits: [ "NameOwner", @@ -393,13 +398,52 @@ Grammar( ], ), + "RefPat": (), + "BindPat": ( traits: ["NameOwner"] ), + "PlaceholderPat": (), + "PathPat": (), + "StructPat": (), + "FieldPatList": (), + "TupleStructPat": (), + "TuplePat": (), + "SlicePat": (), + "RangePat": (), + + "Pat": ( + enum: [ + "RefPat", + "BindPat", + "PlaceholderPat", + "PathPat", + "StructPat", + "FieldPatList", + "TupleStructPat", + "TuplePat", + "SlicePat", + "RangePat", + ], + ), + "Name": (), "NameRef": (), "Attr": ( options: [ ["value", "TokenTree"] ] ), "TokenTree": (), "TypeParamList": ( collections: [ ["type_params", "TypeParam" ] ]), - "TypeParam": ( traits: ["NameOwner"]), + "TypeParam": ( traits: ["NameOwner"] ), "WhereClause": (), - "Block": (), + "LetStmt": ( options: [ ["pat", "Pat"] ]), + "Block": ( + collections: [ + ["let_stmts", "LetStmt"], + ] + ), + "ParamList": ( + collections: [ + ["params", "Param"] + ] + ), + "Param": ( + options: [["pat", "Pat"]], + ) }, ) diff --git a/crates/libsyntax2/src/grammar/items/mod.rs b/crates/libsyntax2/src/grammar/items/mod.rs index a285892df..7c6f7b63e 100644 --- a/crates/libsyntax2/src/grammar/items/mod.rs +++ b/crates/libsyntax2/src/grammar/items/mod.rs @@ -115,7 +115,7 @@ pub(super) fn maybe_item(p: &mut Parser, flavor: ItemFlavor) -> MaybeItem { // test unsafe_fn // unsafe fn foo() {} FN_KW => { - function(p, flavor); + fn_def(p, flavor); FN_DEF } @@ -227,7 +227,7 @@ fn extern_item_list(p: &mut Parser) { m.complete(p, EXTERN_ITEM_LIST); } -fn function(p: &mut Parser, flavor: ItemFlavor) { +fn fn_def(p: &mut Parser, flavor: ItemFlavor) { assert!(p.at(FN_KW)); p.bump(); -- cgit v1.2.3