okami.mom/glamour

git clone https://git.nkp.pet/okami.mom/glamour

Kieran Klukaschore: fix pre-existing lint failuresc293649

main
5.3 KiB234 linesraw
1package ansi
2
3import (
4	"bytes"
5	"fmt"
6	"io"
7	"net/url"
8	"slices"
9	"strconv"
10	"strings"
11
12	"charm.land/glamour/v2/internal/autolink"
13	xansi "github.com/charmbracelet/x/ansi"
14	"github.com/charmbracelet/x/exp/slice"
15	"github.com/yuin/goldmark/ast"
16	astext "github.com/yuin/goldmark/extension/ast"
17)
18
19type tableLink struct {
20	href     string
21	title    string
22	content  string
23	linkType linkType
24}
25
26type linkType int
27
28const (
29	_ linkType = iota
30	linkTypeAuto
31	linkTypeImage
32	linkTypeRegular
33)
34
35func (e *TableElement) printTableLinks(ctx RenderContext) {
36	if !e.shouldPrintTableLinks(ctx) {
37		return
38	}
39
40	w := ctx.blockStack.Current().Block
41	termWidth := int(ctx.blockStack.Width(ctx))
42
43	renderLinkText := func(link tableLink, position, padding int) string {
44		token := strings.Repeat(" ", padding)
45		style := ctx.options.Styles.LinkText
46
47		switch link.linkType {
48		case linkTypeAuto, linkTypeRegular:
49			token += fmt.Sprintf("[%d]: %s", position, link.content)
50		case linkTypeImage:
51			token += link.content
52			style = ctx.options.Styles.ImageText
53			style.Prefix = fmt.Sprintf("[%d]: %s", position, style.Prefix)
54		}
55
56		var b bytes.Buffer
57		el := &BaseElement{Token: token, Style: style}
58		_ = el.Render(io.MultiWriter(w, &b), ctx)
59		return b.String()
60	}
61
62	renderLinkHref := func(link tableLink, linkText string) {
63		hyperlink, resetHyperlink, _ := makeHyperlink(link.href)
64
65		style := ctx.options.Styles.Link
66		if link.linkType == linkTypeImage {
67			style = ctx.options.Styles.Image
68		}
69
70		linkMaxWidth := max(termWidth-xansi.StringWidth(linkText)-1, 0)
71		token := hyperlink + xansi.Truncate(link.href, linkMaxWidth, "…") + resetHyperlink
72
73		el := &BaseElement{Token: token, Style: style}
74		_ = el.Render(w, ctx)
75	}
76
77	renderString := func(str string) {
78		_, _ = renderText(w, ctx.blockStack.Current().Style.StylePrimitive, str)
79	}
80
81	paddingFor := func(total, position int) int {
82		totalSize := len(strconv.Itoa(total))
83		positionSize := len(strconv.Itoa(position))
84
85		return max(totalSize-positionSize, 0)
86	}
87
88	renderList := func(list []tableLink) {
89		for i, item := range list {
90			position := i + 1
91			padding := paddingFor(len(list), position)
92
93			renderString("\n")
94			linkText := renderLinkText(item, position, padding)
95			renderString(" ")
96			renderLinkHref(item, linkText)
97		}
98	}
99
100	if len(ctx.table.tableLinks) > 0 {
101		renderString("\n")
102	}
103	renderList(ctx.table.tableLinks)
104
105	if len(ctx.table.tableImages) > 0 {
106		renderString("\n")
107	}
108	renderList(ctx.table.tableImages)
109}
110
111func (e *TableElement) shouldPrintTableLinks(ctx RenderContext) bool {
112	if ctx.options.InlineTableLinks {
113		return false
114	}
115	if len(ctx.table.tableLinks) == 0 && len(ctx.table.tableImages) == 0 {
116		return false
117	}
118	return true
119}
120
121func (e *TableElement) collectLinksAndImages(ctx RenderContext) error {
122	images := make([]tableLink, 0)
123	links := make([]tableLink, 0)
124
125	err := ast.Walk(e.table, func(node ast.Node, entering bool) (ast.WalkStatus, error) {
126		if !entering {
127			return ast.WalkContinue, nil
128		}
129
130		switch n := node.(type) {
131		case *ast.AutoLink:
132			uri := string(n.URL(e.source))
133			autoLink := tableLink{
134				href:     uri,
135				content:  linkDomain(uri),
136				linkType: linkTypeAuto,
137			}
138			if shortned, ok := autolink.Detect(uri); ok {
139				autoLink.content = shortned
140			}
141			links = append(links, autoLink)
142		case *ast.Image:
143			content, err := nodeContent(node, e.source)
144			if err != nil {
145				return ast.WalkStop, err
146			}
147			image := tableLink{
148				href:     string(n.Destination),
149				title:    string(n.Title),
150				content:  string(content),
151				linkType: linkTypeImage,
152			}
153			if image.content == "" {
154				image.content = linkDomain(image.href)
155			}
156			images = append(images, image)
157		case *ast.Link:
158			content, err := nodeContent(node, e.source)
159			if err != nil {
160				return ast.WalkStop, err
161			}
162			link := tableLink{
163				href:     string(n.Destination),
164				title:    string(n.Title),
165				content:  string(content),
166				linkType: linkTypeRegular,
167			}
168			links = append(links, link)
169		}
170
171		return ast.WalkContinue, nil
172	})
173	if err != nil {
174		return fmt.Errorf("glamour: error collecting links: %w", err)
175	}
176
177	ctx.table.tableImages = slice.Uniq(images)
178	ctx.table.tableLinks = slice.Uniq(links)
179	return nil
180}
181
182func isInsideTable(node ast.Node) bool {
183	parent := node.Parent()
184	for parent != nil {
185		switch parent.Kind() {
186		case astext.KindTable, astext.KindTableHeader, astext.KindTableRow, astext.KindTableCell:
187			return true
188		default:
189			parent = parent.Parent()
190		}
191	}
192	return false
193}
194
195func nodeContent(node ast.Node, source []byte) ([]byte, error) {
196	var builder bytes.Buffer
197
198	var traverse func(node ast.Node) error
199	traverse = func(node ast.Node) error {
200		for n := node.FirstChild(); n != nil; n = n.NextSibling() {
201			switch nn := n.(type) {
202			case *ast.Text:
203				if _, err := builder.Write(nn.Segment.Value(source)); err != nil {
204					return fmt.Errorf("glamour: error writing text node: %w", err)
205				}
206			default:
207				if err := traverse(nn); err != nil {
208					return err
209				}
210			}
211		}
212		return nil
213	}
214	if err := traverse(node); err != nil {
215		return nil, err
216	}
217
218	return builder.Bytes(), nil
219}
220
221func linkDomain(href string) string {
222	if uri, err := url.Parse(href); err == nil {
223		return uri.Hostname()
224	}
225	return "link"
226}
227
228func linkWithSuffix(tl tableLink, list []tableLink) string {
229	index := slices.Index(list, tl)
230	if index == -1 {
231		return tl.content
232	}
233	return fmt.Sprintf("%s[%d]", tl.content, index+1)
234}