@@ -70,6 +70,45 @@ pub(crate) fn inlay_hints(
70
70
res
71
71
}
72
72
73
+ fn get_chaining_hints (
74
+ acc : & mut Vec < InlayHint > ,
75
+ sema : & Semantics < RootDatabase > ,
76
+ options : & InlayHintsOptions ,
77
+ expr : ast:: Expr ,
78
+ ) -> Option < ( ) > {
79
+ if !options. chaining_hints {
80
+ return None ;
81
+ }
82
+
83
+ let ty = sema. type_of_expr ( & expr) ?;
84
+ let label = ty. display_truncated ( sema. db , options. max_length ) . to_string ( ) ;
85
+ if ty. is_unknown ( ) {
86
+ return None ;
87
+ }
88
+
89
+ let mut tokens = expr. syntax ( )
90
+ . siblings_with_tokens ( Direction :: Next )
91
+ . filter_map ( NodeOrToken :: into_token)
92
+ . filter ( |t| match t. kind ( ) {
93
+ SyntaxKind :: WHITESPACE if !t. text ( ) . contains ( '\n' ) => false ,
94
+ SyntaxKind :: COMMENT => false ,
95
+ _ => true ,
96
+ } ) ;
97
+
98
+ // Chaining can be defined as an expression whose next sibling tokens are newline and dot
99
+ // Ignoring extra whitespace and comments
100
+ let next = tokens. next ( ) ?. kind ( ) ;
101
+ let next_next = tokens. next ( ) ?. kind ( ) ;
102
+ if next == SyntaxKind :: WHITESPACE && next_next == SyntaxKind :: DOT {
103
+ acc. push ( InlayHint {
104
+ range : expr. syntax ( ) . text_range ( ) ,
105
+ kind : InlayKind :: ChainingHint ,
106
+ label : label. into ( ) ,
107
+ } ) ;
108
+ }
109
+ Some ( ( ) )
110
+ }
111
+
73
112
fn get_param_name_hints (
74
113
acc : & mut Vec < InlayHint > ,
75
114
sema : & Semantics < RootDatabase > ,
@@ -233,89 +272,13 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
233
272
}
234
273
}
235
274
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
-
275
275
#[ cfg( test) ]
276
276
mod tests {
277
277
use crate :: inlay_hints:: InlayHintsOptions ;
278
278
use insta:: assert_debug_snapshot;
279
279
280
280
use crate :: mock_analysis:: single_file;
281
281
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
-
319
282
#[ test]
320
283
fn param_hints_only ( ) {
321
284
let ( analysis, file_id) = single_file (
@@ -1139,4 +1102,124 @@ fn main() {
1139
1102
"###
1140
1103
) ;
1141
1104
}
1105
+
1106
+ #[ test]
1107
+ fn chaining_hints_ignore_comments ( ) {
1108
+ let ( analysis, file_id) = single_file (
1109
+ r#"
1110
+ struct A(B);
1111
+ impl A { fn into_b(self) -> B { self.0 } }
1112
+ struct B(C)
1113
+ impl B { fn into_c(self) -> C { self.0 } }
1114
+ struct C;
1115
+
1116
+ fn main() {
1117
+ let c = A(B(C))
1118
+ .into_b() // This is a comment
1119
+ .into_c();
1120
+ }"# ,
1121
+ ) ;
1122
+ assert_debug_snapshot ! ( analysis. inlay_hints( file_id, & InlayHintsOptions { parameter_hints: false , type_hints: false , chaining_hints: true , max_length: None } ) . unwrap( ) , @r###"
1123
+ [
1124
+ InlayHint {
1125
+ range: [231; 268),
1126
+ kind: ChainingHint,
1127
+ label: "B",
1128
+ },
1129
+ InlayHint {
1130
+ range: [231; 238),
1131
+ kind: ChainingHint,
1132
+ label: "A",
1133
+ },
1134
+ ]"### ) ;
1135
+ }
1136
+
1137
+ #[ test]
1138
+ fn chaining_hints_without_newlines ( ) {
1139
+ let ( analysis, file_id) = single_file (
1140
+ r#"
1141
+ struct A(B);
1142
+ impl A { fn into_b(self) -> B { self.0 } }
1143
+ struct B(C)
1144
+ impl B { fn into_c(self) -> C { self.0 } }
1145
+ struct C;
1146
+
1147
+ fn main() {
1148
+ let c = A(B(C)).into_b().into_c();
1149
+ }"# ,
1150
+ ) ;
1151
+ assert_debug_snapshot ! ( analysis. inlay_hints( file_id, & InlayHintsOptions { parameter_hints: false , type_hints: false , chaining_hints: true , max_length: None } ) . unwrap( ) , @r###"[]"### ) ;
1152
+ }
1153
+
1154
+ #[ test]
1155
+ fn struct_access_chaining_hints ( ) {
1156
+ let ( analysis, file_id) = single_file (
1157
+ r#"
1158
+ struct A { pub b: B }
1159
+ struct B { pub c: C }
1160
+ struct C(pub bool);
1161
+
1162
+ fn main() {
1163
+ let x = A { b: B { c: C(true) } }
1164
+ .b
1165
+ .c
1166
+ .0;
1167
+ }"# ,
1168
+ ) ;
1169
+ assert_debug_snapshot ! ( analysis. inlay_hints( file_id, & InlayHintsOptions { parameter_hints: false , type_hints: false , chaining_hints: true , max_length: None } ) . unwrap( ) , @r###"
1170
+ [
1171
+ InlayHint {
1172
+ range: [150; 221),
1173
+ kind: ChainingHint,
1174
+ label: "C",
1175
+ },
1176
+ InlayHint {
1177
+ range: [150; 198),
1178
+ kind: ChainingHint,
1179
+ label: "B",
1180
+ },
1181
+ InlayHint {
1182
+ range: [150; 175),
1183
+ kind: ChainingHint,
1184
+ label: "A",
1185
+ },
1186
+ ]"### ) ;
1187
+ }
1188
+
1189
+ #[ test]
1190
+ fn generic_chaining_hints ( ) {
1191
+ let ( analysis, file_id) = single_file (
1192
+ r#"
1193
+ struct A<T>(T);
1194
+ struct B<T>(T);
1195
+ struct C<T>(T);
1196
+ struct X<T,R>(T, R);
1197
+
1198
+ impl<T> A<T> {
1199
+ fn new(t: T) -> Self { A(t) }
1200
+ fn into_b(self) -> B<T> { B(self.0) }
1201
+ }
1202
+ impl<T> B<T> {
1203
+ fn into_c(self) -> C<T> { C(self.0) }
1204
+ }
1205
+ fn main() {
1206
+ let c = A::new(X(42, true))
1207
+ .into_b()
1208
+ .into_c();
1209
+ }"# ,
1210
+ ) ;
1211
+ assert_debug_snapshot ! ( analysis. inlay_hints( file_id, & InlayHintsOptions { parameter_hints: false , type_hints: false , chaining_hints: true , max_length: None } ) . unwrap( ) , @r###"
1212
+ [
1213
+ InlayHint {
1214
+ range: [416; 465),
1215
+ kind: ChainingHint,
1216
+ label: "B<X<i32, bool>>",
1217
+ },
1218
+ InlayHint {
1219
+ range: [416; 435),
1220
+ kind: ChainingHint,
1221
+ label: "A<X<i32, bool>>",
1222
+ },
1223
+ ]"### ) ;
1224
+ }
1142
1225
}
0 commit comments