]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / ide / src / inlay_hints / closing_brace.rs
1 //! Implementation of "closing brace" inlay hints:
2 //! ```no_run
3 //! fn g() {
4 //! } /* fn g */
5 //! ```
6 use hir::{HirDisplay, Semantics};
7 use ide_db::{base_db::FileRange, RootDatabase};
8 use syntax::{
9 ast::{self, AstNode, HasName},
10 match_ast, SyntaxKind, SyntaxNode, T,
11 };
12
13 use crate::{
14 inlay_hints::InlayHintLabelPart, FileId, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
15 };
16
17 pub(super) fn hints(
18 acc: &mut Vec<InlayHint>,
19 sema: &Semantics<'_, RootDatabase>,
20 config: &InlayHintsConfig,
21 file_id: FileId,
22 node: SyntaxNode,
23 ) -> Option<()> {
24 let min_lines = config.closing_brace_hints_min_lines?;
25
26 let name = |it: ast::Name| it.syntax().text_range();
27
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()?;
31
32 let parent = item_list.syntax().parent()?;
33 match_ast! {
34 match parent {
35 ast::Impl(imp) => {
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)),
42 };
43 (hint_text, None)
44 },
45 ast::Trait(tr) => {
46 (format!("trait {}", tr.name()?), tr.name().map(name))
47 },
48 _ => return None,
49 }
50 }
51 } else if let Some(list) = ast::ItemList::cast(node.clone()) {
52 closing_token = list.r_curly_token()?;
53
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()?;
58
59 let parent = block.syntax().parent()?;
60 match_ast! {
61 match parent {
62 ast::Fn(it) => {
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))
66 },
67 ast::Static(it) => (format!("static {}", it.name()?), it.name().map(name)),
68 ast::Const(it) => {
69 if it.underscore_token().is_some() {
70 ("const _".into(), None)
71 } else {
72 (format!("const {}", it.name()?), it.name().map(name))
73 }
74 },
75 _ => return None,
76 }
77 }
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 {
81 return None;
82 }
83 closing_token = last_token;
84
85 (
86 format!("{}!", mac.path()?),
87 mac.path().and_then(|it| it.segment()).map(|it| it.syntax().text_range()),
88 )
89 } else {
90 return None;
91 };
92
93 if let Some(mut next) = closing_token.next_token() {
94 if next.kind() == T![;] {
95 if let Some(tok) = next.next_token() {
96 closing_token = next;
97 next = tok;
98 }
99 }
100 if !(next.kind() == SyntaxKind::WHITESPACE && next.text().contains('\n')) {
101 // Only display the hint if the `}` is the last token on the line
102 return None;
103 }
104 }
105
106 let mut lines = 1;
107 node.text().for_each_chunk(|s| lines += s.matches('\n').count());
108 if lines < min_lines {
109 return None;
110 }
111
112 let linked_location = config
113 .location_links
114 .then(|| name_range.map(|range| FileRange { file_id, range }))
115 .flatten();
116 acc.push(InlayHint {
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
121 });
122
123 None
124 }
125
126 #[cfg(test)]
127 mod tests {
128 use crate::{
129 inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
130 InlayHintsConfig,
131 };
132
133 #[test]
134 fn hints_closing_brace() {
135 check_with_config(
136 InlayHintsConfig { closing_brace_hints_min_lines: Some(2), ..DISABLED_CONFIG },
137 r#"
138 fn a() {}
139
140 fn f() {
141 } // no hint unless `}` is the last token on the line
142
143 fn g() {
144 }
145 //^ fn g
146
147 fn h<T>(with: T, arguments: u8, ...) {
148 }
149 //^ fn h
150
151 trait Tr {
152 fn f();
153 fn g() {
154 }
155 //^ fn g
156 }
157 //^ trait Tr
158 impl Tr for () {
159 }
160 //^ impl Tr for ()
161 impl dyn Tr {
162 }
163 //^ impl dyn Tr
164
165 static S0: () = 0;
166 static S1: () = {};
167 static S2: () = {
168 };
169 //^ static S2
170 const _: () = {
171 };
172 //^ const _
173
174 mod m {
175 }
176 //^ mod m
177
178 m! {}
179 m!();
180 m!(
181 );
182 //^ m!
183
184 m! {
185 }
186 //^ m!
187
188 fn f() {
189 let v = vec![
190 ];
191 }
192 //^ fn f
193 "#,
194 );
195 }
196 }