mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 13:51:31 +00:00
Use cached module scopes for completion
This commit is contained in:
parent
9b88ec488b
commit
397c235086
5 changed files with 71 additions and 51 deletions
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
{
|
|
||||||
if let Some(items) = items {
|
|
||||||
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);
|
let scopes = FnScopes::new(fn_def);
|
||||||
complete_fn(name_ref, &scopes, acc);
|
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()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -62,6 +62,11 @@ impl LocalSyntaxPtr {
|
||||||
local: self,
|
local: self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seems unfortunate to expose
|
||||||
|
pub(crate) fn range(self) -> TextRange {
|
||||||
|
self.range
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue