Skip to content
quarry Artifact [5f35dca697]
Login

Artifact [5f35dca697]

Artifact 5f35dca69709801fd4b415088e2c427a6a6719041472c40db40430cc288ee7e8:


package markup

import (
	"bytes"
	"html/template"

	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
)

// ChatLine renders one line of plain text, a title, with each ^N drawn
// the way chat draws it: the chat icon and the number, linked to the
// message. Nothing else in the line is read as markup, so a title is
// still the text its author wrote. A ^N means a message wherever it is
// written (dmestas, 2026-11-08); repo is the repository a bare ^N names.
func ChatLine(text, repo string) template.HTML { return chatLine(text, repo, true) }

// ChatLineIn is ChatLine for a title that is itself a link, a list
// row's: the message wears the same chip, as a span, because a link
// cannot hold another.
func ChatLineIn(text, repo string) template.HTML { return chatLine(text, repo, false) }

func chatLine(text, repo string, linked bool) template.HTML {
	span := &html.Node{Type: html.ElementNode, Data: "span", DataAtom: atom.Span}
	span.AppendChild(&html.Node{Type: html.TextNode, Data: text})
	walkResolve(span, chatOnly{repo: repo})
	if !linked {
		for c := span.FirstChild; c != nil; c = c.NextSibling {
			if c.Type == html.ElementNode && c.Data == "a" {
				c.Data, c.DataAtom = "span", atom.Span
				attrs := c.Attr[:0]
				for _, a := range c.Attr {
					if a.Key != "href" {
						attrs = append(attrs, a)
					}
				}
				c.Attr = attrs
			}
		}
	}
	var out bytes.Buffer
	for c := span.FirstChild; c != nil; c = c.NextSibling {
		if err := html.Render(&out, c); err != nil {
			return template.HTML(template.HTMLEscapeString(text))
		}
	}
	return template.HTML(out.String())
}

// chatOnly resolves message numbers and nothing else. A message needs
// no lookup: its permalink page is honest about one that is gone or
// unreadable, as chat's own links are.
type chatOnly struct{ repo string }

func (c chatOnly) ResolveToken(t RefToken) (string, bool) {
	if t.Kind != RefChat {
		return "", false
	}
	repo := t.Repo
	if repo == "" {
		repo = c.repo
	}
	if repo == "" {
		return "", false
	}
	return "/r/" + repo + "/chat/" + t.ID, true
}

func (chatOnly) CaretIsChat() bool                     { return true }
func (chatOnly) SeqKind() string                       { return "" }
func (chatOnly) ResolveHash(string) string             { return "" }
func (chatOnly) ResolveWiki(string) string             { return "" }
func (chatOnly) ResolvePath(string) string             { return "" }
func (chatOnly) ResolveAttachment(string, bool) string { return "" }
func (chatOnly) UserMark(string) string                { return "" }