~adnano/gmnitohtml

ref: 58cd619add99a9c516f1224188c2105440f46ffc gmnitohtml/main.go -rw-r--r-- 2.6 KiB
58cd619aAdnan Maolood Use aria-label for inaccessible <pre> text 1 year, 4 months ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
// Command gmnitohtml is a Gemini text to HTML converter.
package main

import (
	"flag"
	"fmt"
	"html"
	"html/template"
	"io"
	"log"
	"os"
	"strings"

	"git.sr.ht/~adnano/go-gemini"
)

func main() {
	var templatePath string
	flag.StringVar(&templatePath, "t", "", "the template to use")
	flag.Parse()

	if templatePath != "" {
		tmpl, err := template.ParseFiles(templatePath)
		if err != nil {
			log.Fatal(err)
		}

		ctx := new(struct {
			Title   string
			Content template.HTML
		})

		b := new(strings.Builder)
		hw := &HTMLWriter{
			out: b,
		}

		var title bool
		gemini.ParseLines(os.Stdin, func(line gemini.Line) {
			if !title {
				if h1, ok := line.(gemini.LineHeading1); ok {
					ctx.Title = string(h1)
					title = true
				}
			}
			hw.Handle(line)
		})
		hw.Finish()
		ctx.Content = template.HTML(b.String())

		if err := tmpl.Execute(os.Stdout, ctx); err != nil {
			log.Fatal(err)
		}

		return
	}

	hw := &HTMLWriter{
		out: os.Stdout,
	}
	gemini.ParseLines(os.Stdin, hw.Handle)
	hw.Finish()
}

type HTMLWriter struct {
	out  io.Writer
	pre  bool
	list bool
}

func (h *HTMLWriter) Handle(line gemini.Line) {
	if _, ok := line.(gemini.LineListItem); ok {
		if !h.list {
			h.list = true
			fmt.Fprint(h.out, "<ul>\n")
		}
	} else if h.list {
		h.list = false
		fmt.Fprint(h.out, "</ul>\n")
	}
	switch line := line.(type) {
	case gemini.LineLink:
		url := html.EscapeString(line.URL)
		name := html.EscapeString(line.Name)
		if name == "" {
			name = url
		}
		fmt.Fprintf(h.out, "<p><a href='%s'>%s</a></p>\n", url, name)
	case gemini.LinePreformattingToggle:
		h.pre = !h.pre
		if h.pre {
			alt := strings.TrimSpace(string(line))
			if alt != "" {
				alt = html.EscapeString(alt)
				fmt.Fprintf(h.out, "<pre aria-label='%s'>\n", alt)
			} else {
				fmt.Fprint(h.out, "<pre>\n")
			}
		} else {
			fmt.Fprint(h.out, "</pre>\n")
		}
	case gemini.LinePreformattedText:
		fmt.Fprintf(h.out, "%s\n", html.EscapeString(string(line)))
	case gemini.LineHeading1:
		fmt.Fprintf(h.out, "<h1>%s</h1>\n", html.EscapeString(string(line)))
	case gemini.LineHeading2:
		fmt.Fprintf(h.out, "<h2>%s</h2>\n", html.EscapeString(string(line)))
	case gemini.LineHeading3:
		fmt.Fprintf(h.out, "<h3>%s</h3>\n", html.EscapeString(string(line)))
	case gemini.LineListItem:
		fmt.Fprintf(h.out, "<li>%s</li>\n", html.EscapeString(string(line)))
	case gemini.LineQuote:
		fmt.Fprintf(h.out, "<blockquote>%s</blockquote>\n", html.EscapeString(string(line)))
	case gemini.LineText:
		if line != "" {
			fmt.Fprintf(h.out, "<p>%s</p>\n", html.EscapeString(string(line)))
		}
	}
}

func (h *HTMLWriter) Finish() {
	if h.pre {
		fmt.Fprint(h.out, "</pre>\n")
	}
	if h.list {
		fmt.Fprint(h.out, "</ul>\n")
	}
}