mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 21:54:42 +00:00
Merge #234
234: Global module r=matklad a=matklad This series of commits re-introdces `ModuleDescriptor` as one stop shop for all information about a module. Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
713c3ea30b
9 changed files with 233 additions and 203 deletions
|
@ -2,7 +2,6 @@ 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,
|
||||||
|
@ -12,8 +11,9 @@ use rustc_hash::{FxHashMap};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::{self, SyntaxDatabase},
|
db::{self, SyntaxDatabase},
|
||||||
descriptors::{DescriptorDatabase, module::ModuleSource},
|
descriptors::{
|
||||||
input::{FilesDatabase},
|
module::{ModuleDescriptor}
|
||||||
|
},
|
||||||
Cancelable, FilePosition
|
Cancelable, FilePosition
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,14 +38,7 @@ 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() {
|
let module = match ModuleDescriptor::guess_from_position(db, position)? {
|
||||||
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),
|
None => return Ok(None),
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
};
|
};
|
||||||
|
@ -55,15 +48,7 @@ pub(crate) fn completions(
|
||||||
// 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(
|
reference_completion::completions(&mut res, db, &module, &file, name_ref)?;
|
||||||
&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);
|
||||||
|
|
|
@ -9,20 +9,16 @@ use ra_syntax::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::RootDatabase,
|
db::RootDatabase,
|
||||||
input::{SourceRootId},
|
|
||||||
completion::CompletionItem,
|
completion::CompletionItem,
|
||||||
descriptors::module::{ModuleId, ModuleTree},
|
descriptors::module::{ModuleDescriptor},
|
||||||
descriptors::function::FnScopes,
|
descriptors::function::FnScopes,
|
||||||
descriptors::DescriptorDatabase,
|
|
||||||
Cancelable
|
Cancelable
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) fn completions(
|
pub(super) fn completions(
|
||||||
acc: &mut Vec<CompletionItem>,
|
acc: &mut Vec<CompletionItem>,
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
source_root_id: SourceRootId,
|
module: &ModuleDescriptor,
|
||||||
module_tree: &ModuleTree,
|
|
||||||
module_id: ModuleId,
|
|
||||||
file: &SourceFileNode,
|
file: &SourceFileNode,
|
||||||
name_ref: ast::NameRef,
|
name_ref: ast::NameRef,
|
||||||
) -> Cancelable<()> {
|
) -> Cancelable<()> {
|
||||||
|
@ -40,7 +36,7 @@ pub(super) fn completions(
|
||||||
complete_expr_snippets(acc);
|
complete_expr_snippets(acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
let module_scope = db.module_scope(source_root_id, module_id)?;
|
let module_scope = module.scope(db)?;
|
||||||
acc.extend(
|
acc.extend(
|
||||||
module_scope
|
module_scope
|
||||||
.entries()
|
.entries()
|
||||||
|
@ -56,9 +52,7 @@ pub(super) fn completions(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
NameRefKind::CratePath(path) => {
|
NameRefKind::CratePath(path) => complete_path(acc, db, module, 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
|
||||||
|
@ -171,16 +165,14 @@ 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,
|
||||||
source_root_id: SourceRootId,
|
module: &ModuleDescriptor,
|
||||||
module_tree: &ModuleTree,
|
|
||||||
module_id: ModuleId,
|
|
||||||
crate_path: Vec<ast::NameRef>,
|
crate_path: Vec<ast::NameRef>,
|
||||||
) -> Cancelable<()> {
|
) -> Cancelable<()> {
|
||||||
let target_module_id = match find_target_module(module_tree, module_id, crate_path) {
|
let target_module = match find_target_module(module, crate_path) {
|
||||||
None => return Ok(()),
|
None => return Ok(()),
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
};
|
};
|
||||||
let module_scope = db.module_scope(source_root_id, target_module_id)?;
|
let module_scope = target_module.scope(db)?;
|
||||||
let completions = module_scope.entries().iter().map(|entry| CompletionItem {
|
let completions = module_scope.entries().iter().map(|entry| CompletionItem {
|
||||||
label: entry.name().to_string(),
|
label: entry.name().to_string(),
|
||||||
lookup: None,
|
lookup: None,
|
||||||
|
@ -191,14 +183,13 @@ fn complete_path(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_target_module(
|
fn find_target_module(
|
||||||
module_tree: &ModuleTree,
|
module: &ModuleDescriptor,
|
||||||
module_id: ModuleId,
|
|
||||||
mut crate_path: Vec<ast::NameRef>,
|
mut crate_path: Vec<ast::NameRef>,
|
||||||
) -> Option<ModuleId> {
|
) -> Option<ModuleDescriptor> {
|
||||||
crate_path.pop();
|
crate_path.pop();
|
||||||
let mut target_module = module_id.root(&module_tree);
|
let mut target_module = module.crate_root();
|
||||||
for name in crate_path {
|
for name in crate_path {
|
||||||
target_module = target_module.child(module_tree, name.text().as_str())?;
|
target_module = target_module.child(name.text().as_str())?;
|
||||||
}
|
}
|
||||||
Some(target_module)
|
Some(target_module)
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,10 +85,10 @@ salsa::database_storage! {
|
||||||
}
|
}
|
||||||
impl DescriptorDatabase {
|
impl DescriptorDatabase {
|
||||||
fn module_tree() for ModuleTreeQuery;
|
fn module_tree() for ModuleTreeQuery;
|
||||||
fn module_descriptor() for SubmodulesQuery;
|
|
||||||
fn module_scope() for ModuleScopeQuery;
|
fn module_scope() for ModuleScopeQuery;
|
||||||
fn fn_syntax() for FnSyntaxQuery;
|
|
||||||
fn fn_scopes() for FnScopesQuery;
|
fn fn_scopes() for FnScopesQuery;
|
||||||
|
fn _fn_syntax() for FnSyntaxQuery;
|
||||||
|
fn _submodules() for SubmodulesQuery;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ pub(crate) fn fn_syntax(db: &impl DescriptorDatabase, fn_id: FnId) -> FnDefNode
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fn_scopes(db: &impl DescriptorDatabase, fn_id: FnId) -> Arc<FnScopes> {
|
pub(crate) fn fn_scopes(db: &impl DescriptorDatabase, fn_id: FnId) -> Arc<FnScopes> {
|
||||||
let syntax = db.fn_syntax(fn_id);
|
let syntax = db._fn_syntax(fn_id);
|
||||||
let res = FnScopes::new(syntax.borrowed());
|
let res = FnScopes::new(syntax.borrowed());
|
||||||
Arc::new(res)
|
Arc::new(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,27 +20,28 @@ use crate::{
|
||||||
|
|
||||||
salsa::query_group! {
|
salsa::query_group! {
|
||||||
pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase {
|
pub(crate) trait DescriptorDatabase: SyntaxDatabase + IdDatabase {
|
||||||
fn module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
|
fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> {
|
||||||
|
type FnScopesQuery;
|
||||||
|
use fn function::imp::fn_scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _module_tree(source_root_id: SourceRootId) -> Cancelable<Arc<ModuleTree>> {
|
||||||
type ModuleTreeQuery;
|
type ModuleTreeQuery;
|
||||||
use fn module::imp::module_tree;
|
use fn module::imp::module_tree;
|
||||||
}
|
}
|
||||||
fn submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> {
|
fn _module_scope(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleScope>> {
|
||||||
type SubmodulesQuery;
|
|
||||||
use fn module::imp::submodules;
|
|
||||||
}
|
|
||||||
fn module_scope(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleScope>> {
|
|
||||||
type ModuleScopeQuery;
|
type ModuleScopeQuery;
|
||||||
use fn module::imp::module_scope;
|
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
|
||||||
storage volatile;
|
storage volatile;
|
||||||
use fn function::imp::fn_syntax;
|
use fn function::imp::fn_syntax;
|
||||||
}
|
}
|
||||||
fn fn_scopes(fn_id: FnId) -> Arc<FnScopes> {
|
fn _submodules(source: ModuleSource) -> Cancelable<Arc<Vec<module::imp::Submodule>>> {
|
||||||
type FnScopesQuery;
|
type SubmodulesQuery;
|
||||||
use fn function::imp::fn_scopes;
|
use fn module::imp::submodules;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ pub(crate) fn module_scope(
|
||||||
source_root_id: SourceRootId,
|
source_root_id: SourceRootId,
|
||||||
module_id: ModuleId,
|
module_id: ModuleId,
|
||||||
) -> Cancelable<Arc<ModuleScope>> {
|
) -> Cancelable<Arc<ModuleScope>> {
|
||||||
let tree = db.module_tree(source_root_id)?;
|
let tree = db._module_tree(source_root_id)?;
|
||||||
let source = module_id.source(&tree).resolve(db);
|
let source = module_id.source(&tree).resolve(db);
|
||||||
let res = match source {
|
let res = match source {
|
||||||
ModuleSourceNode::SourceFile(it) => ModuleScope::new(it.borrowed().items()),
|
ModuleSourceNode::SourceFile(it) => ModuleScope::new(it.borrowed().items()),
|
||||||
|
@ -155,7 +155,7 @@ fn build_subtree(
|
||||||
parent,
|
parent,
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
});
|
});
|
||||||
for sub in db.submodules(source)?.iter() {
|
for sub in db._submodules(source)?.iter() {
|
||||||
let link = tree.push_link(LinkData {
|
let link = tree.push_link(LinkData {
|
||||||
name: sub.name().clone(),
|
name: sub.name().clone(),
|
||||||
owner: id,
|
owner: id,
|
||||||
|
|
|
@ -1,16 +1,137 @@
|
||||||
pub(super) mod imp;
|
pub(super) mod imp;
|
||||||
pub(crate) mod scope;
|
pub(crate) mod scope;
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use ra_editor::find_node_at_offset;
|
||||||
|
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
|
algo::generate,
|
||||||
ast::{self, AstNode, NameOwner},
|
ast::{self, AstNode, NameOwner},
|
||||||
SmolStr, SyntaxNode, SyntaxNodeRef,
|
SmolStr, SyntaxNode,
|
||||||
};
|
};
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
|
||||||
use crate::{db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId};
|
use crate::{
|
||||||
|
db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId, FilePosition, Cancelable,
|
||||||
|
descriptors::DescriptorDatabase,
|
||||||
|
input::SourceRootId
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) use self::scope::ModuleScope;
|
pub(crate) use self::scope::ModuleScope;
|
||||||
|
|
||||||
|
/// `ModuleDescriptor` is API entry point to get all the information
|
||||||
|
/// about a particular module.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub(crate) struct ModuleDescriptor {
|
||||||
|
tree: Arc<ModuleTree>,
|
||||||
|
source_root_id: SourceRootId,
|
||||||
|
module_id: ModuleId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleDescriptor {
|
||||||
|
/// Lookup `ModuleDescriptor` by `FileId`. Note that this is inherently
|
||||||
|
/// lossy transformation: in general, a single source might correspond to
|
||||||
|
/// several modules.
|
||||||
|
pub fn guess_from_file_id(
|
||||||
|
db: &impl DescriptorDatabase,
|
||||||
|
file_id: FileId,
|
||||||
|
) -> Cancelable<Option<ModuleDescriptor>> {
|
||||||
|
ModuleDescriptor::guess_from_source(db, file_id, ModuleSource::SourceFile(file_id))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lookup `ModuleDescriptor` by position in the source code. Note that this
|
||||||
|
/// is inherently lossy transformation: in general, a single source might
|
||||||
|
/// correspond to several modules.
|
||||||
|
pub fn guess_from_position(
|
||||||
|
db: &impl DescriptorDatabase,
|
||||||
|
position: FilePosition,
|
||||||
|
) -> Cancelable<Option<ModuleDescriptor>> {
|
||||||
|
let file = db.file_syntax(position.file_id);
|
||||||
|
let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
|
||||||
|
{
|
||||||
|
Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m),
|
||||||
|
_ => ModuleSource::SourceFile(position.file_id),
|
||||||
|
};
|
||||||
|
ModuleDescriptor::guess_from_source(db, position.file_id, module_source)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn guess_from_source(
|
||||||
|
db: &impl DescriptorDatabase,
|
||||||
|
file_id: FileId,
|
||||||
|
module_source: ModuleSource,
|
||||||
|
) -> Cancelable<Option<ModuleDescriptor>> {
|
||||||
|
let source_root_id = db.file_source_root(file_id);
|
||||||
|
let module_tree = db._module_tree(source_root_id)?;
|
||||||
|
|
||||||
|
let res = match module_tree.any_module_for_source(module_source) {
|
||||||
|
None => None,
|
||||||
|
Some(module_id) => Some(ModuleDescriptor {
|
||||||
|
tree: module_tree,
|
||||||
|
source_root_id,
|
||||||
|
module_id,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `mod foo;` or `mod foo {}` node whihc declared this module.
|
||||||
|
/// Returns `None` for the root module
|
||||||
|
pub fn parent_link_source(
|
||||||
|
&self,
|
||||||
|
db: &impl DescriptorDatabase,
|
||||||
|
) -> Option<(FileId, ast::ModuleNode)> {
|
||||||
|
let link = self.module_id.parent_link(&self.tree)?;
|
||||||
|
let file_id = link.owner(&self.tree).source(&self.tree).file_id();
|
||||||
|
let src = link.bind_source(&self.tree, db);
|
||||||
|
Some((file_id, src))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn source(&self) -> ModuleSource {
|
||||||
|
self.module_id.source(&self.tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parent module. Returns `None` if this is a root module.
|
||||||
|
pub fn parent(&self) -> Option<ModuleDescriptor> {
|
||||||
|
let parent_id = self.module_id.parent(&self.tree)?;
|
||||||
|
Some(ModuleDescriptor {
|
||||||
|
module_id: parent_id,
|
||||||
|
..self.clone()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The root of the tree this module is part of
|
||||||
|
pub fn crate_root(&self) -> ModuleDescriptor {
|
||||||
|
generate(Some(self.clone()), |it| it.parent())
|
||||||
|
.last()
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `name` is `None` for the crate's root module
|
||||||
|
pub fn name(&self) -> Option<SmolStr> {
|
||||||
|
let link = self.module_id.parent_link(&self.tree)?;
|
||||||
|
Some(link.name(&self.tree))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds a child module with the specified name.
|
||||||
|
pub fn child(&self, name: &str) -> Option<ModuleDescriptor> {
|
||||||
|
let child_id = self.module_id.child(&self.tree, name)?;
|
||||||
|
Some(ModuleDescriptor {
|
||||||
|
module_id: child_id,
|
||||||
|
..self.clone()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `ModuleScope`: a set of items, visible in this module.
|
||||||
|
pub fn scope(&self, db: &impl DescriptorDatabase) -> Cancelable<Arc<ModuleScope>> {
|
||||||
|
db._module_scope(self.source_root_id, self.module_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn problems(&self, db: &impl DescriptorDatabase) -> Vec<(SyntaxNode, Problem)> {
|
||||||
|
self.module_id.problems(&self.tree, db)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Phisically, rust source is organized as a set of files, but logically it is
|
/// Phisically, rust source is organized as a set of files, but logically it is
|
||||||
/// organized as a tree of modules. Usually, a single file corresponds to a
|
/// organized as a tree of modules. Usually, a single file corresponds to a
|
||||||
/// single module, but it is not nessary the case.
|
/// single module, but it is not nessary the case.
|
||||||
|
@ -25,7 +146,7 @@ pub(crate) struct ModuleTree {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleTree {
|
impl ModuleTree {
|
||||||
pub(crate) fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
|
fn modules_for_source(&self, source: ModuleSource) -> Vec<ModuleId> {
|
||||||
self.mods
|
self.mods
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
|
@ -34,7 +155,7 @@ impl ModuleTree {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> {
|
fn any_module_for_source(&self, source: ModuleSource) -> Option<ModuleId> {
|
||||||
self.modules_for_source(source).pop()
|
self.modules_for_source(source).pop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,17 +179,8 @@ enum ModuleSourceNode {
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
|
||||||
pub(crate) struct ModuleId(u32);
|
pub(crate) struct ModuleId(u32);
|
||||||
|
|
||||||
impl crate::loc2id::NumericId for ModuleId {
|
|
||||||
fn from_u32(id: u32) -> Self {
|
|
||||||
ModuleId(id)
|
|
||||||
}
|
|
||||||
fn to_u32(self) -> u32 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
|
||||||
pub(crate) struct LinkId(u32);
|
struct LinkId(u32);
|
||||||
|
|
||||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||||
pub enum Problem {
|
pub enum Problem {
|
||||||
|
@ -82,30 +194,17 @@ pub enum Problem {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleId {
|
impl ModuleId {
|
||||||
pub(crate) fn source(self, tree: &ModuleTree) -> ModuleSource {
|
fn source(self, tree: &ModuleTree) -> ModuleSource {
|
||||||
tree.module(self).source
|
tree.module(self).source
|
||||||
}
|
}
|
||||||
pub(crate) fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
|
fn parent_link(self, tree: &ModuleTree) -> Option<LinkId> {
|
||||||
tree.module(self).parent
|
tree.module(self).parent
|
||||||
}
|
}
|
||||||
pub(crate) fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
|
fn parent(self, tree: &ModuleTree) -> Option<ModuleId> {
|
||||||
let link = self.parent_link(tree)?;
|
let link = self.parent_link(tree)?;
|
||||||
Some(tree.link(link).owner)
|
Some(tree.link(link).owner)
|
||||||
}
|
}
|
||||||
pub(crate) fn root(self, tree: &ModuleTree) -> ModuleId {
|
fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
|
||||||
let mut curr = self;
|
|
||||||
let mut i = 0;
|
|
||||||
while let Some(next) = curr.parent(tree) {
|
|
||||||
curr = next;
|
|
||||||
i += 1;
|
|
||||||
// simplistic cycle detection
|
|
||||||
if i > 100 {
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
curr
|
|
||||||
}
|
|
||||||
pub(crate) fn child(self, tree: &ModuleTree, name: &str) -> Option<ModuleId> {
|
|
||||||
let link = tree
|
let link = tree
|
||||||
.module(self)
|
.module(self)
|
||||||
.children
|
.children
|
||||||
|
@ -114,11 +213,7 @@ impl ModuleId {
|
||||||
.find(|it| it.name == name)?;
|
.find(|it| it.name == name)?;
|
||||||
Some(*link.points_to.first()?)
|
Some(*link.points_to.first()?)
|
||||||
}
|
}
|
||||||
pub(crate) fn problems(
|
fn problems(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> Vec<(SyntaxNode, Problem)> {
|
||||||
self,
|
|
||||||
tree: &ModuleTree,
|
|
||||||
db: &impl SyntaxDatabase,
|
|
||||||
) -> Vec<(SyntaxNode, Problem)> {
|
|
||||||
tree.module(self)
|
tree.module(self)
|
||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -133,14 +228,13 @@ impl ModuleId {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LinkId {
|
impl LinkId {
|
||||||
pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId {
|
fn owner(self, tree: &ModuleTree) -> ModuleId {
|
||||||
tree.link(self).owner
|
tree.link(self).owner
|
||||||
}
|
}
|
||||||
pub(crate) fn bind_source<'a>(
|
fn name(self, tree: &ModuleTree) -> SmolStr {
|
||||||
self,
|
tree.link(self).name.clone()
|
||||||
tree: &ModuleTree,
|
}
|
||||||
db: &impl SyntaxDatabase,
|
fn bind_source<'a>(self, tree: &ModuleTree, db: &impl SyntaxDatabase) -> ast::ModuleNode {
|
||||||
) -> ast::ModuleNode {
|
|
||||||
let owner = self.owner(tree);
|
let owner = self.owner(tree);
|
||||||
match owner.source(tree).resolve(db) {
|
match owner.source(tree).resolve(db) {
|
||||||
ModuleSourceNode::SourceFile(root) => {
|
ModuleSourceNode::SourceFile(root) => {
|
||||||
|
@ -163,17 +257,7 @@ struct ModuleData {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleSource {
|
impl ModuleSource {
|
||||||
pub(crate) fn for_node(file_id: FileId, node: SyntaxNodeRef) -> ModuleSource {
|
fn new_inline(file_id: FileId, module: ast::Module) -> 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 {
|
|
||||||
assert!(!module.has_semi());
|
assert!(!module.has_semi());
|
||||||
let ptr = SyntaxPtr::new(file_id, module.syntax());
|
let ptr = SyntaxPtr::new(file_id, module.syntax());
|
||||||
ModuleSource::Module(ptr)
|
ModuleSource::Module(ptr)
|
||||||
|
|
|
@ -21,7 +21,7 @@ use crate::{
|
||||||
db::{self, FileSyntaxQuery, SyntaxDatabase},
|
db::{self, FileSyntaxQuery, SyntaxDatabase},
|
||||||
descriptors::{
|
descriptors::{
|
||||||
function::{FnDescriptor, FnId},
|
function::{FnDescriptor, FnId},
|
||||||
module::{ModuleSource, ModuleTree, Problem},
|
module::{ModuleDescriptor, Problem},
|
||||||
DeclarationDescriptor, DescriptorDatabase,
|
DeclarationDescriptor, DescriptorDatabase,
|
||||||
},
|
},
|
||||||
input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE},
|
input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE},
|
||||||
|
@ -216,52 +216,41 @@ impl AnalysisImpl {
|
||||||
.sweep(salsa::SweepStrategy::default().discard_values());
|
.sweep(salsa::SweepStrategy::default().discard_values());
|
||||||
Ok(query.search(&buf))
|
Ok(query.search(&buf))
|
||||||
}
|
}
|
||||||
fn module_tree(&self, file_id: FileId) -> Cancelable<Arc<ModuleTree>> {
|
/// This return `Vec`: a module may be included from several places. We
|
||||||
let source_root = self.db.file_source_root(file_id);
|
/// don't handle this case yet though, so the Vec has length at most one.
|
||||||
self.db.module_tree(source_root)
|
|
||||||
}
|
|
||||||
pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
|
pub fn parent_module(&self, position: FilePosition) -> Cancelable<Vec<(FileId, FileSymbol)>> {
|
||||||
let module_tree = self.module_tree(position.file_id)?;
|
let descr = match ModuleDescriptor::guess_from_position(&*self.db, position)? {
|
||||||
let file = self.db.file_syntax(position.file_id);
|
None => return Ok(Vec::new()),
|
||||||
let module_source = match find_node_at_offset::<ast::Module>(file.syntax(), position.offset)
|
Some(it) => it,
|
||||||
{
|
|
||||||
Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m),
|
|
||||||
_ => ModuleSource::SourceFile(position.file_id),
|
|
||||||
};
|
};
|
||||||
|
let (file_id, decl) = match descr.parent_link_source(&*self.db) {
|
||||||
let res = module_tree
|
None => return Ok(Vec::new()),
|
||||||
.modules_for_source(module_source)
|
Some(it) => it,
|
||||||
.into_iter()
|
};
|
||||||
.filter_map(|module_id| {
|
let decl = decl.borrowed();
|
||||||
let link = module_id.parent_link(&module_tree)?;
|
let decl_name = decl.name().unwrap();
|
||||||
let file_id = link.owner(&module_tree).source(&module_tree).file_id();
|
let sym = FileSymbol {
|
||||||
let decl = link.bind_source(&module_tree, &*self.db);
|
name: decl_name.text(),
|
||||||
let decl = decl.borrowed();
|
node_range: decl_name.syntax().range(),
|
||||||
|
kind: MODULE,
|
||||||
let decl_name = decl.name().unwrap();
|
};
|
||||||
|
Ok(vec![(file_id, sym)])
|
||||||
let sym = FileSymbol {
|
|
||||||
name: decl_name.text(),
|
|
||||||
node_range: decl_name.syntax().range(),
|
|
||||||
kind: MODULE,
|
|
||||||
};
|
|
||||||
Some((file_id, sym))
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Ok(res)
|
|
||||||
}
|
}
|
||||||
|
/// Returns `Vec` for the same reason as `parent_module`
|
||||||
pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
|
pub fn crate_for(&self, file_id: FileId) -> Cancelable<Vec<CrateId>> {
|
||||||
let module_tree = self.module_tree(file_id)?;
|
let descr = match ModuleDescriptor::guess_from_file_id(&*self.db, file_id)? {
|
||||||
let crate_graph = self.db.crate_graph();
|
None => return Ok(Vec::new()),
|
||||||
let res = module_tree
|
Some(it) => it,
|
||||||
.modules_for_source(ModuleSource::SourceFile(file_id))
|
};
|
||||||
.into_iter()
|
let root = descr.crate_root();
|
||||||
.map(|it| it.root(&module_tree))
|
let file_id = root
|
||||||
.filter_map(|it| it.source(&module_tree).as_file())
|
.source()
|
||||||
.filter_map(|it| crate_graph.crate_id_for_crate_root(it))
|
.as_file()
|
||||||
.collect();
|
.expect("root module always has a file as a source");
|
||||||
|
|
||||||
Ok(res)
|
let crate_graph = self.db.crate_graph();
|
||||||
|
let crate_id = crate_graph.crate_id_for_crate_root(file_id);
|
||||||
|
Ok(crate_id.into_iter().collect())
|
||||||
}
|
}
|
||||||
pub fn crate_root(&self, crate_id: CrateId) -> FileId {
|
pub fn crate_root(&self, crate_id: CrateId) -> FileId {
|
||||||
self.db.crate_graph().crate_roots[&crate_id]
|
self.db.crate_graph().crate_roots[&crate_id]
|
||||||
|
@ -273,7 +262,6 @@ impl AnalysisImpl {
|
||||||
&self,
|
&self,
|
||||||
position: FilePosition,
|
position: FilePosition,
|
||||||
) -> Cancelable<Vec<(FileId, FileSymbol)>> {
|
) -> Cancelable<Vec<(FileId, FileSymbol)>> {
|
||||||
let module_tree = self.module_tree(position.file_id)?;
|
|
||||||
let file = self.db.file_syntax(position.file_id);
|
let file = self.db.file_syntax(position.file_id);
|
||||||
let syntax = file.syntax();
|
let syntax = file.syntax();
|
||||||
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
|
if let Some(name_ref) = find_node_at_offset::<ast::NameRef>(syntax, position.offset) {
|
||||||
|
@ -299,25 +287,23 @@ impl AnalysisImpl {
|
||||||
if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
|
if let Some(name) = find_node_at_offset::<ast::Name>(syntax, position.offset) {
|
||||||
if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
|
if let Some(module) = name.syntax().parent().and_then(ast::Module::cast) {
|
||||||
if module.has_semi() {
|
if module.has_semi() {
|
||||||
let file_ids = self.resolve_module(&*module_tree, position.file_id, module);
|
let parent_module =
|
||||||
|
ModuleDescriptor::guess_from_file_id(&*self.db, position.file_id)?;
|
||||||
let res = file_ids
|
let child_name = module.name();
|
||||||
.into_iter()
|
match (parent_module, child_name) {
|
||||||
.map(|id| {
|
(Some(parent_module), Some(child_name)) => {
|
||||||
let name = module
|
if let Some(child) = parent_module.child(&child_name.text()) {
|
||||||
.name()
|
let file_id = child.source().file_id();
|
||||||
.map(|n| n.text())
|
let symbol = FileSymbol {
|
||||||
.unwrap_or_else(|| SmolStr::new(""));
|
name: child_name.text(),
|
||||||
let symbol = FileSymbol {
|
node_range: TextRange::offset_len(0.into(), 0.into()),
|
||||||
name,
|
kind: MODULE,
|
||||||
node_range: TextRange::offset_len(0.into(), 0.into()),
|
};
|
||||||
kind: MODULE,
|
return Ok(vec![(file_id, symbol)]);
|
||||||
};
|
}
|
||||||
(id, symbol)
|
}
|
||||||
})
|
_ => (),
|
||||||
.collect();
|
}
|
||||||
|
|
||||||
return Ok(res);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,7 +350,6 @@ impl AnalysisImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
|
pub fn diagnostics(&self, file_id: FileId) -> Cancelable<Vec<Diagnostic>> {
|
||||||
let module_tree = self.module_tree(file_id)?;
|
|
||||||
let syntax = self.db.file_syntax(file_id);
|
let syntax = self.db.file_syntax(file_id);
|
||||||
|
|
||||||
let mut res = ra_editor::diagnostics(&syntax)
|
let mut res = ra_editor::diagnostics(&syntax)
|
||||||
|
@ -375,8 +360,8 @@ impl AnalysisImpl {
|
||||||
fix: None,
|
fix: None,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if let Some(m) = module_tree.any_module_for_source(ModuleSource::SourceFile(file_id)) {
|
if let Some(m) = ModuleDescriptor::guess_from_file_id(&*self.db, file_id)? {
|
||||||
for (name_node, problem) in m.problems(&module_tree, &*self.db) {
|
for (name_node, problem) in m.problems(&*self.db) {
|
||||||
let diag = match problem {
|
let diag = match problem {
|
||||||
Problem::UnresolvedModule { candidate } => {
|
Problem::UnresolvedModule { candidate } => {
|
||||||
let create_file = FileSystemEdit::CreateFile {
|
let create_file = FileSystemEdit::CreateFile {
|
||||||
|
@ -526,27 +511,6 @@ impl AnalysisImpl {
|
||||||
query.limit(4);
|
query.limit(4);
|
||||||
self.world_symbols(query)
|
self.world_symbols(query)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_module(
|
|
||||||
&self,
|
|
||||||
module_tree: &ModuleTree,
|
|
||||||
file_id: FileId,
|
|
||||||
module: ast::Module,
|
|
||||||
) -> Vec<FileId> {
|
|
||||||
let name = match module.name() {
|
|
||||||
Some(name) => name.text(),
|
|
||||||
None => return Vec::new(),
|
|
||||||
};
|
|
||||||
let module_id = match module_tree.any_module_for_source(ModuleSource::SourceFile(file_id)) {
|
|
||||||
Some(id) => id,
|
|
||||||
None => return Vec::new(),
|
|
||||||
};
|
|
||||||
module_id
|
|
||||||
.child(module_tree, name.as_str())
|
|
||||||
.and_then(|it| it.source(&module_tree).as_file())
|
|
||||||
.into_iter()
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceChange {
|
impl SourceChange {
|
||||||
|
|
|
@ -72,17 +72,22 @@ pub(crate) trait NumericId: Clone + Eq + Hash {
|
||||||
fn to_u32(self) -> u32;
|
fn to_u32(self) -> u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! impl_numeric_id {
|
||||||
|
($id:ident) => {
|
||||||
|
impl NumericId for $id {
|
||||||
|
fn from_u32(id: u32) -> Self {
|
||||||
|
$id(id)
|
||||||
|
}
|
||||||
|
fn to_u32(self) -> u32 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
pub(crate) struct FnId(u32);
|
pub(crate) struct FnId(u32);
|
||||||
|
impl_numeric_id!(FnId);
|
||||||
impl NumericId for FnId {
|
|
||||||
fn from_u32(id: u32) -> FnId {
|
|
||||||
FnId(id)
|
|
||||||
}
|
|
||||||
fn to_u32(self) -> u32 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) trait IdDatabase: salsa::Database {
|
pub(crate) trait IdDatabase: salsa::Database {
|
||||||
fn id_maps(&self) -> &IdMaps;
|
fn id_maps(&self) -> &IdMaps;
|
||||||
|
|
Loading…
Reference in a new issue