name res uses paths

This commit is contained in:
Aleksey Kladov 2018-11-21 17:51:02 +03:00
parent 5a87a24f82
commit 11f19b7849
4 changed files with 74 additions and 47 deletions

View file

@ -10,8 +10,11 @@ use ra_syntax::{
use crate::{ use crate::{
db::RootDatabase, db::RootDatabase,
completion::CompletionItem, completion::CompletionItem,
descriptors::module::{ModuleDescriptor}, descriptors::{
descriptors::function::FnScopes, module::{ModuleDescriptor},
function::FnScopes,
Path, PathKind,
},
Cancelable Cancelable
}; };
@ -55,7 +58,7 @@ pub(super) fn completions(
}), }),
); );
} }
NameRefKind::CratePath(path) => complete_path(acc, db, module, path)?, NameRefKind::Path(path) => complete_path(acc, db, module, 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
@ -79,8 +82,8 @@ enum NameRefKind<'a> {
LocalRef { LocalRef {
enclosing_fn: Option<ast::FnDef<'a>>, enclosing_fn: Option<ast::FnDef<'a>>,
}, },
/// NameRef is the last segment in crate:: path /// NameRef is the last segment in some path
CratePath(Vec<ast::NameRef<'a>>), Path(Path),
/// NameRef is bare identifier at the module's root. /// NameRef is bare identifier at the module's root.
/// Used for keyword completion /// Used for keyword completion
BareIdentInMod, BareIdentInMod,
@ -102,8 +105,10 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
let parent = name_ref.syntax().parent()?; let parent = name_ref.syntax().parent()?;
if let Some(segment) = ast::PathSegment::cast(parent) { if let Some(segment) = ast::PathSegment::cast(parent) {
let path = segment.parent_path(); let path = segment.parent_path();
if let Some(crate_path) = crate_path(path) { if let Some(path) = Path::from_ast(path) {
return Some(NameRefKind::CratePath(crate_path)); if !path.is_ident() {
return Some(NameRefKind::Path(path));
}
} }
if path.qualifier().is_none() { if path.qualifier().is_none() {
let enclosing_fn = name_ref let enclosing_fn = name_ref
@ -117,32 +122,6 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
None None
} }
fn crate_path(mut path: ast::Path) -> Option<Vec<ast::NameRef>> {
let mut res = Vec::new();
loop {
let segment = path.segment()?;
match segment.kind()? {
ast::PathSegmentKind::Name(name) => res.push(name),
ast::PathSegmentKind::CrateKw => break,
ast::PathSegmentKind::SelfKw | ast::PathSegmentKind::SuperKw => return None,
}
path = qualifier(path)?;
}
res.reverse();
return Some(res);
fn qualifier(path: ast::Path) -> Option<ast::Path> {
if let Some(q) = path.qualifier() {
return Some(q);
}
// TODO: this bottom up traversal is not too precise.
// Should we handle do a top-down analysiss, recording results?
let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
let use_tree = use_tree_list.parent_use_tree();
use_tree.path()
}
}
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>) {
let mut shadowed = FxHashSet::default(); let mut shadowed = FxHashSet::default();
acc.extend( acc.extend(
@ -169,9 +148,9 @@ fn complete_path(
acc: &mut Vec<CompletionItem>, acc: &mut Vec<CompletionItem>,
db: &RootDatabase, db: &RootDatabase,
module: &ModuleDescriptor, module: &ModuleDescriptor,
crate_path: Vec<ast::NameRef>, path: Path,
) -> Cancelable<()> { ) -> Cancelable<()> {
let target_module = match find_target_module(module, crate_path) { let target_module = match find_target_module(module, path) {
None => return Ok(()), None => return Ok(()),
Some(it) => it, Some(it) => it,
}; };
@ -188,14 +167,15 @@ fn complete_path(
Ok(()) Ok(())
} }
fn find_target_module( fn find_target_module(module: &ModuleDescriptor, path: Path) -> Option<ModuleDescriptor> {
module: &ModuleDescriptor, if path.kind != PathKind::Crate {
mut crate_path: Vec<ast::NameRef>, return None;
) -> Option<ModuleDescriptor> { }
crate_path.pop(); let mut segments = path.segments;
segments.pop();
let mut target_module = module.crate_root(); let mut target_module = module.crate_root();
for name in crate_path { for name in segments {
target_module = target_module.child(name.text().as_str())?; target_module = target_module.child(&name)?;
} }
Some(target_module) Some(target_module)
} }

View file

@ -5,7 +5,7 @@ mod path;
use std::sync::Arc; use std::sync::Arc;
use ra_syntax::{ use ra_syntax::{
ast::{self, FnDefNode}, ast::{self, FnDefNode, AstNode},
TextRange, TextRange,
}; };

View file

@ -23,7 +23,7 @@ use rustc_hash::FxHashMap;
use ra_syntax::{ use ra_syntax::{
SmolStr, SyntaxKind::{self, *}, SmolStr, SyntaxKind::{self, *},
ast::{self, AstNode, ModuleItemOwner} ast::{self, ModuleItemOwner}
}; };
use crate::{ use crate::{
@ -309,7 +309,7 @@ where
let mut curr = match import.path.kind { let mut curr = match import.path.kind {
// TODO: handle extern crates // TODO: handle extern crates
PathKind::Abs => return, PathKind::Plain => return,
PathKind::Self_ => module_id, PathKind::Self_ => module_id,
PathKind::Super => { PathKind::Super => {
match module_id.parent(&self.module_tree) { match module_id.parent(&self.module_tree) {

View file

@ -10,13 +10,14 @@ pub(crate) struct Path {
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PathKind { pub(crate) enum PathKind {
Abs, Plain,
Self_, Self_,
Super, Super,
Crate, Crate,
} }
impl Path { impl Path {
/// Calls `cb` with all paths, represented by this use item.
pub(crate) fn expand_use_item( pub(crate) fn expand_use_item(
item: ast::UseItem, item: ast::UseItem,
mut cb: impl FnMut(Path, Option<LocalSyntaxPtr>), mut cb: impl FnMut(Path, Option<LocalSyntaxPtr>),
@ -25,6 +26,52 @@ impl Path {
expand_use_tree(None, tree, &mut cb); expand_use_tree(None, tree, &mut cb);
} }
} }
/// Converts an `ast::Path` to `Path`. Works with use trees.
pub(crate) fn from_ast(mut path: ast::Path) -> Option<Path> {
let mut kind = PathKind::Plain;
let mut segments = Vec::new();
loop {
let segment = path.segment()?;
match segment.kind()? {
ast::PathSegmentKind::Name(name) => segments.push(name.text()),
ast::PathSegmentKind::CrateKw => {
kind = PathKind::Crate;
break;
}
ast::PathSegmentKind::SelfKw => {
kind = PathKind::Self_;
break;
}
ast::PathSegmentKind::SuperKw => {
kind = PathKind::Super;
break;
}
}
path = match qualifier(path) {
Some(it) => it,
None => break,
};
}
segments.reverse();
return Some(Path { kind, segments });
fn qualifier(path: ast::Path) -> Option<ast::Path> {
if let Some(q) = path.qualifier() {
return Some(q);
}
// TODO: this bottom up traversal is not too precise.
// Should we handle do a top-down analysiss, recording results?
let use_tree_list = path.syntax().ancestors().find_map(ast::UseTreeList::cast)?;
let use_tree = use_tree_list.parent_use_tree();
use_tree.path()
}
}
/// `true` is this path is a single identifier, like `foo`
pub(crate) fn is_ident(&self) -> bool {
self.kind == PathKind::Plain && self.segments.len() == 1
}
} }
fn expand_use_tree( fn expand_use_tree(
@ -68,7 +115,7 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
let res = match segment.kind()? { let res = match segment.kind()? {
ast::PathSegmentKind::Name(name) => { ast::PathSegmentKind::Name(name) => {
let mut res = prefix.unwrap_or_else(|| Path { let mut res = prefix.unwrap_or_else(|| Path {
kind: PathKind::Abs, kind: PathKind::Plain,
segments: Vec::with_capacity(1), segments: Vec::with_capacity(1),
}); });
res.segments.push(name.text()); res.segments.push(name.text());