mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 05:38:46 +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,
|
ancestors,
|
||||||
visit::{visitor_ctx, VisitorCtx},
|
visit::{visitor_ctx, VisitorCtx},
|
||||||
walk::preorder,
|
walk::preorder,
|
||||||
|
generate,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,86 +28,125 @@ pub fn scope_completion(file: &File, offset: TextUnit) -> Option<Vec<CompletionI
|
||||||
file.incremental_reparse(&edit)?
|
file.incremental_reparse(&edit)?
|
||||||
};
|
};
|
||||||
let name_ref = find_node_at_offset::<ast::NameRef>(file.syntax(), offset)?;
|
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> {
|
fn complete(name_ref: ast::NameRef, scopes: &FnScopes) -> Vec<CompletionItem> {
|
||||||
let mut res = Vec::new();
|
scopes.scope_chain(name_ref.syntax())
|
||||||
for node in ancestors(name_ref.syntax()) {
|
.flat_map(|scope| scopes.entries(scope).iter())
|
||||||
process_scope(node, &mut res);
|
.map(|entry| CompletionItem {
|
||||||
}
|
name: entry.name().to_string()
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
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| {
|
.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()
|
fn_def.param_list().into_iter()
|
||||||
.flat_map(|it| it.params())
|
.flat_map(|it| it.params())
|
||||||
.filter_map(|it| it.pat())
|
.filter_map(|it| it.pat())
|
||||||
.for_each(move |it| process_pat(it, sink))
|
.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)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.accept(node);
|
.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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scopes
|
||||||
|
}
|
||||||
|
|
||||||
fn process_pat(pat: ast::Pat, sink: &mut Vec<CompletionItem>) {
|
type ScopeId = usize;
|
||||||
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::cast)
|
||||||
.filter_map(ast::BindPat::name)
|
.filter_map(ScopeEntry::new);
|
||||||
.map(|name| CompletionItem { name: name.text().to_string() });
|
self.scopes[scope].entries.extend(entries);
|
||||||
sink.extend(items);
|
}
|
||||||
|
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 {
|
struct ScopeData {
|
||||||
// let mut scopes = FnScopes::new();
|
parent: Option<ScopeId>,
|
||||||
// }
|
entries: Vec<ScopeEntry>
|
||||||
|
}
|
||||||
|
|
||||||
// type ScopeId = usize;
|
struct ScopeEntry {
|
||||||
|
syntax: SyntaxNode
|
||||||
|
}
|
||||||
|
|
||||||
// struct FnScopes {
|
impl ScopeEntry {
|
||||||
// scopes: Vec<ScopeData>,
|
fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
|
||||||
// scope_for_expr: HashMap<SyntaxNode, ScopeId>,
|
if pat.name().is_some() {
|
||||||
// }
|
Some(ScopeEntry { syntax: pat.syntax().owned() })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// impl FnScopes {
|
fn name(&self) -> SmolStr {
|
||||||
// fn new() -> FnScopes {
|
self.ast().name()
|
||||||
// FnScopes {
|
.unwrap()
|
||||||
// scopes: vec![],
|
.text()
|
||||||
// scope_for_expr: HashMap::new(),
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// fn new_scope(&mut Self) -> ScopeId {
|
fn ast(&self) -> ast::BindPat {
|
||||||
// let res = self.scopes.len();
|
ast::BindPat::cast(self.syntax.borrowed())
|
||||||
// self.scopes.push(ScopeData { parent: None, entries: vec![] })
|
.unwrap()
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
// 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()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
@ -268,7 +268,8 @@ fn test_completion() {
|
||||||
do_check(r"
|
do_check(r"
|
||||||
fn quux(x: i32) {
|
fn quux(x: i32) {
|
||||||
let y = 92;
|
let y = 92;
|
||||||
1 + <|>
|
1 + <|>;
|
||||||
|
let z = ();
|
||||||
}
|
}
|
||||||
", r#"[CompletionItem { name: "y" },
|
", r#"[CompletionItem { name: "y" },
|
||||||
CompletionItem { name: "x" }]"#);
|
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)
|
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| {
|
::itertools::unfold(seed, move |slot| {
|
||||||
slot.take().map(|curr| {
|
slot.take().map(|curr| {
|
||||||
*slot = step(&curr);
|
*slot = step(&curr);
|
||||||
|
|
|
@ -383,6 +383,24 @@ impl<'a> AstNode<'a> for Expr<'a> {
|
||||||
|
|
||||||
impl<'a> 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
|
// FieldExpr
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct FieldExpr<'a> {
|
pub struct FieldExpr<'a> {
|
||||||
|
@ -442,6 +460,10 @@ impl<'a> FnDef<'a> {
|
||||||
pub fn param_list(self) -> Option<ParamList<'a>> {
|
pub fn param_list(self) -> Option<ParamList<'a>> {
|
||||||
super::child_opt(self)
|
super::child_opt(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn body(self) -> Option<Block<'a>> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FnPointerType
|
// FnPointerType
|
||||||
|
@ -626,6 +648,10 @@ impl<'a> LetStmt<'a> {
|
||||||
pub fn pat(self) -> Option<Pat<'a>> {
|
pub fn pat(self) -> Option<Pat<'a>> {
|
||||||
super::child_opt(self)
|
super::child_opt(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn initializer(self) -> Option<Expr<'a>> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoopExpr
|
// LoopExpr
|
||||||
|
|
|
@ -248,7 +248,8 @@ Grammar(
|
||||||
"AttrsOwner",
|
"AttrsOwner",
|
||||||
],
|
],
|
||||||
options: [
|
options: [
|
||||||
["param_list", "ParamList"]
|
["param_list", "ParamList"],
|
||||||
|
["body", "Block"],
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
"StructDef": (
|
"StructDef": (
|
||||||
|
@ -431,7 +432,11 @@ Grammar(
|
||||||
"TypeParamList": ( collections: [ ["type_params", "TypeParam" ] ]),
|
"TypeParamList": ( collections: [ ["type_params", "TypeParam" ] ]),
|
||||||
"TypeParam": ( traits: ["NameOwner"] ),
|
"TypeParam": ( traits: ["NameOwner"] ),
|
||||||
"WhereClause": (),
|
"WhereClause": (),
|
||||||
"LetStmt": ( options: [ ["pat", "Pat"] ]),
|
"ExprStmt": (),
|
||||||
|
"LetStmt": ( options: [
|
||||||
|
["pat", "Pat"],
|
||||||
|
["initializer", "Expr"],
|
||||||
|
]),
|
||||||
"Block": (
|
"Block": (
|
||||||
collections: [
|
collections: [
|
||||||
["let_stmts", "LetStmt"],
|
["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>);
|
pub(crate) struct RedPtr(ptr::NonNull<RedNode>);
|
||||||
|
|
||||||
unsafe impl Send for RedPtr {}
|
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;
|
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> 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>>;
|
pub type SyntaxNodeRef<'a> = SyntaxNode<RefRoot<'a>>;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue