mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 12:33:33 +00:00
Fix the completion labels and tests
This commit is contained in:
parent
33c83e72b9
commit
24a5d3b19d
4 changed files with 115 additions and 64 deletions
|
@ -144,7 +144,7 @@ pub(crate) fn import_on_the_fly(acc: &mut Completions, ctx: &CompletionContext)
|
||||||
.filter_map(|import| {
|
.filter_map(|import| {
|
||||||
render_resolution_with_import(
|
render_resolution_with_import(
|
||||||
RenderContext::new(ctx),
|
RenderContext::new(ctx),
|
||||||
ImportEdit { import, import_scope: import_scope.clone() },
|
ImportEdit { import, scope: import_scope.clone() },
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -690,8 +690,8 @@ fn main() {
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED
|
|
||||||
ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
|
ct SPECIAL_CONST (dep::test_mod::TestTrait) DEPRECATED
|
||||||
|
fn weird_function() (dep::test_mod::TestTrait) -> () DEPRECATED
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -807,7 +807,12 @@ fn main() {
|
||||||
bar::baz::Ite$0
|
bar::baz::Ite$0
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
check(fixture, expect![["st Item (foo::bar::baz::Item)"]]);
|
check(
|
||||||
|
fixture,
|
||||||
|
expect![[r#"
|
||||||
|
st foo::bar::baz::Item
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
|
||||||
check_edit(
|
check_edit(
|
||||||
"Item",
|
"Item",
|
||||||
|
@ -825,8 +830,7 @@ fn main() {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
bar::baz::Item
|
bar::baz::Item
|
||||||
}
|
}"#,
|
||||||
"#,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -845,7 +849,12 @@ fn main() {
|
||||||
Item::TEST_A$0
|
Item::TEST_A$0
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
check(fixture, expect![["ct TEST_ASSOC (foo::bar::baz::Item)"]]);
|
check(
|
||||||
|
fixture,
|
||||||
|
expect![[r#"
|
||||||
|
ct TEST_ASSOC (foo::Item)
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
|
||||||
check_edit(
|
check_edit(
|
||||||
"TEST_ASSOC",
|
"TEST_ASSOC",
|
||||||
|
@ -863,8 +872,7 @@ mod foo {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
Item::TEST_ASSOC
|
Item::TEST_ASSOC
|
||||||
}
|
}"#,
|
||||||
"#,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -885,7 +893,12 @@ fn main() {
|
||||||
bar::Item::TEST_A$0
|
bar::Item::TEST_A$0
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
check(fixture, expect![["ct TEST_ASSOC (foo::bar::baz::Item)"]]);
|
check(
|
||||||
|
fixture,
|
||||||
|
expect![[r#"
|
||||||
|
ct TEST_ASSOC (foo::bar::Item)
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
|
||||||
check_edit(
|
check_edit(
|
||||||
"TEST_ASSOC",
|
"TEST_ASSOC",
|
||||||
|
@ -905,8 +918,7 @@ mod foo {
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
bar::Item::TEST_ASSOC
|
bar::Item::TEST_ASSOC
|
||||||
}
|
}"#,
|
||||||
"#,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ use ide_db::{
|
||||||
},
|
},
|
||||||
SymbolKind,
|
SymbolKind,
|
||||||
};
|
};
|
||||||
use stdx::{impl_from, never};
|
use stdx::{format_to, impl_from, never};
|
||||||
use syntax::{algo, TextRange};
|
use syntax::{algo, TextRange};
|
||||||
use text_edit::TextEdit;
|
use text_edit::TextEdit;
|
||||||
|
|
||||||
|
@ -274,7 +274,7 @@ impl CompletionItem {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ImportEdit {
|
pub struct ImportEdit {
|
||||||
pub import: LocatedImport,
|
pub import: LocatedImport,
|
||||||
pub import_scope: ImportScope,
|
pub scope: ImportScope,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ImportEdit {
|
impl ImportEdit {
|
||||||
|
@ -284,7 +284,7 @@ impl ImportEdit {
|
||||||
let _p = profile::span("ImportEdit::to_text_edit");
|
let _p = profile::span("ImportEdit::to_text_edit");
|
||||||
|
|
||||||
let rewriter = insert_use::insert_use(
|
let rewriter = insert_use::insert_use(
|
||||||
&self.import_scope,
|
&self.scope,
|
||||||
mod_path_to_ast(&self.import.import_path),
|
mod_path_to_ast(&self.import.import_path),
|
||||||
cfg,
|
cfg,
|
||||||
);
|
);
|
||||||
|
@ -302,7 +302,6 @@ impl ImportEdit {
|
||||||
pub(crate) struct Builder {
|
pub(crate) struct Builder {
|
||||||
source_range: TextRange,
|
source_range: TextRange,
|
||||||
completion_kind: CompletionKind,
|
completion_kind: CompletionKind,
|
||||||
// TODO kb also add a db here, to resolve the completion label?
|
|
||||||
import_to_add: Option<ImportEdit>,
|
import_to_add: Option<ImportEdit>,
|
||||||
label: String,
|
label: String,
|
||||||
insert_text: Option<String>,
|
insert_text: Option<String>,
|
||||||
|
@ -322,22 +321,24 @@ impl Builder {
|
||||||
pub(crate) fn build(self) -> CompletionItem {
|
pub(crate) fn build(self) -> CompletionItem {
|
||||||
let _p = profile::span("item::Builder::build");
|
let _p = profile::span("item::Builder::build");
|
||||||
|
|
||||||
let label = self.label;
|
let mut label = self.label;
|
||||||
let lookup = self.lookup;
|
let mut lookup = self.lookup;
|
||||||
let insert_text = self.insert_text;
|
let mut insert_text = self.insert_text;
|
||||||
|
|
||||||
if let Some(_import_to_add) = self.import_to_add.as_ref() {
|
if let Some(original_path) = self
|
||||||
todo!("todo kb")
|
.import_to_add
|
||||||
// let import = &import_to_add.import;
|
.as_ref()
|
||||||
// let item_to_import = import.item_to_import();
|
.and_then(|import_edit| import_edit.import.original_path.as_ref())
|
||||||
// lookup = lookup.or_else(|| Some(label.clone()));
|
{
|
||||||
// insert_text = insert_text.or_else(|| Some(label.clone()));
|
lookup = lookup.or_else(|| Some(label.clone()));
|
||||||
// let display_path = import_to_add.import.display_path();
|
insert_text = insert_text.or_else(|| Some(label.clone()));
|
||||||
// if import_to_add.import {
|
|
||||||
// label = format!("{} ({})", label, display_path);
|
let original_path_label = original_path.to_string();
|
||||||
// } else {
|
if original_path_label.ends_with(&label) {
|
||||||
// label = display_path.to_string();
|
label = original_path_label;
|
||||||
// }
|
} else {
|
||||||
|
format_to!(label, " ({})", original_path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let text_edit = match self.text_edit {
|
let text_edit = match self.text_edit {
|
||||||
|
|
|
@ -144,7 +144,7 @@ pub fn resolve_completion_edits(
|
||||||
) -> Option<Vec<TextEdit>> {
|
) -> Option<Vec<TextEdit>> {
|
||||||
let ctx = CompletionContext::new(db, position, config)?;
|
let ctx = CompletionContext::new(db, position, config)?;
|
||||||
let position_for_import = position_for_import(&ctx, None)?;
|
let position_for_import = position_for_import(&ctx, None)?;
|
||||||
let import_scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
|
let scope = ImportScope::find_insert_use_container(position_for_import, &ctx.sema)?;
|
||||||
|
|
||||||
let current_module = ctx.sema.scope(position_for_import).module()?;
|
let current_module = ctx.sema.scope(position_for_import).module()?;
|
||||||
let current_crate = current_module.krate();
|
let current_crate = current_module.krate();
|
||||||
|
@ -158,9 +158,10 @@ pub fn resolve_completion_edits(
|
||||||
.zip(Some(candidate))
|
.zip(Some(candidate))
|
||||||
})
|
})
|
||||||
.find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
|
.find(|(mod_path, _)| mod_path.to_string() == full_import_path)?;
|
||||||
let import = LocatedImport::new(import_path, item_to_import, item_to_import);
|
let import =
|
||||||
|
LocatedImport::new(import_path.clone(), item_to_import, item_to_import, Some(import_path));
|
||||||
|
|
||||||
ImportEdit { import, import_scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
|
ImportEdit { import, scope }.to_text_edit(config.insert_use).map(|edit| vec![edit])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -132,11 +132,17 @@ pub struct LocatedImport {
|
||||||
pub import_path: ModPath,
|
pub import_path: ModPath,
|
||||||
pub item_to_import: ItemInNs,
|
pub item_to_import: ItemInNs,
|
||||||
pub original_item: ItemInNs,
|
pub original_item: ItemInNs,
|
||||||
|
pub original_path: Option<ModPath>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LocatedImport {
|
impl LocatedImport {
|
||||||
pub fn new(import_path: ModPath, item_to_import: ItemInNs, original_item: ItemInNs) -> Self {
|
pub fn new(
|
||||||
Self { import_path, item_to_import, original_item }
|
import_path: ModPath,
|
||||||
|
item_to_import: ItemInNs,
|
||||||
|
original_item: ItemInNs,
|
||||||
|
original_path: Option<ModPath>,
|
||||||
|
) -> Self {
|
||||||
|
Self { import_path, item_to_import, original_item, original_path }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn original_item_name(&self, db: &RootDatabase) -> Option<Name> {
|
pub fn original_item_name(&self, db: &RootDatabase) -> Option<Name> {
|
||||||
|
@ -238,7 +244,9 @@ impl<'a> ImportAssets<'a> {
|
||||||
let _p = profile::span("import_assets::applicable_defs");
|
let _p = profile::span("import_assets::applicable_defs");
|
||||||
let current_crate = self.module_with_candidate.krate();
|
let current_crate = self.module_with_candidate.krate();
|
||||||
|
|
||||||
let mod_path = |item| get_mod_path(db, item, &self.module_with_candidate, prefixed);
|
let mod_path = |item| {
|
||||||
|
get_mod_path(db, item_for_path_search(db, item)?, &self.module_with_candidate, prefixed)
|
||||||
|
};
|
||||||
|
|
||||||
match &self.import_candidate {
|
match &self.import_candidate {
|
||||||
ImportCandidate::Path(path_candidate) => {
|
ImportCandidate::Path(path_candidate) => {
|
||||||
|
@ -276,7 +284,9 @@ fn path_applicable_imports(
|
||||||
Qualifier::Absent => {
|
Qualifier::Absent => {
|
||||||
return items_with_candidate_name
|
return items_with_candidate_name
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|item| Some(LocatedImport::new(mod_path(item)?, item, item)))
|
.filter_map(|item| {
|
||||||
|
Some(LocatedImport::new(mod_path(item)?, item, item, mod_path(item)))
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
}
|
}
|
||||||
Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => {
|
Qualifier::FirstSegmentUnresolved(first_segment, qualifier) => {
|
||||||
|
@ -300,46 +310,69 @@ fn import_for_item(
|
||||||
original_item: ItemInNs,
|
original_item: ItemInNs,
|
||||||
) -> Option<LocatedImport> {
|
) -> Option<LocatedImport> {
|
||||||
let _p = profile::span("import_assets::import_for_item");
|
let _p = profile::span("import_assets::import_for_item");
|
||||||
let (item_candidate, trait_to_import) = match original_item.as_module_def_id() {
|
|
||||||
Some(module_def_id) => {
|
|
||||||
match ModuleDef::from(module_def_id).as_assoc_item(db).map(|assoc| assoc.container(db))
|
|
||||||
{
|
|
||||||
Some(AssocItemContainer::Trait(trait_)) => {
|
|
||||||
let trait_item = ItemInNs::from(ModuleDef::from(trait_));
|
|
||||||
(trait_item, Some(trait_item))
|
|
||||||
}
|
|
||||||
Some(AssocItemContainer::Impl(impl_)) => {
|
|
||||||
(ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?)), None)
|
|
||||||
}
|
|
||||||
None => (original_item, None),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => (original_item, None),
|
|
||||||
};
|
|
||||||
let import_path_candidate = mod_path(item_candidate)?;
|
|
||||||
|
|
||||||
|
let original_item_candidate = item_for_path_search(db, original_item)?;
|
||||||
|
let import_path_candidate = mod_path(original_item_candidate)?;
|
||||||
let import_path_string = import_path_candidate.to_string();
|
let import_path_string = import_path_candidate.to_string();
|
||||||
|
|
||||||
if !import_path_string.contains(unresolved_first_segment)
|
if !import_path_string.contains(unresolved_first_segment)
|
||||||
|| !import_path_string.contains(unresolved_qualifier)
|
|| !import_path_string.contains(unresolved_qualifier)
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let segment_import = find_import_for_segment(db, item_candidate, &unresolved_first_segment)?;
|
let segment_import =
|
||||||
Some(match (segment_import == item_candidate, trait_to_import) {
|
find_import_for_segment(db, original_item_candidate, &unresolved_first_segment)?;
|
||||||
|
let trait_item_to_import = original_item
|
||||||
|
.as_module_def_id()
|
||||||
|
.and_then(|module_def_id| {
|
||||||
|
ModuleDef::from(module_def_id).as_assoc_item(db)?.containing_trait(db)
|
||||||
|
})
|
||||||
|
.map(|trait_| ItemInNs::from(ModuleDef::from(trait_)));
|
||||||
|
Some(match (segment_import == original_item_candidate, trait_item_to_import) {
|
||||||
(true, Some(_)) => {
|
(true, Some(_)) => {
|
||||||
// FIXME we should be able to import both the trait and the segment,
|
// FIXME we should be able to import both the trait and the segment,
|
||||||
// but it's unclear what to do with overlapping edits (merge imports?)
|
// but it's unclear what to do with overlapping edits (merge imports?)
|
||||||
// especially in case of lazy completion edit resolutions.
|
// especially in case of lazy completion edit resolutions.
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
(false, Some(trait_to_import)) => {
|
(false, Some(trait_to_import)) => LocatedImport::new(
|
||||||
LocatedImport::new(mod_path(trait_to_import)?, trait_to_import, original_item)
|
mod_path(trait_to_import)?,
|
||||||
|
trait_to_import,
|
||||||
|
original_item,
|
||||||
|
mod_path(original_item),
|
||||||
|
),
|
||||||
|
(true, None) => LocatedImport::new(
|
||||||
|
import_path_candidate,
|
||||||
|
original_item_candidate,
|
||||||
|
original_item,
|
||||||
|
mod_path(original_item),
|
||||||
|
),
|
||||||
|
(false, None) => LocatedImport::new(
|
||||||
|
mod_path(segment_import)?,
|
||||||
|
segment_import,
|
||||||
|
original_item,
|
||||||
|
mod_path(original_item),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn item_for_path_search(db: &RootDatabase, item: ItemInNs) -> Option<ItemInNs> {
|
||||||
|
Some(match item {
|
||||||
|
ItemInNs::Types(module_def_id) | ItemInNs::Values(module_def_id) => {
|
||||||
|
let module_def = ModuleDef::from(module_def_id);
|
||||||
|
|
||||||
|
match module_def.as_assoc_item(db) {
|
||||||
|
Some(assoc_item) => match assoc_item.container(db) {
|
||||||
|
AssocItemContainer::Trait(trait_) => ItemInNs::from(ModuleDef::from(trait_)),
|
||||||
|
AssocItemContainer::Impl(impl_) => {
|
||||||
|
ItemInNs::from(ModuleDef::from(impl_.target_ty(db).as_adt()?))
|
||||||
}
|
}
|
||||||
(true, None) => LocatedImport::new(import_path_candidate, item_candidate, original_item),
|
},
|
||||||
(false, None) => {
|
None => item,
|
||||||
LocatedImport::new(mod_path(segment_import)?, segment_import, original_item)
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
ItemInNs::Macros(_) => item,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,10 +453,12 @@ fn trait_applicable_items(
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
|
let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
|
||||||
|
let original_item = assoc_to_item(assoc);
|
||||||
located_imports.insert(LocatedImport::new(
|
located_imports.insert(LocatedImport::new(
|
||||||
mod_path(item)?,
|
mod_path(item)?,
|
||||||
item,
|
item,
|
||||||
assoc_to_item(assoc),
|
original_item,
|
||||||
|
mod_path(original_item),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
None::<()>
|
None::<()>
|
||||||
|
@ -439,10 +474,12 @@ fn trait_applicable_items(
|
||||||
let assoc = function.as_assoc_item(db)?;
|
let assoc = function.as_assoc_item(db)?;
|
||||||
if required_assoc_items.contains(&assoc) {
|
if required_assoc_items.contains(&assoc) {
|
||||||
let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
|
let item = ItemInNs::from(ModuleDef::from(assoc.containing_trait(db)?));
|
||||||
|
let original_item = assoc_to_item(assoc);
|
||||||
located_imports.insert(LocatedImport::new(
|
located_imports.insert(LocatedImport::new(
|
||||||
mod_path(item)?,
|
mod_path(item)?,
|
||||||
item,
|
item,
|
||||||
assoc_to_item(assoc),
|
original_item,
|
||||||
|
mod_path(original_item),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
None::<()>
|
None::<()>
|
||||||
|
|
Loading…
Reference in a new issue