mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-11-03 05:13:35 +00:00
Simplify completion config path resolutions
This commit is contained in:
parent
45954ebaa4
commit
c5bda0d3f7
3 changed files with 49 additions and 94 deletions
|
|
@ -2060,6 +2060,11 @@ impl SemanticsScope<'_> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_mod_path(&self, path: &ModPath) -> impl Iterator<Item = ItemInNs> {
|
||||||
|
let items = self.resolver.resolve_module_path_in_items(self.db.upcast(), path);
|
||||||
|
items.iter_items().map(|(item, _)| item.into())
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterates over associated types that may be specified after the given path (using
|
/// Iterates over associated types that may be specified after the given path (using
|
||||||
/// `Ty::Assoc` syntax).
|
/// `Ty::Assoc` syntax).
|
||||||
pub fn assoc_type_shorthand_candidates<R>(
|
pub fn assoc_type_shorthand_candidates<R>(
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ mod tests;
|
||||||
use std::{iter, ops::ControlFlow};
|
use std::{iter, ops::ControlFlow};
|
||||||
|
|
||||||
use hir::{
|
use hir::{
|
||||||
db::DefDatabase, HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics,
|
HasAttrs, Local, ModPath, ModuleDef, ModuleSource, Name, PathResolution, ScopeDef, Semantics,
|
||||||
SemanticsScope, Symbol, Type, TypeInfo,
|
SemanticsScope, Symbol, Type, TypeInfo,
|
||||||
};
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
|
|
@ -758,15 +758,43 @@ impl<'a> CompletionContext<'a> {
|
||||||
});
|
});
|
||||||
|
|
||||||
let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db))
|
let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db))
|
||||||
// `BlockExpr` modules are not count as module depth
|
// `BlockExpr` modules do not count towards module depth
|
||||||
.filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_)))
|
.filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_)))
|
||||||
.count()
|
.count()
|
||||||
// exclude `m` itself
|
// exclude `m` itself
|
||||||
.saturating_sub(1);
|
.saturating_sub(1);
|
||||||
|
|
||||||
let exclude_traits = resolve_exclude_traits_list(db, config.exclude_traits);
|
let exclude_traits: FxHashSet<_> = config
|
||||||
let mut exclude_flyimport_traits =
|
.exclude_traits
|
||||||
resolve_exclude_traits_list(db, config.exclude_flyimport_traits);
|
.iter()
|
||||||
|
.filter_map(|path| {
|
||||||
|
scope
|
||||||
|
.resolve_mod_path(&ModPath::from_segments(
|
||||||
|
hir::PathKind::Plain,
|
||||||
|
path.split("::").map(Symbol::intern).map(Name::new_symbol_root),
|
||||||
|
))
|
||||||
|
.find_map(|it| match it {
|
||||||
|
hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut exclude_flyimport_traits: FxHashSet<_> = config
|
||||||
|
.exclude_flyimport_traits
|
||||||
|
.iter()
|
||||||
|
.filter_map(|path| {
|
||||||
|
scope
|
||||||
|
.resolve_mod_path(&ModPath::from_segments(
|
||||||
|
hir::PathKind::Plain,
|
||||||
|
path.split("::").map(Symbol::intern).map(Name::new_symbol_root),
|
||||||
|
))
|
||||||
|
.find_map(|it| match it {
|
||||||
|
hir::ItemInNs::Types(ModuleDef::Trait(t)) => Some(t),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
exclude_flyimport_traits.extend(exclude_traits.iter().copied());
|
exclude_flyimport_traits.extend(exclude_traits.iter().copied());
|
||||||
|
|
||||||
let complete_semicolon = if config.add_semicolon_to_unit {
|
let complete_semicolon = if config.add_semicolon_to_unit {
|
||||||
|
|
@ -841,71 +869,6 @@ impl<'a> CompletionContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_exclude_traits_list(db: &RootDatabase, traits: &[String]) -> FxHashSet<hir::Trait> {
|
|
||||||
let _g = tracing::debug_span!("resolve_exclude_trait_list", ?traits).entered();
|
|
||||||
let crate_graph = db.crate_graph();
|
|
||||||
let mut crate_name_to_def_map = FxHashMap::default();
|
|
||||||
let mut result = FxHashSet::default();
|
|
||||||
'process_traits: for trait_ in traits {
|
|
||||||
let mut segments = trait_.split("::").peekable();
|
|
||||||
let Some(crate_name) = segments.next() else {
|
|
||||||
tracing::error!(
|
|
||||||
?trait_,
|
|
||||||
"error resolving trait from traits exclude list: invalid path"
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let Some(def_map) = crate_name_to_def_map.entry(crate_name).or_insert_with(|| {
|
|
||||||
let krate = crate_graph
|
|
||||||
.iter()
|
|
||||||
.find(|&krate| crate_graph[krate].display_name.as_deref() == Some(crate_name));
|
|
||||||
let def_map = krate.map(|krate| db.crate_def_map(krate));
|
|
||||||
if def_map.is_none() {
|
|
||||||
tracing::error!(
|
|
||||||
"error resolving `{trait_}` from trait exclude lists: crate could not be found"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
def_map
|
|
||||||
}) else {
|
|
||||||
// Do not report more than one error for the same crate.
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let mut module = &def_map[hir::DefMap::ROOT];
|
|
||||||
let trait_name = 'lookup_mods: {
|
|
||||||
while let Some(segment) = segments.next() {
|
|
||||||
if segments.peek().is_none() {
|
|
||||||
break 'lookup_mods segment;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(&inner) =
|
|
||||||
module.children.get(&Name::new_symbol_root(hir::Symbol::intern(segment)))
|
|
||||||
else {
|
|
||||||
tracing::error!(
|
|
||||||
"error resolving `{trait_}` from trait exclude lists: could not find module `{segment}`"
|
|
||||||
);
|
|
||||||
continue 'process_traits;
|
|
||||||
};
|
|
||||||
module = &def_map[inner];
|
|
||||||
}
|
|
||||||
|
|
||||||
tracing::error!("error resolving `{trait_}` from trait exclude lists: invalid path");
|
|
||||||
continue 'process_traits;
|
|
||||||
};
|
|
||||||
let resolved_trait = module
|
|
||||||
.scope
|
|
||||||
.trait_by_name(&Name::new_symbol_root(hir::Symbol::intern(trait_name)))
|
|
||||||
.map(hir::Trait::from);
|
|
||||||
let Some(resolved_trait) = resolved_trait else {
|
|
||||||
tracing::error!(
|
|
||||||
"error resolving `{trait_}` from trait exclude lists: trait could not be found"
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
result.insert(resolved_trait);
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
const OP_TRAIT_LANG_NAMES: &[&str] = &[
|
const OP_TRAIT_LANG_NAMES: &[&str] = &[
|
||||||
"add_assign",
|
"add_assign",
|
||||||
"add",
|
"add",
|
||||||
|
|
|
||||||
|
|
@ -100,9 +100,9 @@
|
||||||
// }
|
// }
|
||||||
// ----
|
// ----
|
||||||
|
|
||||||
|
use hir::{ModPath, Name, Symbol};
|
||||||
use ide_db::imports::import_assets::LocatedImport;
|
use ide_db::imports::import_assets::LocatedImport;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use syntax::{ast, AstNode, GreenNode, SyntaxNode};
|
|
||||||
|
|
||||||
use crate::context::CompletionContext;
|
use crate::context::CompletionContext;
|
||||||
|
|
||||||
|
|
@ -123,10 +123,7 @@ pub struct Snippet {
|
||||||
pub scope: SnippetScope,
|
pub scope: SnippetScope,
|
||||||
pub description: Option<Box<str>>,
|
pub description: Option<Box<str>>,
|
||||||
snippet: String,
|
snippet: String,
|
||||||
// These are `ast::Path`'s but due to SyntaxNodes not being Send we store these
|
requires: Box<[ModPath]>,
|
||||||
// and reconstruct them on demand instead. This is cheaper than reparsing them
|
|
||||||
// from strings
|
|
||||||
requires: Box<[GreenNode]>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Snippet {
|
impl Snippet {
|
||||||
|
|
@ -143,7 +140,6 @@ impl Snippet {
|
||||||
}
|
}
|
||||||
let (requires, snippet, description) = validate_snippet(snippet, description, requires)?;
|
let (requires, snippet, description) = validate_snippet(snippet, description, requires)?;
|
||||||
Some(Snippet {
|
Some(Snippet {
|
||||||
// Box::into doesn't work as that has a Copy bound 😒
|
|
||||||
postfix_triggers: postfix_triggers.iter().map(String::as_str).map(Into::into).collect(),
|
postfix_triggers: postfix_triggers.iter().map(String::as_str).map(Into::into).collect(),
|
||||||
prefix_triggers: prefix_triggers.iter().map(String::as_str).map(Into::into).collect(),
|
prefix_triggers: prefix_triggers.iter().map(String::as_str).map(Into::into).collect(),
|
||||||
scope,
|
scope,
|
||||||
|
|
@ -167,15 +163,11 @@ impl Snippet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_edits(ctx: &CompletionContext<'_>, requires: &[GreenNode]) -> Option<Vec<LocatedImport>> {
|
fn import_edits(ctx: &CompletionContext<'_>, requires: &[ModPath]) -> Option<Vec<LocatedImport>> {
|
||||||
let import_cfg = ctx.config.import_path_config();
|
let import_cfg = ctx.config.import_path_config();
|
||||||
|
|
||||||
let resolve = |import: &GreenNode| {
|
let resolve = |import| {
|
||||||
let path = ast::Path::cast(SyntaxNode::new_root(import.clone()))?;
|
let item = ctx.scope.resolve_mod_path(import).next()?;
|
||||||
let item = match ctx.scope.speculative_resolve(&path)? {
|
|
||||||
hir::PathResolution::Def(def) => def.into(),
|
|
||||||
_ => return None,
|
|
||||||
};
|
|
||||||
let path = ctx.module.find_use_path(
|
let path = ctx.module.find_use_path(
|
||||||
ctx.db,
|
ctx.db,
|
||||||
item,
|
item,
|
||||||
|
|
@ -198,19 +190,14 @@ fn validate_snippet(
|
||||||
snippet: &[String],
|
snippet: &[String],
|
||||||
description: &str,
|
description: &str,
|
||||||
requires: &[String],
|
requires: &[String],
|
||||||
) -> Option<(Box<[GreenNode]>, String, Option<Box<str>>)> {
|
) -> Option<(Box<[ModPath]>, String, Option<Box<str>>)> {
|
||||||
let mut imports = Vec::with_capacity(requires.len());
|
let mut imports = Vec::with_capacity(requires.len());
|
||||||
for path in requires.iter() {
|
for path in requires.iter() {
|
||||||
let use_path =
|
let use_path = ModPath::from_segments(
|
||||||
ast::SourceFile::parse(&format!("use {path};"), syntax::Edition::CURRENT_FIXME)
|
hir::PathKind::Plain,
|
||||||
.syntax_node()
|
path.split("::").map(Symbol::intern).map(Name::new_symbol_root),
|
||||||
.descendants()
|
);
|
||||||
.find_map(ast::Path::cast)?;
|
imports.push(use_path);
|
||||||
if use_path.syntax().text() != path.as_str() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let green = use_path.syntax().green().into_owned();
|
|
||||||
imports.push(green);
|
|
||||||
}
|
}
|
||||||
let snippet = snippet.iter().join("\n");
|
let snippet = snippet.iter().join("\n");
|
||||||
let description = (!description.is_empty())
|
let description = (!description.is_empty())
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue