package gemtext import ( "servitor/style" "regexp" "strings" ) /* Specification: https://gemini.circumlunar.space/docs/specification.html */ type Markup struct { tree []string cached string cachedWidth int } func NewMarkup(text string) (*Markup, []string, error) { lines := strings.Split(text, "\n") rendered, links := renderWithLinks(lines, 80) return &Markup{ tree: lines, cached: rendered, cachedWidth: 80, }, links, nil } func (m *Markup) Render(width int) string { if m.cachedWidth == width { return m.cached } rendered, _ := renderWithLinks(m.tree, width) m.cached = rendered m.cachedWidth = width return rendered } func renderWithLinks(lines []string, width int) (string, []string) { links := []string{} result := "" preformattedMode := false preformattedBuffer := "" for _, line := range lines { if strings.HasPrefix(line, "```") { if preformattedMode { result += style.CodeBlock(strings.TrimSuffix(preformattedBuffer, "\n")) + "\n" preformattedBuffer = "" preformattedMode = false } else { preformattedMode = true } continue } if preformattedMode { preformattedBuffer += line + "\n" continue } if match := regexp.MustCompile(`^=>[ \t]*(.*?)(?:[ \t]+(.*))?$`).FindStringSubmatch(line); len(match) == 3 { uri := match[1] alt := match[2] if alt == "" { alt = uri } links = append(links, uri) result += style.LinkBlock(alt, len(links)) + "\n" } else if match := regexp.MustCompile(`^#[ \t]+(.*)$`).FindStringSubmatch(line); len(match) == 2 { result += style.Header(match[1], 1) + "\n" } else if match := regexp.MustCompile(`^##[ \t]+(.*)$`).FindStringSubmatch(line); len(match) == 2 { result += style.Header(match[1], 2) + "\n" } else if match := regexp.MustCompile(`^###[ \t]+(.*)$`).FindStringSubmatch(line); len(match) == 2 { result += style.Header(match[1], 3) + "\n" } else if match := regexp.MustCompile(`^\* (.*)$`).FindStringSubmatch(line); len(match) == 2 { result += style.Bullet(match[1]) + "\n" } else if match := regexp.MustCompile(`^> ?(.*)$`).FindStringSubmatch(line); len(match) == 2 { result += style.QuoteBlock(match[1]) + "\n" } else { result += line + "\n" } } // If trailing backticks are omitted, implicitly automatically add them if preformattedMode { result += style.CodeBlock(strings.TrimSuffix(preformattedBuffer, "\n")) + "\n" } return strings.Trim(result, "\n"), links }