Use cached module scopes for completion

This commit is contained in:
Aleksey Kladov 2018-11-07 21:08:11 +03:00
parent 9b88ec488b
commit 397c235086
5 changed files with 71 additions and 51 deletions

View file

@ -2,6 +2,7 @@ mod reference_completion;
use ra_editor::find_node_at_offset; use ra_editor::find_node_at_offset;
use ra_syntax::{ use ra_syntax::{
algo::find_leaf_at_offset,
algo::visit::{visitor_ctx, VisitorCtx}, algo::visit::{visitor_ctx, VisitorCtx},
ast, ast,
AstNode, AtomEdit, AstNode, AtomEdit,
@ -11,6 +12,8 @@ use rustc_hash::{FxHashMap};
use crate::{ use crate::{
db::{self, SyntaxDatabase}, db::{self, SyntaxDatabase},
descriptors::{DescriptorDatabase, module::ModuleSource},
input::{FilesDatabase},
Cancelable, FilePosition Cancelable, FilePosition
}; };
@ -35,12 +38,32 @@ pub(crate) fn completions(
original_file.reparse(&edit) original_file.reparse(&edit)
}; };
let leaf = match find_leaf_at_offset(original_file.syntax(), position.offset).left_biased() {
None => return Ok(None),
Some(it) => it,
};
let source_root_id = db.file_source_root(position.file_id);
let module_tree = db.module_tree(source_root_id)?;
let module_source = ModuleSource::for_node(position.file_id, leaf);
let module_id = match module_tree.any_module_for_source(module_source) {
None => return Ok(None),
Some(it) => it,
};
let mut res = Vec::new(); let mut res = Vec::new();
let mut has_completions = false; let mut has_completions = false;
// First, let's try to complete a reference to some declaration. // First, let's try to complete a reference to some declaration.
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) { if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(file.syntax(), position.offset) {
has_completions = true; has_completions = true;
reference_completion::completions(&mut res, db, position.file_id, &file, name_ref)?; reference_completion::completions(
&mut res,
db,
source_root_id,
&module_tree,
module_id,
&file,
name_ref,
)?;
// special case, `trait T { fn foo(i_am_a_name_ref) {} }` // special case, `trait T { fn foo(i_am_a_name_ref) {} }`
if is_node::<ast::Param>(name_ref.syntax()) { if is_node::<ast::Param>(name_ref.syntax()) {
param_completions(name_ref.syntax(), &mut res); param_completions(name_ref.syntax(), &mut res);

View file

@ -3,24 +3,26 @@ use ra_editor::find_node_at_offset;
use ra_syntax::{ use ra_syntax::{
algo::visit::{visitor, Visitor}, algo::visit::{visitor, Visitor},
SourceFileNode, AstNode, SourceFileNode, AstNode,
ast::{self, AstChildren, ModuleItemOwner, LoopBodyOwner}, ast::{self, LoopBodyOwner},
SyntaxKind::*, SyntaxKind::*,
}; };
use crate::{ use crate::{
db::RootDatabase, db::RootDatabase,
input::FilesDatabase, input::{SourceRootId},
completion::CompletionItem, completion::CompletionItem,
descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource}, descriptors::module::{ModuleId, ModuleScope, ModuleTree},
descriptors::function::FnScopes, descriptors::function::FnScopes,
descriptors::DescriptorDatabase, descriptors::DescriptorDatabase,
FileId, Cancelable Cancelable
}; };
pub(super) fn completions( pub(super) fn completions(
acc: &mut Vec<CompletionItem>, acc: &mut Vec<CompletionItem>,
db: &RootDatabase, db: &RootDatabase,
file_id: FileId, source_root_id: SourceRootId,
module_tree: &ModuleTree,
module_id: ModuleId,
file: &SourceFileNode, file: &SourceFileNode,
name_ref: ast::NameRef, name_ref: ast::NameRef,
) -> Cancelable<()> { ) -> Cancelable<()> {
@ -28,14 +30,18 @@ pub(super) fn completions(
Some(it) => it, Some(it) => it,
None => return Ok(()), None => return Ok(()),
}; };
match kind { match kind {
NameRefKind::LocalRef => { NameRefKind::LocalRef => {
if let Some(fn_def) = complete_local_name(acc, &file, name_ref) { let module_scope = db.module_scope(source_root_id, module_id)?;
if let Some(fn_def) = complete_local_name(acc, &module_scope, name_ref) {
complete_expr_keywords(&file, fn_def, name_ref, acc); complete_expr_keywords(&file, fn_def, name_ref, acc);
complete_expr_snippets(acc); complete_expr_snippets(acc);
} }
} }
NameRefKind::CratePath(path) => complete_path(acc, db, file_id, path)?, NameRefKind::CratePath(path) => {
complete_path(acc, db, source_root_id, module_tree, module_id, path)?
}
NameRefKind::BareIdentInMod => { NameRefKind::BareIdentInMod => {
let name_range = name_ref.syntax().range(); let name_range = name_ref.syntax().range();
let top_node = name_ref let top_node = name_ref
@ -107,45 +113,26 @@ fn crate_path(mut path: ast::Path) -> Option<Vec<ast::NameRef>> {
fn complete_local_name<'a>( fn complete_local_name<'a>(
acc: &mut Vec<CompletionItem>, acc: &mut Vec<CompletionItem>,
file: &SourceFileNode, module_scope: &ModuleScope,
name_ref: ast::NameRef<'a>, name_ref: ast::NameRef<'a>,
) -> Option<ast::FnDef<'a>> { ) -> Option<ast::FnDef<'a>> {
let mut enclosing_fn = None; let enclosing_fn = name_ref
for node in name_ref.syntax().ancestors() { .syntax()
if let Some(items) = visitor() .ancestors()
.visit::<ast::SourceFile, _>(|it| Some(it.items())) .take_while(|it| it.kind() != SOURCE_FILE && it.kind() != MODULE)
.visit::<ast::Module, _>(|it| Some(it.item_list()?.items())) .find_map(ast::FnDef::cast);
.accept(node) if let Some(fn_def) = enclosing_fn {
{ let scopes = FnScopes::new(fn_def);
if let Some(items) = items { complete_fn(name_ref, &scopes, acc);
complete_module_items(file, items, Some(name_ref), acc);
}
break;
} else if enclosing_fn.is_none() {
if let Some(fn_def) = ast::FnDef::cast(node) {
enclosing_fn = Some(fn_def);
let scopes = FnScopes::new(fn_def);
complete_fn(name_ref, &scopes, acc);
}
}
} }
enclosing_fn
}
fn complete_module_items(
file: &SourceFileNode,
items: AstChildren<ast::ModuleItem>,
this_item: Option<ast::NameRef>,
acc: &mut Vec<CompletionItem>,
) {
let scope = ModuleScope::new(items); // FIXME
acc.extend( acc.extend(
scope module_scope
.entries() .entries()
.iter() .iter()
.filter(|entry| { .filter(|entry| {
let syntax = entry.ptr().resolve(file); // Don't expose this item
Some(syntax.borrowed()) != this_item.map(|it| it.syntax()) !entry.ptr().range().is_subrange(&name_ref.syntax().range())
}) })
.map(|entry| CompletionItem { .map(|entry| CompletionItem {
label: entry.name().to_string(), label: entry.name().to_string(),
@ -153,6 +140,7 @@ fn complete_module_items(
snippet: None, snippet: None,
}), }),
); );
enclosing_fn
} }
fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) { fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<CompletionItem>) {
@ -180,16 +168,12 @@ fn complete_fn(name_ref: ast::NameRef, scopes: &FnScopes, acc: &mut Vec<Completi
fn complete_path( fn complete_path(
acc: &mut Vec<CompletionItem>, acc: &mut Vec<CompletionItem>,
db: &RootDatabase, db: &RootDatabase,
file_id: FileId, source_root_id: SourceRootId,
module_tree: &ModuleTree,
module_id: ModuleId,
crate_path: Vec<ast::NameRef>, crate_path: Vec<ast::NameRef>,
) -> Cancelable<()> { ) -> Cancelable<()> {
let source_root_id = db.file_source_root(file_id); let target_module_id = match find_target_module(module_tree, module_id, crate_path) {
let module_tree = db.module_tree(source_root_id)?;
let module_id = match module_tree.any_module_for_source(ModuleSource::SourceFile(file_id)) {
None => return Ok(()),
Some(it) => it,
};
let target_module_id = match find_target_module(&module_tree, module_id, crate_path) {
None => return Ok(()), None => return Ok(()),
Some(it) => it, Some(it) => it,
}; };
@ -327,5 +311,3 @@ fn complete_expr_snippets(acc: &mut Vec<CompletionItem>) {
snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()), snippet: Some("eprintln!(\"$0 = {:#?}\", $0);".to_string()),
}); });
} }

View file

@ -3,7 +3,7 @@ pub(crate) mod scope;
use ra_syntax::{ use ra_syntax::{
ast::{self, AstNode, NameOwner}, ast::{self, AstNode, NameOwner},
SmolStr, SyntaxNode, SmolStr, SyntaxNode, SyntaxNodeRef,
}; };
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
@ -154,6 +154,16 @@ struct ModuleData {
} }
impl ModuleSource { impl ModuleSource {
pub(crate) fn for_node(file_id: FileId, node: SyntaxNodeRef) -> ModuleSource {
for node in node.ancestors() {
if let Some(m) = ast::Module::cast(node) {
if !m.has_semi() {
return ModuleSource::new_inline(file_id, m);
}
}
}
ModuleSource::SourceFile(file_id)
}
pub(crate) fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource { pub(crate) fn new_inline(file_id: FileId, module: ast::Module) -> ModuleSource {
assert!(!module.has_semi()); assert!(!module.has_semi());
let ptr = SyntaxPtr::new(file_id, module.syntax()); let ptr = SyntaxPtr::new(file_id, module.syntax());

View file

@ -25,7 +25,7 @@ enum EntryKind {
} }
impl ModuleScope { impl ModuleScope {
pub(crate) fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> ModuleScope { pub(super) fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> ModuleScope {
let mut entries = Vec::new(); let mut entries = Vec::new();
for item in items { for item in items {
let entry = match item { let entry = match item {

View file

@ -62,6 +62,11 @@ impl LocalSyntaxPtr {
local: self, local: self,
} }
} }
// Seems unfortunate to expose
pub(crate) fn range(self) -> TextRange {
self.range
}
} }
#[test] #[test]