mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-14 02:49:40 +00:00
Auto merge of #14513 - hecatia-elegua:alias-based-completion2, r=Veykril
doc(alias)-based completion round 2 Follow-up on #14433 We can now complete fields, functions and some use/mods. Flyimports don't behave, I don't really have the time to understand the structure there either. While reading the flyimport code, I removed one method only used there, the closure-tree was a bit confusing, I can revert that if you want.
This commit is contained in:
commit
420a038da9
@ -78,14 +78,6 @@ impl Completions {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_all<I>(&mut self, items: I)
|
||||
where
|
||||
I: IntoIterator,
|
||||
I::Item: Into<CompletionItem>,
|
||||
{
|
||||
items.into_iter().for_each(|item| self.add(item.into()))
|
||||
}
|
||||
|
||||
pub(crate) fn add_keyword(&mut self, ctx: &CompletionContext<'_>, keyword: &'static str) {
|
||||
let item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), keyword);
|
||||
item.add_to(self);
|
||||
@ -472,8 +464,9 @@ impl Completions {
|
||||
Visible::Editable => true,
|
||||
Visible::No => return,
|
||||
};
|
||||
let doc_aliases = ctx.doc_aliases(&field);
|
||||
let item = render_field(
|
||||
RenderContext::new(ctx).private_editable(is_private_editable),
|
||||
RenderContext::new(ctx).private_editable(is_private_editable).doc_aliases(doc_aliases),
|
||||
dot_access,
|
||||
receiver,
|
||||
field,
|
||||
|
@ -88,7 +88,13 @@ pub(crate) fn complete_expr_path(
|
||||
let module_scope = module.scope(ctx.db, Some(ctx.module));
|
||||
for (name, def) in module_scope {
|
||||
if scope_def_applicable(def) {
|
||||
acc.add_path_resolution(ctx, path_ctx, name, def, vec![]);
|
||||
acc.add_path_resolution(
|
||||
ctx,
|
||||
path_ctx,
|
||||
name,
|
||||
def,
|
||||
ctx.doc_aliases_in_scope(def),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,32 +257,24 @@ fn import_on_the_fly(
|
||||
};
|
||||
let user_input_lowercased = potential_import_name.to_lowercase();
|
||||
|
||||
acc.add_all(
|
||||
import_assets
|
||||
.search_for_imports(
|
||||
&ctx.sema,
|
||||
ctx.config.insert_use.prefix_kind,
|
||||
ctx.config.prefer_no_std,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(ns_filter)
|
||||
.filter(|import| {
|
||||
let original_item = &import.original_item;
|
||||
!ctx.is_item_hidden(&import.item_to_import)
|
||||
&& !ctx.is_item_hidden(original_item)
|
||||
&& ctx.check_stability(original_item.attrs(ctx.db).as_deref())
|
||||
})
|
||||
.sorted_by_key(|located_import| {
|
||||
compute_fuzzy_completion_order_key(
|
||||
&located_import.import_path,
|
||||
&user_input_lowercased,
|
||||
)
|
||||
})
|
||||
.filter_map(|import| {
|
||||
render_resolution_with_import(RenderContext::new(ctx), path_ctx, import)
|
||||
})
|
||||
.map(|builder| builder.build()),
|
||||
);
|
||||
import_assets
|
||||
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std)
|
||||
.into_iter()
|
||||
.filter(ns_filter)
|
||||
.filter(|import| {
|
||||
let original_item = &import.original_item;
|
||||
!ctx.is_item_hidden(&import.item_to_import)
|
||||
&& !ctx.is_item_hidden(original_item)
|
||||
&& ctx.check_stability(original_item.attrs(ctx.db).as_deref())
|
||||
})
|
||||
.sorted_by_key(|located_import| {
|
||||
compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
|
||||
})
|
||||
.filter_map(|import| {
|
||||
render_resolution_with_import(RenderContext::new(ctx), path_ctx, import)
|
||||
})
|
||||
.map(|builder| builder.build())
|
||||
.for_each(|item| acc.add(item));
|
||||
Some(())
|
||||
}
|
||||
|
||||
@ -307,32 +299,24 @@ fn import_on_the_fly_pat_(
|
||||
};
|
||||
let user_input_lowercased = potential_import_name.to_lowercase();
|
||||
|
||||
acc.add_all(
|
||||
import_assets
|
||||
.search_for_imports(
|
||||
&ctx.sema,
|
||||
ctx.config.insert_use.prefix_kind,
|
||||
ctx.config.prefer_no_std,
|
||||
)
|
||||
.into_iter()
|
||||
.filter(ns_filter)
|
||||
.filter(|import| {
|
||||
let original_item = &import.original_item;
|
||||
!ctx.is_item_hidden(&import.item_to_import)
|
||||
&& !ctx.is_item_hidden(original_item)
|
||||
&& ctx.check_stability(original_item.attrs(ctx.db).as_deref())
|
||||
})
|
||||
.sorted_by_key(|located_import| {
|
||||
compute_fuzzy_completion_order_key(
|
||||
&located_import.import_path,
|
||||
&user_input_lowercased,
|
||||
)
|
||||
})
|
||||
.filter_map(|import| {
|
||||
render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import)
|
||||
})
|
||||
.map(|builder| builder.build()),
|
||||
);
|
||||
import_assets
|
||||
.search_for_imports(&ctx.sema, ctx.config.insert_use.prefix_kind, ctx.config.prefer_no_std)
|
||||
.into_iter()
|
||||
.filter(ns_filter)
|
||||
.filter(|import| {
|
||||
let original_item = &import.original_item;
|
||||
!ctx.is_item_hidden(&import.item_to_import)
|
||||
&& !ctx.is_item_hidden(original_item)
|
||||
&& ctx.check_stability(original_item.attrs(ctx.db).as_deref())
|
||||
})
|
||||
.sorted_by_key(|located_import| {
|
||||
compute_fuzzy_completion_order_key(&located_import.import_path, &user_input_lowercased)
|
||||
})
|
||||
.filter_map(|import| {
|
||||
render_resolution_with_import_pat(RenderContext::new(ctx), pattern_ctx, import)
|
||||
})
|
||||
.map(|builder| builder.build())
|
||||
.for_each(|item| acc.add(item));
|
||||
Some(())
|
||||
}
|
||||
|
||||
|
@ -443,6 +443,14 @@ impl CompletionContext<'_> {
|
||||
self.is_visible_impl(&vis, &attrs, item.krate(self.db))
|
||||
}
|
||||
|
||||
pub(crate) fn doc_aliases<I>(&self, item: &I) -> Vec<SmolStr>
|
||||
where
|
||||
I: hir::HasAttrs + Copy,
|
||||
{
|
||||
let attrs = item.attrs(self.db);
|
||||
attrs.doc_aliases().collect()
|
||||
}
|
||||
|
||||
/// Check if an item is `#[doc(hidden)]`.
|
||||
pub(crate) fn is_item_hidden(&self, item: &hir::ItemInNs) -> bool {
|
||||
let attrs = item.attrs(self.db);
|
||||
@ -507,7 +515,7 @@ impl CompletionContext<'_> {
|
||||
if self.is_scope_def_hidden(def) {
|
||||
return;
|
||||
}
|
||||
let doc_aliases = self.doc_aliases(def);
|
||||
let doc_aliases = self.doc_aliases_in_scope(def);
|
||||
f(name, def, doc_aliases);
|
||||
});
|
||||
}
|
||||
@ -555,7 +563,7 @@ impl CompletionContext<'_> {
|
||||
self.krate != defining_crate && attrs.has_doc_hidden()
|
||||
}
|
||||
|
||||
fn doc_aliases(&self, scope_def: ScopeDef) -> Vec<SmolStr> {
|
||||
pub(crate) fn doc_aliases_in_scope(&self, scope_def: ScopeDef) -> Vec<SmolStr> {
|
||||
if let Some(attrs) = scope_def.attrs(self.db) {
|
||||
attrs.doc_aliases().collect()
|
||||
} else {
|
||||
|
@ -4,6 +4,7 @@ use std::fmt;
|
||||
|
||||
use hir::{Documentation, Mutability};
|
||||
use ide_db::{imports::import_assets::LocatedImport, SnippetCap, SymbolKind};
|
||||
use itertools::Itertools;
|
||||
use smallvec::SmallVec;
|
||||
use stdx::{impl_from, never};
|
||||
use syntax::{SmolStr, TextRange, TextSize};
|
||||
@ -353,7 +354,7 @@ impl CompletionItem {
|
||||
relevance: CompletionRelevance::default(),
|
||||
ref_match: None,
|
||||
imports_to_add: Default::default(),
|
||||
doc_aliases: None,
|
||||
doc_aliases: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -386,7 +387,7 @@ pub(crate) struct Builder {
|
||||
source_range: TextRange,
|
||||
imports_to_add: SmallVec<[LocatedImport; 1]>,
|
||||
trait_name: Option<SmolStr>,
|
||||
doc_aliases: Option<SmolStr>,
|
||||
doc_aliases: Vec<SmolStr>,
|
||||
label: SmolStr,
|
||||
insert_text: Option<String>,
|
||||
is_snippet: bool,
|
||||
@ -408,7 +409,13 @@ impl Builder {
|
||||
local_name: hir::Name,
|
||||
resolution: hir::ScopeDef,
|
||||
) -> Self {
|
||||
render_path_resolution(RenderContext::new(ctx), path_ctx, local_name, resolution)
|
||||
let doc_aliases = ctx.doc_aliases_in_scope(resolution);
|
||||
render_path_resolution(
|
||||
RenderContext::new(ctx).doc_aliases(doc_aliases),
|
||||
path_ctx,
|
||||
local_name,
|
||||
resolution,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn build(self) -> CompletionItem {
|
||||
@ -418,7 +425,8 @@ impl Builder {
|
||||
let mut lookup = self.lookup.unwrap_or_else(|| label.clone());
|
||||
let insert_text = self.insert_text.unwrap_or_else(|| label.to_string());
|
||||
|
||||
if let Some(doc_aliases) = self.doc_aliases {
|
||||
if !self.doc_aliases.is_empty() {
|
||||
let doc_aliases = self.doc_aliases.into_iter().join(", ");
|
||||
label = SmolStr::from(format!("{label} (alias {doc_aliases})"));
|
||||
lookup = SmolStr::from(format!("{lookup} {doc_aliases}"));
|
||||
}
|
||||
@ -464,8 +472,8 @@ impl Builder {
|
||||
self.trait_name = Some(trait_name);
|
||||
self
|
||||
}
|
||||
pub(crate) fn doc_aliases(&mut self, doc_aliases: SmolStr) -> &mut Builder {
|
||||
self.doc_aliases = Some(doc_aliases);
|
||||
pub(crate) fn doc_aliases(&mut self, doc_aliases: Vec<SmolStr>) -> &mut Builder {
|
||||
self.doc_aliases = doc_aliases;
|
||||
self
|
||||
}
|
||||
pub(crate) fn insert_text(&mut self, insert_text: impl Into<String>) -> &mut Builder {
|
||||
|
@ -14,7 +14,6 @@ use hir::{AsAssocItem, HasAttrs, HirDisplay, ScopeDef};
|
||||
use ide_db::{
|
||||
helpers::item_name, imports::import_assets::LocatedImport, RootDatabase, SnippetCap, SymbolKind,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use syntax::{AstNode, SmolStr, SyntaxKind, TextRange};
|
||||
|
||||
use crate::{
|
||||
@ -152,6 +151,7 @@ pub(crate) fn render_field(
|
||||
}
|
||||
}
|
||||
}
|
||||
item.doc_aliases(ctx.doc_aliases);
|
||||
item.build()
|
||||
}
|
||||
|
||||
@ -209,7 +209,9 @@ pub(crate) fn render_resolution_with_import(
|
||||
) -> Option<Builder> {
|
||||
let resolution = ScopeDef::from(import_edit.original_item);
|
||||
let local_name = scope_def_to_name(resolution, &ctx, &import_edit)?;
|
||||
|
||||
//this now just renders the alias text, but we need to find the aliases earlier and call this with the alias instead
|
||||
let doc_aliases = ctx.completion.doc_aliases_in_scope(resolution);
|
||||
let ctx = ctx.doc_aliases(doc_aliases);
|
||||
Some(render_resolution_path(ctx, path_ctx, local_name, Some(import_edit), resolution))
|
||||
}
|
||||
|
||||
@ -361,11 +363,7 @@ fn render_resolution_simple_(
|
||||
item.add_import(import_to_add);
|
||||
}
|
||||
|
||||
let doc_aliases = ctx.doc_aliases;
|
||||
if !doc_aliases.is_empty() {
|
||||
let doc_aliases = doc_aliases.into_iter().join(", ").into();
|
||||
item.doc_aliases(doc_aliases);
|
||||
}
|
||||
item.doc_aliases(ctx.doc_aliases);
|
||||
item
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,8 @@ fn render(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
item.doc_aliases(ctx.doc_aliases);
|
||||
item
|
||||
}
|
||||
|
||||
|
@ -1086,3 +1086,141 @@ fn here_we_go() {
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_field_name_via_doc_alias_in_fn_body() {
|
||||
check(
|
||||
r#"
|
||||
struct Foo {
|
||||
#[doc(alias = "qux")]
|
||||
bar: u8
|
||||
};
|
||||
|
||||
fn here_we_go() {
|
||||
let foo = Foo { q$0 }
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fd bar (alias qux) u8
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_fn_name_via_doc_alias_in_fn_body() {
|
||||
check(
|
||||
r#"
|
||||
#[doc(alias = "qux")]
|
||||
fn foo() {}
|
||||
fn bar() { qu$0 }
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn bar() fn()
|
||||
fn foo() (alias qux) fn()
|
||||
bt u32
|
||||
kw const
|
||||
kw crate::
|
||||
kw enum
|
||||
kw extern
|
||||
kw false
|
||||
kw fn
|
||||
kw for
|
||||
kw if
|
||||
kw if let
|
||||
kw impl
|
||||
kw let
|
||||
kw loop
|
||||
kw match
|
||||
kw mod
|
||||
kw return
|
||||
kw self::
|
||||
kw static
|
||||
kw struct
|
||||
kw trait
|
||||
kw true
|
||||
kw type
|
||||
kw union
|
||||
kw unsafe
|
||||
kw use
|
||||
kw while
|
||||
kw while let
|
||||
sn macro_rules
|
||||
sn pd
|
||||
sn ppd
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_struct_name_via_doc_alias_in_another_mod() {
|
||||
check(
|
||||
r#"
|
||||
mod foo {
|
||||
#[doc(alias = "Qux")]
|
||||
pub struct Bar(u8);
|
||||
}
|
||||
|
||||
fn here_we_go() {
|
||||
use foo;
|
||||
let foo = foo::Q$0
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
st Bar (alias Qux)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_use_via_doc_alias_in_another_mod() {
|
||||
check(
|
||||
r#"
|
||||
mod foo {
|
||||
#[doc(alias = "Qux")]
|
||||
pub struct Bar(u8);
|
||||
}
|
||||
|
||||
fn here_we_go() {
|
||||
use foo::Q$0;
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
st Bar (alias Qux)
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn completes_flyimport_with_doc_alias_in_another_mod() {
|
||||
check(
|
||||
r#"
|
||||
mod foo {
|
||||
#[doc(alias = "Qux")]
|
||||
pub struct Bar();
|
||||
}
|
||||
|
||||
fn here_we_go() {
|
||||
let foo = Bar$0
|
||||
}
|
||||
"#,
|
||||
expect![[r#"
|
||||
fn here_we_go() fn()
|
||||
md foo
|
||||
st Bar (alias Qux) (use foo::Bar)
|
||||
bt u32
|
||||
kw crate::
|
||||
kw false
|
||||
kw for
|
||||
kw if
|
||||
kw if let
|
||||
kw loop
|
||||
kw match
|
||||
kw return
|
||||
kw self::
|
||||
kw true
|
||||
kw unsafe
|
||||
kw while
|
||||
kw while let
|
||||
"#]],
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user