Skip to content

Commit a197abb

Browse files
committed
Added new inlay hint kind and rules for method chaining
1 parent fae6271 commit a197abb

File tree

5 files changed

+98
-5
lines changed

5 files changed

+98
-5
lines changed

crates/ra_ide/src/inlay_hints.rs

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use ra_ide_db::RootDatabase;
55
use ra_prof::profile;
66
use ra_syntax::{
77
ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
8-
match_ast, SmolStr, TextRange,
8+
match_ast, SmolStr, TextRange, NodeOrToken, SyntaxKind, Direction
99
};
1010

1111
use crate::{FileId, FunctionSignature};
@@ -14,19 +14,26 @@ use crate::{FileId, FunctionSignature};
1414
pub struct InlayHintsOptions {
1515
pub type_hints: bool,
1616
pub parameter_hints: bool,
17+
pub chaining_hints: bool,
1718
pub max_length: Option<usize>,
1819
}
1920

2021
impl Default for InlayHintsOptions {
2122
fn default() -> Self {
22-
Self { type_hints: true, parameter_hints: true, max_length: None }
23+
Self {
24+
type_hints: true,
25+
parameter_hints: true,
26+
chaining_hints: true,
27+
max_length: None
28+
}
2329
}
2430
}
2531

2632
#[derive(Clone, Debug, PartialEq, Eq)]
2733
pub enum InlayKind {
2834
TypeHint,
2935
ParameterHint,
36+
ChainingHint,
3037
}
3138

3239
#[derive(Debug)]
@@ -47,6 +54,10 @@ pub(crate) fn inlay_hints(
4754

4855
let mut res = Vec::new();
4956
for node in file.syntax().descendants() {
57+
if let Some(expr) = ast::Expr::cast(node.clone()) {
58+
get_chaining_hints(&mut res, &sema, options, expr);
59+
}
60+
5061
match_ast! {
5162
match node {
5263
ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); },
@@ -222,13 +233,89 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
222233
}
223234
}
224235

236+
fn get_chaining_hints(
237+
acc: &mut Vec<InlayHint>,
238+
sema: &Semantics<RootDatabase>,
239+
options: &InlayHintsOptions,
240+
expr: ast::Expr,
241+
) -> Option<()> {
242+
if !options.chaining_hints {
243+
return None;
244+
}
245+
246+
let ty = sema.type_of_expr(&expr)?;
247+
let label = ty.display_truncated(sema.db, options.max_length).to_string();
248+
if ty.is_unknown() {
249+
return None;
250+
}
251+
252+
let mut tokens = expr.syntax()
253+
.siblings_with_tokens(Direction::Next)
254+
.filter_map(NodeOrToken::into_token)
255+
.filter(|t| match t.kind() {
256+
SyntaxKind::WHITESPACE if !t.text().contains('\n') => false,
257+
SyntaxKind::COMMENT => false,
258+
_ => true,
259+
});
260+
261+
// Chaining can be defined as an expression whose next sibling tokens are newline and dot
262+
// Ignoring extra whitespace and comments
263+
let next = tokens.next()?.kind();
264+
let next_next = tokens.next()?.kind();
265+
if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT {
266+
acc.push(InlayHint {
267+
range: expr.syntax().text_range(),
268+
kind: InlayKind::ChainingHint,
269+
label: label.into(),
270+
});
271+
}
272+
Some(())
273+
}
274+
225275
#[cfg(test)]
226276
mod tests {
227277
use crate::inlay_hints::InlayHintsOptions;
228278
use insta::assert_debug_snapshot;
229279

230280
use crate::mock_analysis::single_file;
231281

282+
#[test]
283+
fn generic_chaining_hints() {
284+
let (analysis, file_id) = single_file(
285+
r#"
286+
struct A<T>(T);
287+
struct B<T>(T);
288+
struct C<T>(T);
289+
struct X<T,R>(T, R);
290+
291+
impl<T> A<T> {
292+
fn new(t: T) -> Self { A(t) }
293+
fn into_b(self) -> B<T> { B(self.0) }
294+
}
295+
impl<T> B<T> {
296+
fn into_c(self) -> C<T> { C(self.0) }
297+
}
298+
fn test() {
299+
let c = A::new(X(42, true))
300+
.into_b() // All the from A -> B -> C
301+
.into_c();
302+
}"#,
303+
);
304+
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
305+
[
306+
InlayHint {
307+
range: [416; 465),
308+
kind: ChainingHint,
309+
label: "B<X<i32, bool>>",
310+
},
311+
InlayHint {
312+
range: [416; 435),
313+
kind: ChainingHint,
314+
label: "A<X<i32, bool>>",
315+
},
316+
]"###);
317+
}
318+
232319
#[test]
233320
fn param_hints_only() {
234321
let (analysis, file_id) = single_file(
@@ -238,7 +325,7 @@ mod tests {
238325
let _x = foo(4, 4);
239326
}"#,
240327
);
241-
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, max_length: None}).unwrap(), @r###"
328+
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
242329
[
243330
InlayHint {
244331
range: [106; 107),
@@ -262,7 +349,7 @@ mod tests {
262349
let _x = foo(4, 4);
263350
}"#,
264351
);
265-
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, max_length: None}).unwrap(), @r###"[]"###);
352+
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
266353
}
267354

268355
#[test]
@@ -274,7 +361,7 @@ mod tests {
274361
let _x = foo(4, 4);
275362
}"#,
276363
);
277-
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, max_length: None}).unwrap(), @r###"
364+
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsOptions{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
278365
[
279366
InlayHint {
280367
range: [97; 99),

crates/rust-analyzer/src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ pub struct ServerConfig {
3434
pub inlay_hints_type: bool,
3535
#[serde(deserialize_with = "nullable_bool_true")]
3636
pub inlay_hints_parameter: bool,
37+
#[serde(deserialize_with = "nullable_bool_true")]
38+
pub inlay_hints_chaining: bool,
3739
pub inlay_hints_max_length: Option<usize>,
3840

3941
pub cargo_watch_enable: bool,
@@ -66,6 +68,7 @@ impl Default for ServerConfig {
6668
lru_capacity: None,
6769
inlay_hints_type: true,
6870
inlay_hints_parameter: true,
71+
inlay_hints_chaining: true,
6972
inlay_hints_max_length: None,
7073
cargo_watch_enable: true,
7174
cargo_watch_args: Vec::new(),

crates/rust-analyzer/src/conv.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ impl ConvWith<&LineIndex> for InlayHint {
332332
kind: match self.kind {
333333
InlayKind::ParameterHint => req::InlayKind::ParameterHint,
334334
InlayKind::TypeHint => req::InlayKind::TypeHint,
335+
InlayKind::ChainingHint => req::InlayKind::ChainingHint,
335336
},
336337
}
337338
}

crates/rust-analyzer/src/main_loop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ pub fn main_loop(
183183
inlay_hints: InlayHintsOptions {
184184
type_hints: config.inlay_hints_type,
185185
parameter_hints: config.inlay_hints_parameter,
186+
chaining_hints: config.inlay_hints_chaining,
186187
max_length: config.inlay_hints_max_length,
187188
},
188189
cargo_watch: CheckOptions {

crates/rust-analyzer/src/req.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ pub struct InlayHintsParams {
200200
pub enum InlayKind {
201201
TypeHint,
202202
ParameterHint,
203+
ChainingHint,
203204
}
204205

205206
#[derive(Debug, Deserialize, Serialize)]

0 commit comments

Comments
 (0)