From fbbee537228538f448a335bb0b2dabec2b3d443e Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 31 Oct 2018 02:08:54 +0300 Subject: Add ModuleScope as a query This is a first step towards queryifing completion and resolve. Some code currently duplicates ra_editor: the plan is to move all completion from ra_editor, but it'll take more than one commit. --- crates/ra_analysis/src/descriptors/module/scope.rs | 128 +++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 crates/ra_analysis/src/descriptors/module/scope.rs (limited to 'crates/ra_analysis/src/descriptors/module/scope.rs') diff --git a/crates/ra_analysis/src/descriptors/module/scope.rs b/crates/ra_analysis/src/descriptors/module/scope.rs new file mode 100644 index 000000000..da58ddce0 --- /dev/null +++ b/crates/ra_analysis/src/descriptors/module/scope.rs @@ -0,0 +1,128 @@ +//! Backend for module-level scope resolution & completion + + +use ra_syntax::{ + ast::{self, AstChildren, ModuleItemOwner}, + File, AstNode, SmolStr, SyntaxNode, SyntaxNodeRef, +}; + +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` 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 fn new(file: &File) -> ModuleScope { + let mut entries = Vec::new(); + for item in file.ast().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 { + let name = item.name()?; + Some(Entry { + name: name.text(), + ptr: LocalSyntaxPtr::new(name.syntax()), + kind: EntryKind::Item, + }) + } + fn new_import(path: ast::Path) -> Option { + 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) { + 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, File}; + + fn do_check(code: &str, expected: &[&str]) { + let file = File::parse(&code); + let scope = ModuleScope::new(&file); + let actual = scope.entries.iter().map(|it| it.name()).collect::>(); + 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"], + ) + } +} -- cgit v1.2.3