@@ -155,7 +155,7 @@ pub fn add_cursor(text: &str, offset: TextSize) -> String {
155155 res
156156}
157157
158- #[ derive( Debug ) ]
158+ #[ derive( Debug , Eq , PartialEq ) ]
159159pub struct FixtureEntry {
160160 pub meta : String ,
161161 pub text : String ,
@@ -170,19 +170,26 @@ pub struct FixtureEntry {
170170/// // - other meta
171171/// ```
172172pub fn parse_fixture ( fixture : & str ) -> Vec < FixtureEntry > {
173- let margin = fixture
174- . lines ( )
175- . filter ( |it| it. trim_start ( ) . starts_with ( "//-" ) )
176- . map ( |it| it. len ( ) - it. trim_start ( ) . len ( ) )
177- . next ( )
178- . expect ( "empty fixture" ) ;
173+ let fixture = indent_first_line ( fixture) ;
174+ let margin = fixture_margin ( & fixture) ;
179175
180176 let mut lines = fixture
181177 . split ( '\n' ) // don't use `.lines` to not drop `\r\n`
182- . filter_map ( |line| {
178+ . enumerate ( )
179+ . filter_map ( |( ix, line) | {
183180 if line. len ( ) >= margin {
184181 assert ! ( line[ ..margin] . trim( ) . is_empty( ) ) ;
185- Some ( & line[ margin..] )
182+ let line_content = & line[ margin..] ;
183+ if !line_content. starts_with ( "//-" ) {
184+ assert ! (
185+ !line_content. contains( "//-" ) ,
186+ r#"Metadata line {} has invalid indentation. All metadata lines need to have the same indentation.
187+ The offending line: {:?}"# ,
188+ ix,
189+ line
190+ ) ;
191+ }
192+ Some ( line_content)
186193 } else {
187194 assert ! ( line. trim( ) . is_empty( ) ) ;
188195 None
@@ -202,6 +209,85 @@ pub fn parse_fixture(fixture: &str) -> Vec<FixtureEntry> {
202209 res
203210}
204211
212+ /// Adjusts the indentation of the first line to the minimum indentation of the rest of the lines.
213+ /// This allows fixtures to start off in a different indentation, e.g. to align the first line with
214+ /// the other lines visually:
215+ /// ```
216+ /// let fixture = "//- /lib.rs
217+ /// mod foo;
218+ /// //- /foo.rs
219+ /// fn bar() {}
220+ /// ";
221+ /// assert_eq!(fixture_margin(fixture),
222+ /// " //- /lib.rs
223+ /// mod foo;
224+ /// //- /foo.rs
225+ /// fn bar() {}
226+ /// ")
227+ /// ```
228+ fn indent_first_line ( fixture : & str ) -> String {
229+ if fixture. is_empty ( ) {
230+ return String :: new ( ) ;
231+ }
232+ let mut lines = fixture. lines ( ) ;
233+ let first_line = lines. next ( ) . unwrap ( ) ;
234+ if first_line. contains ( "//-" ) {
235+ let rest = lines. collect :: < Vec < _ > > ( ) . join ( "\n " ) ;
236+ let fixed_margin = fixture_margin ( & rest) ;
237+ let fixed_indent = fixed_margin - indent_len ( first_line) ;
238+ format ! ( "\n {}{}\n {}" , " " . repeat( fixed_indent) , first_line, rest)
239+ } else {
240+ fixture. to_owned ( )
241+ }
242+ }
243+
244+ fn fixture_margin ( fixture : & str ) -> usize {
245+ fixture
246+ . lines ( )
247+ . filter ( |it| it. trim_start ( ) . starts_with ( "//-" ) )
248+ . map ( indent_len)
249+ . next ( )
250+ . expect ( "empty fixture" )
251+ }
252+
253+ fn indent_len ( s : & str ) -> usize {
254+ s. len ( ) - s. trim_start ( ) . len ( )
255+ }
256+
257+ #[ test]
258+ #[ should_panic]
259+ fn parse_fixture_checks_further_indented_metadata ( ) {
260+ parse_fixture (
261+ r"
262+ //- /lib.rs
263+ mod bar;
264+
265+ fn foo() {}
266+ //- /bar.rs
267+ pub fn baz() {}
268+ " ,
269+ ) ;
270+ }
271+
272+ #[ test]
273+ fn parse_fixture_can_handle_unindented_first_line ( ) {
274+ let fixture = "//- /lib.rs
275+ mod foo;
276+ //- /foo.rs
277+ struct Bar;
278+ " ;
279+ assert_eq ! (
280+ parse_fixture( fixture) ,
281+ parse_fixture(
282+ "//- /lib.rs
283+ mod foo;
284+ //- /foo.rs
285+ struct Bar;
286+ "
287+ )
288+ )
289+ }
290+
205291/// Same as `parse_fixture`, except it allow empty fixture
206292pub fn parse_single_fixture ( fixture : & str ) -> Option < FixtureEntry > {
207293 if !fixture. lines ( ) . any ( |it| it. trim_start ( ) . starts_with ( "//-" ) ) {
0 commit comments