1 //! Implementation of "closing brace" inlay hints:
6 use hir
::{HirDisplay, Semantics}
;
7 use ide_db
::{base_db::FileRange, RootDatabase}
;
9 ast
::{self, AstNode, HasName}
,
10 match_ast
, SyntaxKind
, SyntaxNode
, T
,
14 inlay_hints
::InlayHintLabelPart
, FileId
, InlayHint
, InlayHintLabel
, InlayHintsConfig
, InlayKind
,
18 acc
: &mut Vec
<InlayHint
>,
19 sema
: &Semantics
<'_
, RootDatabase
>,
20 config
: &InlayHintsConfig
,
24 let min_lines
= config
.closing_brace_hints_min_lines?
;
26 let name
= |it
: ast
::Name
| it
.syntax().text_range();
28 let mut closing_token
;
29 let (label
, name_range
) = if let Some(item_list
) = ast
::AssocItemList
::cast(node
.clone()) {
30 closing_token
= item_list
.r_curly_token()?
;
32 let parent
= item_list
.syntax().parent()?
;
36 let imp
= sema
.to_def(&imp
)?
;
37 let ty
= imp
.self_ty(sema
.db
);
38 let trait_
= imp
.trait_(sema
.db
);
39 let hint_text
= match trait_
{
40 Some(tr
) => format
!("impl {} for {}", tr
.name(sema
.db
), ty
.display_truncated(sema
.db
, config
.max_length
)),
41 None
=> format
!("impl {}", ty
.display_truncated(sema
.db
, config
.max_length
)),
46 (format
!("trait {}", tr
.name()?
), tr
.name().map(name
))
51 } else if let Some(list
) = ast
::ItemList
::cast(node
.clone()) {
52 closing_token
= list
.r_curly_token()?
;
54 let module
= ast
::Module
::cast(list
.syntax().parent()?
)?
;
55 (format
!("mod {}", module
.name()?
), module
.name().map(name
))
56 } else if let Some(block
) = ast
::BlockExpr
::cast(node
.clone()) {
57 closing_token
= block
.stmt_list()?
.r_curly_token()?
;
59 let parent
= block
.syntax().parent()?
;
63 // FIXME: this could include parameters, but `HirDisplay` prints too much info
64 // and doesn't respect the max length either, so the hints end up way too long
65 (format
!("fn {}", it
.name()?
), it
.name().map(name
))
67 ast
::Static(it
) => (format
!("static {}", it
.name()?
), it
.name().map(name
)),
69 if it
.underscore_token().is_some() {
70 ("const _".into(), None
)
72 (format
!("const {}", it
.name()?
), it
.name().map(name
))
78 } else if let Some(mac
) = ast
::MacroCall
::cast(node
.clone()) {
79 let last_token
= mac
.syntax().last_token()?
;
80 if last_token
.kind() != T
![;] && last_token
.kind() != SyntaxKind
::R_CURLY
{
83 closing_token
= last_token
;
86 format
!("{}!", mac
.path()?
),
87 mac
.path().and_then(|it
| it
.segment()).map(|it
| it
.syntax().text_range()),
93 if let Some(mut next
) = closing_token
.next_token() {
94 if next
.kind() == T
![;] {
95 if let Some(tok
) = next
.next_token() {
100 if !(next
.kind() == SyntaxKind
::WHITESPACE
&& next
.text().contains('
\n'
)) {
101 // Only display the hint if the `}` is the last token on the line
107 node
.text().for_each_chunk(|s
| lines
+= s
.matches('
\n'
).count());
108 if lines
< min_lines
{
112 let linked_location
= config
114 .then(|| name_range
.map(|range
| FileRange { file_id, range }
))
117 range
: closing_token
.text_range(),
118 kind
: InlayKind
::ClosingBraceHint
,
119 label
: InlayHintLabel { parts: vec![InlayHintLabelPart { text: label, linked_location }
] },
120 tooltip
: None
, // provided by label part location
129 inlay_hints
::tests
::{check_with_config, DISABLED_CONFIG}
,
134 fn hints_closing_brace() {
136 InlayHintsConfig { closing_brace_hints_min_lines: Some(2), ..DISABLED_CONFIG }
,
141 } // no hint unless `}` is the last token on the line
147 fn h<T>(with: T, arguments: u8, ...) {