475: Show types of fields in completion r=matklad a=matklad

![image](https://user-images.githubusercontent.com/1711539/50910524-0f146200-143f-11e9-84d6-0ba80761cd89.png)

r? @flodiebold 


Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-01-10 13:07:19 +00:00
commit aca14c591f
6 changed files with 99 additions and 59 deletions

View File

@ -4,7 +4,7 @@ use ra_db::Cancelable;
use ra_syntax::ast::{self, NameOwner, StructFlavor, AstNode};
use crate::{
DefId, Name, AsName, Struct, Enum, VariantData, StructField, HirDatabase, DefKind,
DefId, Name, AsName, Struct, Enum, HirDatabase, DefKind,
type_ref::TypeRef,
};
@ -12,6 +12,10 @@ impl Struct {
pub(crate) fn new(def_id: DefId) -> Self {
Struct { def_id }
}
pub(crate) fn variant_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<VariantData>> {
Ok(db.struct_data(self.def_id)?.variant_data.clone())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
@ -83,6 +87,51 @@ impl EnumData {
}
}
/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StructField {
pub(crate) name: Name,
pub(crate) type_ref: TypeRef,
}
/// Fields of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VariantData {
Struct(Vec<StructField>),
Tuple(Vec<StructField>),
Unit,
}
impl VariantData {
pub fn fields(&self) -> &[StructField] {
match self {
VariantData::Struct(fields) | VariantData::Tuple(fields) => fields,
_ => &[],
}
}
pub fn is_struct(&self) -> bool {
match self {
VariantData::Struct(..) => true,
_ => false,
}
}
pub fn is_tuple(&self) -> bool {
match self {
VariantData::Tuple(..) => true,
_ => false,
}
}
pub fn is_unit(&self) -> bool {
match self {
VariantData::Unit => true,
_ => false,
}
}
}
impl VariantData {
fn new(flavor: StructFlavor) -> Self {
match flavor {
@ -114,7 +163,7 @@ impl VariantData {
pub(crate) fn get_field_type_ref(&self, field_name: &Name) -> Option<&TypeRef> {
self.fields()
.iter()
.find(|f| f.name() == field_name)
.map(|f| f.type_ref())
.find(|f| f.name == *field_name)
.map(|f| &f.type_ref)
}
}

View File

@ -5,12 +5,13 @@ use ra_db::{CrateId, Cancelable, FileId};
use ra_syntax::{ast, TreePtr, SyntaxNode};
use crate::{
Name, DefId, Path, PerNs, ScopesWithSyntaxMapping,
Name, DefId, Path, PerNs, ScopesWithSyntaxMapping, Ty,
type_ref::TypeRef,
nameres::ModuleScope,
db::HirDatabase,
expr::BodySyntaxMapping,
ty::InferenceResult,
adt::VariantData,
};
/// hir::Crate describes a single crate. It's the main interface with which
@ -137,58 +138,18 @@ impl Module {
}
}
/// A single field of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StructField {
pub(crate) name: Name,
pub(crate) type_ref: TypeRef,
struct_: Struct,
name: Name,
}
impl StructField {
pub fn name(&self) -> &Name {
&self.name
}
pub fn type_ref(&self) -> &TypeRef {
&self.type_ref
}
}
/// Fields of an enum variant or struct
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VariantData {
Struct(Vec<StructField>),
Tuple(Vec<StructField>),
Unit,
}
impl VariantData {
pub fn fields(&self) -> &[StructField] {
match self {
VariantData::Struct(fields) | VariantData::Tuple(fields) => fields,
_ => &[],
}
}
pub fn is_struct(&self) -> bool {
match self {
VariantData::Struct(..) => true,
_ => false,
}
}
pub fn is_tuple(&self) -> bool {
match self {
VariantData::Tuple(..) => true,
_ => false,
}
}
pub fn is_unit(&self) -> bool {
match self {
VariantData::Unit => true,
_ => false,
}
pub fn ty(&self, db: &impl HirDatabase) -> Cancelable<Option<Ty>> {
db.type_for_field(self.struct_.def_id, self.name.clone())
}
}
@ -206,8 +167,18 @@ impl Struct {
Ok(db.struct_data(self.def_id)?.name.clone())
}
pub fn variant_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<VariantData>> {
Ok(db.struct_data(self.def_id)?.variant_data.clone())
pub fn fields(&self, db: &impl HirDatabase) -> Cancelable<Vec<StructField>> {
let res = db
.struct_data(self.def_id)?
.variant_data
.fields()
.iter()
.map(|it| StructField {
struct_: self.clone(),
name: it.name.clone(),
})
.collect();
Ok(res)
}
}

View File

@ -56,6 +56,6 @@ pub use self::code_model_api::{
Crate, CrateDependency,
Def,
Module, ModuleSource, Problem,
Struct, Enum, VariantData, StructField,
Struct, Enum,
Function, FnSignature,
};

View File

@ -28,13 +28,13 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty)
Ty::Adt { def_id, .. } => {
match def_id.resolve(ctx.db)? {
Def::Struct(s) => {
let variant_data = s.variant_data(ctx.db)?;
for field in variant_data.fields() {
for field in s.fields(ctx.db)? {
CompletionItem::new(
CompletionKind::Reference,
field.name().to_string(),
)
.kind(CompletionItemKind::Field)
.set_detail(field.ty(ctx.db)?.map(|ty| ty.to_string()))
.add_to(acc);
}
}
@ -72,7 +72,7 @@ mod tests {
a.<|>
}
",
r#"the_field"#,
r#"the_field "u32""#,
);
}
@ -80,14 +80,14 @@ mod tests {
fn test_struct_field_completion_self() {
check_ref_completion(
r"
struct A { the_field: u32 }
struct A { the_field: (u32,) }
impl A {
fn foo(self) {
self.<|>
}
}
",
r#"the_field"#,
r#"the_field "(u32,)""#,
);
}
@ -95,14 +95,14 @@ mod tests {
fn test_struct_field_completion_autoderef() {
check_ref_completion(
r"
struct A { the_field: u32 }
struct A { the_field: (u32, i32) }
impl A {
fn foo(&self) {
self.<|>
}
}
",
r#"the_field"#,
r#"the_field "(u32, i32)""#,
);
}

View File

@ -11,6 +11,7 @@ pub struct CompletionItem {
/// completion.
completion_kind: CompletionKind,
label: String,
detail: Option<String>,
lookup: Option<String>,
snippet: Option<String>,
kind: Option<CompletionItemKind>,
@ -51,6 +52,7 @@ impl CompletionItem {
Builder {
completion_kind,
label,
detail: None,
lookup: None,
snippet: None,
kind: None,
@ -60,6 +62,10 @@ impl CompletionItem {
pub fn label(&self) -> &str {
&self.label
}
/// Short one-line additional information, like a type
pub fn detail(&self) -> Option<&str> {
self.detail.as_ref().map(|it| it.as_str())
}
/// What string is used for filtering.
pub fn lookup(&self) -> &str {
self.lookup
@ -87,6 +93,7 @@ impl CompletionItem {
pub(crate) struct Builder {
completion_kind: CompletionKind,
label: String,
detail: Option<String>,
lookup: Option<String>,
snippet: Option<String>,
kind: Option<CompletionItemKind>,
@ -100,6 +107,7 @@ impl Builder {
pub(crate) fn build(self) -> CompletionItem {
CompletionItem {
label: self.label,
detail: self.detail,
lookup: self.lookup,
snippet: self.snippet,
kind: self.kind,
@ -118,6 +126,14 @@ impl Builder {
self.kind = Some(kind);
self
}
#[allow(unused)]
pub(crate) fn detail(self, detail: impl Into<String>) -> Builder {
self.set_detail(Some(detail))
}
pub(crate) fn set_detail(mut self, detail: Option<impl Into<String>>) -> Builder {
self.detail = detail.map(Into::into);
self
}
pub(super) fn from_resolution(
mut self,
ctx: &CompletionContext,
@ -227,6 +243,9 @@ impl Completions {
} else {
res.push_str(&c.label);
}
if let Some(detail) = &c.detail {
res.push_str(&format!(" {:?}", detail));
}
if let Some(snippet) = &c.snippet {
res.push_str(&format!(" {:?}", snippet));
}

View File

@ -75,6 +75,7 @@ impl Conv for CompletionItem {
fn conv(self) -> <Self as Conv>::Output {
let mut res = ::languageserver_types::CompletionItem {
label: self.label().to_string(),
detail: self.detail().map(|it| it.to_string()),
filter_text: Some(self.lookup().to_string()),
kind: self.kind().map(|it| it.conv()),
..Default::default()