From 8b0298ce095b6dd635f7ed35dc97f1874157040b Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 27 Aug 2018 10:01:31 +0300 Subject: scopes --- crates/libeditor/src/completion.rs | 178 ++++++++++++++++++++------------- crates/libeditor/tests/test.rs | 3 +- crates/libsyntax2/src/algo/mod.rs | 2 +- crates/libsyntax2/src/ast/generated.rs | 26 +++++ crates/libsyntax2/src/grammar.ron | 9 +- crates/libsyntax2/src/yellow/mod.rs | 2 +- crates/libsyntax2/src/yellow/syntax.rs | 10 +- 7 files changed, 155 insertions(+), 75 deletions(-) (limited to 'crates') diff --git a/crates/libeditor/src/completion.rs b/crates/libeditor/src/completion.rs index 351781ec4..6335dba17 100644 --- a/crates/libeditor/src/completion.rs +++ b/crates/libeditor/src/completion.rs @@ -7,6 +7,7 @@ use libsyntax2::{ ancestors, visit::{visitor_ctx, VisitorCtx}, walk::preorder, + generate, }, }; @@ -27,86 +28,125 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option(file.syntax(), offset)?; - Some(complete(name_ref)) + let fn_def = ancestors(name_ref.syntax()).filter_map(ast::FnDef::cast).next()?; + let scopes = compute_scopes(fn_def); + Some(complete(name_ref, &scopes)) } -fn complete(name_ref: ast::NameRef) -> Vec { - 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)) +fn complete(name_ref: ast::NameRef, scopes: &FnScopes) -> Vec { + scopes.scope_chain(name_ref.syntax()) + .flat_map(|scope| scopes.entries(scope).iter()) + .map(|entry| CompletionItem { + name: entry.name().to_string() }) - .accept(node); + .collect() +} - 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); +fn compute_scopes(fn_def: ast::FnDef) -> FnScopes { + let mut scopes = FnScopes::new(); + let root = scopes.root_scope(); + fn_def.param_list().into_iter() + .flat_map(|it| it.params()) + .filter_map(|it| it.pat()) + .for_each(|it| scopes.add_bindings(root, it)); + + let mut scope = root; + if let Some(body) = fn_def.body() { + for child in body.syntax().children() { + let _ = visitor_ctx((&mut scopes, &mut scope)) + .visit::(|stmt, (scopes, scope)| { + *scope = scopes.new_scope(*scope); + if let Some(pat) = stmt.pat() { + scopes.add_bindings(*scope, pat); + } + if let Some(expr) = stmt.initializer() { + scopes.set_scope(expr.syntax(), *scope) + } + }) + .visit::(|expr, (scopes, scope)| { + scopes.set_scope(expr.syntax(), *scope) + }) + .visit::(|expr, (scopes, scope)| { + scopes.set_scope(expr.syntax(), *scope) + }) + .accept(child); + } } + scopes } -// fn compute_scopes(fn_def: ast::FnDef) -> FnScopes { -// let mut scopes = FnScopes::new(); -// } - -// type ScopeId = usize; +type ScopeId = usize; -// struct FnScopes { -// scopes: Vec, -// scope_for_expr: HashMap, -// } - -// impl FnScopes { -// fn new() -> FnScopes { -// FnScopes { -// scopes: vec![], -// scope_for_expr: HashMap::new(), -// } -// } +struct FnScopes { + scopes: Vec, + scope_for: HashMap, +} -// fn new_scope(&mut Self) -> ScopeId { -// let res = self.scopes.len(); -// self.scopes.push(ScopeData { parent: None, entries: vec![] }) -// } +impl FnScopes { + fn new() -> FnScopes { + FnScopes { + scopes: vec![], + scope_for: HashMap::new(), + } + } + fn root_scope(&mut self) -> ScopeId { + let res = self.scopes.len(); + self.scopes.push(ScopeData { parent: None, entries: vec![] }); + res + } + fn new_scope(&mut self, parent: ScopeId) -> ScopeId { + let res = self.scopes.len(); + self.scopes.push(ScopeData { parent: Some(parent), entries: vec![] }); + res + } + fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) { + let entries = preorder(pat.syntax()) + .filter_map(ast::BindPat::cast) + .filter_map(ScopeEntry::new); + self.scopes[scope].entries.extend(entries); + } + fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) { + self.scope_for.insert(node.owned(), scope); + } + fn entries(&self, scope: ScopeId) -> &[ScopeEntry] { + &self.scopes[scope].entries + } + fn scope_for(&self, node: SyntaxNodeRef) -> Option { + ancestors(node) + .filter_map(|it| self.scope_for.get(&it.owned()).map(|&scope| scope)) + .next() + } + fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator + 'a { + generate(self.scope_for(node), move |&scope| self.scopes[scope].parent) + } +} -// fn set_parent -// } +struct ScopeData { + parent: Option, + entries: Vec +} -// struct ScopeData { -// parent: Option, -// entries: Vec -// } +struct ScopeEntry { + syntax: SyntaxNode +} -// struct ScopeEntry { -// syntax: SyntaxNode -// } +impl ScopeEntry { + fn new(pat: ast::BindPat) -> Option { + if pat.name().is_some() { + Some(ScopeEntry { syntax: pat.syntax().owned() }) + } else { + None + } + } -// impl ScopeEntry { -// fn name(&self) -> SmolStr { -// self.ast().name() -// .unwrap() -// .text() -// } + fn name(&self) -> SmolStr { + self.ast().name() + .unwrap() + .text() + } -// fn ast(&self) -> ast::BindPat { -// ast::BindPat::cast(self.syntax.borrowed()) -// .unwrap() -// } -// } + fn ast(&self) -> ast::BindPat { + ast::BindPat::cast(self.syntax.borrowed()) + .unwrap() + } +} diff --git a/crates/libeditor/tests/test.rs b/crates/libeditor/tests/test.rs index ecdc149c7..7979bfffe 100644 --- a/crates/libeditor/tests/test.rs +++ b/crates/libeditor/tests/test.rs @@ -268,7 +268,8 @@ fn test_completion() { do_check(r" fn quux(x: i32) { let y = 92; - 1 + <|> + 1 + <|>; + let z = (); } ", r#"[CompletionItem { name: "y" }, CompletionItem { name: "x" }]"#); diff --git a/crates/libsyntax2/src/algo/mod.rs b/crates/libsyntax2/src/algo/mod.rs index 2640d60ea..7287f5bb2 100644 --- a/crates/libsyntax2/src/algo/mod.rs +++ b/crates/libsyntax2/src/algo/mod.rs @@ -119,7 +119,7 @@ fn common_ancestor<'a>(n1: SyntaxNodeRef<'a>, n2: SyntaxNodeRef<'a>) -> SyntaxNo panic!("Can't find common ancestor of {:?} and {:?}", n1, n2) } -fn generate(seed: Option, step: impl Fn(&T) -> Option) -> impl Iterator { +pub fn generate(seed: Option, step: impl Fn(&T) -> Option) -> impl Iterator { ::itertools::unfold(seed, move |slot| { slot.take().map(|curr| { *slot = step(&curr); diff --git a/crates/libsyntax2/src/ast/generated.rs b/crates/libsyntax2/src/ast/generated.rs index 6926c0535..b937fe5a2 100644 --- a/crates/libsyntax2/src/ast/generated.rs +++ b/crates/libsyntax2/src/ast/generated.rs @@ -383,6 +383,24 @@ impl<'a> AstNode<'a> for Expr<'a> { impl<'a> Expr<'a> {} +// ExprStmt +#[derive(Debug, Clone, Copy)] +pub struct ExprStmt<'a> { + syntax: SyntaxNodeRef<'a>, +} + +impl<'a> AstNode<'a> for ExprStmt<'a> { + fn cast(syntax: SyntaxNodeRef<'a>) -> Option { + match syntax.kind() { + EXPR_STMT => Some(ExprStmt { syntax }), + _ => None, + } + } + fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax } +} + +impl<'a> ExprStmt<'a> {} + // FieldExpr #[derive(Debug, Clone, Copy)] pub struct FieldExpr<'a> { @@ -442,6 +460,10 @@ impl<'a> FnDef<'a> { pub fn param_list(self) -> Option> { super::child_opt(self) } + + pub fn body(self) -> Option> { + super::child_opt(self) + } } // FnPointerType @@ -626,6 +648,10 @@ impl<'a> LetStmt<'a> { pub fn pat(self) -> Option> { super::child_opt(self) } + + pub fn initializer(self) -> Option> { + super::child_opt(self) + } } // LoopExpr diff --git a/crates/libsyntax2/src/grammar.ron b/crates/libsyntax2/src/grammar.ron index 3a125ace6..aa2742b3e 100644 --- a/crates/libsyntax2/src/grammar.ron +++ b/crates/libsyntax2/src/grammar.ron @@ -248,7 +248,8 @@ Grammar( "AttrsOwner", ], options: [ - ["param_list", "ParamList"] + ["param_list", "ParamList"], + ["body", "Block"], ], ), "StructDef": ( @@ -431,7 +432,11 @@ Grammar( "TypeParamList": ( collections: [ ["type_params", "TypeParam" ] ]), "TypeParam": ( traits: ["NameOwner"] ), "WhereClause": (), - "LetStmt": ( options: [ ["pat", "Pat"] ]), + "ExprStmt": (), + "LetStmt": ( options: [ + ["pat", "Pat"], + ["initializer", "Expr"], + ]), "Block": ( collections: [ ["let_stmts", "LetStmt"], diff --git a/crates/libsyntax2/src/yellow/mod.rs b/crates/libsyntax2/src/yellow/mod.rs index 3c4510fe7..b94c794fe 100644 --- a/crates/libsyntax2/src/yellow/mod.rs +++ b/crates/libsyntax2/src/yellow/mod.rs @@ -66,7 +66,7 @@ impl SyntaxRoot { } } -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] pub(crate) struct RedPtr(ptr::NonNull); unsafe impl Send for RedPtr {} diff --git a/crates/libsyntax2/src/yellow/syntax.rs b/crates/libsyntax2/src/yellow/syntax.rs index 0045598d4..75b6cb7dc 100644 --- a/crates/libsyntax2/src/yellow/syntax.rs +++ b/crates/libsyntax2/src/yellow/syntax.rs @@ -1,4 +1,7 @@ -use std::{fmt, sync::Arc}; +use std::{ + fmt, sync::Arc, + hash::{Hasher, Hash}, +}; use smol_str::SmolStr; @@ -27,6 +30,11 @@ impl PartialEq> for SyntaxNode { } impl Eq for SyntaxNode {} +impl Hash for SyntaxNode { + fn hash(&self, state: &mut H) { + self.red.hash(state) + } +} pub type SyntaxNodeRef<'a> = SyntaxNode>; -- cgit v1.2.3