package plaintext import ( "servitor/ansi" "servitor/style" "regexp" "strings" ) type Markup struct { text string cached string cachedWidth int } func NewMarkup(text string) (*Markup, []string, error) { rendered, links := renderWithLinks(text, 80) return &Markup{ text: text, cached: rendered, cachedWidth: 80, }, links, nil } func (m *Markup) Render(width int) string { if m.cachedWidth == width { return m.cached } rendered, _ := renderWithLinks(m.text, width) m.cached = rendered m.cachedWidth = width return rendered } func renderWithLinks(text string, width int) (string, []string) { /* Oversimplistic URL regexp based on RFC 3986, Appendix A It matches: <scheme>://<hierarchy> Where <scheme> is ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) <hierarchy> is any of the characters listed in Appendix A: A-Z a-z 0-9 - . ? # / @ : [ ] % _ ~ ! $ & ' ( ) * + , ; = */ links := []string{} url := regexp.MustCompile(`[A-Za-z][A-Za-z0-9+\-.]*://[A-Za-z0-9.?#/@:%_~!$&'()*+,;=\[\]\-]+`) rendered := url.ReplaceAllStringFunc(text, func(link string) string { links = append(links, link) return style.Link(link, len(links)) }) wrapped := ansi.Wrap(rendered, width) return strings.Trim(wrapped, "\n"), links }