most basic html formatter

This commit is contained in:
Erik Winter 2022-03-22 06:53:01 +01:00 committed by Erik Winter
parent e098f4bcf7
commit 19966f0fc6
3 changed files with 239 additions and 4 deletions

68
format/html.go Normal file
View File

@ -0,0 +1,68 @@
package format
import (
"fmt"
"html"
"ewintr.nl/adoc"
"ewintr.nl/adoc/element"
"ewintr.nl/go-kit/slugify"
)
const pageTemplate = `<!DOCTYPE html>
<html>
<head>
<title>%s</title>
</head>
<body>
%s</body>
</html>
`
func HTML(doc *adoc.ADoc) string {
return fmt.Sprintf(pageTemplate, html.EscapeString(doc.Title), HTMLFragment(doc.Content...))
}
func HTMLFragment(els ...element.Element) string {
var html string
for _, el := range els {
html += htmlElement(el)
}
return html
}
func htmlElement(el element.Element) string {
switch v := el.(type) {
case element.SubTitle:
return fmt.Sprintf("<h2 id=%q>%s</h2>\n", slugify.Slugify(v.Text()), html.EscapeString(v.Text()))
case element.SubSubTitle:
return fmt.Sprintf("<h3 id=%q>%s</h3>\n", slugify.Slugify(v.Text()), html.EscapeString(v.Text()))
case element.List:
var items []element.Element
for _, i := range v {
items = append(items, i)
}
return fmt.Sprintf("<ul>\n%s</ul>\n", HTMLFragment(items...))
case element.ListItem:
return fmt.Sprintf("<li>%s</li>\n", HTMLFragment(v...))
case element.CodeBlock:
return fmt.Sprintf("<pre><code>%s</code></pre>", v.Text())
case element.Paragraph:
return fmt.Sprintf("<p>%s</p>\n", HTMLFragment(v.Elements...))
case element.Strong:
return fmt.Sprintf("<strong>%s</strong>", HTMLFragment(v...))
case element.Emphasis:
return fmt.Sprintf("<em>%s</em>", HTMLFragment(v...))
case element.Code:
return fmt.Sprintf("<code>%s</code>", HTMLFragment(v...))
case element.Link:
return fmt.Sprintf("<a href=%q>%s</a>", v.URL, html.EscapeString(v.Title))
case element.Word:
return html.EscapeString(v.Text())
case element.WhiteSpace:
return " "
default:
return ""
}
}

166
format/html_test.go Normal file
View File

@ -0,0 +1,166 @@
package format_test
import (
"strings"
"testing"
"ewintr.nl/adoc/element"
"ewintr.nl/adoc/format"
"ewintr.nl/adoc/parser"
"ewintr.nl/go-kit/test"
)
func TestHTML(t *testing.T) {
input := `= A Title
Some document
With some text.`
exp := `<!DOCTYPE html>
<html>
<head>
<title>A Title</title>
</head>
<body>
<p>Some document</p>
<p>With some text.</p>
</body>
</html>
`
doc := parser.New(strings.NewReader(input)).Parse()
test.Equals(t, exp, format.HTML(doc))
}
func TestHTMLFragment(t *testing.T) {
for _, tc := range []struct {
name string
input element.Element
exp string
}{
{
name: "whitespace",
input: element.WhiteSpace("\n"),
exp: " ",
},
{
name: "word",
input: element.Word("word"),
exp: "word",
},
{
name: "word with html",
input: element.Word("<h1>hi</h1>"),
exp: "&lt;h1&gt;hi&lt;/h1&gt;",
},
{
name: "pararaphs",
input: element.Paragraph{
Elements: []element.Element{
element.Word("a"),
element.WhiteSpace(" "),
element.Word("word"),
},
},
exp: "<p>a word</p>\n",
},
{
name: "strong",
input: element.Strong{
element.Word("something"),
element.WhiteSpace(" "),
element.Word("strong"),
},
exp: "<strong>something strong</strong>",
},
{
name: "nested",
input: element.Paragraph{
Elements: []element.Element{
element.Word("normal"),
element.WhiteSpace(" "),
element.Word("text"),
element.Strong{
element.WhiteSpace(" "),
element.Word("and"),
element.WhiteSpace(" "),
element.Word("strong"),
},
element.WhiteSpace(" "),
element.Word("too"),
},
},
exp: "<p>normal text<strong> and strong</strong> too</p>\n",
},
{
name: "emphasis",
input: element.Emphasis{
element.Word("yes"),
},
exp: "<em>yes</em>",
},
{
name: "code",
input: element.Code{
element.Word("simple"),
},
exp: "<code>simple</code>",
},
{
name: "link",
input: element.Link{
URL: "http://example.com",
Title: "an example",
},
exp: `<a href="http://example.com">an example</a>`,
},
{
name: "list",
input: element.List{
element.ListItem{
element.Word("item"),
element.WhiteSpace(" "),
element.Word("1"),
},
element.ListItem{
element.Word("item"),
element.WhiteSpace(" "),
element.Word("2"),
},
},
exp: `<ul>
<li>item 1</li>
<li>item 2</li>
</ul>
`,
},
{
name: "code block",
input: element.CodeBlock{
element.Word("some"),
element.WhiteSpace(" "),
element.Word("text"),
element.WhiteSpace("\n"),
element.Word("with"),
element.WhiteSpace("\t"),
element.Word("formatting"),
},
exp: `<pre><code>some text
with formatting</code></pre>`,
},
{
name: "subtitle",
input: element.SubTitle("a subtitle"),
exp: "<h2 id=\"a-subtitle\">a subtitle</h2>\n",
},
{
name: "subsubtitle",
input: element.SubSubTitle("a subsubtitle"),
exp: "<h3 id=\"a-subsubtitle\">a subsubtitle</h3>\n",
},
} {
t.Run(tc.name, func(t *testing.T) {
test.Equals(t, tc.exp, format.HTMLFragment(tc.input))
})
}
}

View File

@ -12,14 +12,15 @@ import (
func TestText(t *testing.T) { func TestText(t *testing.T) {
input := `= A Title input := `= A Title
Some Document Some document
With some text.`
With some text`
exp := `A Title exp := `A Title
Some Document Some document
With some text With some text.
` `