okami.mom/glamour
git clone https://git.nkp.pet/okami.mom/glamour
052c391
main
1package glamour 2 3import ( 4"regexp" 5"strings" 6"testing" 7 8"charm.land/glamour/v2/ansi" 9"charm.land/glamour/v2/styles" 10"github.com/yuin/goldmark" 11"github.com/yuin/goldmark/ast" 12"github.com/yuin/goldmark/parser" 13"github.com/yuin/goldmark/text" 14"github.com/yuin/goldmark/util" 15) 16 17var kindShout = ast .NewNodeKind ("Shout" ) 18 19// shout is %word%, a custom inline node for the extension tests. 20type shout struct { 21 ast.BaseInline 22word string 23} 24 25func (n * shout )Kind () ast.NodeKind {return kindShout } 26func (n * shout )Dump (source []byte ,level int ) {ast .DumpHelper (n ,source ,level ,nil ,nil ) } 27 28// shoutExtension parses %word% into a shout, or into an ast.String when 29// asString: a kind glamour registers but has no case for. 30type shoutExtension struct {asString bool } 31 32func (e shoutExtension )Extend (m goldmark.Markdown ) { 33m .Parser ().AddOptions (parser .WithInlineParsers (util .Prioritized (shoutParser (e ),500 ))) 34} 35 36type shoutParser struct {asString bool } 37 38func (shoutParser )Trigger () []byte {return []byte {'%' } } 39 40func (p shoutParser )Parse (_ ast.Node ,block text.Reader ,_ parser.Context ) ast.Node { 41line ,_ := block .PeekLine () 42end := strings .IndexByte (string (line [1 :]),'%' ) 43if end < 1 { 44return nil 45 } 46block .Advance (end + 2 ) 47if p .asString { 48return ast .NewString (line [1 :end + 1 ]) 49 } 50return & shout {word :string (line [1 :end + 1 ])} 51} 52 53func renderShout (t * testing.T ,in string ,extension shoutExtension ,element ansi.ElementFunc )string { 54t .Helper () 55options := []TermRendererOption { 56WithStyles (styles .NoTTYStyleConfig ),WithWordWrap (40 ),WithExtensions (extension ), 57 } 58if element != nil { 59options = append (options ,WithElement (kindShout ,element )) 60 } 61r ,err := NewTermRenderer (options ... ) 62if err != nil { 63t .Fatal (err ) 64 } 65out ,err := r .Render (in ) 66if err != nil { 67t .Fatal (err ) 68 } 69return strings .TrimSpace (out ) 70} 71 72func TestWithElementRendersExtensionNodes (t * testing.T ) { 73upper := func (node ast.Node ,_ []byte ,_ ansi.RenderContext ) ansi.Element { 74return ansi.Element {Renderer :& ansi.BaseElement {Token :strings .ToUpper (node .(* shout ).word ),Verbatim :true }} 75 } 76for in ,want := range map [string ]string { 77"say %hi% now" :"say HI now" , 78"say *%hi%*" :"say *HI*" , 79`%a\[b%` :`A\[B` , 80 } { 81if got := renderShout (t ,in ,shoutExtension {},upper );got != want { 82t .Errorf ("%q rendered %q, want %q" ,in ,got ,want ) 83 } 84 } 85} 86 87func TestElementsSeeTheBlockWidth (t * testing.T ) { 88var widths []int 89width := func (_ ast.Node ,_ []byte ,ctx ansi.RenderContext ) ansi.Element { 90widths = append (widths ,ctx .Width ()) 91return ansi.Element {} 92 } 93renderShout (t ,"%a%\n\n> %b%" ,shoutExtension {},width ) 94if len (widths )< 2 || widths [0 ]<= widths [len (widths )- 1 ] { 95t .Errorf ("widths %v: a quote should leave less room than the document" ,widths ) 96 } 97} 98 99func TestUnhandledKindsRenderNothing (t * testing.T ) { 100if got := renderShout (t ,"say %hi% now" ,shoutExtension {asString :true },nil );got != "say now" { 101t .Errorf ("rendered %q, want the unknown node dropped" ,got ) 102 } 103} 104 105var trailingSpace = regexp .MustCompile (`(?m) +$` ) 106 107func TestListItemsHangTheirRows (t * testing.T ) { 108r ,err := NewTermRenderer (WithStyles (styles .NoTTYStyleConfig ),WithWordWrap (24 )) 109if err != nil { 110t .Fatal (err ) 111 } 112out ,err := r .Render ("- one two three four five six\n\n second paragraph\n\n ```\n code\n ```\n- next" ) 113if err != nil { 114t .Fatal (err ) 115 } 116want := " • one two three four\n five six\n\n second paragraph\n\n code\n • next" 117if got := strings .Trim (trailingSpace .ReplaceAllString (out ,"" ),"\n" );got != want { 118t .Errorf ("rendered\n%s\nwant\n%s" ,got ,want ) 119 } 120}