From c3a4c4429de83450654795534e64e878a774a088 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 18 Feb 2020 18:35:10 +0100 Subject: Refactor primary IDE API This introduces the new type -- Semantics. Semantics maps SyntaxNodes to various semantic info, such as type, name resolution or macro expansions. To do so, Semantics maintains a HashMap which maps every node it saw to the file from which the node originated. This is enough to get all the necessary hir bits just from syntax. --- crates/ra_ide/src/completion/complete_dot.rs | 4 +- .../completion/complete_macro_in_item_position.rs | 2 +- crates/ra_ide/src/completion/complete_path.rs | 4 +- crates/ra_ide/src/completion/complete_pattern.rs | 2 +- crates/ra_ide/src/completion/complete_postfix.rs | 2 +- .../src/completion/complete_record_literal.rs | 5 +- .../src/completion/complete_record_pattern.rs | 5 +- crates/ra_ide/src/completion/complete_scope.rs | 4 +- .../ra_ide/src/completion/complete_trait_impl.rs | 33 ++++++----- crates/ra_ide/src/completion/completion_context.rs | 65 +++++++++++++--------- 10 files changed, 66 insertions(+), 60 deletions(-) (limited to 'crates/ra_ide/src/completion') diff --git a/crates/ra_ide/src/completion/complete_dot.rs b/crates/ra_ide/src/completion/complete_dot.rs index 2ca78c927..a6e0158b2 100644 --- a/crates/ra_ide/src/completion/complete_dot.rs +++ b/crates/ra_ide/src/completion/complete_dot.rs @@ -16,7 +16,7 @@ pub(super) fn complete_dot(acc: &mut Completions, ctx: &CompletionContext) { _ => return, }; - let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { + let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { Some(ty) => ty, _ => return, }; @@ -55,7 +55,7 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: &Ty fn complete_methods(acc: &mut Completions, ctx: &CompletionContext, receiver: &Type) { if let Some(krate) = ctx.module.map(|it| it.krate()) { let mut seen_methods = FxHashSet::default(); - let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db); + let traits_in_scope = ctx.scope().traits_in_scope(); receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| { if func.has_self_param(ctx.db) && seen_methods.insert(func.name(ctx.db)) { acc.add_function(ctx, func); diff --git a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs index faadd1e3f..1866d9e6c 100644 --- a/crates/ra_ide/src/completion/complete_macro_in_item_position.rs +++ b/crates/ra_ide/src/completion/complete_macro_in_item_position.rs @@ -5,7 +5,7 @@ use crate::completion::{CompletionContext, Completions}; pub(super) fn complete_macro_in_item_position(acc: &mut Completions, ctx: &CompletionContext) { // Show only macros in top level. if ctx.is_new_item { - ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { + ctx.scope().process_all_names(&mut |name, res| { if let hir::ScopeDef::MacroDef(mac) = res { acc.add_macro(ctx, Some(name.to_string()), mac); } diff --git a/crates/ra_ide/src/completion/complete_path.rs b/crates/ra_ide/src/completion/complete_path.rs index 2d7f09a6c..c626e90cc 100644 --- a/crates/ra_ide/src/completion/complete_path.rs +++ b/crates/ra_ide/src/completion/complete_path.rs @@ -11,7 +11,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { Some(path) => path.clone(), _ => return, }; - let def = match ctx.analyzer.resolve_hir_path(ctx.db, &path) { + let def = match ctx.scope().resolve_hir_path(&path) { Some(PathResolution::Def(def)) => def, _ => return, }; @@ -49,7 +49,7 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) { // FIXME: complete T::AssocType let krate = ctx.module.map(|m| m.krate()); if let Some(krate) = krate { - let traits_in_scope = ctx.analyzer.traits_in_scope(ctx.db); + let traits_in_scope = ctx.scope().traits_in_scope(); ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| { match item { hir::AssocItem::Function(func) => { diff --git a/crates/ra_ide/src/completion/complete_pattern.rs b/crates/ra_ide/src/completion/complete_pattern.rs index fd03b1c40..c2c6ca002 100644 --- a/crates/ra_ide/src/completion/complete_pattern.rs +++ b/crates/ra_ide/src/completion/complete_pattern.rs @@ -9,7 +9,7 @@ pub(super) fn complete_pattern(acc: &mut Completions, ctx: &CompletionContext) { } // FIXME: ideally, we should look at the type we are matching against and // suggest variants + auto-imports - ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { + ctx.scope().process_all_names(&mut |name, res| { let def = match &res { hir::ScopeDef::ModuleDef(def) => def, _ => return, diff --git a/crates/ra_ide/src/completion/complete_postfix.rs b/crates/ra_ide/src/completion/complete_postfix.rs index 5470dc291..8a74f993a 100644 --- a/crates/ra_ide/src/completion/complete_postfix.rs +++ b/crates/ra_ide/src/completion/complete_postfix.rs @@ -29,7 +29,7 @@ pub(super) fn complete_postfix(acc: &mut Completions, ctx: &CompletionContext) { dot_receiver.syntax().text().to_string() }; - let receiver_ty = match ctx.analyzer.type_of(ctx.db, &dot_receiver) { + let receiver_ty = match ctx.sema.type_of_expr(&dot_receiver) { Some(it) => it, None => return, }; diff --git a/crates/ra_ide/src/completion/complete_record_literal.rs b/crates/ra_ide/src/completion/complete_record_literal.rs index 577c394d2..f98353d76 100644 --- a/crates/ra_ide/src/completion/complete_record_literal.rs +++ b/crates/ra_ide/src/completion/complete_record_literal.rs @@ -5,10 +5,7 @@ use crate::completion::{CompletionContext, Completions}; /// Complete fields in fields literals. pub(super) fn complete_record_literal(acc: &mut Completions, ctx: &CompletionContext) { let (ty, variant) = match ctx.record_lit_syntax.as_ref().and_then(|it| { - Some(( - ctx.analyzer.type_of(ctx.db, &it.clone().into())?, - ctx.analyzer.resolve_record_literal(it)?, - )) + Some((ctx.sema.type_of_expr(&it.clone().into())?, ctx.sema.resolve_record_literal(it)?)) }) { Some(it) => it, _ => return, diff --git a/crates/ra_ide/src/completion/complete_record_pattern.rs b/crates/ra_ide/src/completion/complete_record_pattern.rs index a56c7e3a1..9bdeae49f 100644 --- a/crates/ra_ide/src/completion/complete_record_pattern.rs +++ b/crates/ra_ide/src/completion/complete_record_pattern.rs @@ -4,10 +4,7 @@ use crate::completion::{CompletionContext, Completions}; pub(super) fn complete_record_pattern(acc: &mut Completions, ctx: &CompletionContext) { let (ty, variant) = match ctx.record_lit_pat.as_ref().and_then(|it| { - Some(( - ctx.analyzer.type_of_pat(ctx.db, &it.clone().into())?, - ctx.analyzer.resolve_record_pattern(it)?, - )) + Some((ctx.sema.type_of_pat(&it.clone().into())?, ctx.sema.resolve_record_pattern(it)?)) }) { Some(it) => it, _ => return, diff --git a/crates/ra_ide/src/completion/complete_scope.rs b/crates/ra_ide/src/completion/complete_scope.rs index e2ee86dd1..aad016d4a 100644 --- a/crates/ra_ide/src/completion/complete_scope.rs +++ b/crates/ra_ide/src/completion/complete_scope.rs @@ -7,9 +7,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { return; } - ctx.analyzer.process_all_names(ctx.db, &mut |name, res| { - acc.add_resolution(ctx, name.to_string(), &res) - }); + ctx.scope().process_all_names(&mut |name, res| acc.add_resolution(ctx, name.to_string(), &res)); } #[cfg(test)] diff --git a/crates/ra_ide/src/completion/complete_trait_impl.rs b/crates/ra_ide/src/completion/complete_trait_impl.rs index 83628e35c..9a27c164b 100644 --- a/crates/ra_ide/src/completion/complete_trait_impl.rs +++ b/crates/ra_ide/src/completion/complete_trait_impl.rs @@ -64,11 +64,12 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext if let (Some(trigger), Some(impl_block)) = (trigger, impl_block) { match trigger.kind() { SyntaxKind::FN_DEF => { - for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) - .iter() - .filter_map(|item| match item { - hir::AssocItem::Function(fn_item) => Some(fn_item), - _ => None, + for missing_fn in + get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| { + match item { + hir::AssocItem::Function(fn_item) => Some(fn_item), + _ => None, + } }) { add_function_impl(&trigger, acc, ctx, &missing_fn); @@ -76,11 +77,12 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext } SyntaxKind::TYPE_ALIAS_DEF => { - for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) - .iter() - .filter_map(|item| match item { - hir::AssocItem::TypeAlias(type_item) => Some(type_item), - _ => None, + for missing_fn in + get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| { + match item { + hir::AssocItem::TypeAlias(type_item) => Some(type_item), + _ => None, + } }) { add_type_alias_impl(&trigger, acc, ctx, &missing_fn); @@ -88,11 +90,12 @@ pub(crate) fn complete_trait_impl(acc: &mut Completions, ctx: &CompletionContext } SyntaxKind::CONST_DEF => { - for missing_fn in get_missing_impl_items(ctx.db, &ctx.analyzer, &impl_block) - .iter() - .filter_map(|item| match item { - hir::AssocItem::Const(const_item) => Some(const_item), - _ => None, + for missing_fn in + get_missing_impl_items(&ctx.sema, &impl_block).iter().filter_map(|item| { + match item { + hir::AssocItem::Const(const_item) => Some(const_item), + _ => None, + } }) { add_const_impl(&trigger, acc, ctx, &missing_fn); diff --git a/crates/ra_ide/src/completion/completion_context.rs b/crates/ra_ide/src/completion/completion_context.rs index 8678a3234..81321a897 100644 --- a/crates/ra_ide/src/completion/completion_context.rs +++ b/crates/ra_ide/src/completion/completion_context.rs @@ -1,9 +1,11 @@ //! FIXME: write short doc here +use hir::{Semantics, SemanticsScope}; +use ra_db::SourceDatabase; use ra_ide_db::RootDatabase; use ra_syntax::{ algo::{find_covering_element, find_node_at_offset}, - ast, AstNode, Parse, SourceFile, + ast, AstNode, SourceFile, SyntaxKind::*, SyntaxNode, SyntaxToken, TextRange, TextUnit, }; @@ -15,8 +17,8 @@ use crate::FilePosition; /// exactly is the cursor, syntax-wise. #[derive(Debug)] pub(crate) struct CompletionContext<'a> { + pub(super) sema: Semantics<'a, RootDatabase>, pub(super) db: &'a RootDatabase, - pub(super) analyzer: hir::SourceAnalyzer, pub(super) offset: TextUnit, pub(super) token: SyntaxToken, pub(super) module: Option, @@ -51,20 +53,26 @@ pub(crate) struct CompletionContext<'a> { impl<'a> CompletionContext<'a> { pub(super) fn new( db: &'a RootDatabase, - original_parse: &'a Parse, position: FilePosition, ) -> Option> { - let mut sb = hir::SourceBinder::new(db); - let module = sb.to_module_def(position.file_id); - let token = - original_parse.tree().syntax().token_at_offset(position.offset).left_biased()?; - let analyzer = sb.analyze( - hir::InFile::new(position.file_id.into(), &token.parent()), - Some(position.offset), - ); + let sema = Semantics::new(db); + + let original_file = sema.parse(position.file_id); + + // Insert a fake ident to get a valid parse tree. We will use this file + // to determine context, though the original_file will be used for + // actual completion. + let file_with_fake_ident = { + let parse = db.parse(position.file_id); + let edit = AtomTextEdit::insert(position.offset, "intellijRulezz".to_string()); + parse.reparse(&edit).tree() + }; + + let module = sema.to_module_def(position.file_id); + let token = original_file.syntax().token_at_offset(position.offset).left_biased()?; let mut ctx = CompletionContext { + sema, db, - analyzer, token, offset: position.offset, module, @@ -87,7 +95,7 @@ impl<'a> CompletionContext<'a> { has_type_args: false, dot_receiver_is_ambiguous_float_literal: false, }; - ctx.fill(&original_parse, position.offset); + ctx.fill(&original_file, file_with_fake_ident, position.offset); Some(ctx) } @@ -100,29 +108,33 @@ impl<'a> CompletionContext<'a> { } } - fn fill(&mut self, original_parse: &'a Parse, offset: TextUnit) { - // Insert a fake ident to get a valid parse tree. We will use this file - // to determine context, though the original_file will be used for - // actual completion. - let file = { - let edit = AtomTextEdit::insert(offset, "intellijRulezz".to_string()); - original_parse.reparse(&edit).tree() - }; + pub(crate) fn scope(&self) -> SemanticsScope<'_, RootDatabase> { + self.sema.scope_at_offset(&self.token.parent(), self.offset) + } + fn fill( + &mut self, + original_file: &ast::SourceFile, + file_with_fake_ident: ast::SourceFile, + offset: TextUnit, + ) { // First, let's try to complete a reference to some declaration. - if let Some(name_ref) = find_node_at_offset::(file.syntax(), offset) { + if let Some(name_ref) = + find_node_at_offset::(file_with_fake_ident.syntax(), offset) + { // Special case, `trait T { fn foo(i_am_a_name_ref) {} }`. // See RFC#1685. if is_node::(name_ref.syntax()) { self.is_param = true; return; } - self.classify_name_ref(original_parse.tree(), name_ref); + self.classify_name_ref(original_file, name_ref); } // Otherwise, see if this is a declaration. We can use heuristics to // suggest declaration names, see `CompletionKind::Magic`. - if let Some(name) = find_node_at_offset::(file.syntax(), offset) { + if let Some(name) = find_node_at_offset::(file_with_fake_ident.syntax(), offset) + { if let Some(bind_pat) = name.syntax().ancestors().find_map(ast::BindPat::cast) { let parent = bind_pat.syntax().parent(); if parent.clone().and_then(ast::MatchArm::cast).is_some() @@ -136,13 +148,12 @@ impl<'a> CompletionContext<'a> { return; } if name.syntax().ancestors().find_map(ast::RecordFieldPatList::cast).is_some() { - self.record_lit_pat = - find_node_at_offset(original_parse.tree().syntax(), self.offset); + self.record_lit_pat = find_node_at_offset(original_file.syntax(), self.offset); } } } - fn classify_name_ref(&mut self, original_file: SourceFile, name_ref: ast::NameRef) { + fn classify_name_ref(&mut self, original_file: &SourceFile, name_ref: ast::NameRef) { self.name_ref_syntax = find_node_at_offset(original_file.syntax(), name_ref.syntax().text_range().start()); let name_range = name_ref.syntax().text_range(); -- cgit v1.2.3