From 33ff7b56ff353410e7bcb7aed27004d4f0a57d8e Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Sun, 27 Jan 2019 20:50:57 +0100 Subject: Use the new Resolver API in completion --- crates/ra_hir/src/expr.rs | 18 +++++-- crates/ra_hir/src/expr/scope.rs | 38 ++++--------- crates/ra_hir/src/nameres.rs | 9 ++++ crates/ra_hir/src/resolve.rs | 57 +++++++++++++++++++- crates/ra_hir/src/source_binder.rs | 31 +++++++++-- crates/ra_ide_api/src/completion/complete_path.rs | 12 ++--- crates/ra_ide_api/src/completion/complete_scope.rs | 63 ++++++---------------- .../src/completion/completion_context.rs | 5 +- .../ra_ide_api/src/completion/completion_item.rs | 44 ++++++++++----- .../completion_item__self_in_methods.snap | 18 ++++++- crates/ra_lsp_server/src/conv.rs | 1 + 11 files changed, 190 insertions(+), 106 deletions(-) diff --git a/crates/ra_hir/src/expr.rs b/crates/ra_hir/src/expr.rs index 503a09f25..6c294bf10 100644 --- a/crates/ra_hir/src/expr.rs +++ b/crates/ra_hir/src/expr.rs @@ -72,11 +72,23 @@ impl Body { } // needs arbitrary_self_types to be a method... or maybe move to the def? -#[allow(dead_code)] -pub fn resolver_for_expr(body: Arc, db: &impl HirDatabase, expr_id: ExprId) -> Resolver { +pub fn resolver_for_expr( + body: Arc, + db: &impl HirDatabase, + expr_id: ExprId, +) -> Resolver<'static> { + let scopes = db.expr_scopes(body.owner); + resolver_for_scope(body, db, scopes.scope_for(expr_id)) +} + +pub fn resolver_for_scope( + body: Arc, + db: &impl HirDatabase, + scope_id: Option, +) -> Resolver<'static> { let mut r = body.owner.resolver(db); let scopes = db.expr_scopes(body.owner); - let scope_chain = scopes.scope_chain_for(expr_id).collect::>(); + let scope_chain = scopes.scope_chain_for(scope_id).collect::>(); for scope in scope_chain.into_iter().rev() { r = r.push_expr_scope(Arc::clone(&scopes), scope); } diff --git a/crates/ra_hir/src/expr/scope.rs b/crates/ra_hir/src/expr/scope.rs index 887ad8dd8..23f1c5e7f 100644 --- a/crates/ra_hir/src/expr/scope.rs +++ b/crates/ra_hir/src/expr/scope.rs @@ -62,25 +62,11 @@ impl ExprScopes { &self.scopes[scope].entries } - pub fn scope_chain_for<'a>(&'a self, expr: ExprId) -> impl Iterator + 'a { - generate(self.scope_for(expr), move |&scope| { - self.scopes[scope].parent - }) - } - - pub fn resolve_local_name<'a>( + pub fn scope_chain_for<'a>( &'a self, - context_expr: ExprId, - name: Name, - ) -> Option<&'a ScopeEntry> { - // TODO replace by Resolver::resolve_name - let mut shadowed = FxHashSet::default(); - let ret = self - .scope_chain_for(context_expr) - .flat_map(|scope| self.entries(scope).iter()) - .filter(|entry| shadowed.insert(entry.name())) - .find(|entry| entry.name() == &name); - ret + scope: Option, + ) -> impl Iterator + 'a { + generate(scope, move |&scope| self.scopes[scope].parent) } fn root_scope(&mut self) -> ScopeId { @@ -123,7 +109,7 @@ impl ExprScopes { self.scope_for.insert(node, scope); } - fn scope_for(&self, expr: ExprId) -> Option { + pub fn scope_for(&self, expr: ExprId) -> Option { self.scope_for.get(&expr).map(|&scope| scope) } } @@ -151,18 +137,14 @@ impl ScopeEntryWithSyntax { } impl ScopesWithSyntaxMapping { - pub fn scope_chain<'a>(&'a self, node: &SyntaxNode) -> impl Iterator + 'a { + fn scope_chain<'a>(&'a self, node: &SyntaxNode) -> impl Iterator + 'a { generate(self.scope_for(node), move |&scope| { self.scopes.scopes[scope].parent }) } - pub fn scope_chain_for_offset<'a>( - &'a self, - offset: TextUnit, - ) -> impl Iterator + 'a { - let scope = self - .scopes + pub fn scope_for_offset<'a>(&'a self, offset: TextUnit) -> Option { + self.scopes .scope_for .iter() .filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope))) @@ -173,9 +155,7 @@ impl ScopesWithSyntaxMapping { ptr.range().len(), ) }) - .map(|(ptr, scope)| self.adjust(ptr, *scope, offset)); - - generate(scope, move |&scope| self.scopes.scopes[scope].parent) + .map(|(ptr, scope)| self.adjust(ptr, *scope, offset)) } // XXX: during completion, cursor might be outside of any particular diff --git a/crates/ra_hir/src/nameres.rs b/crates/ra_hir/src/nameres.rs index 193c6a977..9b020db81 100644 --- a/crates/ra_hir/src/nameres.rs +++ b/crates/ra_hir/src/nameres.rs @@ -81,6 +81,15 @@ pub struct PerNs { pub values: Option, } +impl Default for PerNs { + fn default() -> Self { + PerNs { + types: None, + values: None, + } + } +} + impl PerNs { pub fn none() -> PerNs { PerNs { diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 36daed65b..30cf9c69e 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -89,8 +89,20 @@ impl Resolver { } } - pub fn all_names(&self) -> FxHashMap { - unimplemented!() + pub fn all_names(&self) -> FxHashMap> { + let mut names = FxHashMap::default(); + for scope in &self.scopes { + scope.collect_names(&mut |name, res| { + let current: &mut PerNs = names.entry(name).or_default(); + if current.types.is_none() { + current.types = res.types; + } + if current.values.is_none() { + current.values = res.values; + } + }); + } + names } fn module(&self) -> Option<(&ItemMap, Module)> { @@ -175,4 +187,45 @@ impl Scope { } } } + + fn collect_names(&self, f: &mut FnMut(Name, PerNs)) { + match self { + Scope::ModuleScope(m) => { + m.item_map[m.module.module_id] + .entries() + .for_each(|(name, res)| { + f(name.clone(), res.def.map(|def| Resolution::Def { def })); + }) + } + Scope::ModuleScopeRef(m) => { + m.item_map[m.module.module_id] + .entries() + .for_each(|(name, res)| { + f(name.clone(), res.def.map(|def| Resolution::Def { def })); + }) + } + Scope::GenericParams(gp) => { + for param in &gp.params { + f( + param.name.clone(), + PerNs::types(Resolution::GenericParam { idx: param.idx }), + ) + } + } + Scope::ImplBlockScope(i) => { + f( + Name::self_type(), + PerNs::types(Resolution::SelfType(i.clone())), + ); + } + Scope::ExprScope(e) => { + e.expr_scopes.entries(e.scope_id).iter().for_each(|e| { + f( + e.name().clone(), + PerNs::values(Resolution::LocalBinding { pat: e.pat() }), + ); + }); + } + } + } } diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 1fdd7d087..998158b3e 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -9,13 +9,14 @@ use ra_db::{FileId, FilePosition}; use ra_syntax::{ SmolStr, TextRange, SyntaxNode, ast::{self, AstNode, NameOwner}, - algo::find_node_at_offset, + algo::{find_node_at_offset, find_leaf_at_offset}, }; use crate::{ HirDatabase, Function, ModuleDef, Struct, Enum, AsName, Module, HirFileId, Crate, Trait, Resolver, ids::{LocationCtx, SourceFileItemId}, + expr }; /// Locates the module by `FileId`. Picks topmost module in the file. @@ -202,7 +203,29 @@ pub fn macro_symbols(db: &impl HirDatabase, file_id: FileId) -> Vec<(SmolStr, Te res } -#[allow(unused_variables)] -pub fn resolver_for_position(db: &impl HirDatabase, position: FilePosition) -> Resolver { - unimplemented!() +pub fn resolver_for_position(db: &impl HirDatabase, position: FilePosition) -> Resolver<'static> { + let file = db.parse(position.file_id); + find_leaf_at_offset(file.syntax(), position.offset) + .find_map(|node| { + node.ancestors().find_map(|node| { + if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() { + if let Some(func) = function_from_child_node(db, position.file_id, node) { + let scopes = func.scopes(db); + let scope = scopes.scope_for_offset(position.offset); + Some(expr::resolver_for_scope(func.body(db), db, scope)) + } else { + // TODO const/static/array length + None + } + } else if let Some(module) = ast::Module::cast(node) { + Some(module_from_declaration(db, position.file_id, module)?.resolver(db)) + } else if let Some(_) = ast::SourceFile::cast(node) { + Some(module_from_source(db, position.file_id.into(), None)?.resolver(db)) + } else { + // TODO add missing cases + None + } + }) + }) + .unwrap_or_default() } diff --git a/crates/ra_ide_api/src/completion/complete_path.rs b/crates/ra_ide_api/src/completion/complete_path.rs index b33ddcde5..9e61c0212 100644 --- a/crates/ra_ide_api/src/completion/complete_path.rs +++ b/crates/ra_ide_api/src/completion/complete_path.rs @@ -7,13 +7,13 @@ use crate::{ use hir::Docs; pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { - let (path, module) = match (&ctx.path_prefix, &ctx.module) { - (Some(path), Some(module)) => (path.clone(), module), + let path = match &ctx.path_prefix { + Some(path) => path.clone(), _ => return, }; - let def_id = match module.resolve_path(ctx.db, &path).take_types() { - Some(it) => it, - None => return, + let def = match ctx.resolver.resolve_path(ctx.db, &path).take_types() { + Some(Resolution::Def { def }) => def, + _ => return, }; match def_id { hir::ModuleDef::Module(module) => { @@ -24,7 +24,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { ctx.source_range(), name.to_string(), ) - .from_resolution(ctx, res) + .from_resolution(ctx, &res.def.map(|def| hir::Resolution::Def { def })) .add_to(acc); } } diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index f837bb1db..3488d6480 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -1,61 +1,32 @@ -use rustc_hash::FxHashSet; -use ra_syntax::ast::AstNode; -use crate::completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}; +use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext}; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { if !ctx.is_trivial_path { return; } - let module = match &ctx.module { - Some(it) => it, - None => return, - }; - if let Some(function) = &ctx.function { - let scopes = function.scopes(ctx.db); - complete_fn(acc, &scopes, ctx); - } + let names = ctx.resolver.all_names(); - let module_scope = module.scope(ctx.db); - module_scope - .entries() - .filter(|(_name, res)| { - // For cases like `use self::foo<|>` don't suggest foo itself. - match res.import { - None => true, - Some(import) => { - let source = module.import_source(ctx.db, import); - !source.syntax().range().is_subrange(&ctx.leaf.range()) - } - } - }) + // let module_scope = module.scope(ctx.db); + names + .into_iter() + // FIXME check tests + // .filter(|(_name, res)| { + // // For cases like `use self::foo<|>` don't suggest foo itself. + // match res.import { + // None => true, + // Some(import) => { + // let source = module.import_source(ctx.db, import); + // !source.syntax().range().is_subrange(&ctx.leaf.range()) + // } + // } + // }) .for_each(|(name, res)| { CompletionItem::new( CompletionKind::Reference, ctx.source_range(), name.to_string(), ) - .from_resolution(ctx, res) - .add_to(acc) - }); -} - -fn complete_fn( - acc: &mut Completions, - scopes: &hir::ScopesWithSyntaxMapping, - ctx: &CompletionContext, -) { - let mut shadowed = FxHashSet::default(); - scopes - .scope_chain_for_offset(ctx.offset) - .flat_map(|scope| scopes.scopes.entries(scope).iter()) - .filter(|entry| shadowed.insert(entry.name())) - .for_each(|entry| { - CompletionItem::new( - CompletionKind::Reference, - ctx.source_range(), - entry.name().to_string(), - ) - .kind(CompletionItemKind::Binding) + .from_resolution(ctx, &res) .add_to(acc) }); } diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index 578af6e5b..aea32fce3 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -5,7 +5,7 @@ use ra_syntax::{ algo::{find_leaf_at_offset, find_covering_node, find_node_at_offset}, SyntaxKind::*, }; -use hir::source_binder; +use hir::{source_binder, Resolver}; use crate::{db, FilePosition}; @@ -16,6 +16,7 @@ pub(crate) struct CompletionContext<'a> { pub(super) db: &'a db::RootDatabase, pub(super) offset: TextUnit, pub(super) leaf: &'a SyntaxNode, + pub(super) resolver: Resolver<'static>, pub(super) module: Option, pub(super) function: Option, pub(super) function_syntax: Option<&'a ast::FnDef>, @@ -42,12 +43,14 @@ impl<'a> CompletionContext<'a> { original_file: &'a SourceFile, position: FilePosition, ) -> Option> { + let resolver = source_binder::resolver_for_position(db, position); let module = source_binder::module_from_position(db, position); let leaf = find_leaf_at_offset(original_file.syntax(), position.offset).left_biased()?; let mut ctx = CompletionContext { db, leaf, offset: position.offset, + resolver, module, function: None, function_syntax: None, diff --git a/crates/ra_ide_api/src/completion/completion_item.rs b/crates/ra_ide_api/src/completion/completion_item.rs index d3bc14894..4101ce88a 100644 --- a/crates/ra_ide_api/src/completion/completion_item.rs +++ b/crates/ra_ide_api/src/completion/completion_item.rs @@ -1,5 +1,7 @@ -use hir::{Docs, Documentation}; -use ra_syntax::TextRange; +use hir::{Docs, Documentation, PerNs, Resolution}; +use ra_syntax::{ + TextRange, +}; use ra_text_edit::TextEdit; use test_utils::tested_by; @@ -48,6 +50,7 @@ pub enum CompletionItemKind { Trait, TypeAlias, Method, + TypeParam, } #[derive(Debug, PartialEq, Eq, Copy, Clone)] @@ -207,23 +210,38 @@ impl Builder { pub(super) fn from_resolution( mut self, ctx: &CompletionContext, - resolution: &hir::Resolution, + resolution: &PerNs, ) -> Builder { - let def = resolution.def.take_types().or(resolution.def.take_values()); + use hir::ModuleDef::*; + + let def = resolution + .as_ref() + .take_types() + .or(resolution.as_ref().take_values()); let def = match def { None => return self, Some(it) => it, }; let (kind, docs) = match def { - hir::ModuleDef::Module(it) => (CompletionItemKind::Module, it.docs(ctx.db)), - hir::ModuleDef::Function(func) => return self.from_function(ctx, func), - hir::ModuleDef::Struct(it) => (CompletionItemKind::Struct, it.docs(ctx.db)), - hir::ModuleDef::Enum(it) => (CompletionItemKind::Enum, it.docs(ctx.db)), - hir::ModuleDef::EnumVariant(it) => (CompletionItemKind::EnumVariant, it.docs(ctx.db)), - hir::ModuleDef::Const(it) => (CompletionItemKind::Const, it.docs(ctx.db)), - hir::ModuleDef::Static(it) => (CompletionItemKind::Static, it.docs(ctx.db)), - hir::ModuleDef::Trait(it) => (CompletionItemKind::Trait, it.docs(ctx.db)), - hir::ModuleDef::Type(it) => (CompletionItemKind::TypeAlias, it.docs(ctx.db)), + Resolution::Def { def: Module(it) } => (CompletionItemKind::Module, it.docs(ctx.db)), + Resolution::Def { + def: Function(func), + } => return self.from_function(ctx, *func), + Resolution::Def { def: Struct(it) } => (CompletionItemKind::Struct, it.docs(ctx.db)), + Resolution::Def { def: Enum(it) } => (CompletionItemKind::Enum, it.docs(ctx.db)), + Resolution::Def { + def: EnumVariant(it), + } => (CompletionItemKind::EnumVariant, it.docs(ctx.db)), + Resolution::Def { def: Const(it) } => (CompletionItemKind::Const, it.docs(ctx.db)), + Resolution::Def { def: Static(it) } => (CompletionItemKind::Static, it.docs(ctx.db)), + Resolution::Def { def: Trait(it) } => (CompletionItemKind::Trait, it.docs(ctx.db)), + Resolution::Def { def: Type(it) } => (CompletionItemKind::TypeAlias, it.docs(ctx.db)), + Resolution::GenericParam { .. } => (CompletionItemKind::TypeParam, None), + Resolution::LocalBinding { .. } => (CompletionItemKind::Binding, None), + Resolution::SelfType { .. } => ( + CompletionItemKind::TypeParam, // (does this need its own kind?) + None, + ), }; self.kind = Some(kind); self.documentation = docs; diff --git a/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap b/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap index 6a49e325c..ba1d4abbd 100644 --- a/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap +++ b/crates/ra_ide_api/src/completion/snapshots/completion_item__self_in_methods.snap @@ -1,10 +1,24 @@ --- -created: "2019-01-23T05:27:32.422259+00:00" -creator: insta@0.4.0 +created: "2019-01-27T20:17:10.051725945+00:00" +creator: insta@0.5.2 expression: kind_completions source: crates/ra_ide_api/src/completion/completion_item.rs --- [ + CompletionItem { + completion_kind: Reference, + label: "Self", + kind: Some( + TypeParam + ), + detail: None, + documentation: None, + lookup: None, + insert_text: None, + insert_text_format: PlainText, + source_range: [25; 25), + text_edit: None + }, CompletionItem { completion_kind: Reference, label: "self", diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index c033ecdea..17fa07340 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -70,6 +70,7 @@ impl Conv for CompletionItemKind { CompletionItemKind::Const => Constant, CompletionItemKind::Static => Value, CompletionItemKind::Method => Method, + CompletionItemKind::TypeParam => TypeParameter, } } } -- cgit v1.2.3