Skip to content

Commit ee2cbe0

Browse files
committed
Auto merge of rust-lang#12244 - jonas-schievink:closing-brace-hints, r=jonas-schievink
feat: Show inlay hints after a `}` to indicate the closed item Closes rust-lang/rust-analyzer#7315 ![screenshot-2022-05-13-19:42:00](https://user-images.githubusercontent.com/1786438/168338713-4cedef50-3611-4667-aa6a-49e154ec16a7.png)
2 parents 58234c6 + fccc129 commit ee2cbe0

File tree

6 files changed

+209
-7
lines changed

6 files changed

+209
-7
lines changed

crates/ide/src/inlay_hints.rs

Lines changed: 169 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub struct InlayHintsConfig {
2626
pub param_names_for_lifetime_elision_hints: bool,
2727
pub hide_named_constructor_hints: bool,
2828
pub max_length: Option<usize>,
29+
pub closing_brace_hints_min_lines: Option<usize>,
2930
}
3031

3132
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -52,6 +53,7 @@ pub enum InlayKind {
5253
LifetimeHint,
5354
ParameterHint,
5455
TypeHint,
56+
ClosingBraceHint,
5557
}
5658

5759
#[derive(Debug)]
@@ -104,7 +106,7 @@ pub(crate) fn inlay_hints(
104106
NodeOrToken::Token(_) => return acc,
105107
NodeOrToken::Node(n) => n
106108
.descendants()
107-
.filter(|descendant| range.contains_range(descendant.text_range()))
109+
.filter(|descendant| range.intersect(descendant.text_range()).is_some())
108110
.for_each(hints),
109111
},
110112
None => file.descendants().for_each(hints),
@@ -124,6 +126,8 @@ fn hints(
124126
None => return,
125127
};
126128

129+
closing_brace_hints(hints, sema, config, node.clone());
130+
127131
if let Some(expr) = ast::Expr::cast(node.clone()) {
128132
chaining_hints(hints, sema, &famous_defs, config, &expr);
129133
match expr {
@@ -147,6 +151,104 @@ fn hints(
147151
}
148152
}
149153

154+
fn closing_brace_hints(
155+
acc: &mut Vec<InlayHint>,
156+
sema: &Semantics<RootDatabase>,
157+
config: &InlayHintsConfig,
158+
node: SyntaxNode,
159+
) -> Option<()> {
160+
let min_lines = config.closing_brace_hints_min_lines?;
161+
162+
let mut closing_token;
163+
let label = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
164+
closing_token = item_list.r_curly_token()?;
165+
166+
let parent = item_list.syntax().parent()?;
167+
match_ast! {
168+
match parent {
169+
ast::Impl(imp) => {
170+
let imp = sema.to_def(&imp)?;
171+
let ty = imp.self_ty(sema.db);
172+
let trait_ = imp.trait_(sema.db);
173+
174+
match trait_ {
175+
Some(tr) => format!("impl {} for {}", tr.name(sema.db), ty.display_truncated(sema.db, config.max_length)),
176+
None => format!("impl {}", ty.display_truncated(sema.db, config.max_length)),
177+
}
178+
},
179+
ast::Trait(tr) => {
180+
format!("trait {}", tr.name()?)
181+
},
182+
_ => return None,
183+
}
184+
}
185+
} else if let Some(list) = ast::ItemList::cast(node.clone()) {
186+
closing_token = list.r_curly_token()?;
187+
188+
let module = ast::Module::cast(list.syntax().parent()?)?;
189+
format!("mod {}", module.name()?)
190+
} else if let Some(block) = ast::BlockExpr::cast(node.clone()) {
191+
closing_token = block.stmt_list()?.r_curly_token()?;
192+
193+
let parent = block.syntax().parent()?;
194+
match_ast! {
195+
match parent {
196+
ast::Fn(it) => {
197+
// FIXME: this could include parameters, but `HirDisplay` prints too much info
198+
// and doesn't respect the max length either, so the hints end up way too long
199+
format!("fn {}", it.name()?)
200+
},
201+
ast::Static(it) => format!("static {}", it.name()?),
202+
ast::Const(it) => {
203+
if it.underscore_token().is_some() {
204+
"const _".into()
205+
} else {
206+
format!("const {}", it.name()?)
207+
}
208+
},
209+
_ => return None,
210+
}
211+
}
212+
} else if let Some(mac) = ast::MacroCall::cast(node.clone()) {
213+
let last_token = mac.syntax().last_token()?;
214+
if last_token.kind() != T![;] && last_token.kind() != SyntaxKind::R_CURLY {
215+
return None;
216+
}
217+
closing_token = last_token;
218+
219+
format!("{}!", mac.path()?)
220+
} else {
221+
return None;
222+
};
223+
224+
if let Some(mut next) = closing_token.next_token() {
225+
if next.kind() == T![;] {
226+
if let Some(tok) = next.next_token() {
227+
closing_token = next;
228+
next = tok;
229+
}
230+
}
231+
if !(next.kind() == SyntaxKind::WHITESPACE && next.text().contains('\n')) {
232+
// Only display the hint if the `}` is the last token on the line
233+
return None;
234+
}
235+
}
236+
237+
let mut lines = 1;
238+
node.text().for_each_chunk(|s| lines += s.matches('\n').count());
239+
if lines < min_lines {
240+
return None;
241+
}
242+
243+
acc.push(InlayHint {
244+
range: closing_token.text_range(),
245+
kind: InlayKind::ClosingBraceHint,
246+
label: label.into(),
247+
});
248+
249+
None
250+
}
251+
150252
fn lifetime_hints(
151253
acc: &mut Vec<InlayHint>,
152254
config: &InlayHintsConfig,
@@ -925,6 +1027,7 @@ mod tests {
9251027
hide_named_constructor_hints: false,
9261028
param_names_for_lifetime_elision_hints: false,
9271029
max_length: None,
1030+
closing_brace_hints_min_lines: None,
9281031
};
9291032
const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
9301033
type_hints: true,
@@ -1422,10 +1525,10 @@ fn main() {
14221525
let foo = foo();
14231526
let foo = foo1();
14241527
let foo = foo2();
1528+
// ^^^ impl Fn(f64, f64)
14251529
let foo = foo3();
14261530
// ^^^ impl Fn(f64, f64) -> u32
14271531
let foo = foo4();
1428-
// ^^^ &dyn Fn(f64, f64) -> u32
14291532
let foo = foo5();
14301533
let foo = foo6();
14311534
let foo = foo7();
@@ -2290,7 +2393,70 @@ fn __(
22902393
//^^^^ &mut
22912394
//^ ref mut
22922395
}
2293-
}
2396+
}"#,
2397+
);
2398+
}
2399+
2400+
#[test]
2401+
fn hints_closing_brace() {
2402+
check_with_config(
2403+
InlayHintsConfig { closing_brace_hints_min_lines: Some(2), ..DISABLED_CONFIG },
2404+
r#"
2405+
fn a() {}
2406+
2407+
fn f() {
2408+
} // no hint unless `}` is the last token on the line
2409+
2410+
fn g() {
2411+
}
2412+
//^ fn g
2413+
2414+
fn h<T>(with: T, arguments: u8, ...) {
2415+
}
2416+
//^ fn h
2417+
2418+
trait Tr {
2419+
fn f();
2420+
fn g() {
2421+
}
2422+
//^ fn g
2423+
}
2424+
//^ trait Tr
2425+
impl Tr for () {
2426+
}
2427+
//^ impl Tr for ()
2428+
impl dyn Tr {
2429+
}
2430+
//^ impl dyn Tr
2431+
2432+
static S0: () = 0;
2433+
static S1: () = {};
2434+
static S2: () = {
2435+
};
2436+
//^ static S2
2437+
const _: () = {
2438+
};
2439+
//^ const _
2440+
2441+
mod m {
2442+
}
2443+
//^ mod m
2444+
2445+
m! {}
2446+
m!();
2447+
m!(
2448+
);
2449+
//^ m!
2450+
2451+
m! {
2452+
}
2453+
//^ m!
2454+
2455+
fn f() {
2456+
let v = vec![
2457+
];
2458+
}
2459+
//^ fn f
22942460
"#,
22952461
);
22962462
}

crates/ide/src/static_index.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ impl StaticIndex<'_> {
116116
param_names_for_lifetime_elision_hints: false,
117117
binding_mode_hints: false,
118118
max_length: Some(25),
119+
closing_brace_hints_min_lines: Some(25),
119120
},
120121
file_id,
121122
None,

