From 0e0ae47b47f80e30fca366e5922c19ff81b0a2e2 Mon Sep 17 00:00:00 2001 From: Andrea Pretto Date: Tue, 19 Feb 2019 17:54:00 +0100 Subject: [PATCH 1/8] auto_import: use TextEditBuilder instead of AssistBuilder to make it more reusable --- crates/ra_assists/src/assist_ctx.rs | 4 +++ crates/ra_assists/src/auto_import.rs | 40 ++++++++++++++++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/crates/ra_assists/src/assist_ctx.rs b/crates/ra_assists/src/assist_ctx.rs index e80e3573868..17a9041c5b4 100644 --- a/crates/ra_assists/src/assist_ctx.rs +++ b/crates/ra_assists/src/assist_ctx.rs @@ -144,6 +144,10 @@ impl AssistBuilder { self.replace(node.range(), replace_with) } + pub(crate) fn set_edit_builder(&mut self, edit: TextEditBuilder) { + self.edit = edit; + } + #[allow(unused)] pub(crate) fn delete(&mut self, range: TextRange) { self.edit.delete(range) diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs index 3fdf6b0d9a2..6bc5caf7643 100644 --- a/crates/ra_assists/src/auto_import.rs +++ b/crates/ra_assists/src/auto_import.rs @@ -1,3 +1,4 @@ +use ra_text_edit::TextEditBuilder; use hir::db::HirDatabase; use ra_syntax::{ @@ -374,7 +375,7 @@ fn best_action_for_target<'b, 'a: 'b>( } } -fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut AssistBuilder) { +fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut TextEditBuilder) { match action { ImportAction::AddNewUse { anchor, add_after_anchor } => { make_assist_add_new_use(anchor, *add_after_anchor, target, edit) @@ -408,7 +409,7 @@ fn make_assist_add_new_use( anchor: &Option<&SyntaxNode>, after: bool, target: &[&ast::PathSegment], - edit: &mut AssistBuilder, + edit: &mut TextEditBuilder, ) { if let Some(anchor) = anchor { let indent = ra_fmt::leading_indent(anchor); @@ -437,7 +438,7 @@ fn make_assist_add_in_tree_list( tree_list: &ast::UseTreeList, target: &[&ast::PathSegment], add_self: bool, - edit: &mut AssistBuilder, + edit: &mut TextEditBuilder, ) { let last = tree_list.use_trees().last(); if let Some(last) = last { @@ -466,7 +467,7 @@ fn make_assist_add_nested_import( first_segment_to_split: &Option<&ast::PathSegment>, target: &[&ast::PathSegment], add_self: bool, - edit: &mut AssistBuilder, + edit: &mut TextEditBuilder, ) { let use_tree = path.syntax().ancestors().find_map(ast::UseTree::cast); if let Some(use_tree) = use_tree { @@ -491,7 +492,7 @@ fn make_assist_add_nested_import( buf.push_str(", "); } edit.insert(start, buf); - edit.insert(end, "}"); + edit.insert(end, "}".to_string()); } } @@ -499,7 +500,7 @@ fn apply_auto_import<'a>( container: &SyntaxNode, path: &ast::Path, target: &[&'a ast::PathSegment], - edit: &mut AssistBuilder, + edit: &mut TextEditBuilder, ) { let action = best_action_for_target(container, path, target); make_assist(&action, target, edit); @@ -513,6 +514,25 @@ fn apply_auto_import<'a>( } } +pub fn auto_import_text_edit<'a>( + position: &SyntaxNode, + path: &ast::Path, + target: &[&'a ast::PathSegment], + edit: &mut TextEditBuilder, +) { + let container = position.ancestors().find_map(|n| { + if let Some(module) = ast::Module::cast(n) { + return module.item_list().map(ast::AstNode::syntax); + } + ast::SourceFile::cast(n).map(ast::AstNode::syntax) + }); + + if let Some(container) = container { + let action = best_action_for_target(container, path, target); + make_assist(&action, target, edit); + } +} + pub(crate) fn auto_import(mut ctx: AssistCtx) -> Option { let path: &ast::Path = ctx.node_at_offset()?; // We don't want to mess with use statements @@ -531,7 +551,9 @@ pub(crate) fn auto_import(mut ctx: AssistCtx) -> Option) -> Option Date: Tue, 19 Feb 2019 22:32:00 +0100 Subject: [PATCH 2/8] auto_import: make auto import working with target as a list of SmolStr instead of ast::Path --- crates/ra_assists/src/auto_import.rs | 128 ++++++++++++++------------- crates/ra_hir/src/name.rs | 4 + 2 files changed, 69 insertions(+), 63 deletions(-) diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs index 6bc5caf7643..7527d805d05 100644 --- a/crates/ra_assists/src/auto_import.rs +++ b/crates/ra_assists/src/auto_import.rs @@ -1,21 +1,15 @@ use ra_text_edit::TextEditBuilder; -use hir::db::HirDatabase; +use hir::{ self, db::HirDatabase}; use ra_syntax::{ - ast::{ self, NameOwner }, AstNode, SyntaxNode, Direction, TextRange, + ast::{ self, NameOwner }, AstNode, SyntaxNode, Direction, TextRange, SmolStr, SyntaxKind::{ PATH, PATH_SEGMENT, COLONCOLON, COMMA } }; use crate::{ AssistId, - assist_ctx::{AssistCtx, Assist, AssistBuilder}, + assist_ctx::{AssistCtx, Assist}, }; -fn collect_path_segments(path: &ast::Path) -> Option> { - let mut v = Vec::new(); - collect_path_segments_raw(&mut v, path)?; - return Some(v); -} - fn collect_path_segments_raw<'a>( segments: &mut Vec<&'a ast::PathSegment>, mut path: &'a ast::Path, @@ -46,59 +40,43 @@ fn collect_path_segments_raw<'a>( return Some(segments.len() - oldlen); } -fn fmt_segments(segments: &[&ast::PathSegment]) -> String { +fn fmt_segments(segments: &[SmolStr]) -> String { let mut buf = String::new(); fmt_segments_raw(segments, &mut buf); return buf; } -fn fmt_segments_raw(segments: &[&ast::PathSegment], buf: &mut String) { - let mut first = true; - for s in segments { - if !first { - buf.push_str("::"); - } - match s.kind() { - Some(ast::PathSegmentKind::Name(nameref)) => buf.push_str(nameref.text()), - Some(ast::PathSegmentKind::SelfKw) => buf.push_str("self"), - Some(ast::PathSegmentKind::SuperKw) => buf.push_str("super"), - Some(ast::PathSegmentKind::CrateKw) => buf.push_str("crate"), - None => {} - } - first = false; +fn fmt_segments_raw(segments: &[SmolStr], buf: &mut String) { + let mut iter = segments.iter(); + if let Some(s) = iter.next() { + buf.push_str(s); + } + for s in iter { + buf.push_str("::"); + buf.push_str(s); } } // Returns the numeber of common segments. -fn compare_path_segments(left: &[&ast::PathSegment], right: &[&ast::PathSegment]) -> usize { +fn compare_path_segments(left: &[SmolStr], right: &[&ast::PathSegment]) -> usize { return left.iter().zip(right).filter(|(l, r)| compare_path_segment(l, r)).count(); } -fn compare_path_segment(a: &ast::PathSegment, b: &ast::PathSegment) -> bool { - if let (Some(ka), Some(kb)) = (a.kind(), b.kind()) { - match (ka, kb) { - (ast::PathSegmentKind::Name(nameref_a), ast::PathSegmentKind::Name(nameref_b)) => { - nameref_a.text() == nameref_b.text() - } - (ast::PathSegmentKind::SelfKw, ast::PathSegmentKind::SelfKw) => true, - (ast::PathSegmentKind::SuperKw, ast::PathSegmentKind::SuperKw) => true, - (ast::PathSegmentKind::CrateKw, ast::PathSegmentKind::CrateKw) => true, - (_, _) => false, +fn compare_path_segment(a: &SmolStr, b: &ast::PathSegment) -> bool { + if let Some(kb) = b.kind() { + match kb { + ast::PathSegmentKind::Name(nameref_b) => a == nameref_b.text(), + ast::PathSegmentKind::SelfKw => a == "self", + ast::PathSegmentKind::SuperKw => a == "super", + ast::PathSegmentKind::CrateKw => a == "crate", } } else { false } } -fn compare_path_segment_with_name(a: &ast::PathSegment, b: &ast::Name) -> bool { - if let Some(ka) = a.kind() { - return match (ka, b) { - (ast::PathSegmentKind::Name(nameref_a), _) => nameref_a.text() == b.text(), - (_, _) => false, - }; - } else { - false - } +fn compare_path_segment_with_name(a: &SmolStr, b: &ast::Name) -> bool { + a == b.text() } #[derive(Copy, Clone)] @@ -190,7 +168,7 @@ fn walk_use_tree_for_best_action<'a>( current_path_segments: &mut Vec<&'a ast::PathSegment>, // buffer containing path segments current_parent_use_tree_list: Option<&'a ast::UseTreeList>, // will be Some value if we are in a nested import current_use_tree: &'a ast::UseTree, // the use tree we are currently examinating - target: &[&'a ast::PathSegment], // the path we want to import + target: &[SmolStr], // the path we want to import ) -> ImportAction<'a> { // We save the number of segments in the buffer so we can restore the correct segments // before returning. Recursive call will add segments so we need to delete them. @@ -216,7 +194,7 @@ fn walk_use_tree_for_best_action<'a>( // This can happen only if current_use_tree is a direct child of a UseItem if let Some(name) = alias.and_then(ast::NameOwner::name) { - if compare_path_segment_with_name(target[0], name) { + if compare_path_segment_with_name(&target[0], name) { return ImportAction::Nothing; } } @@ -345,8 +323,8 @@ fn walk_use_tree_for_best_action<'a>( fn best_action_for_target<'b, 'a: 'b>( container: &'a SyntaxNode, - path: &'a ast::Path, - target: &'b [&'a ast::PathSegment], + anchor: &'a SyntaxNode, + target: &'b [SmolStr], ) -> ImportAction<'a> { let mut storage = Vec::with_capacity(16); // this should be the only allocation let best_action = container @@ -368,14 +346,14 @@ fn best_action_for_target<'b, 'a: 'b>( .children() .find_map(ast::ModuleItem::cast) .map(AstNode::syntax) - .or(Some(path.syntax())); + .or(Some(anchor)); return ImportAction::add_new_use(anchor, false); } } } -fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut TextEditBuilder) { +fn make_assist(action: &ImportAction, target: &[SmolStr], edit: &mut TextEditBuilder) { match action { ImportAction::AddNewUse { anchor, add_after_anchor } => { make_assist_add_new_use(anchor, *add_after_anchor, target, edit) @@ -408,7 +386,7 @@ fn make_assist(action: &ImportAction, target: &[&ast::PathSegment], edit: &mut T fn make_assist_add_new_use( anchor: &Option<&SyntaxNode>, after: bool, - target: &[&ast::PathSegment], + target: &[SmolStr], edit: &mut TextEditBuilder, ) { if let Some(anchor) = anchor { @@ -436,7 +414,7 @@ fn make_assist_add_new_use( fn make_assist_add_in_tree_list( tree_list: &ast::UseTreeList, - target: &[&ast::PathSegment], + target: &[SmolStr], add_self: bool, edit: &mut TextEditBuilder, ) { @@ -465,7 +443,7 @@ fn make_assist_add_in_tree_list( fn make_assist_add_nested_import( path: &ast::Path, first_segment_to_split: &Option<&ast::PathSegment>, - target: &[&ast::PathSegment], + target: &[SmolStr], add_self: bool, edit: &mut TextEditBuilder, ) { @@ -496,28 +474,51 @@ fn make_assist_add_nested_import( } } -fn apply_auto_import<'a>( +fn apply_auto_import( container: &SyntaxNode, path: &ast::Path, - target: &[&'a ast::PathSegment], + target: &[SmolStr], edit: &mut TextEditBuilder, ) { - let action = best_action_for_target(container, path, target); + let action = best_action_for_target(container, path.syntax(), target); make_assist(&action, target, edit); - if let (Some(first), Some(last)) = (target.first(), target.last()) { + if let Some(last) = path.segment() { // Here we are assuming the assist will provide a correct use statement // so we can delete the path qualifier edit.delete(TextRange::from_to( - first.syntax().range().start(), + path.syntax().range().start(), last.syntax().range().start(), )); } } -pub fn auto_import_text_edit<'a>( +#[allow(unused)] +pub fn collect_hir_path_segments(path: &hir::Path) -> Vec { + let mut ps = Vec::::with_capacity(10); + match path.kind { + hir::PathKind::Abs => ps.push("".into()), + hir::PathKind::Crate => ps.push("crate".into()), + hir::PathKind::Plain => {}, + hir::PathKind::Self_ => ps.push("self".into()), + hir::PathKind::Super => ps.push("super".into()) + } + for s in path.segments.iter() { + ps.push(s.name.to_smolstr()); + } + ps +} + +// This function produces sequence of text edits into edit +// to import the target path in the most appropriate scope given +// the cursor position +#[allow(unused)] +pub fn auto_import_text_edit( + // Ideally the position of the cursor, used to position: &SyntaxNode, - path: &ast::Path, - target: &[&'a ast::PathSegment], + // The statement to use as anchor (last resort) + anchor: &SyntaxNode, + // The path to import as a sequence of strings + target: &[SmolStr], edit: &mut TextEditBuilder, ) { let container = position.ancestors().find_map(|n| { @@ -528,7 +529,7 @@ pub fn auto_import_text_edit<'a>( }); if let Some(container) = container { - let action = best_action_for_target(container, path, target); + let action = best_action_for_target(container, anchor, target); make_assist(&action, target, edit); } } @@ -540,7 +541,8 @@ pub(crate) fn auto_import(mut ctx: AssistCtx) -> Option SmolStr { + self.text.clone() + } + pub(crate) fn as_known_name(&self) -> Option { let name = match self.text.as_str() { "isize" => KnownName::Isize, From cf0eff2e332f46eda4fcecb043854c9c0d710e4e Mon Sep 17 00:00:00 2001 From: Andrea Pretto Date: Fri, 19 Apr 2019 22:30:05 +0200 Subject: [PATCH 3/8] auto_import: better no anchor management --- crates/ra_assists/src/auto_import.rs | 55 ++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs index 7527d805d05..b002d0e4db4 100644 --- a/crates/ra_assists/src/auto_import.rs +++ b/crates/ra_assists/src/auto_import.rs @@ -168,7 +168,7 @@ fn walk_use_tree_for_best_action<'a>( current_path_segments: &mut Vec<&'a ast::PathSegment>, // buffer containing path segments current_parent_use_tree_list: Option<&'a ast::UseTreeList>, // will be Some value if we are in a nested import current_use_tree: &'a ast::UseTree, // the use tree we are currently examinating - target: &[SmolStr], // the path we want to import + target: &[SmolStr], // the path we want to import ) -> ImportAction<'a> { // We save the number of segments in the buffer so we can restore the correct segments // before returning. Recursive call will add segments so we need to delete them. @@ -341,11 +341,11 @@ fn best_action_for_target<'b, 'a: 'b>( None => { // We have no action and no UseItem was found in container so we find // another item and we use it as anchor. - // If there are no items, we choose the target path itself as anchor. + // If there are no items above, we choose the target path itself as anchor. + // todo: we should include even whitespace blocks as anchor candidates let anchor = container .children() - .find_map(ast::ModuleItem::cast) - .map(AstNode::syntax) + .find(|n| n.range().start() < anchor.range().start()) .or(Some(anchor)); return ImportAction::add_new_use(anchor, false); @@ -498,9 +498,9 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Vec { match path.kind { hir::PathKind::Abs => ps.push("".into()), hir::PathKind::Crate => ps.push("crate".into()), - hir::PathKind::Plain => {}, + hir::PathKind::Plain => {} hir::PathKind::Self_ => ps.push("self".into()), - hir::PathKind::Super => ps.push("super".into()) + hir::PathKind::Super => ps.push("super".into()), } for s in path.segments.iter() { ps.push(s.name.to_smolstr()); @@ -513,7 +513,7 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Vec { // the cursor position #[allow(unused)] pub fn auto_import_text_edit( - // Ideally the position of the cursor, used to + // Ideally the position of the cursor, used to position: &SyntaxNode, // The statement to use as anchor (last resort) anchor: &SyntaxNode, @@ -590,6 +590,47 @@ std::fmt::Debug<|> " use std::fmt::Debug; +Debug<|> + ", + ); + } + #[test] + fn test_auto_import_add_use_no_anchor_with_item_below() { + check_assist( + auto_import, + " +std::fmt::Debug<|> + +fn main() { +} + ", + " +use std::fmt::Debug; + +Debug<|> + +fn main() { +} + ", + ); + } + + #[test] + fn test_auto_import_add_use_no_anchor_with_item_above() { + check_assist( + auto_import, + " +fn main() { +} + +std::fmt::Debug<|> + ", + " +use std::fmt::Debug; + +fn main() { +} + Debug<|> ", ); From 914421495835380b96e0016763fda6eff31a8179 Mon Sep 17 00:00:00 2001 From: Andrea Pretto Date: Mon, 15 Apr 2019 16:11:32 +0200 Subject: [PATCH 4/8] complete_import: add new import resolver infrastructure with some hardcoded importable name. Changes complete_scope to support that. --- crates/ra_assists/src/lib.rs | 2 +- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/resolve.rs | 65 +++++++++++++++++++ crates/ra_hir/src/source_binder.rs | 19 +++++- .../src/completion/complete_scope.rs | 55 ++++++++++++++-- .../src/completion/completion_context.rs | 23 ++++++- 6 files changed, 155 insertions(+), 11 deletions(-) diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index ded401b63dd..173c004cdc8 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -98,7 +98,7 @@ mod inline_local_variable; mod replace_if_let_with_match; mod split_import; mod remove_dbg; -mod auto_import; +pub mod auto_import; mod add_missing_impl_members; fn all_assists() -> &'static [fn(AssistCtx) -> Option] { diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index 4411715dea4..f156e3f0771 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -52,7 +52,7 @@ use crate::{ db::{HirDatabase, DefDatabase}, name::{AsName, KnownName}, source_id::{FileAstId, AstId}, - resolve::Resolver, + resolve::Resolver, resolve::ImportResolver, }; pub use self::{ diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index f2c85eb668e..0f866e6c293 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -3,6 +3,8 @@ use std::sync::Arc; use rustc_hash::FxHashMap; +use ra_syntax::SmolStr; + use crate::{ ModuleDef, code_model_api::Crate, @@ -12,8 +14,12 @@ use crate::{ generics::GenericParams, expr::{scope::{ExprScopes, ScopeId}, PatId}, impl_block::ImplBlock, +<<<<<<< HEAD path::Path, Trait +======= + path::Path, Trait, +>>>>>>> complete_import: add new import resolver infrastructure with some hardcoded importable name. }; #[derive(Debug, Clone, Default)] @@ -21,6 +27,12 @@ pub(crate) struct Resolver { scopes: Vec, } +#[derive(Debug, Clone, Default)] +pub(crate) struct ImportResolver { + // todo: use fst crate or something like that + dummy_names: Vec<(SmolStr, Vec)>, +} + // FIXME how to store these best #[derive(Debug, Clone)] pub(crate) struct ModuleItemMap { @@ -309,3 +321,56 @@ impl Scope { } } } + +impl ImportResolver { + pub(crate) fn new() -> Self { + let dummy_names = vec![ + (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]), + (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]), + (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]), + (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]), + ( + SmolStr::new("Debug"), + vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")], + ), + ( + SmolStr::new("Display"), + vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")], + ), + ( + SmolStr::new("Hash"), + vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")], + ), + ( + SmolStr::new("Hasher"), + vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")], + ), + ( + SmolStr::new("Iterator"), + vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")], + ), + ]; + + ImportResolver { dummy_names } + } + + // Returns a map of importable items filtered by name. + // The map associates item name with its full path. + // todo: should return Resolutions + pub(crate) fn all_names( + &self, + _db: &impl HirDatabase, + name: &Name, + ) -> FxHashMap> { + let name = name.to_smolstr(); + if name.len() > 1 { + self.dummy_names + .iter() + .filter(|(n, _)| n.as_str().contains(name.as_str())) + .cloned() + .collect() + } else { + FxHashMap::default() + } + } +} diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index f1bb13bc671..a6f0ab2896b 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -14,14 +14,19 @@ use ra_syntax::{ ast::{self, AstNode, NameOwner}, algo::find_node_at_offset, SyntaxKind::*, + SmolStr, }; use crate::{ HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name, +<<<<<<< HEAD AsName, Module, HirFileId, Crate, Trait, Resolver, Ty, +======= + AsName, Module, HirFileId, Crate, Trait, Resolver, ImportResolver, +>>>>>>> complete_import: add new import resolver infrastructure with some hardcoded importable name. expr::{BodySourceMap, scope::{ScopeId, ExprScopes}}, ids::LocationCtx, - expr, AstId + expr, AstId, }; /// Locates the module by `FileId`. Picks topmost module in the file. @@ -170,6 +175,7 @@ fn def_with_body_from_child_node( #[derive(Debug)] pub struct SourceAnalyzer { resolver: Resolver, + import_resolver: ImportResolver, body_source_map: Option>, infer: Option>, scopes: Option>, @@ -217,6 +223,7 @@ impl SourceAnalyzer { offset: Option, ) -> SourceAnalyzer { let def_with_body = def_with_body_from_child_node(db, file_id, node); + let import_resolver = ImportResolver::new(); if let Some(def) = def_with_body { let source_map = def.body_source_map(db); let scopes = db.expr_scopes(def); @@ -227,6 +234,7 @@ impl SourceAnalyzer { let resolver = expr::resolver_for_scope(def.body(db), db, scope); SourceAnalyzer { resolver, + import_resolver, body_source_map: Some(source_map), infer: Some(def.infer(db)), scopes: Some(scopes), @@ -237,6 +245,7 @@ impl SourceAnalyzer { .ancestors() .find_map(|node| try_get_resolver_for_node(db, file_id, node)) .unwrap_or_default(), + import_resolver, body_source_map: None, infer: None, scopes: None, @@ -323,6 +332,14 @@ impl SourceAnalyzer { self.resolver.all_names(db) } + pub fn all_import_names( + &self, + db: &impl HirDatabase, + name: &Name, + ) -> FxHashMap> { + self.import_resolver.all_names(db, name) + } + pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec { // FIXME: at least, this should work with any DefWithBody, but ideally // this should be hir-based altogether diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index fd256fc3b34..63d4758230d 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -1,12 +1,57 @@ -use crate::completion::{Completions, CompletionContext}; +use ra_text_edit::TextEditBuilder; +use ra_syntax::SmolStr; +use ra_assists::auto_import; +use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext}; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { - if !ctx.is_trivial_path { - return; + if ctx.is_trivial_path { + let names = ctx.analyzer.all_names(ctx.db); + names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res)); } - let names = ctx.analyzer.all_names(ctx.db); - names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res)); + if let Some(name) = ctx.path_ident.as_ref() { + let import_names = ctx.analyzer.all_import_names(ctx.db, name); + import_names.into_iter().for_each(|(name, path)| { + let edit = { + let mut builder = TextEditBuilder::default(); + builder.replace(ctx.source_range(), name.to_string()); + auto_import::auto_import_text_edit( + ctx.token.parent(), + ctx.token.parent(), + &path, + &mut builder, + ); + builder.finish() + }; + CompletionItem::new( + CompletionKind::Reference, + ctx.source_range(), + build_import_label(&name, &path), + ) + .text_edit(edit) + .add_to(acc) + }); + } +} + +fn build_import_label(name: &str, path: &Vec) -> String { + let mut buf = String::with_capacity(64); + buf.push_str(name); + buf.push_str(" ("); + fmt_import_path(path, &mut buf); + buf.push_str(")"); + buf +} + +fn fmt_import_path(path: &Vec, buf: &mut String) { + let mut segments = path.iter(); + if let Some(s) = segments.next() { + buf.push_str(&s); + } + for s in segments { + buf.push_str("::"); + buf.push_str(&s); + } } #[cfg(test)] diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index 359f2cffa54..ca8f7900d12 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -5,10 +5,10 @@ use ra_syntax::{ algo::{find_token_at_offset, find_covering_element, find_node_at_offset}, SyntaxKind::*, }; -use hir::source_binder; + +use hir::{ source_binder, Name }; use crate::{db, FilePosition}; - /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. #[derive(Debug)] @@ -27,8 +27,10 @@ pub(crate) struct CompletionContext<'a> { pub(super) is_pat_binding: bool, /// A single-indent path, like `foo`. `::foo` should not be considered a trivial path. pub(super) is_trivial_path: bool, - /// If not a trivial, path, the prefix (qualifier). + /// If not a trivial path, the prefix (qualifier). pub(super) path_prefix: Option, + /// If a trivial path, the ident. + pub(super) path_ident: Option, pub(super) after_if: bool, /// `true` if we are a statement or a last expr in the block. pub(super) can_be_stmt: bool, @@ -63,6 +65,7 @@ impl<'a> CompletionContext<'a> { is_pat_binding: false, is_trivial_path: false, path_prefix: None, + path_ident: None, after_if: false, can_be_stmt: false, is_new_item: false, @@ -83,6 +86,18 @@ impl<'a> CompletionContext<'a> { } fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) { + // We heed the original NameRef before the "intellijRulezz" hack + if let Some(name_ref) = find_node_at_offset::(original_file.syntax(), offset) + { + if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { + if let Some(path) = hir::Path::from_ast(path) { + if let Some(ident) = path.as_ident() { + self.path_ident = Some(ident.clone()); + } + } + } + } + // 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. @@ -151,6 +166,7 @@ impl<'a> CompletionContext<'a> { Some(it) => it, None => return, }; + if let Some(segment) = ast::PathSegment::cast(parent) { let path = segment.parent_path(); self.is_call = path @@ -167,6 +183,7 @@ impl<'a> CompletionContext<'a> { return; } } + if path.qualifier().is_none() { self.is_trivial_path = true; From 200032852be0c66b978c875a8edf0eca1f08b901 Mon Sep 17 00:00:00 2001 From: Andrea Pretto Date: Sun, 21 Apr 2019 23:55:47 +0200 Subject: [PATCH 5/8] complete_import: prevent panic when the anchor is the completion source range (fix rebase mess) Please enter the commit message for your changes. Lines starting --- crates/ra_hir/src/resolve.rs | 4 ---- crates/ra_hir/src/source_binder.rs | 6 +---- .../src/completion/complete_scope.rs | 24 +++++++++++++------ 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index 0f866e6c293..ce80be17fed 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -14,12 +14,8 @@ use crate::{ generics::GenericParams, expr::{scope::{ExprScopes, ScopeId}, PatId}, impl_block::ImplBlock, -<<<<<<< HEAD path::Path, Trait -======= - path::Path, Trait, ->>>>>>> complete_import: add new import resolver infrastructure with some hardcoded importable name. }; #[derive(Debug, Clone, Default)] diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index a6f0ab2896b..42399622adb 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -19,11 +19,7 @@ use ra_syntax::{ use crate::{ HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name, -<<<<<<< HEAD - AsName, Module, HirFileId, Crate, Trait, Resolver, Ty, -======= - AsName, Module, HirFileId, Crate, Trait, Resolver, ImportResolver, ->>>>>>> complete_import: add new import resolver infrastructure with some hardcoded importable name. + AsName, Module, HirFileId, Crate, Trait, Resolver, Ty, ImportResolver, expr::{BodySourceMap, scope::{ScopeId, ExprScopes}}, ids::LocationCtx, expr, AstId, diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index 63d4758230d..5bd5376c92c 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -23,13 +23,23 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { ); builder.finish() }; - CompletionItem::new( - CompletionKind::Reference, - ctx.source_range(), - build_import_label(&name, &path), - ) - .text_edit(edit) - .add_to(acc) + + // Hack: copied this check form conv.rs beacause auto import can produce edits + // that invalidate assert in conv_with. + if edit + .as_atoms() + .iter() + .filter(|atom| !ctx.source_range().is_subrange(&atom.delete)) + .all(|atom| ctx.source_range().intersection(&atom.delete).is_none()) + { + CompletionItem::new( + CompletionKind::Reference, + ctx.source_range(), + build_import_label(&name, &path), + ) + .text_edit(edit) + .add_to(acc); + } }); } } From e01052d1f0b8bf70a418a10538528923c5f400d5 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 22 Apr 2019 15:56:28 +0300 Subject: [PATCH 6/8] move auto-imoprter into IDE auto-import is purely an IDE concern, so it should be done outside of HIR --- crates/ra_assists/src/auto_import.rs | 4 +- crates/ra_hir/src/lib.rs | 2 +- crates/ra_hir/src/name.rs | 13 +++- crates/ra_hir/src/resolve.rs | 59 ------------------- crates/ra_hir/src/source_binder.rs | 15 +---- .../src/completion/complete_scope.rs | 55 ++++++++++++++++- .../src/completion/completion_context.rs | 12 ---- 7 files changed, 68 insertions(+), 92 deletions(-) diff --git a/crates/ra_assists/src/auto_import.rs b/crates/ra_assists/src/auto_import.rs index b002d0e4db4..7c856c19b61 100644 --- a/crates/ra_assists/src/auto_import.rs +++ b/crates/ra_assists/src/auto_import.rs @@ -492,7 +492,6 @@ fn apply_auto_import( } } -#[allow(unused)] pub fn collect_hir_path_segments(path: &hir::Path) -> Vec { let mut ps = Vec::::with_capacity(10); match path.kind { @@ -503,7 +502,7 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Vec { hir::PathKind::Super => ps.push("super".into()), } for s in path.segments.iter() { - ps.push(s.name.to_smolstr()); + ps.push(s.name.to_string().into()); } ps } @@ -511,7 +510,6 @@ pub fn collect_hir_path_segments(path: &hir::Path) -> Vec { // This function produces sequence of text edits into edit // to import the target path in the most appropriate scope given // the cursor position -#[allow(unused)] pub fn auto_import_text_edit( // Ideally the position of the cursor, used to position: &SyntaxNode, diff --git a/crates/ra_hir/src/lib.rs b/crates/ra_hir/src/lib.rs index f156e3f0771..4411715dea4 100644 --- a/crates/ra_hir/src/lib.rs +++ b/crates/ra_hir/src/lib.rs @@ -52,7 +52,7 @@ use crate::{ db::{HirDatabase, DefDatabase}, name::{AsName, KnownName}, source_id::{FileAstId, AstId}, - resolve::Resolver, resolve::ImportResolver, + resolve::Resolver, }; pub use self::{ diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index 331da602760..9a999e66c7d 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -46,8 +46,17 @@ impl Name { Name::new(idx.to_string().into()) } - pub fn to_smolstr(&self) -> SmolStr { - self.text.clone() + // There's should be no way to extract a string out of `Name`: `Name` in the + // future, `Name` will include hygiene information, and you can't encode + // hygiene into a String. + // + // If you need to compare something with `Name`, compare `Name`s directly. + // + // If you need to render `Name` for the user, use the `Display` impl, but be + // aware that it strips hygiene info. + #[deprecated(note = "use to_string instead")] + pub fn as_smolstr(&self) -> &SmolStr { + &self.text } pub(crate) fn as_known_name(&self) -> Option { diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index ce80be17fed..bd0f074c1d1 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -23,12 +23,6 @@ pub(crate) struct Resolver { scopes: Vec, } -#[derive(Debug, Clone, Default)] -pub(crate) struct ImportResolver { - // todo: use fst crate or something like that - dummy_names: Vec<(SmolStr, Vec)>, -} - // FIXME how to store these best #[derive(Debug, Clone)] pub(crate) struct ModuleItemMap { @@ -317,56 +311,3 @@ impl Scope { } } } - -impl ImportResolver { - pub(crate) fn new() -> Self { - let dummy_names = vec![ - (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]), - (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]), - (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]), - (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]), - ( - SmolStr::new("Debug"), - vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")], - ), - ( - SmolStr::new("Display"), - vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")], - ), - ( - SmolStr::new("Hash"), - vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")], - ), - ( - SmolStr::new("Hasher"), - vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")], - ), - ( - SmolStr::new("Iterator"), - vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")], - ), - ]; - - ImportResolver { dummy_names } - } - - // Returns a map of importable items filtered by name. - // The map associates item name with its full path. - // todo: should return Resolutions - pub(crate) fn all_names( - &self, - _db: &impl HirDatabase, - name: &Name, - ) -> FxHashMap> { - let name = name.to_smolstr(); - if name.len() > 1 { - self.dummy_names - .iter() - .filter(|(n, _)| n.as_str().contains(name.as_str())) - .cloned() - .collect() - } else { - FxHashMap::default() - } - } -} diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 42399622adb..2959e3eca29 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -14,12 +14,11 @@ use ra_syntax::{ ast::{self, AstNode, NameOwner}, algo::find_node_at_offset, SyntaxKind::*, - SmolStr, }; use crate::{ HirDatabase, Function, Struct, Enum, Const, Static, Either, DefWithBody, PerNs, Name, - AsName, Module, HirFileId, Crate, Trait, Resolver, Ty, ImportResolver, + AsName, Module, HirFileId, Crate, Trait, Resolver, Ty, expr::{BodySourceMap, scope::{ScopeId, ExprScopes}}, ids::LocationCtx, expr, AstId, @@ -171,7 +170,6 @@ fn def_with_body_from_child_node( #[derive(Debug)] pub struct SourceAnalyzer { resolver: Resolver, - import_resolver: ImportResolver, body_source_map: Option>, infer: Option>, scopes: Option>, @@ -219,7 +217,6 @@ impl SourceAnalyzer { offset: Option, ) -> SourceAnalyzer { let def_with_body = def_with_body_from_child_node(db, file_id, node); - let import_resolver = ImportResolver::new(); if let Some(def) = def_with_body { let source_map = def.body_source_map(db); let scopes = db.expr_scopes(def); @@ -230,7 +227,6 @@ impl SourceAnalyzer { let resolver = expr::resolver_for_scope(def.body(db), db, scope); SourceAnalyzer { resolver, - import_resolver, body_source_map: Some(source_map), infer: Some(def.infer(db)), scopes: Some(scopes), @@ -241,7 +237,6 @@ impl SourceAnalyzer { .ancestors() .find_map(|node| try_get_resolver_for_node(db, file_id, node)) .unwrap_or_default(), - import_resolver, body_source_map: None, infer: None, scopes: None, @@ -328,14 +323,6 @@ impl SourceAnalyzer { self.resolver.all_names(db) } - pub fn all_import_names( - &self, - db: &impl HirDatabase, - name: &Name, - ) -> FxHashMap> { - self.import_resolver.all_names(db, name) - } - pub fn find_all_refs(&self, pat: &ast::BindPat) -> Vec { // FIXME: at least, this should work with any DefWithBody, but ideally // this should be hir-based altogether diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index 5bd5376c92c..a2523c5efea 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -1,6 +1,8 @@ +use rustc_hash::FxHashMap; use ra_text_edit::TextEditBuilder; use ra_syntax::SmolStr; use ra_assists::auto_import; + use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext}; pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { @@ -10,7 +12,8 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { } if let Some(name) = ctx.path_ident.as_ref() { - let import_names = ctx.analyzer.all_import_names(ctx.db, name); + let import_resolver = ImportResolver::new(); + let import_names = import_resolver.all_names(&name.to_string()); import_names.into_iter().for_each(|(name, path)| { let edit = { let mut builder = TextEditBuilder::default(); @@ -64,6 +67,56 @@ fn fmt_import_path(path: &Vec, buf: &mut String) { } } +#[derive(Debug, Clone, Default)] +pub(crate) struct ImportResolver { + // todo: use fst crate or something like that + dummy_names: Vec<(SmolStr, Vec)>, +} + +impl ImportResolver { + pub(crate) fn new() -> Self { + let dummy_names = vec![ + (SmolStr::new("fmt"), vec![SmolStr::new("std"), SmolStr::new("fmt")]), + (SmolStr::new("io"), vec![SmolStr::new("std"), SmolStr::new("io")]), + (SmolStr::new("iter"), vec![SmolStr::new("std"), SmolStr::new("iter")]), + (SmolStr::new("hash"), vec![SmolStr::new("std"), SmolStr::new("hash")]), + ( + SmolStr::new("Debug"), + vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Debug")], + ), + ( + SmolStr::new("Display"), + vec![SmolStr::new("std"), SmolStr::new("fmt"), SmolStr::new("Display")], + ), + ( + SmolStr::new("Hash"), + vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hash")], + ), + ( + SmolStr::new("Hasher"), + vec![SmolStr::new("std"), SmolStr::new("hash"), SmolStr::new("Hasher")], + ), + ( + SmolStr::new("Iterator"), + vec![SmolStr::new("std"), SmolStr::new("iter"), SmolStr::new("Iterator")], + ), + ]; + + ImportResolver { dummy_names } + } + + // Returns a map of importable items filtered by name. + // The map associates item name with its full path. + // todo: should return Resolutions + pub(crate) fn all_names(&self, name: &str) -> FxHashMap> { + if name.len() > 1 { + self.dummy_names.iter().filter(|(n, _)| n.contains(name)).cloned().collect() + } else { + FxHashMap::default() + } + } +} + #[cfg(test)] mod tests { use crate::completion::{CompletionKind, check_completion}; diff --git a/crates/ra_ide_api/src/completion/completion_context.rs b/crates/ra_ide_api/src/completion/completion_context.rs index ca8f7900d12..0d630fdf6c8 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -86,18 +86,6 @@ impl<'a> CompletionContext<'a> { } fn fill(&mut self, original_file: &'a SourceFile, offset: TextUnit) { - // We heed the original NameRef before the "intellijRulezz" hack - if let Some(name_ref) = find_node_at_offset::(original_file.syntax(), offset) - { - if let Some(path) = name_ref.syntax().ancestors().find_map(ast::Path::cast) { - if let Some(path) = hir::Path::from_ast(path) { - if let Some(ident) = path.as_ident() { - self.path_ident = Some(ident.clone()); - } - } - } - } - // 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. From c27a3da5e86b1fcbfb562e0723fb97d72268fa72 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 22 Apr 2019 16:04:56 +0300 Subject: [PATCH 7/8] remove path_ident from CompletionContext We really shouldn't be looking at the identifier at point. Instead, all filtering and sorting should be implemented at the layer above. This layer should probably be home for auto-import completions as well, but, since that is not yet implemented, let's just stick this into complete_scope. --- .../src/completion/complete_scope.rs | 70 ++++++++++--------- .../src/completion/completion_context.rs | 7 +- 2 files changed, 38 insertions(+), 39 deletions(-) diff --git a/crates/ra_ide_api/src/completion/complete_scope.rs b/crates/ra_ide_api/src/completion/complete_scope.rs index a2523c5efea..2473e58b405 100644 --- a/crates/ra_ide_api/src/completion/complete_scope.rs +++ b/crates/ra_ide_api/src/completion/complete_scope.rs @@ -1,6 +1,6 @@ use rustc_hash::FxHashMap; use ra_text_edit::TextEditBuilder; -use ra_syntax::SmolStr; +use ra_syntax::{SmolStr, ast, AstNode}; use ra_assists::auto_import; use crate::completion::{CompletionItem, Completions, CompletionKind, CompletionContext}; @@ -9,41 +9,43 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) { if ctx.is_trivial_path { let names = ctx.analyzer.all_names(ctx.db); names.into_iter().for_each(|(name, res)| acc.add_resolution(ctx, name.to_string(), &res)); - } - if let Some(name) = ctx.path_ident.as_ref() { - let import_resolver = ImportResolver::new(); - let import_names = import_resolver.all_names(&name.to_string()); - import_names.into_iter().for_each(|(name, path)| { - let edit = { - let mut builder = TextEditBuilder::default(); - builder.replace(ctx.source_range(), name.to_string()); - auto_import::auto_import_text_edit( - ctx.token.parent(), - ctx.token.parent(), - &path, - &mut builder, - ); - builder.finish() - }; + // auto-import + // We fetch ident from the original file, because we need to pre-filter auto-imports + if ast::NameRef::cast(ctx.token.parent()).is_some() { + let import_resolver = ImportResolver::new(); + let import_names = import_resolver.all_names(ctx.token.text()); + import_names.into_iter().for_each(|(name, path)| { + let edit = { + let mut builder = TextEditBuilder::default(); + builder.replace(ctx.source_range(), name.to_string()); + auto_import::auto_import_text_edit( + ctx.token.parent(), + ctx.token.parent(), + &path, + &mut builder, + ); + builder.finish() + }; - // Hack: copied this check form conv.rs beacause auto import can produce edits - // that invalidate assert in conv_with. - if edit - .as_atoms() - .iter() - .filter(|atom| !ctx.source_range().is_subrange(&atom.delete)) - .all(|atom| ctx.source_range().intersection(&atom.delete).is_none()) - { - CompletionItem::new( - CompletionKind::Reference, - ctx.source_range(), - build_import_label(&name, &path), - ) - .text_edit(edit) - .add_to(acc); - } - }); + // Hack: copied this check form conv.rs beacause auto import can produce edits + // that invalidate assert in conv_with. + if edit + .as_atoms() + .iter() + .filter(|atom| !ctx.source_range().is_subrange(&atom.delete)) + .all(|atom| ctx.source_range().intersection(&atom.delete).is_none()) + { + CompletionItem::new( + CompletionKind::Reference, + ctx.source_range(), + build_import_label(&name, &path), + ) + .text_edit(edit) + .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 0d630fdf6c8..a8c8cc7b047 100644 --- a/crates/ra_ide_api/src/completion/completion_context.rs +++ b/crates/ra_ide_api/src/completion/completion_context.rs @@ -5,10 +5,10 @@ use ra_syntax::{ algo::{find_token_at_offset, find_covering_element, find_node_at_offset}, SyntaxKind::*, }; - -use hir::{ source_binder, Name }; +use hir::source_binder; use crate::{db, FilePosition}; + /// `CompletionContext` is created early during completion to figure out, where /// exactly is the cursor, syntax-wise. #[derive(Debug)] @@ -29,8 +29,6 @@ pub(crate) struct CompletionContext<'a> { pub(super) is_trivial_path: bool, /// If not a trivial path, the prefix (qualifier). pub(super) path_prefix: Option, - /// If a trivial path, the ident. - pub(super) path_ident: Option, pub(super) after_if: bool, /// `true` if we are a statement or a last expr in the block. pub(super) can_be_stmt: bool, @@ -65,7 +63,6 @@ impl<'a> CompletionContext<'a> { is_pat_binding: false, is_trivial_path: false, path_prefix: None, - path_ident: None, after_if: false, can_be_stmt: false, is_new_item: false, From aa1ef6ae9a9a61ab6ddc42d7987753df3aa67263 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Mon, 22 Apr 2019 16:18:14 +0300 Subject: [PATCH 8/8] unused import --- crates/ra_hir/src/resolve.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/ra_hir/src/resolve.rs b/crates/ra_hir/src/resolve.rs index bd0f074c1d1..f2c85eb668e 100644 --- a/crates/ra_hir/src/resolve.rs +++ b/crates/ra_hir/src/resolve.rs @@ -3,8 +3,6 @@ use std::sync::Arc; use rustc_hash::FxHashMap; -use ra_syntax::SmolStr; - use crate::{ ModuleDef, code_model_api::Crate,