| //! Random assortment of ide helpers for high-level ide features that don't fit in any other module. |
| |
| use std::collections::VecDeque; |
| |
| use base_db::SourceDatabase; |
| use hir::{Crate, ItemInNs, ModuleDef, Name, Semantics}; |
| use span::{Edition, FileId}; |
| use syntax::{ |
| AstToken, SyntaxKind, SyntaxToken, ToSmolStr, TokenAtOffset, |
| ast::{self, make}, |
| }; |
| |
| use crate::{ |
| RootDatabase, |
| defs::{Definition, IdentClass}, |
| generated, |
| }; |
| |
| pub fn item_name(db: &RootDatabase, item: ItemInNs) -> Option<Name> { |
| match item { |
| ItemInNs::Types(module_def_id) => module_def_id.name(db), |
| ItemInNs::Values(module_def_id) => module_def_id.name(db), |
| ItemInNs::Macros(macro_def_id) => Some(macro_def_id.name(db)), |
| } |
| } |
| |
| /// Picks the token with the highest rank returned by the passed in function. |
| pub fn pick_best_token( |
| tokens: TokenAtOffset<SyntaxToken>, |
| f: impl Fn(SyntaxKind) -> usize, |
| ) -> Option<SyntaxToken> { |
| tokens.max_by_key(move |t| f(t.kind())) |
| } |
| pub fn pick_token<T: AstToken>(mut tokens: TokenAtOffset<SyntaxToken>) -> Option<T> { |
| tokens.find_map(T::cast) |
| } |
| |
| /// Converts the mod path struct into its ast representation. |
| pub fn mod_path_to_ast(path: &hir::ModPath, edition: Edition) -> ast::Path { |
| let _p = tracing::info_span!("mod_path_to_ast").entered(); |
| |
| let mut segments = Vec::new(); |
| let mut is_abs = false; |
| match path.kind { |
| hir::PathKind::Plain => {} |
| hir::PathKind::SELF => segments.push(make::path_segment_self()), |
| hir::PathKind::Super(n) => segments.extend((0..n).map(|_| make::path_segment_super())), |
| hir::PathKind::DollarCrate(_) | hir::PathKind::Crate => { |
| segments.push(make::path_segment_crate()) |
| } |
| hir::PathKind::Abs => is_abs = true, |
| } |
| |
| segments.extend(path.segments().iter().map(|segment| { |
| make::path_segment(make::name_ref(&segment.display_no_db(edition).to_smolstr())) |
| })); |
| make::path_from_segments(segments, is_abs) |
| } |
| |
| /// Iterates all `ModuleDef`s and `Impl` blocks of the given file. |
| pub fn visit_file_defs( |
| sema: &Semantics<'_, RootDatabase>, |
| file_id: FileId, |
| cb: &mut dyn FnMut(Definition), |
| ) { |
| let db = sema.db; |
| let module = match sema.file_to_module_def(file_id) { |
| Some(it) => it, |
| None => return, |
| }; |
| let mut defs: VecDeque<_> = module.declarations(db).into(); |
| while let Some(def) = defs.pop_front() { |
| if let ModuleDef::Module(submodule) = def |
| && submodule.is_inline(db) |
| { |
| defs.extend(submodule.declarations(db)); |
| submodule.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into())); |
| } |
| cb(def.into()); |
| } |
| module.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into())); |
| |
| let is_root = module.is_crate_root(); |
| module |
| .legacy_macros(db) |
| .into_iter() |
| // don't show legacy macros declared in the crate-root that were already covered in declarations earlier |
| .filter(|it| !(is_root && it.is_macro_export(db))) |
| .for_each(|mac| cb(mac.into())); |
| } |
| |
| /// Checks if the given lint is equal or is contained by the other lint which may or may not be a group. |
| pub fn lint_eq_or_in_group(lint: &str, lint_is: &str) -> bool { |
| if lint == lint_is { |
| return true; |
| } |
| |
| if let Some(group) = generated::lints::DEFAULT_LINT_GROUPS |
| .iter() |
| .chain(generated::lints::CLIPPY_LINT_GROUPS.iter()) |
| .chain(generated::lints::RUSTDOC_LINT_GROUPS.iter()) |
| .find(|&check| check.lint.label == lint_is) |
| { |
| group.children.contains(&lint) |
| } else { |
| false |
| } |
| } |
| |
| pub fn is_editable_crate(krate: Crate, db: &RootDatabase) -> bool { |
| let root_file = krate.root_file(db); |
| let source_root_id = db.file_source_root(root_file).source_root_id(db); |
| !db.source_root(source_root_id).source_root(db).is_library |
| } |
| |
| // FIXME: This is a weird function |
| pub fn get_definition( |
| sema: &Semantics<'_, RootDatabase>, |
| token: SyntaxToken, |
| ) -> Option<Definition> { |
| for token in sema.descend_into_macros_exact(token) { |
| let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); |
| if let Some(&[x]) = def.as_deref() { |
| return Some(x); |
| } |
| } |
| None |
| } |