Simplify most of the inlay hints tests

This commit is contained in:
Aleksey Kladov 2020-06-30 18:04:25 +02:00
parent 0954d31bee
commit 442c13ba17
2 changed files with 199 additions and 631 deletions

View file

@ -3,7 +3,7 @@ use ra_ide_db::RootDatabase;
use ra_prof::profile; use ra_prof::profile;
use ra_syntax::{ use ra_syntax::{
ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner},
match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
}; };
use crate::{FileId, FunctionSignature}; use crate::{FileId, FunctionSignature};
@ -112,7 +112,7 @@ fn get_chaining_hints(
// Ignoring extra whitespace and comments // Ignoring extra whitespace and comments
let next = tokens.next()?.kind(); let next = tokens.next()?.kind();
let next_next = tokens.next()?.kind(); let next_next = tokens.next()?.kind();
if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { if next == SyntaxKind::WHITESPACE && next_next == T![.] {
let ty = sema.type_of_expr(&expr)?; let ty = sema.type_of_expr(&expr)?;
if ty.is_unknown() { if ty.is_unknown() {
return None; return None;
@ -345,583 +345,245 @@ fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::inlay_hints::InlayHintsConfig;
use insta::assert_debug_snapshot; use insta::assert_debug_snapshot;
use test_utils::extract_annotations;
use crate::mock_analysis::single_file; use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
fn check(ra_fixture: &str) {
check_with_config(ra_fixture, InlayHintsConfig::default());
}
fn check_with_config(ra_fixture: &str, config: InlayHintsConfig) {
let (analysis, file_id) = single_file(ra_fixture);
let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
let actual =
inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
assert_eq!(expected, actual);
}
#[test] #[test]
fn param_hints_only() { fn param_hints_only() {
let (analysis, file_id) = single_file( check_with_config(
r#" r#"
fn foo(a: i32, b: i32) -> i32 { a + b } fn foo(a: i32, b: i32) -> i32 { a + b }
fn main() { fn main() {
let _x = foo(4, 4); let _x = foo(
}"#, 4,
//^ a
4,
//^ b
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" }"#,
[ InlayHintsConfig {
InlayHint { parameter_hints: true,
range: 69..70, type_hints: false,
kind: ParameterHint, chaining_hints: false,
label: "a", max_length: None,
}, },
InlayHint { );
range: 72..73,
kind: ParameterHint,
label: "b",
},
]
"###);
} }
#[test] #[test]
fn hints_disabled() { fn hints_disabled() {
let (analysis, file_id) = single_file( check_with_config(
r#" r#"
fn foo(a: i32, b: i32) -> i32 { a + b } fn foo(a: i32, b: i32) -> i32 { a + b }
fn main() { fn main() {
let _x = foo(4, 4); let _x = foo(4, 4);
}"#, }"#,
InlayHintsConfig {
type_hints: false,
parameter_hints: false,
chaining_hints: false,
max_length: None,
},
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
} }
#[test] #[test]
fn type_hints_only() { fn type_hints_only() {
let (analysis, file_id) = single_file( check_with_config(
r#" r#"
fn foo(a: i32, b: i32) -> i32 { a + b } fn foo(a: i32, b: i32) -> i32 { a + b }
fn main() { fn main() {
let _x = foo(4, 4); let _x = foo(4, 4);
//^^ i32
}"#, }"#,
); InlayHintsConfig {
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" type_hints: true,
[ parameter_hints: false,
InlayHint { chaining_hints: false,
range: 60..62, max_length: None,
kind: TypeHint,
label: "i32",
}, },
] );
"###);
} }
#[test] #[test]
fn default_generic_types_should_not_be_displayed() { fn default_generic_types_should_not_be_displayed() {
let (analysis, file_id) = single_file( check(
r#" r#"
struct Test<K, T = u8> { struct Test<K, T = u8> { k: K, t: T }
k: K,
t: T,
}
fn main() { fn main() {
let zz = Test { t: 23u8, k: 33 }; let zz = Test { t: 23u8, k: 33 };
//^^ Test<i32>
let zz_ref = &zz; let zz_ref = &zz;
//^^^^^^ &Test<i32>
}"#, }"#,
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
[
InlayHint {
range: 68..70,
kind: TypeHint,
label: "Test<i32>",
},
InlayHint {
range: 106..112,
kind: TypeHint,
label: "&Test<i32>",
},
]
"###
);
} }
#[test] #[test]
fn let_statement() { fn let_statement() {
let (analysis, file_id) = single_file( check(
r#" r#"
#[derive(PartialEq)] #[derive(PartialEq)]
enum CustomOption<T> { enum Option<T> { None, Some(T) }
None,
Some(T),
}
#[derive(PartialEq)] #[derive(PartialEq)]
struct Test { struct Test { a: Option<u32>, b: u8 }
a: CustomOption<u32>,
b: u8,
}
fn main() { fn main() {
struct InnerStruct {} struct InnerStruct {}
let test = 54; let test = 54;
//^^^^ i32
let test: i32 = 33; let test: i32 = 33;
let mut test = 33; let mut test = 33;
//^^^^^^^^ i32
let _ = 22; let _ = 22;
let test = "test"; let test = "test";
//^^^^ &str
let test = InnerStruct {}; let test = InnerStruct {};
let test = vec![222]; let test = unresolved();
let test: Vec<_> = (0..3).collect();
let test = (0..3).collect::<Vec<i128>>();
let test = (0..3).collect::<Vec<_>>();
let mut test = Vec::new();
test.push(333);
let test = (42, 'a'); let test = (42, 'a');
let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); //^^^^ (i32, char)
let (a, (b, (c,)) = (2, (3, (9.2,));
//^ i32 ^ i32 ^ f64
let &x = &92; let &x = &92;
//^ i32
}"#, }"#,
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
[
InlayHint {
range: 192..196,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 235..243,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 274..278,
kind: TypeHint,
label: "&str",
},
InlayHint {
range: 538..542,
kind: TypeHint,
label: "(i32, char)",
},
InlayHint {
range: 565..566,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 569..570,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 572..573,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 576..577,
kind: TypeHint,
label: "f64",
},
InlayHint {
range: 579..580,
kind: TypeHint,
label: "f64",
},
InlayHint {
range: 583..584,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 626..627,
kind: TypeHint,
label: "i32",
},
]
"###
);
} }
#[test] #[test]
fn closure_parameters() { fn closure_parameters() {
let (analysis, file_id) = single_file( check(
r#" r#"
fn main() { fn main() {
let mut start = 0; let mut start = 0;
(0..2).for_each(|increment| { //^^^^^^^^^ i32
start += increment; (0..2).for_each(|increment| { start += increment; });
}); //^^^^^^^^^ i32
let multiply = |a, b, c, d| a * b * c * d; let multiply =
let _: i32 = multiply(1, 2, 3, 4); //^^^^^^^^ |…| -> i32
| a, b| a * b
//^ i32 ^ i32
;
let _: i32 = multiply(1, 2);
let multiply_ref = &multiply; let multiply_ref = &multiply;
//^^^^^^^^^^^^ &|…| -> i32
let return_42 = || 42; let return_42 = || 42;
//^^^^^^^^^ || -> i32
}"#, }"#,
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
[
InlayHint {
range: 20..29,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 56..65,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 114..122,
kind: TypeHint,
label: "|…| -> i32",
},
InlayHint {
range: 126..127,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 129..130,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 132..133,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 135..136,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 200..212,
kind: TypeHint,
label: "&|…| -> i32",
},
InlayHint {
range: 235..244,
kind: TypeHint,
label: "|| -> i32",
},
]
"###
);
} }
#[test] #[test]
fn for_expression() { fn for_expression() {
let (analysis, file_id) = single_file( check(
r#" r#"
fn main() { fn main() {
let mut start = 0; let mut start = 0;
for increment in 0..2 { //^^^^^^^^^ i32
start += increment; for increment in 0..2 { start += increment; }
} //^^^^^^^^^ i32
}"#, }"#,
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
[
InlayHint {
range: 20..29,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 43..52,
kind: TypeHint,
label: "i32",
},
]
"###
);
} }
#[test] #[test]
fn if_expr() { fn if_expr() {
let (analysis, file_id) = single_file( check(
r#" r#"
#[derive(PartialEq)] enum Option<T> { None, Some(T) }
enum CustomOption<T> { use Option::*;
None,
Some(T),
}
#[derive(PartialEq)] struct Test { a: Option<u32>, b: u8 }
struct Test {
a: CustomOption<u32>,
b: u8,
}
use CustomOption::*;
fn main() { fn main() {
let test = Some(Test { a: Some(3), b: 1 }); let test = Some(Test { a: Some(3), b: 1 });
//^^^^ Option<Test>
if let None = &test {}; if let None = &test {};
if let test = &test {}; if let test = &test {};
//^^^^ &Option<Test>
if let Some(test) = &test {}; if let Some(test) = &test {};
//^^^^ &Test
if let Some(Test { a, b }) = &test {}; if let Some(Test { a, b }) = &test {};
//^ &Option<u32> ^ &u8
if let Some(Test { a: x, b: y }) = &test {}; if let Some(Test { a: x, b: y }) = &test {};
//^ &Option<u32> ^ &u8
if let Some(Test { a: Some(x), b: y }) = &test {}; if let Some(Test { a: Some(x), b: y }) = &test {};
//^ &u32 ^ &u8
if let Some(Test { a: None, b: y }) = &test {}; if let Some(Test { a: None, b: y }) = &test {};
//^ &u8
if let Some(Test { b: y, .. }) = &test {}; if let Some(Test { b: y, .. }) = &test {};
//^ &u8
if test == None {} if test == None {}
}"#, }"#,
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
[
InlayHint {
range: 187..191,
kind: TypeHint,
label: "CustomOption<Test>",
},
InlayHint {
range: 266..270,
kind: TypeHint,
label: "&CustomOption<Test>",
},
InlayHint {
range: 299..303,
kind: TypeHint,
label: "&Test",
},
InlayHint {
range: 340..341,
kind: TypeHint,
label: "&CustomOption<u32>",
},
InlayHint {
range: 343..344,
kind: TypeHint,
label: "&u8",
},
InlayHint {
range: 386..387,
kind: TypeHint,
label: "&CustomOption<u32>",
},
InlayHint {
range: 392..393,
kind: TypeHint,
label: "&u8",
},
InlayHint {
range: 440..441,
kind: TypeHint,
label: "&u32",
},
InlayHint {
range: 447..448,
kind: TypeHint,
label: "&u8",
},
InlayHint {
range: 499..500,
kind: TypeHint,
label: "&u8",
},
InlayHint {
range: 542..543,
kind: TypeHint,
label: "&u8",
},
]
"###
);
} }
#[test] #[test]
fn while_expr() { fn while_expr() {
let (analysis, file_id) = single_file( check(
r#" r#"
#[derive(PartialEq)] enum Option<T> { None, Some(T) }
enum CustomOption<T> { use Option::*;
None,
Some(T),
}
#[derive(PartialEq)] struct Test { a: Option<u32>, b: u8 }
struct Test {
a: CustomOption<u32>,
b: u8,
}
use CustomOption::*;
fn main() { fn main() {
let test = Some(Test { a: Some(3), b: 1 }); let test = Some(Test { a: Some(3), b: 1 });
while let None = &test {}; //^^^^ Option<Test>
while let test = &test {};
while let Some(test) = &test {};
while let Some(Test { a, b }) = &test {};
while let Some(Test { a: x, b: y }) = &test {};
while let Some(Test { a: Some(x), b: y }) = &test {}; while let Some(Test { a: Some(x), b: y }) = &test {};
while let Some(Test { a: None, b: y }) = &test {}; //^ &u32 ^ &u8
while let Some(Test { b: y, .. }) = &test {};
while test == None {}
}"#, }"#,
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
[
InlayHint {
range: 187..191,
kind: TypeHint,
label: "CustomOption<Test>",
},
InlayHint {
range: 272..276,
kind: TypeHint,
label: "&CustomOption<Test>",
},
InlayHint {
range: 308..312,
kind: TypeHint,
label: "&Test",
},
InlayHint {
range: 352..353,
kind: TypeHint,
label: "&CustomOption<u32>",
},
InlayHint {
range: 355..356,
kind: TypeHint,
label: "&u8",
},
InlayHint {
range: 401..402,
kind: TypeHint,
label: "&CustomOption<u32>",
},
InlayHint {
range: 407..408,
kind: TypeHint,
label: "&u8",
},
InlayHint {
range: 458..459,
kind: TypeHint,
label: "&u32",
},
InlayHint {
range: 465..466,
kind: TypeHint,
label: "&u8",
},
InlayHint {
range: 520..521,
kind: TypeHint,
label: "&u8",
},
InlayHint {
range: 566..567,
kind: TypeHint,
label: "&u8",
},
]
"###
);
} }
#[test] #[test]
fn match_arm_list() { fn match_arm_list() {
let (analysis, file_id) = single_file( check(
r#" r#"
#[derive(PartialEq)] enum Option<T> { None, Some(T) }
enum CustomOption<T> { use Option::*;
None,
Some(T),
}
#[derive(PartialEq)] struct Test { a: Option<u32>, b: u8 }
struct Test {
a: CustomOption<u32>,
b: u8,
}
use CustomOption::*;
fn main() { fn main() {
match Some(Test { a: Some(3), b: 1 }) { match Some(Test { a: Some(3), b: 1 }) {
None => (), None => (),
test => (), test => (),
Some(test) => (), //^^^^ Option<Test>
Some(Test { a, b }) => (),
Some(Test { a: x, b: y }) => (),
Some(Test { a: Some(x), b: y }) => (), Some(Test { a: Some(x), b: y }) => (),
Some(Test { a: None, b: y }) => (), //^ u32 ^ u8
Some(Test { b: y, .. }) => (),
_ => {} _ => {}
} }
}"#, }"#,
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
[
InlayHint {
range: 251..255,
kind: TypeHint,
label: "CustomOption<Test>",
},
InlayHint {
range: 276..280,
kind: TypeHint,
label: "Test",
},
InlayHint {
range: 309..310,
kind: TypeHint,
label: "CustomOption<u32>",
},
InlayHint {
range: 312..313,
kind: TypeHint,
label: "u8",
},
InlayHint {
range: 347..348,
kind: TypeHint,
label: "CustomOption<u32>",
},
InlayHint {
range: 353..354,
kind: TypeHint,
label: "u8",
},
InlayHint {
range: 393..394,
kind: TypeHint,
label: "u32",
},
InlayHint {
range: 400..401,
kind: TypeHint,
label: "u8",
},
InlayHint {
range: 444..445,
kind: TypeHint,
label: "u8",
},
InlayHint {
range: 479..480,
kind: TypeHint,
label: "u8",
},
]
"###
);
} }
#[test] #[test]
fn hint_truncation() { fn hint_truncation() {
let (analysis, file_id) = single_file( check_with_config(
r#" r#"
struct Smol<T>(T); struct Smol<T>(T);
@ -929,52 +591,26 @@ struct VeryLongOuterName<T>(T);
fn main() { fn main() {
let a = Smol(0u32); let a = Smol(0u32);
//^ Smol<u32>
let b = VeryLongOuterName(0usize); let b = VeryLongOuterName(0usize);
//^ VeryLongOuterName<…>
let c = Smol(Smol(0u32)) let c = Smol(Smol(0u32))
//^ Smol<Smol<…>>
}"#, }"#,
); InlayHintsConfig { max_length: Some(8), ..Default::default() },
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
[
InlayHint {
range: 73..74,
kind: TypeHint,
label: "Smol<u32>",
},
InlayHint {
range: 97..98,
kind: TypeHint,
label: "VeryLongOuterName<…>",
},
InlayHint {
range: 136..137,
kind: TypeHint,
label: "Smol<Smol<…>>",
},
]
"###
); );
} }
#[test] #[test]
fn function_call_parameter_hint() { fn function_call_parameter_hint() {
let (analysis, file_id) = single_file( check(
r#" r#"
enum CustomOption<T> { enum Option<T> { None, Some(T) }
None, use Option::*;
Some(T),
}
use CustomOption::*;
struct FileId {} struct FileId {}
struct SmolStr {} struct SmolStr {}
impl From<&str> for SmolStr {
fn from(_: &str) -> Self {
unimplemented!()
}
}
struct TextRange {} struct TextRange {}
struct SyntaxKind {} struct SyntaxKind {}
struct NavigationTarget {} struct NavigationTarget {}
@ -982,18 +618,15 @@ struct NavigationTarget {}
struct Test {} struct Test {}
impl Test { impl Test {
fn method(&self, mut param: i32) -> i32 { fn method(&self, mut param: i32) -> i32 { param * 2 }
param * 2
}
fn from_syntax( fn from_syntax(
file_id: FileId, file_id: FileId,
name: SmolStr, name: SmolStr,
focus_range: CustomOption<TextRange>, focus_range: Option<TextRange>,
full_range: TextRange, full_range: TextRange,
kind: SyntaxKind, kind: SyntaxKind,
docs: CustomOption<String>, docs: Option<String>,
description: CustomOption<String>,
) -> NavigationTarget { ) -> NavigationTarget {
NavigationTarget {} NavigationTarget {}
} }
@ -1005,108 +638,35 @@ fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
fn main() { fn main() {
let not_literal = 1; let not_literal = 1;
//^^^^^^^^^^^ i32
let _: i32 = test_func(1, 2, "hello", 3, not_literal); let _: i32 = test_func(1, 2, "hello", 3, not_literal);
//^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
let t: Test = Test {}; let t: Test = Test {};
t.method(123); t.method(123);
//^^^ param
Test::method(&t, 3456); Test::method(&t, 3456);
//^^ &self ^^^^ param
Test::from_syntax( Test::from_syntax(
FileId {}, FileId {},
//^^^^^^^^^ file_id
"impl".into(), "impl".into(),
//^^^^^^^^^^^^^ name
None, None,
//^^^^ focus_range
TextRange {}, TextRange {},
//^^^^^^^^^^^^ full_range
SyntaxKind {}, SyntaxKind {},
//^^^^^^^^^^^^^ kind
None, None,
None, //^^^^ docs
); );
}"#, }"#,
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
[
InlayHint {
range: 797..808,
kind: TypeHint,
label: "i32",
},
InlayHint {
range: 841..842,
kind: ParameterHint,
label: "foo",
},
InlayHint {
range: 844..845,
kind: ParameterHint,
label: "bar",
},
InlayHint {
range: 847..854,
kind: ParameterHint,
label: "msg",
},
InlayHint {
range: 859..870,
kind: ParameterHint,
label: "last",
},
InlayHint {
range: 913..916,
kind: ParameterHint,
label: "param",
},
InlayHint {
range: 936..938,
kind: ParameterHint,
label: "&self",
},
InlayHint {
range: 940..944,
kind: ParameterHint,
label: "param",
},
InlayHint {
range: 979..988,
kind: ParameterHint,
label: "file_id",
},
InlayHint {
range: 998..1011,
kind: ParameterHint,
label: "name",
},
InlayHint {
range: 1021..1025,
kind: ParameterHint,
label: "focus_range",
},
InlayHint {
range: 1035..1047,
kind: ParameterHint,
label: "full_range",
},
InlayHint {
range: 1057..1070,
kind: ParameterHint,
label: "kind",
},
InlayHint {
range: 1080..1084,
kind: ParameterHint,
label: "docs",
},
InlayHint {
range: 1094..1098,
kind: ParameterHint,
label: "description",
},
]
"###
);
} }
#[test] #[test]
fn omitted_parameters_hints_heuristics() { fn omitted_parameters_hints_heuristics() {
let (analysis, file_id) = single_file( check_with_config(
r#" r#"
fn map(f: i32) {} fn map(f: i32) {}
fn filter(predicate: i32) {} fn filter(predicate: i32) {}
@ -1187,23 +747,16 @@ fn main() {
let _: f64 = a.div_euclid(b); let _: f64 = a.div_euclid(b);
let _: f64 = a.abs_sub(b); let _: f64 = a.abs_sub(b);
}"#, }"#,
); InlayHintsConfig { max_length: Some(8), ..Default::default() },
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
[]
"###
); );
} }
#[test] #[test]
fn unit_structs_have_no_type_hints() { fn unit_structs_have_no_type_hints() {
let (analysis, file_id) = single_file( check_with_config(
r#" r#"
enum CustomResult<T, E> { enum Result<T, E> { Ok(T), Err(E) }
Ok(T), use Result::*;
Err(E),
}
use CustomResult::*;
struct SyntheticSyntax; struct SyntheticSyntax;
@ -1213,11 +766,7 @@ fn main() {
Err(SyntheticSyntax) => (), Err(SyntheticSyntax) => (),
} }
}"#, }"#,
); InlayHintsConfig { max_length: Some(8), ..Default::default() },
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
[]
"###
); );
} }
@ -1255,7 +804,7 @@ fn main() {
#[test] #[test]
fn chaining_hints_without_newlines() { fn chaining_hints_without_newlines() {
let (analysis, file_id) = single_file( check_with_config(
r#" r#"
struct A(B); struct A(B);
impl A { fn into_b(self) -> B { self.0 } } impl A { fn into_b(self) -> B { self.0 } }
@ -1266,8 +815,13 @@ fn main() {
fn main() { fn main() {
let c = A(B(C)).into_b().into_c(); let c = A(B(C)).into_b().into_c();
}"#, }"#,
InlayHintsConfig {
parameter_hints: false,
type_hints: false,
chaining_hints: true,
max_length: None,
},
); );
assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
} }
#[test] #[test]

View file

@ -11,7 +11,7 @@ pub mod mark;
mod fixture; mod fixture;
use std::{ use std::{
convert::TryInto, convert::{TryFrom, TryInto},
env, fs, env, fs,
path::{Path, PathBuf}, path::{Path, PathBuf},
}; };
@ -169,10 +169,9 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
for line in lines_with_ends(text) { for line in lines_with_ends(text) {
if let Some(idx) = line.find("//^") { if let Some(idx) = line.find("//^") {
let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]); let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]);
let marker_and_data = &line[idx + "//".len()..]; for (line_range, text) in extract_line_annotations(&line[idx + "//".len()..]) {
let len = marker_and_data.chars().take_while(|&it| it == '^').count(); res.push((line_range + offset, text))
let data = marker_and_data[len..].trim().to_string(); }
res.push((TextRange::at(offset, len.try_into().unwrap()), data))
} }
prev_line_start = Some(line_start); prev_line_start = Some(line_start);
line_start += TextSize::of(line); line_start += TextSize::of(line);
@ -180,13 +179,28 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> {
res res
} }
fn extract_line_annotations(mut line: &str) -> Vec<(TextRange, String)> {
let mut res = Vec::new();
let mut offset: TextSize = 0.into();
while !line.is_empty() {
let len = line.chars().take_while(|&it| it == '^').count();
assert!(len > 0);
let range = TextRange::at(offset, len.try_into().unwrap());
let next = line[len..].find('^').map_or(line.len(), |it| it + len);
res.push((range, line[len..][..next - len].trim().to_string()));
line = &line[next..];
offset += TextSize::try_from(next).unwrap();
}
res
}
#[test] #[test]
fn test_extract_annotations() { fn test_extract_annotations() {
let text = stdx::trim_indent( let text = stdx::trim_indent(
r#" r#"
fn main() { fn main() {
let x = 92; let (x, y) = (9, 2);
//^ def //^ def ^ def
zoo + 1 zoo + 1
} //^^^ i32 } //^^^ i32
"#, "#,
@ -195,7 +209,7 @@ fn main() {
.into_iter() .into_iter()
.map(|(range, ann)| (&text[range], ann)) .map(|(range, ann)| (&text[range], ann))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
assert_eq!(res, vec![("x", "def".into()), ("zoo", "i32".into()),]); assert_eq!(res, vec![("x", "def".into()), ("y", "def".into()), ("zoo", "i32".into()),]);
} }
// Comparison functionality borrowed from cargo: // Comparison functionality borrowed from cargo: