plaintext.go 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
  1. package plaintext
  2. import (
  3. "servitor/ansi"
  4. "servitor/style"
  5. "regexp"
  6. "strings"
  7. )
  8. type Markup struct {
  9. text string
  10. cached string
  11. cachedWidth int
  12. }
  13. func NewMarkup(text string) (*Markup, []string, error) {
  14. rendered, links := renderWithLinks(text, 80)
  15. return &Markup{
  16. text: text,
  17. cached: rendered,
  18. cachedWidth: 80,
  19. }, links, nil
  20. }
  21. func (m *Markup) Render(width int) string {
  22. if m.cachedWidth == width {
  23. return m.cached
  24. }
  25. rendered, _ := renderWithLinks(m.text, width)
  26. m.cached = rendered
  27. m.cachedWidth = width
  28. return rendered
  29. }
  30. func renderWithLinks(text string, width int) (string, []string) {
  31. /*
  32. Oversimplistic URL regexp based on RFC 3986, Appendix A
  33. It matches:
  34. <scheme>://<hierarchy>
  35. Where
  36. <scheme> is ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
  37. <hierarchy> is any of the characters listed in Appendix A:
  38. A-Z a-z 0-9 - . ? # / @ : [ ] % _ ~ ! $ & ' ( ) * + , ; =
  39. */
  40. links := []string{}
  41. url := regexp.MustCompile(`[A-Za-z][A-Za-z0-9+\-.]*://[A-Za-z0-9.?#/@:%_~!$&'()*+,;=\[\]\-]+`)
  42. rendered := url.ReplaceAllStringFunc(text, func(link string) string {
  43. links = append(links, link)
  44. return style.Link(link, len(links))
  45. })
  46. wrapped := ansi.Wrap(rendered, width)
  47. return strings.Trim(wrapped, "\n"), links
  48. }