@@ -449,3 +449,268 @@ func TestInlineBlocks_ScannerHandlesSimpleFile(t *testing.T) {
449449 assert .Nil (t , first .NextSibling (), "scanner run has a single paragraph" )
450450 }
451451}
452+
453+ // --- Dedicated helper tests (plan 2606231013) ---
454+
455+ func TestScanRunEligible (t * testing.T ) {
456+ assert .False (t , scanRunEligible (nil ), "nil is not eligible" )
457+ assert .False (t , scanRunEligible ([]byte ("" )), "empty is not eligible" )
458+ assert .False (t , scanRunEligible ([]byte ("line one\n line two" )), "multiline not eligible" )
459+ assert .False (t , scanRunEligible ([]byte ("# heading" )), "ATX heading not eligible" )
460+ assert .False (t , scanRunEligible ([]byte ("- list" )), "list item not eligible" )
461+ assert .False (t , scanRunEligible ([]byte ("> quote" )), "block quote not eligible" )
462+ assert .False (t , scanRunEligible ([]byte ("*em*" )), "emphasis asterisk not eligible" )
463+ assert .False (t , scanRunEligible ([]byte ("_em_" )), "emphasis underscore not eligible" )
464+ assert .False (t , scanRunEligible ([]byte (`a\b` )), "backslash not eligible" )
465+ assert .False (t , scanRunEligible ([]byte ("a&b" )), "entity not eligible" )
466+ assert .True (t , scanRunEligible ([]byte ("plain text" )), "plain text eligible" )
467+ assert .True (t , scanRunEligible ([]byte ("[link](url)" )), "link chars eligible" )
468+ assert .True (t , scanRunEligible ([]byte ("`code`" )), "backtick eligible" )
469+ assert .True (t , scanRunEligible ([]byte ("<autolink>" )), "angle bracket eligible" )
470+ }
471+
472+ func TestMergeAppendText (t * testing.T ) {
473+ a := arena .New ()
474+ run := []byte ("hello world" )
475+ para := a .Paragraph ()
476+
477+ // end <= start: no child appended.
478+ mergeAppendText (run , 3 , 3 , para , a )
479+ assert .Nil (t , para .FirstChild (), "end==start emits nothing" )
480+
481+ // Normal range: appends a Text node.
482+ mergeAppendText (run , 0 , 5 , para , a )
483+ child := para .FirstChild ()
484+ require .NotNil (t , child , "Text node appended" )
485+ txt , ok := child .(* ast.Text )
486+ require .True (t , ok , "child is *ast.Text" )
487+ assert .Equal (t , "hello" , string (txt .Segment .Value (run )))
488+
489+ // Adjacent range [5:11): merges into the existing Text node.
490+ mergeAppendText (run , 5 , 11 , para , a )
491+ assert .Nil (t , child .NextSibling (), "adjacent text merged, no new sibling" )
492+ assert .Equal (t , "hello world" , string (txt .Segment .Value (run )), "merged bounds" )
493+ }
494+
495+ func TestFinalAppendText (t * testing.T ) {
496+ a := arena .New ()
497+ run := []byte ("hello " )
498+ para := a .Paragraph ()
499+
500+ // Trailing spaces trimmed: appends "hello".
501+ finalAppendText (run , 0 , len (run ), para , a )
502+ child := para .FirstChild ()
503+ require .NotNil (t , child )
504+ txt , ok := child .(* ast.Text )
505+ require .True (t , ok )
506+ assert .Equal (t , "hello" , string (txt .Segment .Value (run )))
507+
508+ // All whitespace: nothing appended.
509+ para2 := a .Paragraph ()
510+ finalAppendText ([]byte (" " ), 0 , 3 , para2 , a )
511+ assert .Nil (t , para2 .FirstChild (), "all-space run appends nothing" )
512+
513+ // end <= start: nothing appended.
514+ para3 := a .Paragraph ()
515+ finalAppendText (run , 5 , 5 , para3 , a )
516+ assert .Nil (t , para3 .FirstChild (), "empty range appends nothing" )
517+ }
518+
519+ func TestScanParagraphInlines (t * testing.T ) {
520+ a := arena .New ()
521+
522+ // Plain text: returns true, one Text child.
523+ para := a .Paragraph ()
524+ ok := scanParagraphInlines ([]byte ("hello" ), para , a )
525+ require .True (t , ok )
526+ require .NotNil (t , para .FirstChild (), "Text child added" )
527+
528+ // Unclosed backtick: scanCodeSpan declines, returns false.
529+ para2 := a .Paragraph ()
530+ ok2 := scanParagraphInlines ([]byte ("`unclosed" ), para2 , a )
531+ assert .False (t , ok2 )
532+ }
533+
534+ func TestApplyCodeSpan (t * testing.T ) {
535+ a := arena .New ()
536+ // Run starts at the backtick so no prior text is flushed first.
537+ run := []byte ("`code` after" )
538+ para := a .Paragraph ()
539+
540+ // i=0 points at the opening backtick; textStart=0 so nothing is flushed.
541+ ni , ns , ok := applyCodeSpan (run , 0 , 0 , para , a )
542+ require .True (t , ok )
543+ assert .Equal (t , 6 , ni , "ni just past closing backtick" )
544+ assert .Equal (t , 6 , ns , "textStart reset to ni" )
545+ child := para .FirstChild ()
546+ require .NotNil (t , child )
547+ _ , isCode := child .(* ast.CodeSpan )
548+ assert .True (t , isCode , "CodeSpan appended" )
549+
550+ // Unclosed backtick: returns false.
551+ para2 := a .Paragraph ()
552+ _ , _ , ok2 := applyCodeSpan ([]byte ("`unclosed" ), 0 , 0 , para2 , a )
553+ assert .False (t , ok2 )
554+ }
555+
556+ func TestApplyAutolink (t * testing.T ) {
557+ a := arena .New ()
558+ run := []byte ("<https://example.com>" )
559+ para := a .Paragraph ()
560+
561+ ni , ns , ok := applyAutolink (run , 0 , 0 , para , a )
562+ require .True (t , ok )
563+ assert .Equal (t , len (run ), ni )
564+ assert .Equal (t , len (run ), ns )
565+ child := para .FirstChild ()
566+ require .NotNil (t , child )
567+ _ , isAL := child .(* ast.AutoLink )
568+ assert .True (t , isAL , "AutoLink appended" )
569+
570+ // Raw HTML angle: returns false.
571+ para2 := a .Paragraph ()
572+ _ , _ , ok2 := applyAutolink ([]byte ("<div>" ), 0 , 0 , para2 , a )
573+ assert .False (t , ok2 )
574+ }
575+
576+ func TestApplyBang (t * testing.T ) {
577+ a := arena .New ()
578+
579+ // `!` without `[`: flushes pending text, returns i+1 with textStart at i.
580+ run := []byte ("a! text" )
581+ para := a .Paragraph ()
582+ ni , ns , ok := applyBang (run , 1 , 0 , para , a )
583+ require .True (t , ok )
584+ assert .Equal (t , 2 , ni , "i advanced past `!`" )
585+ assert .Equal (t , 1 , ns , "textStart set to position of `!`" )
586+ require .NotNil (t , para .FirstChild (), "pending text flushed as child" )
587+
588+ // ``: appends Image node.
589+ run2 := []byte ("" )
590+ para2 := a .Paragraph ()
591+ ni2 , _ , ok2 := applyBang (run2 , 0 , 0 , para2 , a )
592+ require .True (t , ok2 )
593+ assert .Equal (t , len (run2 ), ni2 )
594+ child := para2 .FirstChild ()
595+ require .NotNil (t , child )
596+ _ , isImg := child .(* ast.Image )
597+ assert .True (t , isImg , "Image appended" )
598+
599+ // `![` with unclosed label: returns false.
600+ para3 := a .Paragraph ()
601+ _ , _ , ok3 := applyBang ([]byte ("![alt" ), 0 , 0 , para3 , a )
602+ assert .False (t , ok3 )
603+ }
604+
605+ func TestApplyLink (t * testing.T ) {
606+ a := arena .New ()
607+ run := []byte ("[text](url)" )
608+ para := a .Paragraph ()
609+
610+ ni , ns , ok := applyLink (run , 0 , 0 , para , a )
611+ require .True (t , ok )
612+ assert .Equal (t , len (run ), ni )
613+ assert .Equal (t , len (run ), ns )
614+ child := para .FirstChild ()
615+ require .NotNil (t , child )
616+ _ , isLink := child .(* ast.Link )
617+ assert .True (t , isLink , "Link appended" )
618+
619+ // Reference link `[text][ref]`: returns false.
620+ para2 := a .Paragraph ()
621+ _ , _ , ok2 := applyLink ([]byte ("[text][ref]" ), 0 , 0 , para2 , a )
622+ assert .False (t , ok2 )
623+ }
624+
625+ func TestScanCodeSpan (t * testing.T ) {
626+ a := arena .New ()
627+
628+ // Single-backtick span.
629+ node , after , ok := scanCodeSpan ([]byte ("`code`" ), 0 , a )
630+ require .True (t , ok )
631+ assert .Equal (t , 6 , after )
632+ require .NotNil (t , node )
633+ _ , isCode := node .(* ast.CodeSpan )
634+ assert .True (t , isCode )
635+
636+ // Double-backtick span containing a single backtick.
637+ node2 , after2 , ok2 := scanCodeSpan ([]byte ("`` a`b ``" ), 0 , a )
638+ require .True (t , ok2 )
639+ assert .Equal (t , 9 , after2 )
640+ require .NotNil (t , node2 )
641+ _ , isCode2 := node2 .(* ast.CodeSpan )
642+ assert .True (t , isCode2 , "double-backtick produces CodeSpan" )
643+
644+ // No closing backtick: returns false.
645+ _ , _ , ok3 := scanCodeSpan ([]byte ("`unclosed" ), 0 , a )
646+ assert .False (t , ok3 )
647+ }
648+
649+ func TestScanLinkOrImage (t * testing.T ) {
650+ a := arena .New ()
651+
652+ // Inline link.
653+ node , after , ok := scanLinkOrImage ([]byte ("[text](url)" ), 0 , false , a )
654+ require .True (t , ok )
655+ assert .Equal (t , 11 , after )
656+ _ , isLink := node .(* ast.Link )
657+ assert .True (t , isLink , "Link node" )
658+
659+ // Inline image.
660+ node2 , after2 , ok2 := scanLinkOrImage ([]byte ("" ), 0 , true , a )
661+ require .True (t , ok2 )
662+ assert .Equal (t , 11 , after2 , "image after matches run length" )
663+ _ , isImg := node2 .(* ast.Image )
664+ assert .True (t , isImg , "Image node" )
665+
666+ // Reference link `[text][ref]`: returns false.
667+ _ , _ , ok3 := scanLinkOrImage ([]byte ("[text][ref]" ), 0 , false , a )
668+ assert .False (t , ok3 )
669+
670+ // Nested brackets in label: returns false.
671+ _ , _ , ok4 := scanLinkOrImage ([]byte ("[[a]](url)" ), 0 , false , a )
672+ assert .False (t , ok4 )
673+ }
674+
675+ func TestScanLinkParens (t * testing.T ) {
676+ // Empty parens.
677+ dest , title , after , ok := scanLinkParens ([]byte ("()" ), 0 )
678+ require .True (t , ok )
679+ assert .Nil (t , dest )
680+ assert .Nil (t , title )
681+ assert .Equal (t , 2 , after )
682+
683+ // Destination only.
684+ dest2 , title2 , after2 , ok2 := scanLinkParens ([]byte ("(url)" ), 0 )
685+ require .True (t , ok2 )
686+ assert .Equal (t , "url" , string (dest2 ))
687+ assert .Nil (t , title2 )
688+ assert .Equal (t , 5 , after2 )
689+
690+ // Destination and title.
691+ dest3 , title3 , after3 , ok3 := scanLinkParens ([]byte (`(url "ttl")` ), 0 )
692+ require .True (t , ok3 )
693+ assert .Equal (t , "url" , string (dest3 ))
694+ assert .Equal (t , "ttl" , string (title3 ))
695+ assert .Equal (t , 11 , after3 )
696+
697+ // Missing closing paren.
698+ _ , _ , _ , ok4 := scanLinkParens ([]byte ("(url" ), 0 )
699+ assert .False (t , ok4 )
700+ }
701+
702+ func TestSkipSpacesAt (t * testing.T ) {
703+ run := []byte (" hello" )
704+ assert .Equal (t , 3 , skipSpacesAt (run , 0 ), "leading spaces skipped" )
705+ assert .Equal (t , 3 , skipSpacesAt (run , 3 ), "no spaces from non-space pos" )
706+ assert .Equal (t , len (run ), skipSpacesAt (run , len (run )), "past end stays at end" )
707+
708+ allSpace := []byte (" " )
709+ assert .Equal (t , 3 , skipSpacesAt (allSpace , 0 ), "all spaces → past end" )
710+ }
711+
712+ func TestIsSpaceOrNewlineByte (t * testing.T ) {
713+ assert .True (t , isSpaceOrNewlineByte (' ' ))
714+ assert .True (t , isSpaceOrNewlineByte ('\n' ))
715+ assert .False (t , isSpaceOrNewlineByte ('\t' ), "tab is not a space-or-newline byte" )
716+ }
0 commit comments