okami.mom/glamour
git clone https://git.nkp.pet/okami.mom/glamour
cf874d7
main
1package ansi 2 3import ( 4"bytes" 5"fmt" 6"hash/fnv" 7"io" 8"net/url" 9 10"github.com/charmbracelet/x/ansi" 11) 12 13// A LinkElement is used to render hyperlinks. 14type LinkElement struct { 15BaseURL string 16URL string 17Children []ElementRenderer 18SkipText bool 19SkipHref bool 20 21hyperlink ,resetHyperlink string 22validURL bool 23} 24 25// Render renders a LinkElement. 26func (e * LinkElement )Render (w io.Writer ,ctx RenderContext )error { 27// Make OSC 8 hyperlink token. 28e .hyperlink ,e .resetHyperlink ,e .validURL = makeHyperlink (e .URL ) 29 30if ! e .SkipText { 31if err := e .renderTextPart (w ,ctx );err != nil { 32return err 33 } 34 } 35if ! e .SkipHref { 36if err := e .renderHrefPart (w ,ctx );err != nil { 37return err 38 } 39 } 40return nil 41} 42 43func (e * LinkElement )renderTextPart (w io.Writer ,ctx RenderContext )error { 44for _ ,child := range e .Children { 45if r ,ok := child .(StyleOverriderElementRenderer );ok {//nolint:nestif 46var b bytes.Buffer 47st := ctx .options .Styles .LinkText 48if err := r .StyleOverrideRender (& b ,ctx ,st );err != nil { 49return fmt .Errorf ("glamour: error rendering with style: %w" ,err ) 50 } 51 52token := e .hyperlink + b .String ()+ e .resetHyperlink 53if _ ,err := io .WriteString (w ,token );err != nil { 54return fmt .Errorf ("glamour: error writing hyperlink: %w" ,err ) 55 } 56 }else { 57var b bytes.Buffer 58if err := child .Render (& b ,ctx );err != nil { 59return fmt .Errorf ("glamour: error rendering: %w" ,err ) 60 } 61token := e .hyperlink + b .String ()+ e .resetHyperlink 62el := & BaseElement { 63Token :token , 64Style :ctx .options .Styles .LinkText , 65 } 66if err := el .Render (w ,ctx );err != nil { 67return fmt .Errorf ("glamour: error rendering: %w" ,err ) 68 } 69 } 70 } 71return nil 72} 73 74func (e * LinkElement )renderHrefPart (w io.Writer ,ctx RenderContext )error { 75prefix := "" 76if ! e .SkipText { 77prefix = " " 78 } 79 80if e .validURL { 81token := e .hyperlink + resolveRelativeURL (e .BaseURL ,e .URL )+ e .resetHyperlink 82el := & BaseElement { 83Token :token , 84Prefix :prefix , 85Style :ctx .options .Styles .Link , 86 } 87if err := el .Render (w ,ctx );err != nil { 88return err 89 } 90 } 91return nil 92} 93 94// makeHyperlink takes a URL and returns an OSC 8 hyperlink token. 95func makeHyperlink (link string ) (string ,string ,bool ) { 96// Make OSC 8 hyperlink token. 97var hyperlink ,resetHyperlink string 98 99u ,err := url .Parse (link ) 100validURL := err == nil && "#" + u .Fragment != link // if the URL only consists of an anchor, ignore it 101if validURL { 102h := fnv .New32a () 103if _ ,err := io .WriteString (h ,link );err != nil { 104return "" ,"" ,false 105 } 106urlID := fmt .Sprintf ("id=%d" ,h .Sum32 ()) 107hyperlink = ansi .SetHyperlink (link ,urlID ) 108resetHyperlink = ansi .ResetHyperlink () 109 } 110 111return hyperlink ,resetHyperlink ,validURL 112}