Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide/src/inlay_hints.rs')
| -rw-r--r-- | crates/ide/src/inlay_hints.rs | 96 |
1 files changed, 60 insertions, 36 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 8311e770b4..dda38ce77e 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1,5 +1,6 @@ use std::{ fmt::{self, Write}, + hash::{BuildHasher, BuildHasherDefault}, mem::take, }; @@ -8,7 +9,7 @@ use hir::{ known, ClosureStyle, HasVisibility, HirDisplay, HirDisplayError, HirWrite, ModuleDef, ModuleDefId, Semantics, }; -use ide_db::{base_db::FileRange, famous_defs::FamousDefs, RootDatabase}; +use ide_db::{base_db::FileRange, famous_defs::FamousDefs, FxHasher, RootDatabase}; use itertools::Itertools; use smallvec::{smallvec, SmallVec}; use stdx::never; @@ -116,7 +117,7 @@ pub enum AdjustmentHintsMode { PreferPostfix, } -#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] pub enum InlayKind { Adjustment, BindingMode, @@ -132,7 +133,7 @@ pub enum InlayKind { RangeExclusive, } -#[derive(Debug)] +#[derive(Debug, Hash)] pub enum InlayHintPosition { Before, After, @@ -151,13 +152,23 @@ pub struct InlayHint { pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. pub text_edit: Option<TextEdit>, - pub needs_resolve: bool, +} + +impl std::hash::Hash for InlayHint { + fn hash<H: std::hash::Hasher>(&self, state: &mut H) { + self.range.hash(state); + self.position.hash(state); + self.pad_left.hash(state); + self.pad_right.hash(state); + self.kind.hash(state); + self.label.hash(state); + self.text_edit.is_some().hash(state); + } } impl InlayHint { fn closing_paren_after(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { - needs_resolve: false, range, kind, label: InlayHintLabel::from(")"), @@ -167,9 +178,9 @@ impl InlayHint { pad_right: false, } } + fn opening_paren_before(kind: InlayKind, range: TextRange) -> InlayHint { InlayHint { - needs_resolve: false, range, kind, label: InlayHintLabel::from("("), @@ -179,15 +190,19 @@ impl InlayHint { pad_right: false, } } + + pub fn needs_resolve(&self) -> bool { + self.text_edit.is_some() || self.label.needs_resolve() + } } -#[derive(Debug)] +#[derive(Debug, Hash)] pub enum InlayTooltip { String(String), Markdown(String), } -#[derive(Default)] +#[derive(Default, Hash)] pub struct InlayHintLabel { pub parts: SmallVec<[InlayHintLabelPart; 1]>, } @@ -265,6 +280,7 @@ impl fmt::Debug for InlayHintLabel { } } +#[derive(Hash)] pub struct InlayHintLabelPart { pub text: String, /// Source location represented by this label part. The client will use this to fetch the part's @@ -313,9 +329,7 @@ impl fmt::Write for InlayHintLabelBuilder<'_> { impl HirWrite for InlayHintLabelBuilder<'_> { fn start_location_link(&mut self, def: ModuleDefId) { - if self.location.is_some() { - never!("location link is already started"); - } + never!(self.location.is_some(), "location link is already started"); self.make_new_part(); let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return }; let location = location.call_site(); @@ -425,11 +439,6 @@ fn ty_to_text_edit( Some(builder.finish()) } -pub enum RangeLimit { - Fixed(TextRange), - NearestParent(TextSize), -} - // Feature: Inlay Hints // // rust-analyzer shows additional information inline with the source code. @@ -451,7 +460,7 @@ pub enum RangeLimit { pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, - range_limit: Option<RangeLimit>, + range_limit: Option<TextRange>, config: &InlayHintsConfig, ) -> Vec<InlayHint> { let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); @@ -466,31 +475,13 @@ pub(crate) fn inlay_hints( let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); match range_limit { - Some(RangeLimit::Fixed(range)) => match file.covering_element(range) { + Some(range) => match file.covering_element(range) { NodeOrToken::Token(_) => return acc, NodeOrToken::Node(n) => n .descendants() .filter(|descendant| range.intersect(descendant.text_range()).is_some()) .for_each(hints), }, - Some(RangeLimit::NearestParent(position)) => { - match file.token_at_offset(position).left_biased() { - Some(token) => { - if let Some(parent_block) = - token.parent_ancestors().find_map(ast::BlockExpr::cast) - { - parent_block.syntax().descendants().for_each(hints) - } else if let Some(parent_item) = - token.parent_ancestors().find_map(ast::Item::cast) - { - parent_item.syntax().descendants().for_each(hints) - } else { - return acc; - } - } - None => return acc, - } - } None => file.descendants().for_each(hints), }; } @@ -498,6 +489,39 @@ pub(crate) fn inlay_hints( acc } +pub(crate) fn inlay_hints_resolve( + db: &RootDatabase, + file_id: FileId, + position: TextSize, + hash: u64, + config: &InlayHintsConfig, +) -> Option<InlayHint> { + let _p = tracing::span!(tracing::Level::INFO, "inlay_hints").entered(); + let sema = Semantics::new(db); + let file = sema.parse(file_id); + let file = file.syntax(); + + let scope = sema.scope(file)?; + let famous_defs = FamousDefs(&sema, scope.krate()); + let mut acc = Vec::new(); + + let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); + match file.token_at_offset(position).left_biased() { + Some(token) => { + if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) { + parent_block.syntax().descendants().for_each(hints) + } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) { + parent_item.syntax().descendants().for_each(hints) + } else { + return None; + } + } + None => return None, + } + + acc.into_iter().find(|hint| BuildHasherDefault::<FxHasher>::default().hash_one(hint) == hash) +} + fn hints( hints: &mut Vec<InlayHint>, famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, |