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::{
db::RootDatabase,
completion::CompletionItem,
descriptors::module::{ModuleDescriptor},
descriptors::function::FnScopes,
descriptors::{
module::{ModuleDescriptor},
function::FnScopes,
Path, PathKind,
},
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 => {
let name_range = name_ref.syntax().range();
let top_node = name_ref
@ -79,8 +82,8 @@ enum NameRefKind<'a> {
LocalRef {
enclosing_fn: Option<ast::FnDef<'a>>,
},
/// NameRef is the last segment in crate:: path
CratePath(Vec<ast::NameRef<'a>>),
/// NameRef is the last segment in some path
Path(Path),
/// NameRef is bare identifier at the module's root.
/// Used for keyword completion
BareIdentInMod,
@ -102,8 +105,10 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
let parent = name_ref.syntax().parent()?;
if let Some(segment) = ast::PathSegment::cast(parent) {
let path = segment.parent_path();
if let Some(crate_path) = crate_path(path) {
return Some(NameRefKind::CratePath(crate_path));
if let Some(path) = Path::from_ast(path) {
if !path.is_ident() {
return Some(NameRefKind::Path(path));
}
}
if path.qualifier().is_none() {
let enclosing_fn = name_ref
@ -117,32 +122,6 @@ fn classify_name_ref(name_ref: ast::NameRef) -> Option<NameRefKind> {
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>) {
let mut shadowed = FxHashSet::default();
acc.extend(
@ -169,9 +148,9 @@ fn complete_path(
acc: &mut Vec<CompletionItem>,
db: &RootDatabase,
module: &ModuleDescriptor,
crate_path: Vec<ast::NameRef>,
path: Path,
) -> Cancelable<()> {
let target_module = match find_target_module(module, crate_path) {
let target_module = match find_target_module(module, path) {
None => return Ok(()),
Some(it) => it,
};
@ -188,14 +167,15 @@ fn complete_path(
Ok(())
}
fn find_target_module(
module: &ModuleDescriptor,
mut crate_path: Vec<ast::NameRef>,
) -> Option<ModuleDescriptor> {
crate_path.pop();
fn find_target_module(module: &ModuleDescriptor, path: Path) -> Option<ModuleDescriptor> {
if path.kind != PathKind::Crate {
return None;
}
let mut segments = path.segments;
segments.pop();
let mut target_module = module.crate_root();
for name in crate_path {
target_module = target_module.child(name.text().as_str())?;
for name in segments {
target_module = target_module.child(&name)?;
}
Some(target_module)
}

View file

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

View file

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

View file

@ -10,13 +10,14 @@ pub(crate) struct Path {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PathKind {
Abs,
Plain,
Self_,
Super,
Crate,
}
impl Path {
/// Calls `cb` with all paths, represented by this use item.
pub(crate) fn expand_use_item(
item: ast::UseItem,
mut cb: impl FnMut(Path, Option<LocalSyntaxPtr>),
@ -25,6 +26,52 @@ impl Path {
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(
@ -68,7 +115,7 @@ fn convert_path(prefix: Option<Path>, path: ast::Path) -> Option<Path> {
let res = match segment.kind()? {
ast::PathSegmentKind::Name(name) => {
let mut res = prefix.unwrap_or_else(|| Path {
kind: PathKind::Abs,
kind: PathKind::Plain,
segments: Vec::with_capacity(1),
});
res.segments.push(name.text());