From 3b8d0c215a922eaea182d4d72f232a534de21e33 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 20 Nov 2018 16:21:02 +0300 Subject: Use more OO API for parent module --- crates/ra_analysis/src/descriptors/module/mod.rs | 79 +++++++++++++++++++++++- crates/ra_analysis/src/imp.rs | 44 +++++-------- 2 files changed, 94 insertions(+), 29 deletions(-) diff --git a/crates/ra_analysis/src/descriptors/module/mod.rs b/crates/ra_analysis/src/descriptors/module/mod.rs index 055a56b54..d62826ee3 100644 --- a/crates/ra_analysis/src/descriptors/module/mod.rs +++ b/crates/ra_analysis/src/descriptors/module/mod.rs @@ -1,16 +1,90 @@ pub(super) mod imp; pub(crate) mod scope; +use std::sync::Arc; + +use ra_editor::find_node_at_offset; + use ra_syntax::{ ast::{self, AstNode, NameOwner}, SmolStr, SyntaxNode, SyntaxNodeRef, }; use relative_path::RelativePathBuf; -use crate::{db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId}; +use crate::{ + db::SyntaxDatabase, syntax_ptr::SyntaxPtr, FileId, FilePosition, Cancelable, + descriptors::DescriptorDatabase, +}; 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, + module_id: ModuleId, +} + +impl ModuleDescriptor { + /// 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> { + let source_root = db.file_source_root(position.file_id); + let module_tree = db.module_tree(source_root)?; + let file = db.file_syntax(position.file_id); + let module_source = match find_node_at_offset::(file.syntax(), position.offset) + { + Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m), + _ => ModuleSource::SourceFile(position.file_id), + }; + let res = match module_tree.any_module_for_source(module_source) { + None => None, + Some(module_id) => Some(ModuleDescriptor { + tree: module_tree, + 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 parent(&self) -> Option { + let parent_id = self.module_id.parent(&self.tree)?; + Some(ModuleDescriptor { + tree: Arc::clone(&self.tree), + module_id: parent_id, + }) + } + /// `name` is `None` for the crate's root module + pub fn name(&self) -> Option { + let link = self.module_id.parent_link(&self.tree)?; + Some(link.name(&self.tree)) + } + pub fn child(&self, name: &str) -> Option { + let child_id = self.module_id.child(&self.tree, name)?; + Some(ModuleDescriptor { + tree: Arc::clone(&self.tree), + module_id: child_id, + }) + } +} + /// 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 /// single module, but it is not nessary the case. @@ -136,6 +210,9 @@ impl LinkId { pub(crate) fn owner(self, tree: &ModuleTree) -> ModuleId { tree.link(self).owner } + pub(crate) fn name(self, tree: &ModuleTree) -> SmolStr { + tree.link(self).name.clone() + } pub(crate) fn bind_source<'a>( self, tree: &ModuleTree, diff --git a/crates/ra_analysis/src/imp.rs b/crates/ra_analysis/src/imp.rs index 812fed32d..c0bed04bf 100644 --- a/crates/ra_analysis/src/imp.rs +++ b/crates/ra_analysis/src/imp.rs @@ -21,7 +21,7 @@ use crate::{ db::{self, FileSyntaxQuery, SyntaxDatabase}, descriptors::{ function::{FnDescriptor, FnId}, - module::{ModuleSource, ModuleTree, Problem}, + module::{ModuleDescriptor, ModuleSource, ModuleTree, Problem}, DeclarationDescriptor, DescriptorDatabase, }, input::{FilesDatabase, SourceRoot, SourceRootId, WORKSPACE}, @@ -221,34 +221,22 @@ impl AnalysisImpl { self.db.module_tree(source_root) } pub fn parent_module(&self, position: FilePosition) -> Cancelable> { - let module_tree = self.module_tree(position.file_id)?; - let file = self.db.file_syntax(position.file_id); - let module_source = match find_node_at_offset::(file.syntax(), position.offset) - { - Some(m) if !m.has_semi() => ModuleSource::new_inline(position.file_id, m), - _ => ModuleSource::SourceFile(position.file_id), + let descr = match ModuleDescriptor::guess_from_position(&*self.db, position)? { + None => return Ok(Vec::new()), + Some(it) => it, }; - - let res = module_tree - .modules_for_source(module_source) - .into_iter() - .filter_map(|module_id| { - let link = module_id.parent_link(&module_tree)?; - let file_id = link.owner(&module_tree).source(&module_tree).file_id(); - let decl = link.bind_source(&module_tree, &*self.db); - let decl = decl.borrowed(); - - let decl_name = decl.name().unwrap(); - - let sym = FileSymbol { - name: decl_name.text(), - node_range: decl_name.syntax().range(), - kind: MODULE, - }; - Some((file_id, sym)) - }) - .collect(); - Ok(res) + let (file_id, decl) = match descr.parent_link_source(&*self.db) { + None => return Ok(Vec::new()), + Some(it) => it, + }; + let decl = decl.borrowed(); + let decl_name = decl.name().unwrap(); + let sym = FileSymbol { + name: decl_name.text(), + node_range: decl_name.syntax().range(), + kind: MODULE, + }; + Ok(vec![(file_id, sym)]) } pub fn crate_for(&self, file_id: FileId) -> Cancelable> { let module_tree = self.module_tree(file_id)?; -- cgit v1.2.3