okami.mom/glamour
git clone https://git.nkp.pet/okami.mom/glamour
4046973
main
1package ansi 2 3import ( 4"fmt" 5"io" 6"net/url" 7"strings" 8 9 east"github.com/yuin/goldmark-emoji/ast" 10"github.com/yuin/goldmark/ast" 11 astext"github.com/yuin/goldmark/extension/ast" 12"github.com/yuin/goldmark/renderer" 13"github.com/yuin/goldmark/util" 14) 15 16// Options is used to configure an ANSIRenderer. 17type Options struct { 18BaseURL string 19WordWrap int 20TableWrap * bool 21InlineTableLinks bool 22PreserveNewLines bool 23Styles StyleConfig 24ChromaFormatter string 25// Elements renders node kinds the caller adds through goldmark 26// extensions, or replaces how a built-in kind renders. 27Elements map [ast.NodeKind ]ElementFunc 28} 29 30// ElementFunc builds the Element for one node. Children it renders itself 31// must not also be walked: give them no ElementFunc of their own. 32type ElementFunc func (node ast.Node ,source []byte ,ctx RenderContext )Element 33 34// ANSIRenderer renders markdown content as ANSI escaped sequences. 35type ANSIRenderer struct {//nolint: revive 36context RenderContext 37} 38 39// NewRenderer returns a new ANSIRenderer with style and options set. 40func NewRenderer (options Options )* ANSIRenderer { 41return & ANSIRenderer { 42context :NewRenderContext (options ), 43 } 44} 45 46// RegisterFuncs implements NodeRenderer.RegisterFuncs. 47func (r * ANSIRenderer )RegisterFuncs (reg renderer.NodeRendererFuncRegisterer ) { 48// blocks 49reg .Register (ast .KindDocument ,r .renderNode ) 50reg .Register (ast .KindHeading ,r .renderNode ) 51reg .Register (ast .KindBlockquote ,r .renderNode ) 52reg .Register (ast .KindCodeBlock ,r .renderNode ) 53reg .Register (ast .KindFencedCodeBlock ,r .renderNode ) 54reg .Register (ast .KindHTMLBlock ,r .renderNode ) 55reg .Register (ast .KindList ,r .renderNode ) 56reg .Register (ast .KindListItem ,r .renderNode ) 57reg .Register (ast .KindParagraph ,r .renderNode ) 58reg .Register (ast .KindTextBlock ,r .renderNode ) 59reg .Register (ast .KindThematicBreak ,r .renderNode ) 60 61// inlines 62reg .Register (ast .KindAutoLink ,r .renderNode ) 63reg .Register (ast .KindCodeSpan ,r .renderNode ) 64reg .Register (ast .KindEmphasis ,r .renderNode ) 65reg .Register (ast .KindImage ,r .renderNode ) 66reg .Register (ast .KindLink ,r .renderNode ) 67reg .Register (ast .KindRawHTML ,r .renderNode ) 68reg .Register (ast .KindText ,r .renderNode ) 69reg .Register (ast .KindString ,r .renderNode ) 70 71// tables 72reg .Register (astext .KindTable ,r .renderNode ) 73reg .Register (astext .KindTableHeader ,r .renderNode ) 74reg .Register (astext .KindTableRow ,r .renderNode ) 75reg .Register (astext .KindTableCell ,r .renderNode ) 76 77// definitions 78reg .Register (astext .KindDefinitionList ,r .renderNode ) 79reg .Register (astext .KindDefinitionTerm ,r .renderNode ) 80reg .Register (astext .KindDefinitionDescription ,r .renderNode ) 81 82// footnotes 83reg .Register (astext .KindFootnote ,r .renderNode ) 84reg .Register (astext .KindFootnoteList ,r .renderNode ) 85reg .Register (astext .KindFootnoteLink ,r .renderNode ) 86reg .Register (astext .KindFootnoteBacklink ,r .renderNode ) 87 88// checkboxes 89reg .Register (astext .KindTaskCheckBox ,r .renderNode ) 90 91// strikethrough 92reg .Register (astext .KindStrikethrough ,r .renderNode ) 93 94// emoji 95reg .Register (east .KindEmoji ,r .renderNode ) 96 97for kind := range r .context .options .Elements { 98reg .Register (kind ,r .renderNode ) 99 } 100} 101 102func (r * ANSIRenderer )renderNode (w util.BufWriter ,source []byte ,node ast.Node ,entering bool ) (ast.WalkStatus ,error ) { 103writeTo := io .Writer (w ) 104bs := r .context .blockStack 105 106// children get rendered by their parent 107if isChild (node ) { 108return ast .WalkContinue ,nil 109 } 110 111e := r .NewElement (node ,source ) 112if entering {//nolint: nestif 113// everything below the Document element gets rendered into a block buffer 114if bs .Len ()> 0 { 115writeTo = io .Writer (bs .Current ().Block ) 116 } 117 118_ ,_ = io .WriteString (writeTo ,e .Entering ) 119if e .Renderer != nil { 120err := e .Renderer .Render (writeTo ,r .context ) 121if err != nil { 122return ast .WalkStop ,fmt .Errorf ("glamour: error rendering: %w" ,err ) 123 } 124 } 125 }else { 126// everything below the Document element gets rendered into a block buffer 127if bs .Len ()> 0 { 128writeTo = io .Writer (bs .Parent ().Block ) 129 } 130 131// if we're finished rendering the entire document, 132// flush to the real writer 133if node .Type ()== ast .TypeDocument { 134writeTo = w 135 } 136 137if e .Finisher != nil { 138err := e .Finisher .Finish (writeTo ,r .context ) 139if err != nil { 140return ast .WalkStop ,fmt .Errorf ("glamour: error finishing render: %w" ,err ) 141 } 142 } 143 144_ ,_ = io .WriteString (bs .Current ().Block ,e .Exiting ) 145 } 146 147return ast .WalkContinue ,nil 148} 149 150func isChild (node ast.Node )bool { 151for n := node .Parent ();n != nil ;n = n .Parent () { 152// These types are already rendered by their parent 153switch n .Kind () { 154case ast .KindCodeSpan ,ast .KindAutoLink ,ast .KindLink ,ast .KindImage ,ast .KindEmphasis ,astext .KindStrikethrough ,astext .KindTableCell : 155return true 156 } 157 } 158 159return false 160} 161 162func resolveRelativeURL (baseURL string ,rel string )string { 163u ,err := url .Parse (rel ) 164if err != nil { 165return rel 166 } 167if u .IsAbs () { 168return rel 169 } 170u .Path = strings .TrimPrefix (u .Path ,"/" ) 171 172base ,err := url .Parse (baseURL ) 173if err != nil { 174return rel 175 } 176return base .ResolveReference (u ).String () 177}