@@ -528,3 +528,240 @@ func TestRefUseLabelBytesCollapsedTrailingEmptyBrackets(t *testing.T) {
528528 require .True (t , ok , "expected match for cursor inside trailing []" )
529529 assert .Equal (t , "Docs API" , string (row [start :end ]))
530530}
531+
532+ // TestIsValidRefDefLine drives isValidRefDefLine with a real ref-def
533+ // line (returns true), a heading line (returns false), and a blank
534+ // line (returns false).
535+ func TestIsValidRefDefLine (t * testing.T ) {
536+ t .Parallel ()
537+ src := []byte ("# T\n \n [docs]: https://example.com\n " )
538+ assert .True (t , isValidRefDefLine (src , 3 ))
539+ assert .False (t , isValidRefDefLine (src , 1 ))
540+ assert .False (t , isValidRefDefLine (src , 2 ))
541+ }
542+
543+ // TestHeadingPrepareRangeATX drives headingPrepareRange with a simple
544+ // ATX heading and verifies the range covers the text only.
545+ func TestHeadingPrepareRangeATX (t * testing.T ) {
546+ t .Parallel ()
547+ src := []byte ("# Hello\n " )
548+ res , ok := headingPrepareRange (src , 1 , "Hello" )
549+ require .True (t , ok )
550+ assert .Equal (t , "Hello" , res .Placeholder )
551+ assert .Equal (t , 0 , res .Range .Start .Line )
552+ assert .Equal (t , 2 , res .Range .Start .Character )
553+ assert .Equal (t , 7 , res .Range .End .Character )
554+ }
555+
556+ // TestAtxHeadingTextStart drives atxHeadingTextStart directly with
557+ // ATX headings, a no-space header, a too-deep header, and plain text.
558+ func TestAtxHeadingTextStart (t * testing.T ) {
559+ t .Parallel ()
560+ cases := []struct {
561+ row string
562+ wantI int
563+ wantOK bool
564+ }{
565+ {"# Hello" , 2 , true },
566+ {"## Hi" , 3 , true },
567+ {"###### Six" , 7 , true },
568+ {" ## Indented" , 6 , true },
569+ {"##\t Tab" , 3 , true },
570+ {"#NoSpace" , 0 , false },
571+ {"####### TooMany" , 0 , false },
572+ {"plain text" , 0 , false },
573+ {"" , 0 , false },
574+ }
575+ for _ , tc := range cases {
576+ i , ok := atxHeadingTextStart ([]byte (tc .row ))
577+ assert .Equal (t , tc .wantOK , ok , "row=%q" , tc .row )
578+ assert .Equal (t , tc .wantI , i , "row=%q" , tc .row )
579+ }
580+ }
581+
582+ // TestTrimTrailingHashRun drives trimTrailingHashRun with a closing
583+ // hash run preceded by space (stripped), a hash run without space
584+ // (kept), and a row with no trailing hash (unchanged).
585+ func TestTrimTrailingHashRun (t * testing.T ) {
586+ t .Parallel ()
587+ cases := []struct {
588+ row string
589+ start , end int
590+ want int
591+ }{
592+ {"## Setup ###" , 3 , 12 , 8 }, // trailing " ###" stripped; text ends at 8
593+ {"## Setup ##" , 3 , 11 , 8 }, // trailing " ##" stripped
594+ {"## Setup #" , 3 , 10 , 8 }, // trailing " #" stripped
595+ {"## Setup#" , 3 , 9 , 9 }, // no preceding space — kept
596+ {"## Setup" , 3 , 8 , 8 }, // no trailing hash — unchanged
597+ {"## Setup " , 3 , 11 , 11 }, // trailing spaces only, no hash — unchanged
598+ }
599+ for _ , tc := range cases {
600+ got := trimTrailingHashRun ([]byte (tc .row ), tc .start , tc .end )
601+ assert .Equal (t , tc .want , got , "row=%q" , tc .row )
602+ }
603+ }
604+
605+ // TestSkipLeadingSpaces drives skipLeadingSpaces with no leading
606+ // spaces, fewer than max spaces, and more than max spaces.
607+ func TestSkipLeadingSpaces (t * testing.T ) {
608+ t .Parallel ()
609+ assert .Equal (t , 0 , skipLeadingSpaces ([]byte ("abc" ), 3 ))
610+ assert .Equal (t , 2 , skipLeadingSpaces ([]byte (" abc" ), 3 ))
611+ assert .Equal (t , 3 , skipLeadingSpaces ([]byte (" abc" ), 3 ))
612+ assert .Equal (t , 3 , skipLeadingSpaces ([]byte (" abc" ), 3 ))
613+ assert .Equal (t , 0 , skipLeadingSpaces ([]byte ("" ), 3 ))
614+ }
615+
616+ // TestTrimRightSpace drives trimRightSpace with trailing spaces,
617+ // trailing tab, no trailing whitespace, and all-whitespace input.
618+ func TestTrimRightSpace (t * testing.T ) {
619+ t .Parallel ()
620+ assert .Equal (t , 5 , trimRightSpace ([]byte ("hello " ), 0 , 7 ))
621+ assert .Equal (t , 5 , trimRightSpace ([]byte ("hello\t " ), 0 , 7 ))
622+ assert .Equal (t , 5 , trimRightSpace ([]byte ("hello" ), 0 , 5 ))
623+ assert .Equal (t , 0 , trimRightSpace ([]byte (" " ), 0 , 3 ))
624+ }
625+
626+ // TestTrimmedRange drives trimmedRange with leading-and-trailing
627+ // whitespace, no whitespace, and an all-whitespace row.
628+ func TestTrimmedRange (t * testing.T ) {
629+ t .Parallel ()
630+ start , end := trimmedRange ([]byte (" hello " ))
631+ assert .Equal (t , 2 , start )
632+ assert .Equal (t , 7 , end )
633+ start , end = trimmedRange ([]byte ("nospace" ))
634+ assert .Equal (t , 0 , start )
635+ assert .Equal (t , 7 , end )
636+ start , end = trimmedRange ([]byte (" " ))
637+ assert .Equal (t , 3 , start )
638+ assert .Equal (t , 3 , end )
639+ }
640+
641+ // TestRefDefPrepareRangeHappy drives refDefPrepareRange with a
642+ // well-formed `[label]: url` line and verifies the returned
643+ // placeholder and range.
644+ func TestRefDefPrepareRangeHappy (t * testing.T ) {
645+ t .Parallel ()
646+ src := []byte ("[docs]: https://example.com\n " )
647+ res , ok := refDefPrepareRange (src , 1 , "" )
648+ require .True (t , ok )
649+ assert .Equal (t , "docs" , res .Placeholder )
650+ assert .Equal (t , 0 , res .Range .Start .Line )
651+ assert .Equal (t , 1 , res .Range .Start .Character )
652+ assert .Equal (t , 5 , res .Range .End .Character )
653+ }
654+
655+ // TestRefUsePrepareRangeHappy drives refUsePrepareRange with the
656+ // cursor inside the trailing `[label]` of a full reference link.
657+ func TestRefUsePrepareRangeHappy (t * testing.T ) {
658+ t .Parallel ()
659+ src := []byte ("See [text][docs] here.\n " )
660+ res , ok := refUsePrepareRange (src , 1 , 12 , "docs" )
661+ require .True (t , ok )
662+ assert .Equal (t , "docs" , res .Placeholder )
663+ assert .Equal (t , 0 , res .Range .Start .Line )
664+ assert .Equal (t , 11 , res .Range .Start .Character )
665+ assert .Equal (t , 15 , res .Range .End .Character )
666+ }
667+
668+ // TestRefUseLabelBytesAllForms drives refUseLabelBytes across the
669+ // four reference forms (full cursor-in-text, full cursor-in-label,
670+ // shortcut, collapsed) and the no-match case.
671+ func TestRefUseLabelBytesAllForms (t * testing.T ) {
672+ t .Parallel ()
673+ cases := []struct {
674+ row string
675+ cursor int
676+ label string
677+ wantOK bool
678+ wantLabel string
679+ }{
680+ {`[text][docs]` , 2 , "docs" , true , "docs" }, // full: cursor in text
681+ {`[text][docs]` , 5 , "docs" , true , "docs" }, // full: cursor at ] of text (pr.close boundary)
682+ {`[text][docs]` , 6 , "docs" , true , "docs" }, // full: cursor at [ of label (pr.open boundary)
683+ {`[text][docs]` , 8 , "docs" , true , "docs" }, // full: cursor in label
684+ {`[docs]` , 2 , "docs" , true , "docs" }, // shortcut
685+ {`[docs][]` , 2 , "docs" , true , "docs" }, // collapsed: cursor in leading
686+ {`[docs][]` , 7 , "docs" , true , "docs" }, // collapsed: cursor in trailing
687+ {`[other]` , 2 , "docs" , false , "" }, // wrong label
688+ {`[docs]` , 99 , "docs" , false , "" }, // cursor outside
689+ }
690+ for _ , tc := range cases {
691+ row := []byte (tc .row )
692+ start , end , ok := refUseLabelBytes (row , tc .cursor , tc .label )
693+ assert .Equal (t , tc .wantOK , ok , "row=%q cursor=%d" , tc .row , tc .cursor )
694+ if ok {
695+ assert .Equal (t , tc .wantLabel , string (row [start :end ]), "row=%q cursor=%d" , tc .row , tc .cursor )
696+ } else {
697+ assert .Equal (t , 0 , start , "row=%q cursor=%d" , tc .row , tc .cursor )
698+ assert .Equal (t , 0 , end , "row=%q cursor=%d" , tc .row , tc .cursor )
699+ }
700+ }
701+ }
702+
703+ // TestMatchLeadingPairHappy drives matchLeadingPair for the full
704+ // `[text][label]` case (returns trailing label) and the collapsed
705+ // `[label][]` case (returns leading pair content).
706+ func TestMatchLeadingPairHappy (t * testing.T ) {
707+ t .Parallel ()
708+ // full: [text][label] — leading pair, returns trailing label
709+ row := []byte (`[text][label]` )
710+ pairs := bracketPairs (row )
711+ require .Len (t , pairs , 2 )
712+ start , end , ok := matchLeadingPair (row , pairs , 0 , "label" )
713+ require .True (t , ok )
714+ assert .Equal (t , "label" , string (row [start :end ]))
715+
716+ // collapsed: [label][] — leading pair, returns leading content
717+ row2 := []byte (`[label][]` )
718+ pairs2 := bracketPairs (row2 )
719+ require .Len (t , pairs2 , 2 )
720+ start2 , end2 , ok2 := matchLeadingPair (row2 , pairs2 , 0 , "label" )
721+ require .True (t , ok2 )
722+ assert .Equal (t , "label" , string (row2 [start2 :end2 ]))
723+ }
724+
725+ // TestMatchTrailingPairHappy drives matchTrailingPair for the full
726+ // `[text][label]` case (returns trailing label) and the collapsed
727+ // `[label][]` case (returns leading pair content).
728+ func TestMatchTrailingPairHappy (t * testing.T ) {
729+ t .Parallel ()
730+ // full: [text][label] — trailing pair, returns label
731+ row := []byte (`[text][label]` )
732+ pairs := bracketPairs (row )
733+ require .Len (t , pairs , 2 )
734+ start , end , ok := matchTrailingPair (row , pairs , 1 , "label" )
735+ require .True (t , ok )
736+ assert .Equal (t , "label" , string (row [start :end ]))
737+
738+ // collapsed: [label][] — trailing empty pair, returns leading content
739+ row2 := []byte (`[label][]` )
740+ pairs2 := bracketPairs (row2 )
741+ require .Len (t , pairs2 , 2 )
742+ start2 , end2 , ok2 := matchTrailingPair (row2 , pairs2 , 1 , "label" )
743+ require .True (t , ok2 )
744+ assert .Equal (t , "label" , string (row2 [start2 :end2 ]))
745+ }
746+
747+ // TestBracketPairsBasic drives bracketPairs with a single pair, two
748+ // adjacent pairs, and an empty row.
749+ func TestBracketPairsBasic (t * testing.T ) {
750+ t .Parallel ()
751+ // single pair
752+ pairs := bracketPairs ([]byte (`[a]` ))
753+ require .Len (t , pairs , 1 )
754+ assert .Equal (t , 0 , pairs [0 ].open )
755+ assert .Equal (t , 2 , pairs [0 ].close )
756+
757+ // two adjacent pairs
758+ row := []byte (`[a][b]` )
759+ pairs = bracketPairs (row )
760+ require .Len (t , pairs , 2 )
761+ assert .Equal (t , "a" , string (row [pairs [0 ].open + 1 :pairs [0 ].close ]))
762+ assert .Equal (t , "b" , string (row [pairs [1 ].open + 1 :pairs [1 ].close ]))
763+
764+ // empty row
765+ pairs = bracketPairs ([]byte (`` ))
766+ assert .Empty (t , pairs )
767+ }
0 commit comments