mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 13:51:31 +00:00
Merge #475
475: Show types of fields in completion r=matklad a=matklad  r? @flodiebold Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
aca14c591f
6 changed files with 99 additions and 59 deletions
|
@ -4,7 +4,7 @@ use ra_db::Cancelable;
|
||||||
use ra_syntax::ast::{self, NameOwner, StructFlavor, AstNode};
|
use ra_syntax::ast::{self, NameOwner, StructFlavor, AstNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
DefId, Name, AsName, Struct, Enum, VariantData, StructField, HirDatabase, DefKind,
|
DefId, Name, AsName, Struct, Enum, HirDatabase, DefKind,
|
||||||
type_ref::TypeRef,
|
type_ref::TypeRef,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -12,6 +12,10 @@ impl Struct {
|
||||||
pub(crate) fn new(def_id: DefId) -> Self {
|
pub(crate) fn new(def_id: DefId) -> Self {
|
||||||
Struct { def_id }
|
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)]
|
#[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 {
|
impl VariantData {
|
||||||
fn new(flavor: StructFlavor) -> Self {
|
fn new(flavor: StructFlavor) -> Self {
|
||||||
match flavor {
|
match flavor {
|
||||||
|
@ -114,7 +163,7 @@ impl VariantData {
|
||||||
pub(crate) fn get_field_type_ref(&self, field_name: &Name) -> Option<&TypeRef> {
|
pub(crate) fn get_field_type_ref(&self, field_name: &Name) -> Option<&TypeRef> {
|
||||||
self.fields()
|
self.fields()
|
||||||
.iter()
|
.iter()
|
||||||
.find(|f| f.name() == field_name)
|
.find(|f| f.name == *field_name)
|
||||||
.map(|f| f.type_ref())
|
.map(|f| &f.type_ref)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,13 @@ use ra_db::{CrateId, Cancelable, FileId};
|
||||||
use ra_syntax::{ast, TreePtr, SyntaxNode};
|
use ra_syntax::{ast, TreePtr, SyntaxNode};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Name, DefId, Path, PerNs, ScopesWithSyntaxMapping,
|
Name, DefId, Path, PerNs, ScopesWithSyntaxMapping, Ty,
|
||||||
type_ref::TypeRef,
|
type_ref::TypeRef,
|
||||||
nameres::ModuleScope,
|
nameres::ModuleScope,
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
expr::BodySyntaxMapping,
|
expr::BodySyntaxMapping,
|
||||||
ty::InferenceResult,
|
ty::InferenceResult,
|
||||||
|
adt::VariantData,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// hir::Crate describes a single crate. It's the main interface with which
|
/// 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, Hash)]
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub struct StructField {
|
pub struct StructField {
|
||||||
pub(crate) name: Name,
|
struct_: Struct,
|
||||||
pub(crate) type_ref: TypeRef,
|
name: Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructField {
|
impl StructField {
|
||||||
pub fn name(&self) -> &Name {
|
pub fn name(&self) -> &Name {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
pub fn ty(&self, db: &impl HirDatabase) -> Cancelable<Option<Ty>> {
|
||||||
pub fn type_ref(&self) -> &TypeRef {
|
db.type_for_field(self.struct_.def_id, self.name.clone())
|
||||||
&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,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,8 +167,18 @@ impl Struct {
|
||||||
Ok(db.struct_data(self.def_id)?.name.clone())
|
Ok(db.struct_data(self.def_id)?.name.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn variant_data(&self, db: &impl HirDatabase) -> Cancelable<Arc<VariantData>> {
|
pub fn fields(&self, db: &impl HirDatabase) -> Cancelable<Vec<StructField>> {
|
||||||
Ok(db.struct_data(self.def_id)?.variant_data.clone())
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,6 @@ pub use self::code_model_api::{
|
||||||
Crate, CrateDependency,
|
Crate, CrateDependency,
|
||||||
Def,
|
Def,
|
||||||
Module, ModuleSource, Problem,
|
Module, ModuleSource, Problem,
|
||||||
Struct, Enum, VariantData, StructField,
|
Struct, Enum,
|
||||||
Function, FnSignature,
|
Function, FnSignature,
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,13 +28,13 @@ fn complete_fields(acc: &mut Completions, ctx: &CompletionContext, receiver: Ty)
|
||||||
Ty::Adt { def_id, .. } => {
|
Ty::Adt { def_id, .. } => {
|
||||||
match def_id.resolve(ctx.db)? {
|
match def_id.resolve(ctx.db)? {
|
||||||
Def::Struct(s) => {
|
Def::Struct(s) => {
|
||||||
let variant_data = s.variant_data(ctx.db)?;
|
for field in s.fields(ctx.db)? {
|
||||||
for field in variant_data.fields() {
|
|
||||||
CompletionItem::new(
|
CompletionItem::new(
|
||||||
CompletionKind::Reference,
|
CompletionKind::Reference,
|
||||||
field.name().to_string(),
|
field.name().to_string(),
|
||||||
)
|
)
|
||||||
.kind(CompletionItemKind::Field)
|
.kind(CompletionItemKind::Field)
|
||||||
|
.set_detail(field.ty(ctx.db)?.map(|ty| ty.to_string()))
|
||||||
.add_to(acc);
|
.add_to(acc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ mod tests {
|
||||||
a.<|>
|
a.<|>
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
r#"the_field"#,
|
r#"the_field "u32""#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,14 +80,14 @@ mod tests {
|
||||||
fn test_struct_field_completion_self() {
|
fn test_struct_field_completion_self() {
|
||||||
check_ref_completion(
|
check_ref_completion(
|
||||||
r"
|
r"
|
||||||
struct A { the_field: u32 }
|
struct A { the_field: (u32,) }
|
||||||
impl A {
|
impl A {
|
||||||
fn foo(self) {
|
fn foo(self) {
|
||||||
self.<|>
|
self.<|>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
r#"the_field"#,
|
r#"the_field "(u32,)""#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,14 +95,14 @@ mod tests {
|
||||||
fn test_struct_field_completion_autoderef() {
|
fn test_struct_field_completion_autoderef() {
|
||||||
check_ref_completion(
|
check_ref_completion(
|
||||||
r"
|
r"
|
||||||
struct A { the_field: u32 }
|
struct A { the_field: (u32, i32) }
|
||||||
impl A {
|
impl A {
|
||||||
fn foo(&self) {
|
fn foo(&self) {
|
||||||
self.<|>
|
self.<|>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
",
|
",
|
||||||
r#"the_field"#,
|
r#"the_field "(u32, i32)""#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ pub struct CompletionItem {
|
||||||
/// completion.
|
/// completion.
|
||||||
completion_kind: CompletionKind,
|
completion_kind: CompletionKind,
|
||||||
label: String,
|
label: String,
|
||||||
|
detail: Option<String>,
|
||||||
lookup: Option<String>,
|
lookup: Option<String>,
|
||||||
snippet: Option<String>,
|
snippet: Option<String>,
|
||||||
kind: Option<CompletionItemKind>,
|
kind: Option<CompletionItemKind>,
|
||||||
|
@ -51,6 +52,7 @@ impl CompletionItem {
|
||||||
Builder {
|
Builder {
|
||||||
completion_kind,
|
completion_kind,
|
||||||
label,
|
label,
|
||||||
|
detail: None,
|
||||||
lookup: None,
|
lookup: None,
|
||||||
snippet: None,
|
snippet: None,
|
||||||
kind: None,
|
kind: None,
|
||||||
|
@ -60,6 +62,10 @@ impl CompletionItem {
|
||||||
pub fn label(&self) -> &str {
|
pub fn label(&self) -> &str {
|
||||||
&self.label
|
&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.
|
/// What string is used for filtering.
|
||||||
pub fn lookup(&self) -> &str {
|
pub fn lookup(&self) -> &str {
|
||||||
self.lookup
|
self.lookup
|
||||||
|
@ -87,6 +93,7 @@ impl CompletionItem {
|
||||||
pub(crate) struct Builder {
|
pub(crate) struct Builder {
|
||||||
completion_kind: CompletionKind,
|
completion_kind: CompletionKind,
|
||||||
label: String,
|
label: String,
|
||||||
|
detail: Option<String>,
|
||||||
lookup: Option<String>,
|
lookup: Option<String>,
|
||||||
snippet: Option<String>,
|
snippet: Option<String>,
|
||||||
kind: Option<CompletionItemKind>,
|
kind: Option<CompletionItemKind>,
|
||||||
|
@ -100,6 +107,7 @@ impl Builder {
|
||||||
pub(crate) fn build(self) -> CompletionItem {
|
pub(crate) fn build(self) -> CompletionItem {
|
||||||
CompletionItem {
|
CompletionItem {
|
||||||
label: self.label,
|
label: self.label,
|
||||||
|
detail: self.detail,
|
||||||
lookup: self.lookup,
|
lookup: self.lookup,
|
||||||
snippet: self.snippet,
|
snippet: self.snippet,
|
||||||
kind: self.kind,
|
kind: self.kind,
|
||||||
|
@ -118,6 +126,14 @@ impl Builder {
|
||||||
self.kind = Some(kind);
|
self.kind = Some(kind);
|
||||||
self
|
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(
|
pub(super) fn from_resolution(
|
||||||
mut self,
|
mut self,
|
||||||
ctx: &CompletionContext,
|
ctx: &CompletionContext,
|
||||||
|
@ -227,6 +243,9 @@ impl Completions {
|
||||||
} else {
|
} else {
|
||||||
res.push_str(&c.label);
|
res.push_str(&c.label);
|
||||||
}
|
}
|
||||||
|
if let Some(detail) = &c.detail {
|
||||||
|
res.push_str(&format!(" {:?}", detail));
|
||||||
|
}
|
||||||
if let Some(snippet) = &c.snippet {
|
if let Some(snippet) = &c.snippet {
|
||||||
res.push_str(&format!(" {:?}", snippet));
|
res.push_str(&format!(" {:?}", snippet));
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ impl Conv for CompletionItem {
|
||||||
fn conv(self) -> <Self as Conv>::Output {
|
fn conv(self) -> <Self as Conv>::Output {
|
||||||
let mut res = ::languageserver_types::CompletionItem {
|
let mut res = ::languageserver_types::CompletionItem {
|
||||||
label: self.label().to_string(),
|
label: self.label().to_string(),
|
||||||
|
detail: self.detail().map(|it| it.to_string()),
|
||||||
filter_text: Some(self.lookup().to_string()),
|
filter_text: Some(self.lookup().to_string()),
|
||||||
kind: self.kind().map(|it| it.conv()),
|
kind: self.kind().map(|it| it.conv()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue