From 5299a35e3dc484ea2e7d42cfeed89aee806425d3 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 28 Dec 2018 20:33:39 +0300 Subject: extend selection works with macros --- crates/ra_analysis/src/extend_selection.rs | 44 ++++++++++++++++++++++++++++-- crates/ra_analysis/src/macros.rs | 11 ++++++++ crates/ra_analysis/src/mock_analysis.rs | 17 ++++++++++-- 3 files changed, 68 insertions(+), 4 deletions(-) diff --git a/crates/ra_analysis/src/extend_selection.rs b/crates/ra_analysis/src/extend_selection.rs index 5e1fbee18..cde6ee101 100644 --- a/crates/ra_analysis/src/extend_selection.rs +++ b/crates/ra_analysis/src/extend_selection.rs @@ -1,4 +1,8 @@ use ra_db::SyntaxDatabase; +use ra_syntax::{ + SyntaxNodeRef, AstNode, + ast, algo::find_covering_node, +}; use crate::{ TextRange, FileRange, @@ -6,6 +10,42 @@ use crate::{ }; pub(crate) fn extend_selection(db: &RootDatabase, frange: FileRange) -> TextRange { - let file = db.source_file(frange.file_id); - ra_editor::extend_selection(&file, frange.range).unwrap_or(frange.range) + let source_file = db.source_file(frange.file_id); + if let Some(macro_call) = find_macro_call(source_file.syntax(), frange.range) { + if let Some(exp) = crate::macros::expand(db, frange.file_id, macro_call) { + if let Some(dst_range) = exp.map_range_forward(frange.range) { + if let Some(dst_range) = ra_editor::extend_selection(exp.source_file(), dst_range) { + if let Some(src_range) = exp.map_range_back(dst_range) { + return src_range; + } + } + } + } + } + ra_editor::extend_selection(&source_file, frange.range).unwrap_or(frange.range) +} + +fn find_macro_call(node: SyntaxNodeRef, range: TextRange) -> Option { + find_covering_node(node, range) + .ancestors() + .find_map(ast::MacroCall::cast) +} + +#[cfg(test)] +mod tests { + use crate::mock_analysis::single_file_with_range; + use test_utils::assert_eq_dbg; + + #[test] + fn extend_selection_inside_macros() { + let (analysis, frange) = single_file_with_range( + " + fn main() { + ctry!(foo(|x| <|>x<|>)); + } + ", + ); + let r = analysis.extend_selection(frange); + assert_eq_dbg("[51; 56)", &r); + } } diff --git a/crates/ra_analysis/src/macros.rs b/crates/ra_analysis/src/macros.rs index c0dd49dc8..b9feb7fad 100644 --- a/crates/ra_analysis/src/macros.rs +++ b/crates/ra_analysis/src/macros.rs @@ -61,4 +61,15 @@ impl MacroExpansion { } None } + pub(crate) fn map_range_forward(&self, src_range: TextRange) -> Option { + for (s_range, t_range) in self.ranges_map.iter() { + if src_range.is_subrange(&s_range) { + let src_at_zero_range = src_range - src_range.start(); + let src_range_offset = src_range.start() - s_range.start(); + let src_range = src_at_zero_range + src_range_offset + t_range.start(); + return Some(src_range); + } + } + None + } } diff --git a/crates/ra_analysis/src/mock_analysis.rs b/crates/ra_analysis/src/mock_analysis.rs index 5ce2aa2b4..960529404 100644 --- a/crates/ra_analysis/src/mock_analysis.rs +++ b/crates/ra_analysis/src/mock_analysis.rs @@ -1,10 +1,10 @@ use std::sync::Arc; use relative_path::RelativePathBuf; -use test_utils::{extract_offset, parse_fixture, CURSOR_MARKER}; +use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER}; use ra_db::mock::FileMap; -use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, SourceRootId}; +use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, FileRange, SourceRootId}; /// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis /// from a set of in-memory files. @@ -66,6 +66,12 @@ impl MockAnalysis { self.files.push((path.to_string(), text.to_string())); FilePosition { file_id, offset } } + pub fn add_file_with_range(&mut self, path: &str, text: &str) -> FileRange { + let (range, text) = extract_range(text); + let file_id = FileId((self.files.len() + 1) as u32); + self.files.push((path.to_string(), text.to_string())); + FileRange { file_id, range } + } pub fn id_of(&self, path: &str) -> FileId { let (idx, _) = self .files @@ -115,3 +121,10 @@ pub fn single_file_with_position(code: &str) -> (Analysis, FilePosition) { let pos = mock.add_file_with_position("/main.rs", code); (mock.analysis(), pos) } + +/// Creates analysis for a single file, returns range marked with a pair of <|>. +pub fn single_file_with_range(code: &str) -> (Analysis, FileRange) { + let mut mock = MockAnalysis::new(); + let pos = mock.add_file_with_range("/main.rs", code); + (mock.analysis(), pos) +} -- cgit v1.2.3 From 1a860dba38225a4d3e122eaf49c926479826becf Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 28 Dec 2018 20:45:58 +0300 Subject: simplify --- crates/ra_hir/src/module.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/crates/ra_hir/src/module.rs b/crates/ra_hir/src/module.rs index 43413acb8..24c346984 100644 --- a/crates/ra_hir/src/module.rs +++ b/crates/ra_hir/src/module.rs @@ -13,8 +13,8 @@ use ra_db::{SourceRootId, FileId, Cancelable}; use relative_path::RelativePathBuf; use crate::{ - DefKind, DefLoc, DefId, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate, - Name, + Def, DefKind, DefLoc, DefId, + Name, Path, PathKind, HirDatabase, SourceItemId, SourceFileItemId, Crate, arena::{Arena, Id}, }; @@ -139,13 +139,8 @@ impl Module { } else { return Ok(PerNs::none()); }; - let module = match curr.loc(db) { - DefLoc { - kind: DefKind::Module, - source_root_id, - module_id, - .. - } => Module::new(db, source_root_id, module_id)?, + let module = match curr.resolve(db)? { + Def::Module(it) => it, // TODO here would be the place to handle enum variants... _ => return Ok(PerNs::none()), }; -- cgit v1.2.3 From 11122e29b7ec5bc2e08822deaa6fdf9a1cc8ffca Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 28 Dec 2018 21:06:08 +0300 Subject: completion for enum variants --- crates/ra_analysis/src/completion/complete_path.rs | 39 +++++++++++++++++----- .../ra_analysis/src/completion/completion_item.rs | 1 + crates/ra_hir/src/adt.rs | 4 +++ crates/ra_lsp_server/src/conv.rs | 1 + 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/crates/ra_analysis/src/completion/complete_path.rs b/crates/ra_analysis/src/completion/complete_path.rs index aaa2c7cee..c73a083a4 100644 --- a/crates/ra_analysis/src/completion/complete_path.rs +++ b/crates/ra_analysis/src/completion/complete_path.rs @@ -1,6 +1,6 @@ use crate::{ Cancelable, - completion::{CompletionItem, Completions, CompletionKind, CompletionContext}, + completion::{CompletionItem, CompletionItemKind, Completions, CompletionKind, CompletionContext}, }; pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> Cancelable<()> { @@ -12,16 +12,25 @@ pub(super) fn complete_path(acc: &mut Completions, ctx: &CompletionContext) -> C Some(it) => it, None => return Ok(()), }; - let target_module = match def_id.resolve(ctx.db)? { - hir::Def::Module(it) => it, + match def_id.resolve(ctx.db)? { + hir::Def::Module(module) => { + let module_scope = module.scope(ctx.db)?; + module_scope.entries().for_each(|(name, res)| { + CompletionItem::new(CompletionKind::Reference, name.to_string()) + .from_resolution(ctx.db, res) + .add_to(acc) + }); + } + hir::Def::Enum(e) => e + .variants(ctx.db)? + .into_iter() + .for_each(|(name, _variant)| { + CompletionItem::new(CompletionKind::Reference, name.to_string()) + .kind(CompletionItemKind::EnumVariant) + .add_to(acc) + }), _ => return Ok(()), }; - let module_scope = target_module.scope(ctx.db)?; - module_scope.entries().for_each(|(name, res)| { - CompletionItem::new(CompletionKind::Reference, name.to_string()) - .from_resolution(ctx.db, res) - .add_to(acc) - }); Ok(()) } @@ -92,4 +101,16 @@ mod tests { "Spam", ); } + + #[test] + fn completes_enum_variant() { + check_reference_completion( + " + //- /lib.rs + enum E { Foo, Bar(i32) } + fn foo() { let _ = E::<|> } + ", + "Foo;Bar", + ); + } } diff --git a/crates/ra_analysis/src/completion/completion_item.rs b/crates/ra_analysis/src/completion/completion_item.rs index c9f9f495d..1d294c553 100644 --- a/crates/ra_analysis/src/completion/completion_item.rs +++ b/crates/ra_analysis/src/completion/completion_item.rs @@ -29,6 +29,7 @@ pub enum CompletionItemKind { Function, Struct, Enum, + EnumVariant, Binding, Field, } diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index 65c461148..6cdaa1888 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -73,6 +73,10 @@ impl Enum { pub fn name(&self, db: &impl HirDatabase) -> Cancelable> { Ok(db.enum_data(self.def_id)?.name.clone()) } + + pub fn variants(&self, db: &impl HirDatabase) -> Cancelable)>> { + Ok(db.enum_data(self.def_id)?.variants.clone()) + } } #[derive(Debug, Clone, PartialEq, Eq)] diff --git a/crates/ra_lsp_server/src/conv.rs b/crates/ra_lsp_server/src/conv.rs index 3d56ccd97..0d6e62727 100644 --- a/crates/ra_lsp_server/src/conv.rs +++ b/crates/ra_lsp_server/src/conv.rs @@ -57,6 +57,7 @@ impl Conv for CompletionItemKind { CompletionItemKind::Function => Function, CompletionItemKind::Struct => Struct, CompletionItemKind::Enum => Enum, + CompletionItemKind::EnumVariant => EnumMember, CompletionItemKind::Binding => Variable, CompletionItemKind::Field => Field, } -- cgit v1.2.3 From 792899587647f5aa0293c2588173677682187c0a Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 28 Dec 2018 21:34:58 +0300 Subject: nameify structs&enums --- crates/ra_hir/src/adt.rs | 43 +++++++++++++++------------------- crates/ra_hir/src/db.rs | 9 +++---- crates/ra_hir/src/name.rs | 16 +++++++++---- crates/ra_hir/src/query_definitions.rs | 8 ++----- crates/ra_hir/src/ty.rs | 24 ++++++------------- 5 files changed, 43 insertions(+), 57 deletions(-) diff --git a/crates/ra_hir/src/adt.rs b/crates/ra_hir/src/adt.rs index 6cdaa1888..e839a5a90 100644 --- a/crates/ra_hir/src/adt.rs +++ b/crates/ra_hir/src/adt.rs @@ -1,10 +1,10 @@ use std::sync::Arc; -use ra_syntax::{SmolStr, ast::{self, NameOwner, StructFlavor}}; +use ra_syntax::ast::{self, NameOwner, StructFlavor}; use crate::{ - DefId, Cancelable, - db::{HirDatabase}, + DefId, Cancelable, Name, AsName, + db::HirDatabase, type_ref::TypeRef, }; @@ -29,26 +29,26 @@ impl Struct { Ok(db.struct_data(self.def_id)?) } - pub fn name(&self, db: &impl HirDatabase) -> Cancelable> { + pub fn name(&self, db: &impl HirDatabase) -> Cancelable> { Ok(db.struct_data(self.def_id)?.name.clone()) } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct StructData { - name: Option, + name: Option, variant_data: Arc, } impl StructData { pub(crate) fn new(struct_def: ast::StructDef) -> StructData { - let name = struct_def.name().map(|n| n.text()); + let name = struct_def.name().map(|n| n.as_name()); let variant_data = VariantData::new(struct_def.flavor()); let variant_data = Arc::new(variant_data); StructData { name, variant_data } } - pub fn name(&self) -> Option<&SmolStr> { + pub fn name(&self) -> Option<&Name> { self.name.as_ref() } @@ -70,31 +70,29 @@ impl Enum { self.def_id } - pub fn name(&self, db: &impl HirDatabase) -> Cancelable> { + pub fn name(&self, db: &impl HirDatabase) -> Cancelable> { Ok(db.enum_data(self.def_id)?.name.clone()) } - pub fn variants(&self, db: &impl HirDatabase) -> Cancelable)>> { + pub fn variants(&self, db: &impl HirDatabase) -> Cancelable)>> { Ok(db.enum_data(self.def_id)?.variants.clone()) } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct EnumData { - name: Option, - variants: Vec<(SmolStr, Arc)>, + name: Option, + variants: Vec<(Name, Arc)>, } impl EnumData { pub(crate) fn new(enum_def: ast::EnumDef) -> Self { - let name = enum_def.name().map(|n| n.text()); + let name = enum_def.name().map(|n| n.as_name()); let variants = if let Some(evl) = enum_def.variant_list() { evl.variants() .map(|v| { ( - v.name() - .map(|n| n.text()) - .unwrap_or_else(|| SmolStr::new("[error]")), + v.name().map(|n| n.as_name()).unwrap_or_else(Name::missing), Arc::new(VariantData::new(v.flavor())), ) }) @@ -109,12 +107,12 @@ impl EnumData { /// A single field of an enum variant or struct #[derive(Debug, Clone, PartialEq, Eq)] pub struct StructField { - name: SmolStr, + name: Name, type_ref: TypeRef, } impl StructField { - pub fn name(&self) -> SmolStr { + pub fn name(&self) -> Name { self.name.clone() } pub fn type_ref(&self) -> &TypeRef { @@ -138,7 +136,7 @@ impl VariantData { .fields() .enumerate() .map(|(i, fd)| StructField { - name: SmolStr::new(i.to_string()), + name: Name::tuple_field_name(i), type_ref: TypeRef::from_ast_opt(fd.type_ref()), }) .collect(); @@ -148,10 +146,7 @@ impl VariantData { let fields = fl .fields() .map(|fd| StructField { - name: fd - .name() - .map(|n| n.text()) - .unwrap_or_else(|| SmolStr::new("[error]")), + name: fd.name().map(|n| n.as_name()).unwrap_or_else(Name::missing), type_ref: TypeRef::from_ast_opt(fd.type_ref()), }) .collect(); @@ -161,10 +156,10 @@ impl VariantData { } } - pub(crate) fn get_field_type_ref(&self, field_name: &str) -> Option<&TypeRef> { + pub(crate) fn get_field_type_ref(&self, field_name: &Name) -> Option<&TypeRef> { self.fields() .iter() - .find(|f| f.name == field_name) + .find(|f| f.name == *field_name) .map(|f| &f.type_ref) } diff --git a/crates/ra_hir/src/db.rs b/crates/ra_hir/src/db.rs index ba43a4502..b41a7429a 100644 --- a/crates/ra_hir/src/db.rs +++ b/crates/ra_hir/src/db.rs @@ -1,13 +1,10 @@ use std::sync::Arc; -use ra_syntax::{ - SmolStr, - SyntaxNode, -}; +use ra_syntax::SyntaxNode; use ra_db::{SourceRootId, LocationIntener, SyntaxDatabase, FileId, Cancelable}; use crate::{ - DefLoc, DefId, + DefLoc, DefId, Name, SourceFileItems, SourceItemId, query_definitions, FnScopes, @@ -47,7 +44,7 @@ pub trait HirDatabase: SyntaxDatabase use fn query_definitions::type_for_def; } - fn type_for_field(def_id: DefId, field: SmolStr) -> Cancelable { + fn type_for_field(def_id: DefId, field: Name) -> Cancelable { type TypeForFieldQuery; use fn query_definitions::type_for_field; } diff --git a/crates/ra_hir/src/name.rs b/crates/ra_hir/src/name.rs index e4fc141a6..51e8b3da8 100644 --- a/crates/ra_hir/src/name.rs +++ b/crates/ra_hir/src/name.rs @@ -23,6 +23,18 @@ impl fmt::Debug for Name { } impl Name { + fn new(text: SmolStr) -> Name { + Name { text } + } + + pub(crate) fn missing() -> Name { + Name::new("[missing name]".into()) + } + + pub(crate) fn tuple_field_name(idx: usize) -> Name { + Name::new(idx.to_string().into()) + } + pub(crate) fn as_known_name(&self) -> Option { let name = match self.text.as_str() { "isize" => KnownName::Isize, @@ -43,10 +55,6 @@ impl Name { }; Some(name) } - - fn new(text: SmolStr) -> Name { - Name { text } - } } pub(crate) trait AsName { diff --git a/crates/ra_hir/src/query_definitions.rs b/crates/ra_hir/src/query_definitions.rs index 56e3f7e9d..016d86ee6 100644 --- a/crates/ra_hir/src/query_definitions.rs +++ b/crates/ra_hir/src/query_definitions.rs @@ -5,7 +5,7 @@ use std::{ use rustc_hash::FxHashMap; use ra_syntax::{ - AstNode, SyntaxNode, SmolStr, + AstNode, SyntaxNode, ast::{self, NameOwner, ModuleItemOwner} }; use ra_db::{SourceRootId, FileId, Cancelable,}; @@ -39,11 +39,7 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable Cancelable { +pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Cancelable { ty::type_for_field(db, def_id, field) } diff --git a/crates/ra_hir/src/ty.rs b/crates/ra_hir/src/ty.rs index ad097d1f1..38720b7b5 100644 --- a/crates/ra_hir/src/ty.rs +++ b/crates/ra_hir/src/ty.rs @@ -10,13 +10,12 @@ use rustc_hash::{FxHashMap}; use ra_db::{LocalSyntaxPtr, Cancelable}; use ra_syntax::{ - SmolStr, ast::{self, AstNode, LoopBodyOwner, ArgListOwner, PrefixOp}, SyntaxNodeRef }; use crate::{ - Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, + Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, db::HirDatabase, adt::VariantData, type_ref::{TypeRef, Mutability}, @@ -45,7 +44,7 @@ pub enum Ty { /// The DefId of the struct/enum. def_id: DefId, /// The name, for displaying. - name: SmolStr, + name: Name, // later we'll need generic substitutions here }, @@ -276,18 +275,14 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable { pub fn type_for_struct(db: &impl HirDatabase, s: Struct) -> Cancelable { Ok(Ty::Adt { def_id: s.def_id(), - name: s - .name(db)? - .unwrap_or_else(|| SmolStr::new("[unnamed struct]")), + name: s.name(db)?.unwrap_or_else(Name::missing), }) } pub fn type_for_enum(db: &impl HirDatabase, s: Enum) -> Cancelable { Ok(Ty::Adt { def_id: s.def_id(), - name: s - .name(db)? - .unwrap_or_else(|| SmolStr::new("[unnamed enum]")), + name: s.name(db)?.unwrap_or_else(Name::missing), }) } @@ -308,11 +303,7 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable { } } -pub(super) fn type_for_field( - db: &impl HirDatabase, - def_id: DefId, - field: SmolStr, -) -> Cancelable { +pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name) -> Cancelable { let def = def_id.resolve(db)?; let variant_data = match def { Def::Struct(s) => { @@ -559,14 +550,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> { ast::Expr::FieldExpr(e) => { let receiver_ty = self.infer_expr_opt(e.expr())?; if let Some(nr) = e.name_ref() { - let text = nr.text(); match receiver_ty { Ty::Tuple(fields) => { - let i = text.parse::().ok(); + let i = nr.text().parse::().ok(); i.and_then(|i| fields.get(i).cloned()) .unwrap_or(Ty::Unknown) } - Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, text)?, + Ty::Adt { def_id, .. } => self.db.type_for_field(def_id, nr.as_name())?, _ => Ty::Unknown, } } else { -- cgit v1.2.3