mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-12 21:28:51 +00:00
scopes
This commit is contained in:
parent
9b69c7df19
commit
8b0298ce09
7 changed files with 155 additions and 75 deletions
|
@ -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<Vec<CompletionI
|
|||
file.incremental_reparse(&edit)?
|
||||
};
|
||||
let name_ref = find_node_at_offset::<ast::NameRef>(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<CompletionItem> {
|
||||
let mut res = Vec::new();
|
||||
for node in ancestors(name_ref.syntax()) {
|
||||
process_scope(node, &mut res);
|
||||
fn complete(name_ref: ast::NameRef, scopes: &FnScopes) -> Vec<CompletionItem> {
|
||||
scopes.scope_chain(name_ref.syntax())
|
||||
.flat_map(|scope| scopes.entries(scope).iter())
|
||||
.map(|entry| CompletionItem {
|
||||
name: entry.name().to_string()
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
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::<ast::LetStmt, _>(|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::<ast::ExprStmt, _>(|expr, (scopes, scope)| {
|
||||
scopes.set_scope(expr.syntax(), *scope)
|
||||
})
|
||||
.visit::<ast::Expr, _>(|expr, (scopes, scope)| {
|
||||
scopes.set_scope(expr.syntax(), *scope)
|
||||
})
|
||||
.accept(child);
|
||||
}
|
||||
}
|
||||
res
|
||||
scopes
|
||||
}
|
||||
|
||||
fn process_scope(node: SyntaxNodeRef, sink: &mut Vec<CompletionItem>) {
|
||||
let _ = visitor_ctx(sink)
|
||||
.visit::<ast::Block, _>(|block, sink| {
|
||||
block.let_stmts()
|
||||
.filter_map(|it| it.pat())
|
||||
.for_each(move |it| process_pat(it, sink))
|
||||
})
|
||||
.visit::<ast::FnDef, _>(|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);
|
||||
type ScopeId = usize;
|
||||
|
||||
fn process_pat(pat: ast::Pat, sink: &mut Vec<CompletionItem>) {
|
||||
let items = preorder(pat.syntax())
|
||||
struct FnScopes {
|
||||
scopes: Vec<ScopeData>,
|
||||
scope_for: HashMap<SyntaxNode, ScopeId>,
|
||||
}
|
||||
|
||||
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(ast::BindPat::name)
|
||||
.map(|name| CompletionItem { name: name.text().to_string() });
|
||||
sink.extend(items);
|
||||
.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<ScopeId> {
|
||||
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<Item=ScopeId> + 'a {
|
||||
generate(self.scope_for(node), move |&scope| self.scopes[scope].parent)
|
||||
}
|
||||
}
|
||||
|
||||
// fn compute_scopes(fn_def: ast::FnDef) -> FnScopes {
|
||||
// let mut scopes = FnScopes::new();
|
||||
// }
|
||||
struct ScopeData {
|
||||
parent: Option<ScopeId>,
|
||||
entries: Vec<ScopeEntry>
|
||||
}
|
||||
|
||||
// type ScopeId = usize;
|
||||
struct ScopeEntry {
|
||||
syntax: SyntaxNode
|
||||
}
|
||||
|
||||
// struct FnScopes {
|
||||
// scopes: Vec<ScopeData>,
|
||||
// scope_for_expr: HashMap<SyntaxNode, ScopeId>,
|
||||
// }
|
||||
impl ScopeEntry {
|
||||
fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
|
||||
if pat.name().is_some() {
|
||||
Some(ScopeEntry { syntax: pat.syntax().owned() })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
// impl FnScopes {
|
||||
// fn new() -> FnScopes {
|
||||
// FnScopes {
|
||||
// scopes: vec![],
|
||||
// scope_for_expr: HashMap::new(),
|
||||
// }
|
||||
// }
|
||||
fn name(&self) -> SmolStr {
|
||||
self.ast().name()
|
||||
.unwrap()
|
||||
.text()
|
||||
}
|
||||
|
||||
// fn new_scope(&mut Self) -> ScopeId {
|
||||
// let res = self.scopes.len();
|
||||
// self.scopes.push(ScopeData { parent: None, entries: vec![] })
|
||||
// }
|
||||
|
||||
// fn set_parent
|
||||
// }
|
||||
|
||||
// struct ScopeData {
|
||||
// parent: Option<ScopeId>,
|
||||
// entries: Vec<ScopeEntry>
|
||||
// }
|
||||
|
||||
// struct ScopeEntry {
|
||||
// syntax: SyntaxNode
|
||||
// }
|
||||
|
||||
// impl ScopeEntry {
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" }]"#);
|
||||
|
|
|
@ -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<T>(seed: Option<T>, step: impl Fn(&T) -> Option<T>) -> impl Iterator<Item=T> {
|
||||
pub fn generate<T>(seed: Option<T>, step: impl Fn(&T) -> Option<T>) -> impl Iterator<Item=T> {
|
||||
::itertools::unfold(seed, move |slot| {
|
||||
slot.take().map(|curr| {
|
||||
*slot = step(&curr);
|
||||
|
|
|
@ -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<Self> {
|
||||
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<ParamList<'a>> {
|
||||
super::child_opt(self)
|
||||
}
|
||||
|
||||
pub fn body(self) -> Option<Block<'a>> {
|
||||
super::child_opt(self)
|
||||
}
|
||||
}
|
||||
|
||||
// FnPointerType
|
||||
|
@ -626,6 +648,10 @@ impl<'a> LetStmt<'a> {
|
|||
pub fn pat(self) -> Option<Pat<'a>> {
|
||||
super::child_opt(self)
|
||||
}
|
||||
|
||||
pub fn initializer(self) -> Option<Expr<'a>> {
|
||||
super::child_opt(self)
|
||||
}
|
||||
}
|
||||
|
||||
// LoopExpr
|
||||
|
|
|
@ -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"],
|
||||
|
|
|
@ -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<RedNode>);
|
||||
|
||||
unsafe impl Send for RedPtr {}
|
||||
|
|
|
@ -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<R1: TreeRoot, R2: TreeRoot> PartialEq<SyntaxNode<R1>> for SyntaxNode<R2> {
|
|||
}
|
||||
|
||||
impl<R: TreeRoot> Eq for SyntaxNode<R> {}
|
||||
impl<R: TreeRoot> Hash for SyntaxNode<R> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.red.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
pub type SyntaxNodeRef<'a> = SyntaxNode<RefRoot<'a>>;
|
||||
|
||||
|
|
Loading…
Reference in a new issue