crates/rust-analyzer/src/config.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,11 @@ config_data! {
259259
inlayHints_bindingModeHints_enable: bool = "false",
260260
/// Whether to show inlay type hints for method chains.
261261
inlayHints_chainingHints_enable: bool = "true",
262+
/// Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
263+
inlayHints_closingBraceHints_enable: bool = "true",
264+
/// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
265+
/// to always show them).
266+
inlayHints_closingBraceHints_minLines: usize = "25",
262267
/// Whether to show inlay type hints for return types of closures with blocks.
263268
inlayHints_closureReturnTypeHints_enable: bool = "false",
264269
/// Whether to show inlay type hints for elided lifetimes in function signatures.
@@ -1005,6 +1010,11 @@ impl Config {
10051010
.data
10061011
.inlayHints_lifetimeElisionHints_useParameterNames,
10071012
max_length: self.data.inlayHints_maxLength,
1013+
closing_brace_hints_min_lines: if self.data.inlayHints_closingBraceHints_enable {
1014+
Some(self.data.inlayHints_closingBraceHints_minLines)
1015+
} else {
1016+
None
1017+
},
10081018
}
10091019
}
10101020

crates/rust-analyzer/src/to_proto.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,8 @@ pub(crate) fn inlay_hint(
426426
| InlayKind::TypeHint
427427
| InlayKind::ChainingHint
428428
| InlayKind::GenericParamListHint
429-
| InlayKind::LifetimeHint => position(line_index, inlay_hint.range.end()),
429+
| InlayKind::LifetimeHint
430+
| InlayKind::ClosingBraceHint => position(line_index, inlay_hint.range.end()),
430431
},
431432
label: lsp_types::InlayHintLabel::String(match inlay_hint.kind {
432433
InlayKind::ParameterHint if render_colons => format!("{}:", inlay_hint.label),
@@ -442,12 +443,13 @@ pub(crate) fn inlay_hint(
442443
InlayKind::BindingModeHint
443444
| InlayKind::GenericParamListHint
444445
| InlayKind::LifetimeHint
445-
| InlayKind::ImplicitReborrowHint => None,
446+
| InlayKind::ImplicitReborrowHint
447+
| InlayKind::ClosingBraceHint => None,
446448
},
447449
tooltip: None,
448450
padding_left: Some(match inlay_hint.kind {
449451
InlayKind::TypeHint => !render_colons,
450-
InlayKind::ChainingHint => true,
452+
InlayKind::ChainingHint | InlayKind::ClosingBraceHint => true,
451453
InlayKind::BindingModeHint
452454
| InlayKind::ClosureReturnTypeHint
453455
| InlayKind::GenericParamListHint
@@ -460,7 +462,8 @@ pub(crate) fn inlay_hint(
460462
| InlayKind::ClosureReturnTypeHint
461463
| InlayKind::GenericParamListHint
462464
| InlayKind::ImplicitReborrowHint
463-
| InlayKind::TypeHint => false,
465+
| InlayKind::TypeHint
466+
| InlayKind::ClosingBraceHint => false,
464467
InlayKind::BindingModeHint => inlay_hint.label != "&",
465468
InlayKind::ParameterHint | InlayKind::LifetimeHint => true,
466469
}),

docs/user/generated_config.adoc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,17 @@ Whether to show inlay type hints for binding modes.
355355
--
356356
Whether to show inlay type hints for method chains.
357357
--
358+
[[rust-analyzer.inlayHints.closingBraceHints.enable]]rust-analyzer.inlayHints.closingBraceHints.enable (default: `true`)::
359+
+
360+
--
361+
Whether to show inlay hints after a closing `}` to indicate what item it belongs to.
362+
--
363+
[[rust-analyzer.inlayHints.closingBraceHints.minLines]]rust-analyzer.inlayHints.closingBraceHints.minLines (default: `25`)::
364+
+
365+
--
366+
Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1
367+
to always show them).
368+
--
358369
[[rust-analyzer.inlayHints.closureReturnTypeHints.enable]]rust-analyzer.inlayHints.closureReturnTypeHints.enable (default: `false`)::
359370
+
360371
--

editors/code/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,17 @@
792792
"default": true,
793793
"type": "boolean"
794794
},
795+
"rust-analyzer.inlayHints.closingBraceHints.enable": {
796+
"markdownDescription": "Whether to show inlay hints after a closing `}` to indicate what item it belongs to.",
797+
"default": true,
798+
"type": "boolean"
799+
},
800+
"rust-analyzer.inlayHints.closingBraceHints.minLines": {
801+
"markdownDescription": "Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1\nto always show them).",
802+
"default": 25,
803+
"type": "integer",
804+
"minimum": 0
805+
},
795806
"rust-analyzer.inlayHints.closureReturnTypeHints.enable": {
796807
"markdownDescription": "Whether to show inlay type hints for return types of closures with blocks.",
797808
"default": false,

0 commit comments

Comments
 (0)