@@ -128,7 +128,12 @@ impl FilePatcher {
128128 struct Visitor < ' a > ( usize , Option < InlineSnapshot > , & ' a [ String ] ) ;
129129
130130 fn indentation ( macro_start : LineColumn , code_lines : & [ String ] ) -> String {
131- code_lines[ macro_start. line - 1 ] [ ..macro_start. column ] . to_owned ( )
131+ // Only capture leading whitespace from the line, not arbitrary code
132+ // that might precede the macro (fixes issue #833)
133+ code_lines[ macro_start. line - 1 ]
134+ . chars ( )
135+ . take_while ( |c| c. is_whitespace ( ) )
136+ . collect ( )
132137 }
133138
134139 fn scan_for_path_start ( tokens : & [ TokenTree ] , pos : usize , code_lines : & [ String ] ) -> String {
@@ -413,4 +418,250 @@ fn test_function() {
413418 assert_debug_snapshot ! ( snapshot5. indentation, @r#"" ""# ) ;
414419 assert_debug_snapshot ! ( snapshot6. indentation, @r#"" ""# ) ;
415420 }
421+
422+ #[ test]
423+ fn test_find_snapshot_macro_with_code_before_macro ( ) {
424+ // Regression test for issue #833
425+ // When there's code before the macro (not just whitespace), the indentation
426+ // should only capture the leading whitespace, not the code.
427+ let content = r######"
428+ use insta::assert_snapshot;
429+
430+ fn test_function() {
431+ let output = assert_snapshot!("test\ntest", @r###"
432+ test
433+ test
434+ "###);
435+ }
436+ "###### ;
437+
438+ let file_patcher = FilePatcher {
439+ filename : PathBuf :: new ( ) ,
440+ lines : content. lines ( ) . map ( String :: from) . collect ( ) ,
441+ source : syn:: parse_file ( content) . unwrap ( ) ,
442+ inline_snapshots : vec ! [ ] ,
443+ } ;
444+
445+ // The snapshot macro starts on line 5 (1-based index)
446+ let snapshot = file_patcher. find_snapshot_macro ( 5 ) . unwrap ( ) ;
447+
448+ // The indentation should only be the leading whitespace (" "),
449+ // NOT " let output = " which would cause the regression described in #833
450+ assert_debug_snapshot ! ( snapshot. indentation, @r#"" ""# ) ;
451+ }
452+
453+ #[ test]
454+ fn test_find_snapshot_macro_in_if_block ( ) {
455+ // Corner case: macro inside if block with code before it on same line
456+ let content = r######"
457+ use insta::assert_snapshot;
458+
459+ fn test_function() {
460+ if true { assert_snapshot!("test", @"test"); }
461+ }
462+ "###### ;
463+
464+ let file_patcher = FilePatcher {
465+ filename : PathBuf :: new ( ) ,
466+ lines : content. lines ( ) . map ( String :: from) . collect ( ) ,
467+ source : syn:: parse_file ( content) . unwrap ( ) ,
468+ inline_snapshots : vec ! [ ] ,
469+ } ;
470+
471+ let snapshot = file_patcher. find_snapshot_macro ( 5 ) . unwrap ( ) ;
472+ // Should only capture leading whitespace, not " if true { "
473+ assert_debug_snapshot ! ( snapshot. indentation, @r#"" ""# ) ;
474+ }
475+
476+ #[ test]
477+ fn test_find_snapshot_macro_in_match_arm ( ) {
478+ // Corner case: macro in match arm
479+ let content = r######"
480+ use insta::assert_snapshot;
481+
482+ fn test_function() {
483+ match x {
484+ _ => assert_snapshot!("test", @"test"),
485+ }
486+ }
487+ "###### ;
488+
489+ let file_patcher = FilePatcher {
490+ filename : PathBuf :: new ( ) ,
491+ lines : content. lines ( ) . map ( String :: from) . collect ( ) ,
492+ source : syn:: parse_file ( content) . unwrap ( ) ,
493+ inline_snapshots : vec ! [ ] ,
494+ } ;
495+
496+ let snapshot = file_patcher. find_snapshot_macro ( 6 ) . unwrap ( ) ;
497+ // Should only capture leading whitespace, not " _ => "
498+ assert_debug_snapshot ! ( snapshot. indentation, @r#"" ""# ) ;
499+ }
500+
501+ #[ test]
502+ fn test_find_snapshot_macro_no_indentation ( ) {
503+ // Corner case: macro at column 0 (no indentation)
504+ let content = r######"
505+ use insta::assert_snapshot;
506+
507+ assert_snapshot!("test", @"test");
508+ "###### ;
509+
510+ let file_patcher = FilePatcher {
511+ filename : PathBuf :: new ( ) ,
512+ lines : content. lines ( ) . map ( String :: from) . collect ( ) ,
513+ source : syn:: parse_file ( content) . unwrap ( ) ,
514+ inline_snapshots : vec ! [ ] ,
515+ } ;
516+
517+ let snapshot = file_patcher. find_snapshot_macro ( 4 ) . unwrap ( ) ;
518+ // No indentation at all
519+ assert_debug_snapshot ! ( snapshot. indentation, @r#""""# ) ;
520+ }
521+
522+ #[ test]
523+ fn test_find_snapshot_macro_after_method_chain ( ) {
524+ // Corner case: macro result used in method chain
525+ let content = r######"
526+ use insta::assert_snapshot;
527+
528+ fn test_function() {
529+ let _ = assert_snapshot!("test", @"test");
530+ foo.bar().baz(assert_snapshot!("nested", @"nested"));
531+ }
532+ "###### ;
533+
534+ let file_patcher = FilePatcher {
535+ filename : PathBuf :: new ( ) ,
536+ lines : content. lines ( ) . map ( String :: from) . collect ( ) ,
537+ source : syn:: parse_file ( content) . unwrap ( ) ,
538+ inline_snapshots : vec ! [ ] ,
539+ } ;
540+
541+ // First snapshot at line 5
542+ let snapshot1 = file_patcher. find_snapshot_macro ( 5 ) . unwrap ( ) ;
543+ assert_debug_snapshot ! ( snapshot1. indentation, @r#"" ""# ) ;
544+
545+ // Second snapshot at line 6 (nested in method call)
546+ let snapshot2 = file_patcher. find_snapshot_macro ( 6 ) . unwrap ( ) ;
547+ // Should only capture leading whitespace, not " foo.bar().baz("
548+ assert_debug_snapshot ! ( snapshot2. indentation, @r#"" ""# ) ;
549+ }
550+
551+ #[ test]
552+ fn test_find_snapshot_macro_mixed_whitespace ( ) {
553+ // Corner case: mixed tabs and spaces (tab then spaces)
554+ let content = "
555+ use insta::assert_snapshot;
556+
557+ fn test_function() {
558+ \t assert_snapshot!(\" test\" , @\" test\" );
559+ }
560+ " ;
561+
562+ let file_patcher = FilePatcher {
563+ filename : PathBuf :: new ( ) ,
564+ lines : content. lines ( ) . map ( String :: from) . collect ( ) ,
565+ source : syn:: parse_file ( content) . unwrap ( ) ,
566+ inline_snapshots : vec ! [ ] ,
567+ } ;
568+
569+ let snapshot = file_patcher. find_snapshot_macro ( 5 ) . unwrap ( ) ;
570+ // Should capture the tab and spaces
571+ assert_debug_snapshot ! ( snapshot. indentation, @r#""\t ""# ) ;
572+ }
573+
574+ #[ test]
575+ fn test_find_snapshot_macro_closure ( ) {
576+ // Corner case: macro inside closure
577+ let content = r######"
578+ use insta::assert_snapshot;
579+
580+ fn test_function() {
581+ let f = || assert_snapshot!("test", @"test");
582+ }
583+ "###### ;
584+
585+ let file_patcher = FilePatcher {
586+ filename : PathBuf :: new ( ) ,
587+ lines : content. lines ( ) . map ( String :: from) . collect ( ) ,
588+ source : syn:: parse_file ( content) . unwrap ( ) ,
589+ inline_snapshots : vec ! [ ] ,
590+ } ;
591+
592+ let snapshot = file_patcher. find_snapshot_macro ( 5 ) . unwrap ( ) ;
593+ // Should only capture leading whitespace, not " let f = || "
594+ assert_debug_snapshot ! ( snapshot. indentation, @r#"" ""# ) ;
595+ }
596+
597+ #[ test]
598+ fn test_find_snapshot_macro_multiple_on_same_line ( ) {
599+ // Corner case: multiple macros on the same line
600+ let content = r######"
601+ use insta::assert_snapshot;
602+
603+ fn test_function() {
604+ if true { assert_snapshot!("a", @"a"); assert_snapshot!("b", @"b"); }
605+ }
606+ "###### ;
607+
608+ let file_patcher = FilePatcher {
609+ filename : PathBuf :: new ( ) ,
610+ lines : content. lines ( ) . map ( String :: from) . collect ( ) ,
611+ source : syn:: parse_file ( content) . unwrap ( ) ,
612+ inline_snapshots : vec ! [ ] ,
613+ } ;
614+
615+ // Both snapshots are on line 5
616+ // Note: find_snapshot_macro returns the first macro found on a line
617+ let snapshot = file_patcher. find_snapshot_macro ( 5 ) . unwrap ( ) ;
618+ // Should only capture leading whitespace
619+ assert_debug_snapshot ! ( snapshot. indentation, @r#"" ""# ) ;
620+ }
621+
622+ #[ test]
623+ fn test_find_snapshot_macro_deeply_nested ( ) {
624+ // Corner case: macro deeply nested in expressions
625+ let content = r######"
626+ use insta::assert_snapshot;
627+
628+ fn test_function() {
629+ foo.bar(|x| x.baz().qux(|| assert_snapshot!("test", @"test")));
630+ }
631+ "###### ;
632+
633+ let file_patcher = FilePatcher {
634+ filename : PathBuf :: new ( ) ,
635+ lines : content. lines ( ) . map ( String :: from) . collect ( ) ,
636+ source : syn:: parse_file ( content) . unwrap ( ) ,
637+ inline_snapshots : vec ! [ ] ,
638+ } ;
639+
640+ let snapshot = file_patcher. find_snapshot_macro ( 5 ) . unwrap ( ) ;
641+ // Should only capture leading whitespace, not the deeply nested expression
642+ assert_debug_snapshot ! ( snapshot. indentation, @r#"" ""# ) ;
643+ }
644+
645+ #[ test]
646+ fn test_find_snapshot_macro_qualified_path_with_code_before ( ) {
647+ // Corner case: fully qualified path (insta::assert_snapshot!) with code before it
648+ // This exercises the scan_for_path_start function which walks back through :: tokens
649+ let content = r######"
650+ fn test_function() {
651+ let output = insta::assert_snapshot!("test", @"test");
652+ }
653+ "###### ;
654+
655+ let file_patcher = FilePatcher {
656+ filename : PathBuf :: new ( ) ,
657+ lines : content. lines ( ) . map ( String :: from) . collect ( ) ,
658+ source : syn:: parse_file ( content) . unwrap ( ) ,
659+ inline_snapshots : vec ! [ ] ,
660+ } ;
661+
662+ let snapshot = file_patcher. find_snapshot_macro ( 3 ) . unwrap ( ) ;
663+ // Should only capture leading whitespace, not " let output = "
664+ // even though scan_for_path_start finds "insta" as the path start
665+ assert_debug_snapshot ! ( snapshot. indentation, @r#"" ""# ) ;
666+ }
416667}
0 commit comments