switch completion to new scope

This commit is contained in:
Aleksey Kladov 2018-11-21 12:57:05 +03:00
parent b70b6bce19
commit 049f8df93c
9 changed files with 69 additions and 198 deletions

View file

@ -204,9 +204,9 @@ mod tests {
<|> <|>
} }
", ",
r#"[CompletionItem { label: "Foo", lookup: None, snippet: None }, r#"[CompletionItem { label: "quux", lookup: None, snippet: None },
CompletionItem { label: "Baz", lookup: None, snippet: None }, CompletionItem { label: "Foo", lookup: None, snippet: None },
CompletionItem { label: "quux", lookup: None, snippet: None }]"#, CompletionItem { label: "Baz", lookup: None, snippet: None }]"#,
); );
} }
@ -230,8 +230,8 @@ mod tests {
fn quux() { <|> } fn quux() { <|> }
} }
", ",
r#"[CompletionItem { label: "Bar", lookup: None, snippet: None }, r#"[CompletionItem { label: "quux", lookup: None, snippet: None },
CompletionItem { label: "quux", lookup: None, snippet: None }]"#, CompletionItem { label: "Bar", lookup: None, snippet: None }]"#,
); );
} }

View file

@ -39,14 +39,17 @@ pub(super) fn completions(
let module_scope = module.scope(db)?; let module_scope = module.scope(db)?;
acc.extend( acc.extend(
module_scope module_scope
.entries() .items
.iter() .iter()
.filter(|entry| { .filter(|(_name, res)| {
// Don't expose this item // Don't expose this item
!entry.ptr().range().is_subrange(&name_ref.syntax().range()) match res.import_name {
None => true,
Some(ptr) => !ptr.range().is_subrange(&name_ref.syntax().range()),
}
}) })
.map(|entry| CompletionItem { .map(|(name, _res)| CompletionItem {
label: entry.name().to_string(), label: name.to_string(),
lookup: None, lookup: None,
snippet: None, snippet: None,
}), }),
@ -173,8 +176,11 @@ fn complete_path(
Some(it) => it, Some(it) => it,
}; };
let module_scope = target_module.scope(db)?; let module_scope = target_module.scope(db)?;
let completions = module_scope.entries().iter().map(|entry| CompletionItem { let completions = module_scope
label: entry.name().to_string(), .items
.iter()
.map(|(name, _res)| CompletionItem {
label: name.to_string(),
lookup: None, lookup: None,
snippet: None, snippet: None,
}); });

View file

@ -7,7 +7,7 @@ use salsa::{self, Database};
use crate::{ use crate::{
db, db,
descriptors::{ descriptors::{
DescriptorDatabase, FnScopesQuery, FnSyntaxQuery, ModuleScopeQuery, ModuleTreeQuery, DescriptorDatabase, FnScopesQuery, FnSyntaxQuery, ModuleTreeQuery,
SubmodulesQuery, ItemMapQuery, InputModuleItemsQuery, SubmodulesQuery, ItemMapQuery, InputModuleItemsQuery,
}, },
symbol_index::SymbolIndex, symbol_index::SymbolIndex,
@ -88,7 +88,6 @@ salsa::database_storage! {
fn fn_scopes() for FnScopesQuery; fn fn_scopes() for FnScopesQuery;
fn _input_module_items() for InputModuleItemsQuery; fn _input_module_items() for InputModuleItemsQuery;
fn _item_map() for ItemMapQuery; fn _item_map() for ItemMapQuery;
fn _module_scope() for ModuleScopeQuery;
fn _fn_syntax() for FnSyntaxQuery; fn _fn_syntax() for FnSyntaxQuery;
fn _submodules() for SubmodulesQuery; fn _submodules() for SubmodulesQuery;
} }

View file

@ -11,7 +11,7 @@ use ra_syntax::{
use crate::{ use crate::{
db::SyntaxDatabase, db::SyntaxDatabase,
descriptors::function::{resolve_local_name, FnId, FnScopes}, descriptors::function::{resolve_local_name, FnId, FnScopes},
descriptors::module::{ModuleId, ModuleScope, ModuleTree, ModuleSource, nameres::{ItemMap, InputModuleItems}}, descriptors::module::{ModuleId, ModuleTree, ModuleSource, nameres::{ItemMap, InputModuleItems}},
input::SourceRootId, input::SourceRootId,
loc2id::IdDatabase, loc2id::IdDatabase,
syntax_ptr::LocalSyntaxPtr, syntax_ptr::LocalSyntaxPtr,
@ -37,10 +37,6 @@ salsa::query_group! {
type ModuleTreeQuery; type ModuleTreeQuery;
use fn module::imp::module_tree; use fn module::imp::module_tree;
} }
fn _module_scope(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleScope>> {
type ModuleScopeQuery;
use fn module::imp::module_scope;
}
fn _fn_syntax(fn_id: FnId) -> FnDefNode { fn _fn_syntax(fn_id: FnId) -> FnDefNode {
type FnSyntaxQuery; type FnSyntaxQuery;
// Don't retain syntax trees in memory // Don't retain syntax trees in memory

View file

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use ra_syntax::{ use ra_syntax::{
ast::{self, ModuleItemOwner, NameOwner}, ast::{self, NameOwner},
SmolStr, SmolStr,
}; };
use relative_path::RelativePathBuf; use relative_path::RelativePathBuf;
@ -15,7 +15,7 @@ use crate::{
}; };
use super::{ use super::{
LinkData, LinkId, ModuleData, ModuleId, ModuleScope, ModuleSource, ModuleSourceNode, LinkData, LinkId, ModuleData, ModuleId, ModuleSource, ModuleSourceNode,
ModuleTree, Problem, ModuleTree, Problem,
}; };
@ -81,23 +81,6 @@ pub(crate) fn modules<'a>(
}) })
} }
pub(crate) fn module_scope(
db: &impl DescriptorDatabase,
source_root_id: SourceRootId,
module_id: ModuleId,
) -> Cancelable<Arc<ModuleScope>> {
let tree = db._module_tree(source_root_id)?;
let source = module_id.source(&tree).resolve(db);
let res = match source {
ModuleSourceNode::SourceFile(it) => ModuleScope::new(it.borrowed().items()),
ModuleSourceNode::Module(it) => match it.borrowed().item_list() {
Some(items) => ModuleScope::new(items.items()),
None => ModuleScope::new(std::iter::empty()),
},
};
Ok(Arc::new(res))
}
pub(crate) fn module_tree( pub(crate) fn module_tree(
db: &impl DescriptorDatabase, db: &impl DescriptorDatabase,
source_root: SourceRootId, source_root: SourceRootId,

View file

@ -1,5 +1,4 @@
pub(super) mod imp; pub(super) mod imp;
mod scope;
pub(super) mod nameres; pub(super) mod nameres;
use std::sync::Arc; use std::sync::Arc;
@ -19,7 +18,7 @@ use crate::{
input::SourceRootId input::SourceRootId
}; };
pub(crate) use self::scope::ModuleScope; pub(crate) use self::{nameres::ModuleScope};
/// `ModuleDescriptor` is API entry point to get all the information /// `ModuleDescriptor` is API entry point to get all the information
/// about a particular module. /// about a particular module.
@ -126,8 +125,10 @@ impl ModuleDescriptor {
} }
/// Returns a `ModuleScope`: a set of items, visible in this module. /// Returns a `ModuleScope`: a set of items, visible in this module.
pub fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<Arc<ModuleScope>> { pub(crate) fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<ModuleScope> {
db._module_scope(self.source_root_id, self.module_id) let item_map = db._item_map(self.source_root_id)?;
let res = item_map.per_module[&self.module_id].clone();
Ok(res)
} }
pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> { pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> {

View file

@ -8,7 +8,7 @@ use rustc_hash::FxHashMap;
use ra_syntax::{ use ra_syntax::{
SmolStr, SyntaxKind::{self, *}, SmolStr, SyntaxKind::{self, *},
ast::{self, NameOwner, AstNode, ModuleItemOwner} ast::{self, AstNode, ModuleItemOwner}
}; };
use crate::{ use crate::{
@ -26,13 +26,13 @@ use crate::{
/// module, the set of visible items. /// module, the set of visible items.
#[derive(Default, Debug, PartialEq, Eq)] #[derive(Default, Debug, PartialEq, Eq)]
pub(crate) struct ItemMap { pub(crate) struct ItemMap {
per_module: FxHashMap<ModuleId, ModuleItems>, pub(crate) per_module: FxHashMap<ModuleId, ModuleScope>,
} }
#[derive(Debug, Default, PartialEq, Eq)] #[derive(Debug, Default, PartialEq, Eq, Clone)]
struct ModuleItems { pub(crate) struct ModuleScope {
items: FxHashMap<SmolStr, Resolution>, pub(crate) items: FxHashMap<SmolStr, Resolution>,
import_resolutions: FxHashMap<LocalSyntaxPtr, DefId>, pub(crate) import_resolutions: FxHashMap<LocalSyntaxPtr, DefId>,
} }
/// A set of items and imports declared inside a module, without relation to /// A set of items and imports declared inside a module, without relation to
@ -117,22 +117,25 @@ pub(crate) fn item_map(
/// Resolution is basically `DefId` atm, but it should account for stuff like /// Resolution is basically `DefId` atm, but it should account for stuff like
/// multiple namespaces, ambiguity and errors. /// multiple namespaces, ambiguity and errors.
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
struct Resolution { pub(crate) struct Resolution {
/// None for unresolved /// None for unresolved
def_id: Option<DefId>, pub(crate) def_id: Option<DefId>,
/// ident by whitch this is imported into local scope.
/// TODO: make this offset-independent.
pub(crate) import_name: Option<LocalSyntaxPtr>,
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] // #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum Namespace { // enum Namespace {
Types, // Types,
Values, // Values,
} // }
#[derive(Debug)] // #[derive(Debug)]
struct PerNs<T> { // struct PerNs<T> {
types: Option<T>, // types: Option<T>,
values: Option<T>, // values: Option<T>,
} // }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
struct ModuleItem { struct ModuleItem {
@ -144,7 +147,7 @@ struct ModuleItem {
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq)]
enum Vis { enum Vis {
Priv, // Priv,
Other, Other,
} }
@ -302,13 +305,17 @@ where
fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) { fn populate_module(&mut self, module_id: ModuleId, input: &InputModuleItems) {
let file_id = module_id.source(&self.module_tree).file_id(); let file_id = module_id.source(&self.module_tree).file_id();
let mut module_items = ModuleItems::default(); let mut module_items = ModuleScope::default();
for import in input.imports.iter() { for import in input.imports.iter() {
if let Some((_, name)) = import.segments.last() { if let Some((ptr, name)) = import.segments.last() {
module_items module_items.items.insert(
.items name.clone(),
.insert(name.clone(), Resolution { def_id: None }); Resolution {
def_id: None,
import_name: Some(*ptr),
},
);
} }
} }
@ -322,6 +329,7 @@ where
let def_id = self.db.id_maps().def_id(def_loc); let def_id = self.db.id_maps().def_id(def_loc);
let resolution = Resolution { let resolution = Resolution {
def_id: Some(def_id), def_id: Some(def_id),
import_name: None,
}; };
module_items.items.insert(item.name.clone(), resolution); module_items.items.insert(item.name.clone(), resolution);
} }
@ -334,6 +342,7 @@ where
let def_id = self.db.id_maps().def_id(def_loc); let def_id = self.db.id_maps().def_id(def_loc);
let resolution = Resolution { let resolution = Resolution {
def_id: Some(def_id), def_id: Some(def_id),
import_name: None,
}; };
module_items.items.insert(name, resolution); module_items.items.insert(name, resolution);
} }
@ -386,6 +395,7 @@ where
self.update(module_id, |items| { self.update(module_id, |items| {
let res = Resolution { let res = Resolution {
def_id: Some(def_id), def_id: Some(def_id),
import_name: Some(*ptr),
}; };
items.items.insert(name.clone(), res); items.items.insert(name.clone(), res);
}) })
@ -393,7 +403,7 @@ where
} }
} }
fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleItems)) { fn update(&mut self, module_id: ModuleId, f: impl FnOnce(&mut ModuleScope)) {
let module_items = self.result.per_module.get_mut(&module_id).unwrap(); let module_items = self.result.per_module.get_mut(&module_id).unwrap();
f(module_items) f(module_items)
} }

