github.com/charmbracelet/glamour@v0.7.0/ansi/baseelement.go (about)

     1  package ansi
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"strings"
     7  	"text/template"
     8  
     9  	"github.com/muesli/termenv"
    10  )
    11  
    12  // BaseElement renders a styled primitive element.
    13  type BaseElement struct {
    14  	Token  string
    15  	Prefix string
    16  	Suffix string
    17  	Style  StylePrimitive
    18  }
    19  
    20  func formatToken(format string, token string) (string, error) {
    21  	var b bytes.Buffer
    22  
    23  	v := make(map[string]interface{})
    24  	v["text"] = token
    25  
    26  	tmpl, err := template.New(format).Funcs(TemplateFuncMap).Parse(format)
    27  	if err != nil {
    28  		return "", err
    29  	}
    30  
    31  	err = tmpl.Execute(&b, v)
    32  	return b.String(), err
    33  }
    34  
    35  func renderText(w io.Writer, p termenv.Profile, rules StylePrimitive, s string) {
    36  	if len(s) == 0 {
    37  		return
    38  	}
    39  
    40  	out := termenv.String(s)
    41  
    42  	if rules.Upper != nil && *rules.Upper {
    43  		out = termenv.String(strings.ToUpper(s))
    44  	}
    45  	if rules.Lower != nil && *rules.Lower {
    46  		out = termenv.String(strings.ToLower(s))
    47  	}
    48  	if rules.Title != nil && *rules.Title {
    49  		out = termenv.String(strings.Title(s))
    50  	}
    51  	if rules.Color != nil {
    52  		out = out.Foreground(p.Color(*rules.Color))
    53  	}
    54  	if rules.BackgroundColor != nil {
    55  		out = out.Background(p.Color(*rules.BackgroundColor))
    56  	}
    57  	if rules.Underline != nil && *rules.Underline {
    58  		out = out.Underline()
    59  	}
    60  	if rules.Bold != nil && *rules.Bold {
    61  		out = out.Bold()
    62  	}
    63  	if rules.Italic != nil && *rules.Italic {
    64  		out = out.Italic()
    65  	}
    66  	if rules.CrossedOut != nil && *rules.CrossedOut {
    67  		out = out.CrossOut()
    68  	}
    69  	if rules.Overlined != nil && *rules.Overlined {
    70  		out = out.Overline()
    71  	}
    72  	if rules.Inverse != nil && *rules.Inverse {
    73  		out = out.Reverse()
    74  	}
    75  	if rules.Blink != nil && *rules.Blink {
    76  		out = out.Blink()
    77  	}
    78  
    79  	_, _ = w.Write([]byte(out.String()))
    80  }
    81  
    82  func (e *BaseElement) Render(w io.Writer, ctx RenderContext) error {
    83  	bs := ctx.blockStack
    84  
    85  	renderText(w, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, e.Prefix)
    86  	defer func() {
    87  		renderText(w, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, e.Suffix)
    88  	}()
    89  
    90  	rules := bs.With(e.Style)
    91  	// render unstyled prefix/suffix
    92  	renderText(w, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, rules.BlockPrefix)
    93  	defer func() {
    94  		renderText(w, ctx.options.ColorProfile, bs.Current().Style.StylePrimitive, rules.BlockSuffix)
    95  	}()
    96  
    97  	// render styled prefix/suffix
    98  	renderText(w, ctx.options.ColorProfile, rules, rules.Prefix)
    99  	defer func() {
   100  		renderText(w, ctx.options.ColorProfile, rules, rules.Suffix)
   101  	}()
   102  
   103  	s := e.Token
   104  	if len(rules.Format) > 0 {
   105  		var err error
   106  		s, err = formatToken(rules.Format, s)
   107  		if err != nil {
   108  			return err
   109  		}
   110  	}
   111  	renderText(w, ctx.options.ColorProfile, rules, s)
   112  	return nil
   113  }