aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src')
-rw-r--r--crates/ra_ide/src/inlay_hints.rs97
1 files changed, 92 insertions, 5 deletions
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index ecd615cf4..2353ad71f 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -5,7 +5,7 @@ use ra_ide_db::RootDatabase;
5use ra_prof::profile; 5use ra_prof::profile;
6use ra_syntax::{ 6use ra_syntax::{
7 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 7 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
8 match_ast, SmolStr, TextRange, 8 match_ast, SmolStr, TextRange, NodeOrToken, SyntaxKind, Direction
9}; 9};
10 10
11use crate::{FileId, FunctionSignature}; 11use crate::{FileId, FunctionSignature};
@@ -14,12 +14,18 @@ use crate::{FileId, FunctionSignature};
14pub struct InlayHintsOptions { 14pub struct InlayHintsOptions {
15 pub type_hints: bool, 15 pub type_hints: bool,
16 pub parameter_hints: bool, 16 pub parameter_hints: bool,
17 pub chaining_hints: bool,
17 pub max_length: Option<usize>, 18 pub max_length: Option<usize>,
18} 19}
19 20
20impl Default for InlayHintsOptions { 21impl Default for InlayHintsOptions {
21 fn default() -> Self { 22 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 }
23 } 29 }
24} 30}
25 31
@@ -27,6 +33,7 @@ impl Default for InlayHintsOptions {
27pub enum InlayKind { 33pub enum InlayKind {
28 TypeHint, 34 TypeHint,
29 ParameterHint, 35 ParameterHint,
36 ChainingHint,
30} 37}
31 38
32#[derive(Debug)] 39#[derive(Debug)]
@@ -47,6 +54,10 @@ pub(crate) fn inlay_hints(
47 54
48 let mut res = Vec::new(); 55 let mut res = Vec::new();
49 for node in file.syntax().descendants() { 56 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
50 match_ast! { 61 match_ast! {
51 match node { 62 match node {
52 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); }, 63 ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, options, ast::Expr::from(it)); },
@@ -222,6 +233,45 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
222 } 233 }
223} 234}
224 235
236fn 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
225#[cfg(test)] 275#[cfg(test)]
226mod tests { 276mod tests {
227 use crate::inlay_hints::InlayHintsOptions; 277 use crate::inlay_hints::InlayHintsOptions;
@@ -230,6 +280,43 @@ mod tests {
230 use crate::mock_analysis::single_file; 280 use crate::mock_analysis::single_file;
231 281
232 #[test] 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 #[test]
233 fn param_hints_only() { 320 fn param_hints_only() {
234 let (analysis, file_id) = single_file( 321 let (analysis, file_id) = single_file(
235 r#" 322 r#"
@@ -238,7 +325,7 @@ mod tests {
238 let _x = foo(4, 4); 325 let _x = foo(4, 4);
239 }"#, 326 }"#,
240 ); 327 );
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###"
242 [ 329 [
243 InlayHint { 330 InlayHint {
244 range: [106; 107), 331 range: [106; 107),
@@ -262,7 +349,7 @@ mod tests {
262 let _x = foo(4, 4); 349 let _x = foo(4, 4);
263 }"#, 350 }"#,
264 ); 351 );
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###"[]"###);
266 } 353 }
267 354
268 #[test] 355 #[test]
@@ -274,7 +361,7 @@ mod tests {
274 let _x = foo(4, 4); 361 let _x = foo(4, 4);
275 }"#, 362 }"#,
276 ); 363 );
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###"
278 [ 365 [
279 InlayHint { 366 InlayHint {
280 range: [97; 99), 367 range: [97; 99),