View file

@ -1,124 +0,0 @@
//! Backend for module-level scope resolution & completion
use ra_syntax::{ast, AstNode, SmolStr};
use crate::syntax_ptr::LocalSyntaxPtr;
/// `ModuleScope` contains all named items declared in the scope.
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ModuleScope {
entries: Vec<Entry>,
}
/// `Entry` is a single named declaration iside a module.
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Entry {
ptr: LocalSyntaxPtr,
kind: EntryKind,
name: SmolStr,
}
#[derive(Debug, PartialEq, Eq)]
enum EntryKind {
Item,
Import,
}
impl ModuleScope {
pub(super) fn new<'a>(items: impl Iterator<Item = ast::ModuleItem<'a>>) -> ModuleScope {
let mut entries = Vec::new();
for item in items {
let entry = match item {
ast::ModuleItem::StructDef(item) => Entry::new(item),
ast::ModuleItem::EnumDef(item) => Entry::new(item),
ast::ModuleItem::FnDef(item) => Entry::new(item),
ast::ModuleItem::ConstDef(item) => Entry::new(item),
ast::ModuleItem::StaticDef(item) => Entry::new(item),
ast::ModuleItem::TraitDef(item) => Entry::new(item),
ast::ModuleItem::TypeDef(item) => Entry::new(item),
ast::ModuleItem::Module(item) => Entry::new(item),
ast::ModuleItem::UseItem(item) => {
if let Some(tree) = item.use_tree() {
collect_imports(tree, &mut entries);
}
continue;
}
ast::ModuleItem::ExternCrateItem(_) | ast::ModuleItem::ImplItem(_) => continue,
};
entries.extend(entry)
}
ModuleScope { entries }
}
pub fn entries(&self) -> &[Entry] {
self.entries.as_slice()
}
}
impl Entry {
fn new<'a>(item: impl ast::NameOwner<'a>) -> Option<Entry> {
let name = item.name()?;
Some(Entry {
name: name.text(),
ptr: LocalSyntaxPtr::new(name.syntax()),
kind: EntryKind::Item,
})
}
fn new_import(path: ast::Path) -> Option<Entry> {
let name_ref = path.segment()?.name_ref()?;
Some(Entry {
name: name_ref.text(),
ptr: LocalSyntaxPtr::new(name_ref.syntax()),
kind: EntryKind::Import,
})
}
pub fn name(&self) -> &SmolStr {
&self.name
}
pub fn ptr(&self) -> LocalSyntaxPtr {
self.ptr
}
}
fn collect_imports(tree: ast::UseTree, acc: &mut Vec<Entry>) {
if let Some(use_tree_list) = tree.use_tree_list() {
return use_tree_list
.use_trees()
.for_each(|it| collect_imports(it, acc));
}
if let Some(path) = tree.path() {
acc.extend(Entry::new_import(path));
}
}
#[cfg(test)]
mod tests {
use super::*;
use ra_syntax::{ast::ModuleItemOwner, SourceFileNode};
fn do_check(code: &str, expected: &[&str]) {
let file = SourceFileNode::parse(&code);
let scope = ModuleScope::new(file.ast().items());
let actual = scope.entries.iter().map(|it| it.name()).collect::<Vec<_>>();
assert_eq!(expected, actual.as_slice());
}
#[test]
fn test_module_scope() {
do_check(
"
struct Foo;
enum Bar {}
mod baz {}
fn quux() {}
use x::{
y::z,
t,
};
type T = ();
",
&["Foo", "Bar", "baz", "quux", "z", "t", "T"],
)
}
}

View file

@ -447,8 +447,8 @@ fn test_complete_crate_path() {
); );
let completions = analysis.completions(position).unwrap().unwrap(); let completions = analysis.completions(position).unwrap().unwrap();
assert_eq_dbg( assert_eq_dbg(
r#"[CompletionItem { label: "foo", lookup: None, snippet: None }, r#"[CompletionItem { label: "Spam", lookup: None, snippet: None },
CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
&completions, &completions,
); );
} }
@ -466,8 +466,8 @@ fn test_complete_crate_path_with_braces() {
); );
let completions = analysis.completions(position).unwrap().unwrap(); let completions = analysis.completions(position).unwrap().unwrap();
assert_eq_dbg( assert_eq_dbg(
r#"[CompletionItem { label: "foo", lookup: None, snippet: None }, r#"[CompletionItem { label: "Spam", lookup: None, snippet: None },
CompletionItem { label: "Spam", lookup: None, snippet: None }]"#, CompletionItem { label: "foo", lookup: None, snippet: None }]"#,
&completions, &completions,
); );
} }