From 442c13ba176a40491deb7f9d2a2e1e24eca29f63 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 30 Jun 2020 18:04:25 +0200 Subject: [PATCH] Simplify most of the inlay hints tests --- crates/ra_ide/src/inlay_hints.rs | 800 +++++++------------------------ crates/test_utils/src/lib.rs | 30 +- 2 files changed, 199 insertions(+), 631 deletions(-) diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index c876525551..64980a832f 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs @@ -3,7 +3,7 @@ use ra_ide_db::RootDatabase; use ra_prof::profile; use ra_syntax::{ ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, - match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, + match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T, }; use crate::{FileId, FunctionSignature}; @@ -112,7 +112,7 @@ fn get_chaining_hints( // Ignoring extra whitespace and comments let 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)?; if ty.is_unknown() { return None; @@ -345,583 +345,245 @@ fn get_fn_signature(sema: &Semantics, expr: &ast::Expr) -> Option< #[cfg(test)] mod tests { - use crate::inlay_hints::InlayHintsConfig; 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::>(); + assert_eq!(expected, actual); + } #[test] fn param_hints_only() { - let (analysis, file_id) = single_file( + check_with_config( r#" - fn foo(a: i32, b: i32) -> i32 { a + b } - fn main() { - let _x = foo(4, 4); - }"#, +fn foo(a: i32, b: i32) -> i32 { a + b } +fn main() { + let _x = foo( + 4, + //^ a + 4, + //^ b + ); +}"#, + InlayHintsConfig { + parameter_hints: true, + type_hints: false, + chaining_hints: false, + max_length: None, + }, ); - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" - [ - InlayHint { - range: 69..70, - kind: ParameterHint, - label: "a", - }, - InlayHint { - range: 72..73, - kind: ParameterHint, - label: "b", - }, - ] - "###); } #[test] fn hints_disabled() { - let (analysis, file_id) = single_file( + check_with_config( r#" - fn foo(a: i32, b: i32) -> i32 { a + b } - fn main() { - let _x = foo(4, 4); - }"#, +fn foo(a: i32, b: i32) -> i32 { a + b } +fn main() { + 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] fn type_hints_only() { - let (analysis, file_id) = single_file( + check_with_config( r#" - fn foo(a: i32, b: i32) -> i32 { a + b } - fn main() { - let _x = foo(4, 4); - }"#, - ); - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###" - [ - InlayHint { - range: 60..62, - kind: TypeHint, - label: "i32", +fn foo(a: i32, b: i32) -> i32 { a + b } +fn main() { + let _x = foo(4, 4); + //^^ i32 +}"#, + InlayHintsConfig { + type_hints: true, + parameter_hints: false, + chaining_hints: false, + max_length: None, }, - ] - "###); + ); } + #[test] fn default_generic_types_should_not_be_displayed() { - let (analysis, file_id) = single_file( + check( r#" -struct Test { - k: K, - t: T, -} +struct Test { k: K, t: T } fn main() { let zz = Test { t: 23u8, k: 33 }; + //^^ Test let zz_ref = &zz; + //^^^^^^ &Test }"#, ); - - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" - [ - InlayHint { - range: 68..70, - kind: TypeHint, - label: "Test", - }, - InlayHint { - range: 106..112, - kind: TypeHint, - label: "&Test", - }, - ] - "### - ); } #[test] fn let_statement() { - let (analysis, file_id) = single_file( + check( r#" #[derive(PartialEq)] -enum CustomOption { - None, - Some(T), -} +enum Option { None, Some(T) } #[derive(PartialEq)] -struct Test { - a: CustomOption, - b: u8, -} +struct Test { a: Option, b: u8 } fn main() { struct InnerStruct {} let test = 54; + //^^^^ i32 let test: i32 = 33; let mut test = 33; + //^^^^^^^^ i32 let _ = 22; let test = "test"; + //^^^^ &str let test = InnerStruct {}; - let test = vec![222]; - let test: Vec<_> = (0..3).collect(); - let test = (0..3).collect::>(); - let test = (0..3).collect::>(); - - let mut test = Vec::new(); - test.push(333); + let test = unresolved(); 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; + //^ 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] fn closure_parameters() { - let (analysis, file_id) = single_file( + check( r#" fn main() { let mut start = 0; - (0..2).for_each(|increment| { - start += increment; - }); + //^^^^^^^^^ i32 + (0..2).for_each(|increment| { start += increment; }); + //^^^^^^^^^ i32 - let multiply = |a, b, c, d| a * b * c * d; - let _: i32 = multiply(1, 2, 3, 4); + let multiply = + //^^^^^^^^ |…| -> i32 + | a, b| a * b + //^ i32 ^ i32 + ; + + let _: i32 = multiply(1, 2); let multiply_ref = &multiply; + //^^^^^^^^^^^^ &|…| -> i32 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] fn for_expression() { - let (analysis, file_id) = single_file( + check( r#" fn main() { let mut start = 0; - for increment in 0..2 { - start += increment; - } + //^^^^^^^^^ i32 + 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] fn if_expr() { - let (analysis, file_id) = single_file( + check( r#" -#[derive(PartialEq)] -enum CustomOption { - None, - Some(T), -} +enum Option { None, Some(T) } +use Option::*; -#[derive(PartialEq)] -struct Test { - a: CustomOption, - b: u8, -} - -use CustomOption::*; +struct Test { a: Option, b: u8 } fn main() { let test = Some(Test { a: Some(3), b: 1 }); + //^^^^ Option if let None = &test {}; if let test = &test {}; + //^^^^ &Option if let Some(test) = &test {}; - if let Some(Test { a, b }) = &test {}; - if let Some(Test { a: x, b: y }) = &test {}; - if let Some(Test { a: Some(x), b: y }) = &test {}; - if let Some(Test { a: None, b: y }) = &test {}; + //^^^^ &Test + if let Some(Test { a, b }) = &test {}; + //^ &Option ^ &u8 + if let Some(Test { a: x, b: y }) = &test {}; + //^ &Option ^ &u8 + if let Some(Test { a: Some(x), b: y }) = &test {}; + //^ &u32 ^ &u8 + if let Some(Test { a: None, b: y }) = &test {}; + //^ &u8 if let Some(Test { b: y, .. }) = &test {}; - + //^ &u8 if test == None {} }"#, ); - - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" - [ - InlayHint { - range: 187..191, - kind: TypeHint, - label: "CustomOption", - }, - InlayHint { - range: 266..270, - kind: TypeHint, - label: "&CustomOption", - }, - InlayHint { - range: 299..303, - kind: TypeHint, - label: "&Test", - }, - InlayHint { - range: 340..341, - kind: TypeHint, - label: "&CustomOption", - }, - InlayHint { - range: 343..344, - kind: TypeHint, - label: "&u8", - }, - InlayHint { - range: 386..387, - kind: TypeHint, - label: "&CustomOption", - }, - 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] fn while_expr() { - let (analysis, file_id) = single_file( + check( r#" -#[derive(PartialEq)] -enum CustomOption { - None, - Some(T), -} +enum Option { None, Some(T) } +use Option::*; -#[derive(PartialEq)] -struct Test { - a: CustomOption, - b: u8, -} - -use CustomOption::*; +struct Test { a: Option, b: u8 } fn main() { let test = Some(Test { a: Some(3), b: 1 }); - while let None = &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: None, b: y }) = &test {}; - while let Some(Test { b: y, .. }) = &test {}; - - while test == None {} + //^^^^ Option + while let Some(Test { a: Some(x), b: y }) = &test {}; + //^ &u32 ^ &u8 }"#, ); - - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" - [ - InlayHint { - range: 187..191, - kind: TypeHint, - label: "CustomOption", - }, - InlayHint { - range: 272..276, - kind: TypeHint, - label: "&CustomOption", - }, - InlayHint { - range: 308..312, - kind: TypeHint, - label: "&Test", - }, - InlayHint { - range: 352..353, - kind: TypeHint, - label: "&CustomOption", - }, - InlayHint { - range: 355..356, - kind: TypeHint, - label: "&u8", - }, - InlayHint { - range: 401..402, - kind: TypeHint, - label: "&CustomOption", - }, - 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] fn match_arm_list() { - let (analysis, file_id) = single_file( + check( r#" -#[derive(PartialEq)] -enum CustomOption { - None, - Some(T), -} +enum Option { None, Some(T) } +use Option::*; -#[derive(PartialEq)] -struct Test { - a: CustomOption, - b: u8, -} - -use CustomOption::*; +struct Test { a: Option, b: u8 } fn main() { match Some(Test { a: Some(3), b: 1 }) { None => (), test => (), - Some(test) => (), - Some(Test { a, b }) => (), - Some(Test { a: x, b: y }) => (), + //^^^^ Option Some(Test { a: Some(x), b: y }) => (), - Some(Test { a: None, b: y }) => (), - Some(Test { b: y, .. }) => (), + //^ u32 ^ u8 _ => {} } }"#, ); - - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###" - [ - InlayHint { - range: 251..255, - kind: TypeHint, - label: "CustomOption", - }, - InlayHint { - range: 276..280, - kind: TypeHint, - label: "Test", - }, - InlayHint { - range: 309..310, - kind: TypeHint, - label: "CustomOption", - }, - InlayHint { - range: 312..313, - kind: TypeHint, - label: "u8", - }, - InlayHint { - range: 347..348, - kind: TypeHint, - label: "CustomOption", - }, - 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] fn hint_truncation() { - let (analysis, file_id) = single_file( + check_with_config( r#" struct Smol(T); @@ -929,52 +591,26 @@ struct VeryLongOuterName(T); fn main() { let a = Smol(0u32); + //^ Smol let b = VeryLongOuterName(0usize); + //^ VeryLongOuterName<…> let c = Smol(Smol(0u32)) + //^ Smol> }"#, - ); - - 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", - }, - InlayHint { - range: 97..98, - kind: TypeHint, - label: "VeryLongOuterName<…>", - }, - InlayHint { - range: 136..137, - kind: TypeHint, - label: "Smol>", - }, - ] - "### + InlayHintsConfig { max_length: Some(8), ..Default::default() }, ); } #[test] fn function_call_parameter_hint() { - let (analysis, file_id) = single_file( + check( r#" -enum CustomOption { - None, - Some(T), -} -use CustomOption::*; +enum Option { None, Some(T) } +use Option::*; struct FileId {} struct SmolStr {} -impl From<&str> for SmolStr { - fn from(_: &str) -> Self { - unimplemented!() - } -} - struct TextRange {} struct SyntaxKind {} struct NavigationTarget {} @@ -982,18 +618,15 @@ struct NavigationTarget {} struct Test {} impl Test { - fn method(&self, mut param: i32) -> i32 { - param * 2 - } + fn method(&self, mut param: i32) -> i32 { param * 2 } fn from_syntax( file_id: FileId, name: SmolStr, - focus_range: CustomOption, + focus_range: Option, full_range: TextRange, kind: SyntaxKind, - docs: CustomOption, - description: CustomOption, + docs: Option, ) -> NavigationTarget { NavigationTarget {} } @@ -1005,108 +638,35 @@ fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 { fn main() { let not_literal = 1; - let _: i32 = test_func(1, 2, "hello", 3, not_literal); + //^^^^^^^^^^^ i32 + let _: i32 = test_func(1, 2, "hello", 3, not_literal); + //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last let t: Test = Test {}; t.method(123); - Test::method(&t, 3456); - + //^^^ param + Test::method(&t, 3456); + //^^ &self ^^^^ param Test::from_syntax( FileId {}, + //^^^^^^^^^ file_id "impl".into(), + //^^^^^^^^^^^^^ name None, + //^^^^ focus_range TextRange {}, + //^^^^^^^^^^^^ full_range SyntaxKind {}, + //^^^^^^^^^^^^^ kind 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] fn omitted_parameters_hints_heuristics() { - let (analysis, file_id) = single_file( + check_with_config( r#" fn map(f: i32) {} fn filter(predicate: i32) {} @@ -1187,23 +747,16 @@ fn main() { let _: f64 = a.div_euclid(b); let _: f64 = a.abs_sub(b); }"#, - ); - - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###" - [] - "### + InlayHintsConfig { max_length: Some(8), ..Default::default() }, ); } #[test] fn unit_structs_have_no_type_hints() { - let (analysis, file_id) = single_file( + check_with_config( r#" -enum CustomResult { - Ok(T), - Err(E), -} -use CustomResult::*; +enum Result { Ok(T), Err(E) } +use Result::*; struct SyntheticSyntax; @@ -1213,11 +766,7 @@ fn main() { Err(SyntheticSyntax) => (), } }"#, - ); - - assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###" - [] - "### + InlayHintsConfig { max_length: Some(8), ..Default::default() }, ); } @@ -1255,42 +804,47 @@ fn main() { #[test] fn chaining_hints_without_newlines() { - let (analysis, file_id) = single_file( + check_with_config( r#" - struct A(B); - impl A { fn into_b(self) -> B { self.0 } } - struct B(C); - impl B { fn into_c(self) -> C { self.0 } } - struct C; +struct A(B); +impl A { fn into_b(self) -> B { self.0 } } +struct B(C); +impl B { fn into_c(self) -> C { self.0 } } +struct C; - fn main() { - let c = A(B(C)).into_b().into_c(); - }"#, +fn main() { + 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] fn struct_access_chaining_hints() { let (analysis, file_id) = single_file( r#" - struct A { pub b: B } - struct B { pub c: C } - struct C(pub bool); - struct D; +struct A { pub b: B } +struct B { pub c: C } +struct C(pub bool); +struct D; - impl D { - fn foo(&self) -> i32 { 42 } - } +impl D { + fn foo(&self) -> i32 { 42 } +} - fn main() { - let x = A { b: B { c: C(true) } } - .b - .c - .0; - let x = D - .foo(); - }"#, +fn main() { + let x = A { b: B { c: C(true) } } + .b + .c + .0; + let x = D + .foo(); +}"#, ); assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###" [ diff --git a/crates/test_utils/src/lib.rs b/crates/test_utils/src/lib.rs index f9d6c6c96c..e32a0a0c31 100644 --- a/crates/test_utils/src/lib.rs +++ b/crates/test_utils/src/lib.rs @@ -11,7 +11,7 @@ pub mod mark; mod fixture; use std::{ - convert::TryInto, + convert::{TryFrom, TryInto}, env, fs, path::{Path, PathBuf}, }; @@ -169,10 +169,9 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { for line in lines_with_ends(text) { if let Some(idx) = line.find("//^") { let offset = prev_line_start.unwrap() + TextSize::of(&line[..idx + "//".len()]); - let marker_and_data = &line[idx + "//".len()..]; - let len = marker_and_data.chars().take_while(|&it| it == '^').count(); - let data = marker_and_data[len..].trim().to_string(); - res.push((TextRange::at(offset, len.try_into().unwrap()), data)) + for (line_range, text) in extract_line_annotations(&line[idx + "//".len()..]) { + res.push((line_range + offset, text)) + } } prev_line_start = Some(line_start); line_start += TextSize::of(line); @@ -180,13 +179,28 @@ pub fn extract_annotations(text: &str) -> Vec<(TextRange, String)> { 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] fn test_extract_annotations() { let text = stdx::trim_indent( r#" fn main() { - let x = 92; - //^ def + let (x, y) = (9, 2); + //^ def ^ def zoo + 1 } //^^^ i32 "#, @@ -195,7 +209,7 @@ fn main() { .into_iter() .map(|(range, ann)| (&text[range], ann)) .collect::>(); - 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: