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 | 67 |
1 files changed, 64 insertions, 3 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index e6360bc6ec..7a8edfea83 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -14,8 +14,9 @@ use smallvec::{smallvec, SmallVec}; use stdx::never; use syntax::{ ast::{self, AstNode}, - match_ast, NodeOrToken, SyntaxNode, TextRange, + match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize, }; +use text_edit::TextEdit; use crate::{navigation_target::TryToNav, FileId}; @@ -113,14 +114,26 @@ pub struct InlayHint { pub kind: InlayKind, /// The actual label to show in the inlay hint. pub label: InlayHintLabel, + /// Text edit to apply when "accepting" this inlay hint. + pub text_edit: Option<TextEdit>, } impl InlayHint { fn closing_paren(range: TextRange) -> InlayHint { - InlayHint { range, kind: InlayKind::ClosingParenthesis, label: InlayHintLabel::from(")") } + InlayHint { + range, + kind: InlayKind::ClosingParenthesis, + label: InlayHintLabel::from(")"), + text_edit: None, + } } fn opening_paren(range: TextRange) -> InlayHint { - InlayHint { range, kind: InlayKind::OpeningParenthesis, label: InlayHintLabel::from("(") } + InlayHint { + range, + kind: InlayKind::OpeningParenthesis, + label: InlayHintLabel::from("("), + text_edit: None, + } } } @@ -346,6 +359,23 @@ fn label_of_ty( Some(r) } +fn ty_to_text_edit( + sema: &Semantics<'_, RootDatabase>, + node_for_hint: &SyntaxNode, + ty: &hir::Type, + offset_to_insert: TextSize, + prefix: String, +) -> Option<TextEdit> { + let scope = sema.scope(node_for_hint)?; + // FIXME: Limit the length and bail out on excess somehow? + let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?; + + let mut builder = TextEdit::builder(); + builder.insert(offset_to_insert, prefix); + builder.insert(offset_to_insert, rendered); + Some(builder.finish()) +} + // Feature: Inlay Hints // // rust-analyzer shows additional information inline with the source code. @@ -553,6 +583,37 @@ mod tests { expect.assert_debug_eq(&inlay_hints) } + /// Computes inlay hints for the fixture, applies all the provided text edits and then runs + /// expect test. + #[track_caller] + pub(super) fn check_edit(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) { + let (analysis, file_id) = fixture::file(ra_fixture); + let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); + + let edits = inlay_hints + .into_iter() + .filter_map(|hint| hint.text_edit) + .reduce(|mut acc, next| { + acc.union(next).expect("merging text edits failed"); + acc + }) + .expect("no edit returned"); + + let mut actual = analysis.file_text(file_id).unwrap().to_string(); + edits.apply(&mut actual); + expect.assert_eq(&actual); + } + + #[track_caller] + pub(super) fn check_no_edit(config: InlayHintsConfig, ra_fixture: &str) { + let (analysis, file_id) = fixture::file(ra_fixture); + let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap(); + + let edits: Vec<_> = inlay_hints.into_iter().filter_map(|hint| hint.text_edit).collect(); + + assert!(edits.is_empty(), "unexpected edits: {edits:?}"); + } + #[test] fn hints_disabled() { check_with_config( |