heading.go

 1package ansi
 2
 3import (
 4	"bytes"
 5	"fmt"
 6	"io"
 7
 8	"github.com/charmbracelet/x/cellbuf"
 9)
10
11// A HeadingElement is used to render headings.
12type HeadingElement struct {
13	Level int
14	First bool
15}
16
17const (
18	h1 = iota + 1
19	h2
20	h3
21	h4
22	h5
23	h6
24)
25
26// Render renders a HeadingElement.
27func (e *HeadingElement) Render(w io.Writer, ctx RenderContext) error {
28	bs := ctx.blockStack
29	rules := ctx.options.Styles.Heading
30
31	switch e.Level {
32	case h1:
33		rules = cascadeStyles(rules, ctx.options.Styles.H1)
34	case h2:
35		rules = cascadeStyles(rules, ctx.options.Styles.H2)
36	case h3:
37		rules = cascadeStyles(rules, ctx.options.Styles.H3)
38	case h4:
39		rules = cascadeStyles(rules, ctx.options.Styles.H4)
40	case h5:
41		rules = cascadeStyles(rules, ctx.options.Styles.H5)
42	case h6:
43		rules = cascadeStyles(rules, ctx.options.Styles.H6)
44	}
45
46	if !e.First {
47		_, _ = renderText(w, bs.Current().Style.StylePrimitive, "\n")
48	}
49
50	be := BlockElement{
51		Block: &bytes.Buffer{},
52		Style: cascadeStyle(bs.Current().Style, rules, false),
53	}
54	bs.Push(be)
55
56	_, _ = renderText(w, bs.Parent().Style.StylePrimitive, rules.BlockPrefix)
57	_, _ = renderText(bs.Current().Block, bs.Current().Style.StylePrimitive, rules.Prefix)
58	return nil
59}
60
61// Finish finishes rendering a HeadingElement.
62func (e *HeadingElement) Finish(w io.Writer, ctx RenderContext) error {
63	bs := ctx.blockStack
64	rules := bs.Current().Style
65	mw := NewMarginWriter(ctx, w, rules)
66
67	flow := cellbuf.Wrap(bs.Current().Block.String(), int(bs.Width(ctx)), "") //nolint: gosec
68	_, err := io.WriteString(mw, flow)
69	if err != nil {
70		return fmt.Errorf("glamour: error writing to writer: %w", err)
71	}
72
73	_, _ = renderText(w, bs.Current().Style.StylePrimitive, rules.Suffix)
74	_, _ = renderText(w, bs.Parent().Style.StylePrimitive, rules.BlockSuffix)
75
76	bs.Current().Block.Reset()
77	bs.Pop()
78	return nil
79}