summaryrefslogtreecommitdiffstats
path: root/src/tools/rust-analyzer/crates/ide-db
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
commitd1b2d29528b7794b41e66fc2136e395a02f8529b (patch)
treea4a17504b260206dec3cf55b2dca82929a348ac2 /src/tools/rust-analyzer/crates/ide-db
parentReleasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz
rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/rust-analyzer/crates/ide-db')
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/defs.rs53
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/helpers.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs60
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/rename.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/source_change.rs233
13 files changed, 265 insertions, 173 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
index 4e75dc4db..faec74206 100644
--- a/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/ide-db/Cargo.toml
@@ -21,7 +21,7 @@ once_cell = "1.17.0"
either = "1.7.0"
itertools = "0.10.5"
arrayvec = "0.7.2"
-indexmap = "1.9.1"
+indexmap = "2.0.0"
memchr = "2.5.0"
triomphe.workspace = true
nohash-hasher.workspace = true
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
index 0dd544d0a..a0b05c87a 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/apply_change.rs
@@ -99,8 +99,8 @@ impl RootDatabase {
hir::db::AstIdMapQuery
hir::db::ParseMacroExpansionQuery
hir::db::InternMacroCallQuery
- hir::db::MacroArgTextQuery
- hir::db::MacroDefQuery
+ hir::db::MacroArgNodeQuery
+ hir::db::DeclMacroExpanderQuery
hir::db::MacroExpandQuery
hir::db::ExpandProcMacroQuery
hir::db::HygieneFrameQuery
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
index 760834bfa..5e4562d9c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs
@@ -7,10 +7,10 @@
use arrayvec::ArrayVec;
use hir::{
- Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field,
- Function, GenericParam, HasVisibility, Impl, Label, Local, Macro, Module, ModuleDef, Name,
- PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias, TypeAlias, Variant,
- Visibility,
+ Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper,
+ ExternCrateDecl, Field, Function, GenericParam, HasVisibility, Impl, Label, Local, Macro,
+ Module, ModuleDef, Name, PathResolution, Semantics, Static, ToolModule, Trait, TraitAlias,
+ TypeAlias, Variant, Visibility,
};
use stdx::impl_from;
use syntax::{
@@ -42,6 +42,7 @@ pub enum Definition {
DeriveHelper(DeriveHelper),
BuiltinAttr(BuiltinAttr),
ToolModule(ToolModule),
+ ExternCrateDecl(ExternCrateDecl),
}
impl Definition {
@@ -73,6 +74,7 @@ impl Definition {
Definition::Local(it) => it.module(db),
Definition::GenericParam(it) => it.module(db),
Definition::Label(it) => it.module(db),
+ Definition::ExternCrateDecl(it) => it.module(db),
Definition::DeriveHelper(it) => it.derive().module(db),
Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => {
return None
@@ -93,6 +95,7 @@ impl Definition {
Definition::TraitAlias(it) => it.visibility(db),
Definition::TypeAlias(it) => it.visibility(db),
Definition::Variant(it) => it.visibility(db),
+ Definition::ExternCrateDecl(it) => it.visibility(db),
Definition::BuiltinType(_) => Visibility::Public,
Definition::Macro(_) => return None,
Definition::BuiltinAttr(_)
@@ -127,6 +130,7 @@ impl Definition {
Definition::BuiltinAttr(_) => return None, // FIXME
Definition::ToolModule(_) => return None, // FIXME
Definition::DeriveHelper(it) => it.name(db),
+ Definition::ExternCrateDecl(it) => return it.alias_or_name(db),
};
Some(name)
}
@@ -196,6 +200,10 @@ impl IdentClass {
res.push(Definition::Local(local_ref));
res.push(Definition::Field(field_ref));
}
+ IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
+ res.push(Definition::ExternCrateDecl(decl));
+ res.push(Definition::Module(krate.root_module()));
+ }
IdentClass::Operator(
OperatorClass::Await(func)
| OperatorClass::Prefix(func)
@@ -222,6 +230,10 @@ impl IdentClass {
res.push(Definition::Local(local_ref));
res.push(Definition::Field(field_ref));
}
+ IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
+ res.push(Definition::ExternCrateDecl(decl));
+ res.push(Definition::Module(krate.root_module()));
+ }
IdentClass::Operator(_) => (),
}
res
@@ -310,6 +322,7 @@ impl NameClass {
ast::Item::Enum(it) => Definition::Adt(hir::Adt::Enum(sema.to_def(&it)?)),
ast::Item::Struct(it) => Definition::Adt(hir::Adt::Struct(sema.to_def(&it)?)),
ast::Item::Union(it) => Definition::Adt(hir::Adt::Union(sema.to_def(&it)?)),
+ ast::Item::ExternCrate(it) => Definition::ExternCrateDecl(sema.to_def(&it)?),
_ => return None,
};
Some(definition)
@@ -346,10 +359,8 @@ impl NameClass {
let path = use_tree.path()?;
sema.resolve_path(&path).map(Definition::from)
} else {
- let extern_crate = rename.syntax().parent().and_then(ast::ExternCrate::cast)?;
- let krate = sema.resolve_extern_crate(&extern_crate)?;
- let root_module = krate.root_module(sema.db);
- Some(Definition::Module(root_module))
+ sema.to_def(&rename.syntax().parent().and_then(ast::ExternCrate::cast)?)
+ .map(Definition::ExternCrateDecl)
}
}
}
@@ -427,7 +438,19 @@ impl OperatorClass {
#[derive(Debug)]
pub enum NameRefClass {
Definition(Definition),
- FieldShorthand { local_ref: Local, field_ref: Field },
+ FieldShorthand {
+ local_ref: Local,
+ field_ref: Field,
+ },
+ /// The specific situation where we have an extern crate decl without a rename
+ /// Here we have both a declaration and a reference.
+ /// ```rs
+ /// extern crate foo;
+ /// ```
+ ExternCrateShorthand {
+ decl: ExternCrateDecl,
+ krate: Crate,
+ },
}
impl NameRefClass {
@@ -513,10 +536,14 @@ impl NameRefClass {
}
None
},
- ast::ExternCrate(extern_crate) => {
- let krate = sema.resolve_extern_crate(&extern_crate)?;
- let root_module = krate.root_module(sema.db);
- Some(NameRefClass::Definition(Definition::Module(root_module)))
+ ast::ExternCrate(extern_crate_ast) => {
+ let extern_crate = sema.to_def(&extern_crate_ast)?;
+ let krate = extern_crate.resolved_crate(sema.db)?;
+ Some(if extern_crate_ast.rename().is_some() {
+ NameRefClass::Definition(Definition::Module(krate.root_module()))
+ } else {
+ NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
+ })
},
_ => None
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
index c8341fed1..b63dde2c2 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
@@ -167,7 +167,7 @@ impl FamousDefs<'_, '_> {
lang_crate => lang_crate,
};
let std_crate = self.find_lang_crate(lang_crate)?;
- let mut module = std_crate.root_module(db);
+ let mut module = std_crate.root_module();
for segment in path {
module = module.children(db).find_map(|child| {
let name = child.name(db)?;
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
index e488300b4..49b37024a 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
@@ -931,22 +931,6 @@ $ cat $(find -name '*.s')
"##,
},
Lint {
- label: "abi_thiscall",
- description: r##"# `abi_thiscall`
-
-The tracking issue for this feature is: [#42202]
-
-[#42202]: https://github.com/rust-lang/rust/issues/42202
-
-------------------------
-
-The MSVC ABI on x86 Windows uses the `thiscall` calling convention for C++
-instance methods by default; it is identical to the usual (C) calling
-convention on x86 Windows except that the first parameter of the method,
-the `this` pointer, is passed in the ECX register.
-"##,
- },
- Lint {
label: "allocator_api",
description: r##"# `allocator_api`
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
index eba9d8afc..1eb8f0002 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/helpers.rs
@@ -9,7 +9,10 @@ use syntax::{
AstToken, SyntaxKind, SyntaxToken, TokenAtOffset,
};
-use crate::{defs::Definition, generated, RootDatabase};
+use crate::{
+ defs::{Definition, IdentClass},
+ generated, RootDatabase,
+};
pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> {
match item {
@@ -109,3 +112,16 @@ pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool {
let source_root_id = db.file_source_root(root_file);
!db.source_root(source_root_id).is_library
}
+
+pub fn get_definition(
+ sema: &Semantics<'_, RootDatabase>,
+ token: SyntaxToken,
+) -> Option<Definition> {
+ for token in sema.descend_into_macros(token) {
+ let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops);
+ if let Some(&[x]) = def.as_deref() {
+ return Some(x);
+ }
+ }
+ None
+}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
index 901d592c6..e52dc3567 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/import_assets.rs
@@ -13,7 +13,7 @@ use syntax::{
use crate::{
helpers::item_name,
- items_locator::{self, AssocItemSearch, DEFAULT_QUERY_SEARCH_LIMIT},
+ items_locator::{self, AssocSearchMode, DEFAULT_QUERY_SEARCH_LIMIT},
RootDatabase,
};
@@ -317,7 +317,7 @@ fn path_applicable_imports(
// * improve the associated completion item matching and/or scoring to ensure no noisy completions appear
//
// see also an ignored test under FIXME comment in the qualify_path.rs module
- AssocItemSearch::Exclude,
+ AssocSearchMode::Exclude,
Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()),
)
.filter_map(|item| {
@@ -334,7 +334,7 @@ fn path_applicable_imports(
sema,
current_crate,
path_candidate.name.clone(),
- AssocItemSearch::Include,
+ AssocSearchMode::Include,
Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()),
)
.filter_map(|item| {
@@ -483,7 +483,7 @@ fn trait_applicable_items(
sema,
current_crate,
trait_candidate.assoc_item_name.clone(),
- AssocItemSearch::AssocItemsOnly,
+ AssocSearchMode::AssocItemsOnly,
Some(DEFAULT_QUERY_SEARCH_LIMIT.inner()),
)
.filter_map(|input| item_as_assoc(db, input))
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
index 46f1353e2..3f7a3ec2d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
@@ -3,10 +3,7 @@
//! The main reason for this module to exist is the fact that project's items and dependencies' items
//! are located in different caches, with different APIs.
use either::Either;
-use hir::{
- import_map::{self, ImportKind},
- AsAssocItem, Crate, ItemInNs, Semantics,
-};
+use hir::{import_map, AsAssocItem, Crate, ItemInNs, Semantics};
use limit::Limit;
use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase};
@@ -14,23 +11,14 @@ use crate::{imports::import_assets::NameToImport, symbol_index, RootDatabase};
/// A value to use, when uncertain which limit to pick.
pub static DEFAULT_QUERY_SEARCH_LIMIT: Limit = Limit::new(40);
-/// Three possible ways to search for the name in associated and/or other items.
-#[derive(Debug, Clone, Copy)]
-pub enum AssocItemSearch {
- /// Search for the name in both associated and other items.
- Include,
- /// Search for the name in other items only.
- Exclude,
- /// Search for the name in the associated items only.
- AssocItemsOnly,
-}
+pub use import_map::AssocSearchMode;
/// Searches for importable items with the given name in the crate and its dependencies.
pub fn items_with_name<'a>(
sema: &'a Semantics<'_, RootDatabase>,
krate: Crate,
name: NameToImport,
- assoc_item_search: AssocItemSearch,
+ assoc_item_search: AssocSearchMode,
limit: Option<usize>,
) -> impl Iterator<Item = ItemInNs> + 'a {
let _p = profile::span("items_with_name").detail(|| {
@@ -48,9 +36,7 @@ pub fn items_with_name<'a>(
let mut local_query = symbol_index::Query::new(exact_name.clone());
local_query.exact();
- let external_query = import_map::Query::new(exact_name)
- .name_only()
- .search_mode(import_map::SearchMode::Equals);
+ let external_query = import_map::Query::new(exact_name);
(
local_query,
@@ -61,17 +47,8 @@ pub fn items_with_name<'a>(
let mut local_query = symbol_index::Query::new(fuzzy_search_string.clone());
let mut external_query = import_map::Query::new(fuzzy_search_string.clone())
- .search_mode(import_map::SearchMode::Fuzzy)
- .name_only();
- match assoc_item_search {
- AssocItemSearch::Include => {}
- AssocItemSearch::Exclude => {
- external_query = external_query.exclude_import_kind(ImportKind::AssociatedItem);
- }
- AssocItemSearch::AssocItemsOnly => {
- external_query = external_query.assoc_items_only();
- }
- }
+ .fuzzy()
+ .assoc_search_mode(assoc_item_search);
if fuzzy_search_string.to_lowercase() != fuzzy_search_string {
local_query.case_sensitive();
@@ -93,13 +70,15 @@ pub fn items_with_name<'a>(
fn find_items<'a>(
sema: &'a Semantics<'_, RootDatabase>,
krate: Crate,
- assoc_item_search: AssocItemSearch,
+ assoc_item_search: AssocSearchMode,
local_query: symbol_index::Query,
external_query: import_map::Query,
) -> impl Iterator<Item = ItemInNs> + 'a {
let _p = profile::span("find_items");
let db = sema.db;
+ // NOTE: `external_query` includes `assoc_item_search`, so we don't need to
+ // filter on our own.
let external_importables =
krate.query_external_importables(db, external_query).map(|external_importable| {
match external_importable {
@@ -112,18 +91,15 @@ fn find_items<'a>(
let local_results = local_query
.search(&symbol_index::crate_symbols(db, krate))
.into_iter()
- .filter_map(|local_candidate| match local_candidate.def {
- hir::ModuleDef::Macro(macro_def) => Some(ItemInNs::Macros(macro_def)),
- def => Some(ItemInNs::from(def)),
+ .filter(move |candidate| match assoc_item_search {
+ AssocSearchMode::Include => true,
+ AssocSearchMode::Exclude => candidate.def.as_assoc_item(db).is_none(),
+ AssocSearchMode::AssocItemsOnly => candidate.def.as_assoc_item(db).is_some(),
+ })
+ .map(|local_candidate| match local_candidate.def {
+ hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
+ def => ItemInNs::from(def),
});
- external_importables.chain(local_results).filter(move |&item| match assoc_item_search {
- AssocItemSearch::Include => true,
- AssocItemSearch::Exclude => !is_assoc_item(item, sema.db),
- AssocItemSearch::AssocItemsOnly => is_assoc_item(item, sema.db),
- })
-}
-
-fn is_assoc_item(item: ItemInNs, db: &RootDatabase) -> bool {
- item.as_module_def().and_then(|module_def| module_def.as_assoc_item(db)).is_some()
+ external_importables.chain(local_results)
}
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
index ff1a20f03..f27ed485d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs
@@ -200,8 +200,8 @@ impl RootDatabase {
hir_db::AstIdMapQuery
// hir_db::ParseMacroExpansionQuery
// hir_db::InternMacroCallQuery
- hir_db::MacroArgTextQuery
- hir_db::MacroDefQuery
+ hir_db::MacroArgNodeQuery
+ hir_db::DeclMacroExpanderQuery
// hir_db::MacroExpandQuery
hir_db::ExpandProcMacroQuery
hir_db::HygieneFrameQuery
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
index 73e6a920e..1d0cb426a 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/path_transform.rs
@@ -195,7 +195,7 @@ fn postorder(item: &SyntaxNode) -> impl Iterator<Item = SyntaxNode> {
})
}
-impl<'a> Ctx<'a> {
+impl Ctx<'_> {
fn apply(&self, item: &SyntaxNode) {
// `transform_path` may update a node's parent and that would break the
// tree traversal. Thus all paths in the tree are collected into a vec
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
index 52a23b4b8..aa0bb7cce 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs
@@ -82,8 +82,9 @@ impl Definition {
}
/// Textual range of the identifier which will change when renaming this
- /// `Definition`. Note that some definitions, like builtin types, can't be
- /// renamed.
+ /// `Definition`. Note that builtin types can't be
+ /// renamed and extern crate names will report its range, though a rename will introduce
+ /// an alias instead.
pub fn range_for_rename(self, sema: &Semantics<'_, RootDatabase>) -> Option<FileRange> {
let res = match self {
Definition::Macro(mac) => {
@@ -146,6 +147,16 @@ impl Definition {
let lifetime = src.value.lifetime()?;
src.with_value(lifetime.syntax()).original_file_range_opt(sema.db)
}
+ Definition::ExternCrateDecl(it) => {
+ let src = it.source(sema.db)?;
+ if let Some(rename) = src.value.rename() {
+ let name = rename.name()?;
+ src.with_value(name.syntax()).original_file_range_opt(sema.db)
+ } else {
+ let name = src.value.name_ref()?;
+ src.with_value(name.syntax()).original_file_range_opt(sema.db)
+ }
+ }
Definition::BuiltinType(_) => return None,
Definition::SelfType(_) => return None,
Definition::BuiltinAttr(_) => return None,
@@ -526,6 +537,9 @@ fn source_edit_from_def(
TextRange::new(range.start() + syntax::TextSize::from(1), range.end()),
new_name.strip_prefix('\'').unwrap_or(new_name).to_owned(),
),
+ Definition::ExternCrateDecl(decl) if decl.alias(sema.db).is_none() => {
+ (TextRange::empty(range.end()), format!(" as {new_name}"))
+ }
_ => (range, new_name.to_owned()),
};
edit.replace(range, new_name);
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index e8ff107bd..d5abd0991 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -127,7 +127,7 @@ impl SearchScope {
}
/// Build a search scope spanning the given module and all its submodules.
- fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope {
+ pub fn module_and_children(db: &RootDatabase, module: hir::Module) -> SearchScope {
let mut entries = IntMap::default();
let (file_id, range) = {
@@ -149,10 +149,8 @@ impl SearchScope {
let mut to_visit: Vec<_> = module.children(db).collect();
while let Some(module) = to_visit.pop() {
- if let InFile { file_id, value: ModuleSource::SourceFile(_) } =
- module.definition_source(db)
- {
- entries.insert(file_id.original_file(db), None);
+ if let Some(file_id) = module.as_source_file_id(db) {
+ entries.insert(file_id, None);
}
to_visit.extend(module.children(db));
}
@@ -331,7 +329,7 @@ impl Definition {
pub struct FindUsages<'a> {
def: Definition,
sema: &'a Semantics<'a, RootDatabase>,
- scope: Option<SearchScope>,
+ scope: Option<&'a SearchScope>,
/// The container of our definition should it be an assoc item
assoc_item_container: Option<hir::AssocItemContainer>,
/// whether to search for the `Self` type of the definition
@@ -342,19 +340,19 @@ pub struct FindUsages<'a> {
impl<'a> FindUsages<'a> {
/// Enable searching for `Self` when the definition is a type or `self` for modules.
- pub fn include_self_refs(mut self) -> FindUsages<'a> {
+ pub fn include_self_refs(mut self) -> Self {
self.include_self_kw_refs = def_to_ty(self.sema, &self.def);
self.search_self_mod = true;
self
}
/// Limit the search to a given [`SearchScope`].
- pub fn in_scope(self, scope: SearchScope) -> FindUsages<'a> {
+ pub fn in_scope(self, scope: &'a SearchScope) -> Self {
self.set_scope(Some(scope))
}
/// Limit the search to a given [`SearchScope`].
- pub fn set_scope(mut self, scope: Option<SearchScope>) -> FindUsages<'a> {
+ pub fn set_scope(mut self, scope: Option<&'a SearchScope>) -> Self {
assert!(self.scope.is_none());
self.scope = scope;
self
@@ -378,7 +376,7 @@ impl<'a> FindUsages<'a> {
res
}
- fn search(&self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) {
+ pub fn search(&self, sink: &mut dyn FnMut(FileId, FileReference) -> bool) {
let _p = profile::span("FindUsages:search");
let sema = self.sema;
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
index 061fb0f05..39763479c 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs
@@ -7,14 +7,17 @@ use std::{collections::hash_map::Entry, iter, mem};
use crate::SnippetCap;
use base_db::{AnchoredPathBuf, FileId};
+use itertools::Itertools;
use nohash_hasher::IntMap;
use stdx::never;
-use syntax::{algo, ast, ted, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize};
+use syntax::{
+ algo, AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize,
+};
use text_edit::{TextEdit, TextEditBuilder};
#[derive(Default, Debug, Clone)]
pub struct SourceChange {
- pub source_file_edits: IntMap<FileId, TextEdit>,
+ pub source_file_edits: IntMap<FileId, (TextEdit, Option<SnippetEdit>)>,
pub file_system_edits: Vec<FileSystemEdit>,
pub is_snippet: bool,
}
@@ -23,7 +26,7 @@ impl SourceChange {
/// Creates a new SourceChange with the given label
/// from the edits.
pub fn from_edits(
- source_file_edits: IntMap<FileId, TextEdit>,
+ source_file_edits: IntMap<FileId, (TextEdit, Option<SnippetEdit>)>,
file_system_edits: Vec<FileSystemEdit>,
) -> Self {
SourceChange { source_file_edits, file_system_edits, is_snippet: false }
@@ -31,7 +34,7 @@ impl SourceChange {
pub fn from_text_edit(file_id: FileId, edit: TextEdit) -> Self {
SourceChange {
- source_file_edits: iter::once((file_id, edit)).collect(),
+ source_file_edits: iter::once((file_id, (edit, None))).collect(),
..Default::default()
}
}
@@ -39,12 +42,31 @@ impl SourceChange {
/// Inserts a [`TextEdit`] for the given [`FileId`]. This properly handles merging existing
/// edits for a file if some already exist.
pub fn insert_source_edit(&mut self, file_id: FileId, edit: TextEdit) {
+ self.insert_source_and_snippet_edit(file_id, edit, None)
+ }
+
+ /// Inserts a [`TextEdit`] and potentially a [`SnippetEdit`] for the given [`FileId`].
+ /// This properly handles merging existing edits for a file if some already exist.
+ pub fn insert_source_and_snippet_edit(
+ &mut self,
+ file_id: FileId,
+ edit: TextEdit,
+ snippet_edit: Option<SnippetEdit>,
+ ) {
match self.source_file_edits.entry(file_id) {
Entry::Occupied(mut entry) => {
- never!(entry.get_mut().union(edit).is_err(), "overlapping edits for same file");
+ let value = entry.get_mut();
+ never!(value.0.union(edit).is_err(), "overlapping edits for same file");
+ never!(
+ value.1.is_some() && snippet_edit.is_some(),
+ "overlapping snippet edits for same file"
+ );
+ if value.1.is_none() {
+ value.1 = snippet_edit;
+ }
}
Entry::Vacant(entry) => {
- entry.insert(edit);
+ entry.insert((edit, snippet_edit));
}
}
}
@@ -53,7 +75,10 @@ impl SourceChange {
self.file_system_edits.push(edit);
}
- pub fn get_source_edit(&self, file_id: FileId) -> Option<&TextEdit> {
+ pub fn get_source_and_snippet_edit(
+ &self,
+ file_id: FileId,
+ ) -> Option<&(TextEdit, Option<SnippetEdit>)> {
self.source_file_edits.get(&file_id)
}
@@ -67,7 +92,18 @@ impl SourceChange {
impl Extend<(FileId, TextEdit)> for SourceChange {
fn extend<T: IntoIterator<Item = (FileId, TextEdit)>>(&mut self, iter: T) {
- iter.into_iter().for_each(|(file_id, edit)| self.insert_source_edit(file_id, edit));
+ self.extend(iter.into_iter().map(|(file_id, edit)| (file_id, (edit, None))))
+ }
+}
+
+impl Extend<(FileId, (TextEdit, Option<SnippetEdit>))> for SourceChange {
+ fn extend<T: IntoIterator<Item = (FileId, (TextEdit, Option<SnippetEdit>))>>(
+ &mut self,
+ iter: T,
+ ) {
+ iter.into_iter().for_each(|(file_id, (edit, snippet_edit))| {
+ self.insert_source_and_snippet_edit(file_id, edit, snippet_edit)
+ });
}
}
@@ -79,6 +115,8 @@ impl Extend<FileSystemEdit> for SourceChange {
impl From<IntMap<FileId, TextEdit>> for SourceChange {
fn from(source_file_edits: IntMap<FileId, TextEdit>) -> SourceChange {
+ let source_file_edits =
+ source_file_edits.into_iter().map(|(file_id, edit)| (file_id, (edit, None))).collect();
SourceChange { source_file_edits, file_system_edits: Vec::new(), is_snippet: false }
}
}
@@ -91,6 +129,65 @@ impl FromIterator<(FileId, TextEdit)> for SourceChange {
}
}
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct SnippetEdit(Vec<(u32, TextRange)>);
+
+impl SnippetEdit {
+ pub fn new(snippets: Vec<Snippet>) -> Self {
+ let mut snippet_ranges = snippets
+ .into_iter()
+ .zip(1..)
+ .with_position()
+ .map(|pos| {
+ let (snippet, index) = match pos {
+ itertools::Position::First(it) | itertools::Position::Middle(it) => it,
+ // last/only snippet gets index 0
+ itertools::Position::Last((snippet, _))
+ | itertools::Position::Only((snippet, _)) => (snippet, 0),
+ };
+
+ let range = match snippet {
+ Snippet::Tabstop(pos) => TextRange::empty(pos),
+ Snippet::Placeholder(range) => range,
+ };
+ (index, range)
+ })
+ .collect_vec();
+
+ snippet_ranges.sort_by_key(|(_, range)| range.start());
+
+ // Ensure that none of the ranges overlap
+ let disjoint_ranges = snippet_ranges
+ .iter()
+ .zip(snippet_ranges.iter().skip(1))
+ .all(|((_, left), (_, right))| left.end() <= right.start() || left == right);
+ stdx::always!(disjoint_ranges);
+
+ SnippetEdit(snippet_ranges)
+ }
+
+ /// Inserts all of the snippets into the given text.
+ pub fn apply(&self, text: &mut String) {
+ // Start from the back so that we don't have to adjust ranges
+ for (index, range) in self.0.iter().rev() {
+ if range.is_empty() {
+ // is a tabstop
+ text.insert_str(range.start().into(), &format!("${index}"));
+ } else {
+ // is a placeholder
+ text.insert(range.end().into(), '}');
+ text.insert_str(range.start().into(), &format!("${{{index}:"));
+ }
+ }
+ }
+
+ /// Gets the underlying snippet index + text range
+ /// Tabstops are represented by an empty range, and placeholders use the range that they were given
+ pub fn into_edit_ranges(self) -> Vec<(u32, TextRange)> {
+ self.0
+ }
+}
+
pub struct SourceChangeBuilder {
pub edit: TextEditBuilder,
pub file_id: FileId,
@@ -149,24 +246,19 @@ impl SourceChangeBuilder {
}
fn commit(&mut self) {
- // Render snippets first so that they get bundled into the tree diff
- if let Some(mut snippets) = self.snippet_builder.take() {
- // Last snippet always has stop index 0
- let last_stop = snippets.places.pop().unwrap();
- last_stop.place(0);
-
- for (index, stop) in snippets.places.into_iter().enumerate() {
- stop.place(index + 1)
- }
- }
+ let snippet_edit = self.snippet_builder.take().map(|builder| {
+ SnippetEdit::new(
+ builder.places.into_iter().map(PlaceSnippet::finalize_position).collect_vec(),
+ )
+ });
if let Some(tm) = self.mutated_tree.take() {
- algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit)
+ algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit);
}
let edit = mem::take(&mut self.edit).finish();
- if !edit.is_empty() {
- self.source_change.insert_source_edit(self.file_id, edit);
+ if !edit.is_empty() || snippet_edit.is_some() {
+ self.source_change.insert_source_and_snippet_edit(self.file_id, edit, snippet_edit);
}
}
@@ -237,19 +329,31 @@ impl SourceChangeBuilder {
/// Adds a tabstop snippet to place the cursor before `node`
pub fn add_tabstop_before(&mut self, _cap: SnippetCap, node: impl AstNode) {
assert!(node.syntax().parent().is_some());
- self.add_snippet(PlaceSnippet::Before(node.syntax().clone()));
+ self.add_snippet(PlaceSnippet::Before(node.syntax().clone().into()));
}
/// Adds a tabstop snippet to place the cursor after `node`
pub fn add_tabstop_after(&mut self, _cap: SnippetCap, node: impl AstNode) {
assert!(node.syntax().parent().is_some());
- self.add_snippet(PlaceSnippet::After(node.syntax().clone()));
+ self.add_snippet(PlaceSnippet::After(node.syntax().clone().into()));
+ }
+
+ /// Adds a tabstop snippet to place the cursor before `token`
+ pub fn add_tabstop_before_token(&mut self, _cap: SnippetCap, token: SyntaxToken) {
+ assert!(token.parent().is_some());
+ self.add_snippet(PlaceSnippet::Before(token.clone().into()));
+ }
+
+ /// Adds a tabstop snippet to place the cursor after `token`
+ pub fn add_tabstop_after_token(&mut self, _cap: SnippetCap, token: SyntaxToken) {
+ assert!(token.parent().is_some());
+ self.add_snippet(PlaceSnippet::After(token.clone().into()));
}
/// Adds a snippet to move the cursor selected over `node`
pub fn add_placeholder_snippet(&mut self, _cap: SnippetCap, node: impl AstNode) {
assert!(node.syntax().parent().is_some());
- self.add_snippet(PlaceSnippet::Over(node.syntax().clone()))
+ self.add_snippet(PlaceSnippet::Over(node.syntax().clone().into()))
}
fn add_snippet(&mut self, snippet: PlaceSnippet) {
@@ -260,6 +364,16 @@ impl SourceChangeBuilder {
pub fn finish(mut self) -> SourceChange {
self.commit();
+
+ // Only one file can have snippet edits
+ stdx::never!(self
+ .source_change
+ .source_file_edits
+ .iter()
+ .filter(|(_, (_, snippet_edit))| snippet_edit.is_some())
+ .at_most_one()
+ .is_err());
+
mem::take(&mut self.source_change)
}
}
@@ -281,65 +395,28 @@ impl From<FileSystemEdit> for SourceChange {
}
}
+pub enum Snippet {
+ /// A tabstop snippet (e.g. `$0`).
+ Tabstop(TextSize),
+ /// A placeholder snippet (e.g. `${0:placeholder}`).
+ Placeholder(TextRange),
+}
+
enum PlaceSnippet {
- /// Place a tabstop before a node
- Before(SyntaxNode),
- /// Place a tabstop before a node
- After(SyntaxNode),
- /// Place a placeholder snippet in place of the node
- Over(SyntaxNode),
+ /// Place a tabstop before an element
+ Before(SyntaxElement),
+ /// Place a tabstop before an element
+ After(SyntaxElement),
+ /// Place a placeholder snippet in place of the element
+ Over(SyntaxElement),
}
impl PlaceSnippet {
- /// Places the snippet before or over a node with the given tab stop index
- fn place(self, order: usize) {
- // ensure the target node is still attached
- match &self {
- PlaceSnippet::Before(node) | PlaceSnippet::After(node) | PlaceSnippet::Over(node) => {
- // node should still be in the tree, but if it isn't
- // then it's okay to just ignore this place
- if stdx::never!(node.parent().is_none()) {
- return;
- }
- }
- }
-
+ fn finalize_position(self) -> Snippet {
match self {
- PlaceSnippet::Before(node) => {
- ted::insert_raw(ted::Position::before(&node), Self::make_tab_stop(order));
- }
- PlaceSnippet::After(node) => {
- ted::insert_raw(ted::Position::after(&node), Self::make_tab_stop(order));
- }
- PlaceSnippet::Over(node) => {
- let position = ted::Position::before(&node);
- node.detach();
-
- let snippet = ast::SourceFile::parse(&format!("${{{order}:_}}"))
- .syntax_node()
- .clone_for_update();
-
- let placeholder =
- snippet.descendants().find_map(ast::UnderscoreExpr::cast).unwrap();
- ted::replace(placeholder.syntax(), node);
-
- ted::insert_raw(position, snippet);
- }
+ PlaceSnippet::Before(it) => Snippet::Tabstop(it.text_range().start()),
+ PlaceSnippet::After(it) => Snippet::Tabstop(it.text_range().end()),
+ PlaceSnippet::Over(it) => Snippet::Placeholder(it.text_range()),
}
}
-
- fn make_tab_stop(order: usize) -> SyntaxNode {
- let stop = ast::SourceFile::parse(&format!("stop!(${order})"))
- .syntax_node()
- .descendants()
- .find_map(ast::TokenTree::cast)
- .unwrap()
- .syntax()
- .clone_for_update();
-
- stop.first_token().unwrap().detach();
- stop.last_token().unwrap().detach();
-
- stop
- }
}