1
0

test rendering

This commit is contained in:
Arpad Ryszka 2025-10-06 00:37:10 +02:00
parent 2f496f4ca2
commit 73250375c9
30 changed files with 1028 additions and 131 deletions

View File

@ -7,30 +7,38 @@ all: clean fmt build cover
build: $(SOURCES) tags promote-to-tags build: $(SOURCES) tags promote-to-tags
go build go build
tags: tags/block.gen.go tags/inline.gen.go tags/void.block.gen.go tags/void.inline.gen.go tags/inlinechildren.gen.go tags/script.gen.go tags: \
tag/block.gen.go \
tag/inline.gen.go \
tag/void.block.gen.go \
tag/void.inline.gen.go \
tag/inlinechildren.gen.go \
tag/script.gen.go
go fmt tag/*
promote-to-tags: tags/promote.gen.go promote-to-tags: tag/promote.gen.go
go fmt tag/*
tags/block.gen.go: $(SOURCES) tags.block.txt tag/block.gen.go: $(SOURCES) tag.block.txt
go run script/generate-tags.go < tags.block.txt > tags/block.gen.go go run script/generate-tags.go < tag.block.txt > tag/block.gen.go
tags/inline.gen.go: $(SOURCES) tags.inline.txt tag/inline.gen.go: $(SOURCES) tag.inline.txt
go run script/generate-tags.go Inline < tags.inline.txt > tags/inline.gen.go go run script/generate-tags.go Inline < tag.inline.txt > tag/inline.gen.go
tags/inlinechildren.gen.go: $(SOURCES) tags.inlinechildren.txt tag/inlinechildren.gen.go: $(SOURCES) tag.inlinechildren.txt
go run script/generate-tags.go InlineChildren < tags.inlinechildren.txt > tags/inlinechildren.gen.go go run script/generate-tags.go InlineChildren < tag.inlinechildren.txt > tag/inlinechildren.gen.go
tags/void.block.gen.go: $(SOURCES) tags.void.block.txt tag/void.block.gen.go: $(SOURCES) tag.void.block.txt
go run script/generate-tags.go Void < tags.void.block.txt > tags/void.block.gen.go go run script/generate-tags.go Void < tag.void.block.txt > tag/void.block.gen.go
tags/void.inline.gen.go: $(SOURCES) tags.void.inline.txt tag/void.inline.gen.go: $(SOURCES) tag.void.inline.txt
go run script/generate-tags.go Void Inline < tags.void.inline.txt > tags/void.inline.gen.go go run script/generate-tags.go Void Inline < tag.void.inline.txt > tag/void.inline.gen.go
tags/script.gen.go: $(SOURCES) tags.script.txt tag/script.gen.go: $(SOURCES) tag.script.txt
go run script/generate-tags.go ScriptContent < tags.script.txt > tags/script.gen.go go run script/generate-tags.go ScriptContent < tag.script.txt > tag/script.gen.go
tags/promote.gen.go: $(SOURCES) promote-to-tags.txt tag/promote.gen.go: $(SOURCES) promote-to-tags.txt
go run script/promote-to-tags.go < promote-to-tags.txt > tags/promote.gen.go go run script/promote-to-tags.go < promote-to-tags.txt > tag/promote.gen.go
fmt: $(SOURCES) tags fmt: $(SOURCES) tags
go fmt ./... go fmt ./...
@ -49,4 +57,4 @@ showcover: .cover
clean: clean:
go clean go clean
rm -f tags/*.gen.go rm -f tag/*.gen.go

12
eq.go
View File

@ -6,13 +6,17 @@ func eq2(t1, t2 Tag) bool {
} }
a1, a2 := AllAttributes(t1), AllAttributes(t2) a1, a2 := AllAttributes(t1), AllAttributes(t2)
if len(a1) != len(a2) { if len(a1.names) != len(a2.names) {
return false return false
} }
for name := range a1 { for i, name := range a1.names {
v1 := a1[name] if a2.names[i] != name {
v2, ok := a2[name] return false
}
v1 := a1.values[name]
v2, ok := a2.values[name]
if !ok || v1 != v2 { if !ok || v1 != v2 {
return false return false
} }

View File

@ -3,7 +3,7 @@ package html_test
import ( import (
"bytes" "bytes"
"code.squareroundforest.org/arpio/html" "code.squareroundforest.org/arpio/html"
. "code.squareroundforest.org/arpio/html/tags" . "code.squareroundforest.org/arpio/html/tag"
"testing" "testing"
) )

View File

@ -15,6 +15,21 @@ type indentWriter struct {
err error err error
} }
func indentLen(indent string) int {
var l int
r := []rune(indent)
for _, ri := range r {
if ri == '\t' {
l += 8
continue
}
l++
}
return l
}
func newIndentWriter(out io.Writer, indent string) *indentWriter { func newIndentWriter(out io.Writer, indent string) *indentWriter {
return &indentWriter{ return &indentWriter{
out: bufio.NewWriter(out), out: bufio.NewWriter(out),
@ -66,6 +81,7 @@ func (w *indentWriter) Write(p []byte) (int, error) {
if r == '\n' { if r == '\n' {
w.write('\n') w.write('\n')
w.started = true
w.lineStarted = false w.lineStarted = false
continue continue
} }

68
lib.go
View File

@ -9,7 +9,10 @@ import (
) )
// when composing html, the Attr convenience function is recommended to construct input attributes // when composing html, the Attr convenience function is recommended to construct input attributes
type Attributes map[string]any type Attributes struct {
names []string
values map[string]any
}
// immutable // immutable
// calling creates a new copy with the passed in attributes and child nodes applied only to the copy // calling creates a new copy with the passed in attributes and child nodes applied only to the copy
@ -31,13 +34,6 @@ type Indentation struct {
Indent string Indent string
} }
func (t Tag) String() string {
buf := bytes.NewBuffer(nil)
r := renderer{out: buf}
t()(r)
return buf.String()
}
// convenience function primarily aimed to help with construction of html with tags // convenience function primarily aimed to help with construction of html with tags
// the names and values are applied using fmt.Sprint, tolerating fmt.Stringer implementations // the names and values are applied using fmt.Sprint, tolerating fmt.Stringer implementations
func Attr(a ...any) Attributes { func Attr(a ...any) Attributes {
@ -45,14 +41,43 @@ func Attr(a ...any) Attributes {
a = append(a, "") a = append(a, "")
} }
am := make(Attributes) var am Attributes
am.values = make(map[string]any)
for i := 0; i < len(a); i += 2 { for i := 0; i < len(a); i += 2 {
am[fmt.Sprint(a[i])] = a[i+1] name := fmt.Sprint(a[i])
am.names = append(am.names, name)
am.values[name] = a[i+1]
} }
return am return am
} }
func (a Attributes) Names() []string {
if len(a.names) == 0 {
return nil
}
n := make([]string, len(a.names))
copy(n, a.names)
return n
}
func (a Attributes) Has(name string) bool {
_, ok := a.values[name]
return ok
}
func (a Attributes) Value(name string) any {
return a.values[name]
}
func (t Tag) String() string {
buf := bytes.NewBuffer(nil)
r := renderer{out: buf}
t()(r)
return buf.String()
}
// defines a new tag with name and initial attributes and child nodes // defines a new tag with name and initial attributes and child nodes
func Define(name string, children ...any) Tag { func Define(name string, children ...any) Tag {
if handleQuery(name, children) { if handleQuery(name, children) {
@ -86,7 +111,7 @@ func Doctype(children ...any) Tag {
} }
func Comment(children ...any) Tag { func Comment(children ...any) Tag {
return Inline(Declaration(append(append([]any{"--"}, children...), "--"))) return Inline(Declaration(append(append([]any{"--"}, children...), "--")...))
} }
// returns the name of a tag // returns the name of a tag
@ -100,9 +125,11 @@ func Name(t Tag) string {
func AllAttributes(t Tag) Attributes { func AllAttributes(t Tag) Attributes {
q := attributesQuery{} q := attributesQuery{}
t()(&q) t()(&q)
a := make(Attributes) a := Attributes{values: make(map[string]any)}
for name, value := range q.value { for _, name := range q.value.names {
a[name] = value value := q.value.values[name]
a.names = append(a.names, name)
a.values[name] = value
} }
return a return a
@ -126,7 +153,14 @@ func DeleteAttribute(t Tag, name string) Tag {
n := Name(t) n := Name(t)
a := AllAttributes(t) a := AllAttributes(t)
c := Children(t) c := Children(t)
delete(a, name) delete(a.values, name)
for i := range a.names {
if a.names[i] == name {
a.names = append(a.names[:i], a.names[i+1:]...)
break
}
}
return Define(n, append(c, a)...) return Define(n, append(c, a)...)
} }
@ -174,6 +208,10 @@ func Children(t Tag) []any {
return c return c
} }
func Indent() Indentation {
return Indentation{Indent: "\t"}
}
// renders html with t as the root node with indentation // renders html with t as the root node with indentation
// child nodes are rendered via fmt.Sprint, tolerating fmt.Stringer implementations // child nodes are rendered via fmt.Sprint, tolerating fmt.Stringer implementations
// consecutive spaces are considered to be so on purpose, and are converted into &nbsp; // consecutive spaces are considered to be so on purpose, and are converted into &nbsp;

View File

@ -3,7 +3,7 @@ package html_test
import ( import (
"bytes" "bytes"
"code.squareroundforest.org/arpio/html" "code.squareroundforest.org/arpio/html"
. "code.squareroundforest.org/arpio/html/tags" . "code.squareroundforest.org/arpio/html/tag"
"code.squareroundforest.org/arpio/notation" "code.squareroundforest.org/arpio/notation"
"testing" "testing"
) )
@ -58,7 +58,7 @@ func TestLib(t *testing.T) {
} }
var b bytes.Buffer var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indentation{Indent: "\t"}, teamHTML(myTeam)); err != nil { if err := html.RenderIndent(&b, html.Indent(), teamHTML(myTeam)); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -3,9 +3,4 @@ recommendation is not to mutate children. Ofc, creatively breaking the rules is
right audience right audience
test wrapped templates test wrapped templates
test empty block test empty block
escape extra space between tag boundaries
declarations: <!doctype html>
comments: <!-- foo -->
render nil as empty
support readers
review which tags should be of type inline-children review which tags should be of type inline-children

View File

@ -53,13 +53,14 @@ func groupChildren(c []any) ([]Attributes, []any, []renderGuide) {
func mergeAttributes(c []any) Attributes { func mergeAttributes(c []any) Attributes {
a, _, _ := groupChildren(c) a, _, _ := groupChildren(c)
if len(a) == 0 { if len(a) == 0 {
return nil return Attributes{}
} }
to := make(Attributes) to := Attributes{values: make(map[string]any)}
for _, ai := range a { for _, ai := range a {
for name, value := range ai { for _, name := range ai.names {
to[name] = value to.names = append(to.names, name)
to.values[name] = ai.values[name]
} }
} }
@ -69,7 +70,7 @@ func mergeAttributes(c []any) Attributes {
func findAttribute(c []any, name string) (any, bool) { func findAttribute(c []any, name string) (any, bool) {
a, _, _ := groupChildren(c) a, _, _ := groupChildren(c)
for i := len(a) - 1; i >= 0; i-- { for i := len(a) - 1; i >= 0; i-- {
value, ok := a[i][name] value, ok := a[i].values[name]
if ok { if ok {
return value, true return value, true
} }

View File

@ -3,7 +3,7 @@ package html_test
import ( import (
"bytes" "bytes"
"code.squareroundforest.org/arpio/html" "code.squareroundforest.org/arpio/html"
. "code.squareroundforest.org/arpio/html/tags" . "code.squareroundforest.org/arpio/html/tag"
"testing" "testing"
) )
@ -11,7 +11,7 @@ func TestQuery(t *testing.T) {
t.Run("group children", func(t *testing.T) { t.Run("group children", func(t *testing.T) {
inlineDiv := html.Inline(Div(Attr("foo", "bar"), "baz")) inlineDiv := html.Inline(Div(Attr("foo", "bar"), "baz"))
attr := html.AllAttributes(inlineDiv) attr := html.AllAttributes(inlineDiv)
if len(attr) != 1 || attr["foo"] != "bar" { if len(attr.Names()) != 1 || attr.Value("foo") != "bar" {
t.Fatal() t.Fatal()
} }
@ -35,7 +35,7 @@ func TestQuery(t *testing.T) {
t.Run("has attributes", func(t *testing.T) { t.Run("has attributes", func(t *testing.T) {
div := Div(Attr("foo", "bar")) div := Div(Attr("foo", "bar"))
attr := html.AllAttributes(div) attr := html.AllAttributes(div)
if len(attr) != 1 || attr["foo"] != "bar" { if len(attr.Names()) != 1 || attr.Value("foo") != "bar" {
t.Fatal() t.Fatal()
} }
}) })
@ -43,7 +43,7 @@ func TestQuery(t *testing.T) {
t.Run("no attributes", func(t *testing.T) { t.Run("no attributes", func(t *testing.T) {
div := Div() div := Div()
attr := html.AllAttributes(div) attr := html.AllAttributes(div)
if len(attr) != 0 { if len(attr.Names()) != 0 {
t.Fatal() t.Fatal()
} }
}) })
@ -84,7 +84,7 @@ func TestQuery(t *testing.T) {
t.Run("all attributes", func(t *testing.T) { t.Run("all attributes", func(t *testing.T) {
div := Div(Attr("foo", "bar", "baz", "qux")) div := Div(Attr("foo", "bar", "baz", "qux"))
attr := html.AllAttributes(div) attr := html.AllAttributes(div)
if len(attr) != 2 || attr["foo"] != "bar" || attr["baz"] != "qux" { if len(attr.Names()) != 2 || attr.Value("foo") != "bar" || attr.Value("baz") != "qux" {
t.Fatal() t.Fatal()
} }
}) })
@ -109,7 +109,7 @@ func TestQuery(t *testing.T) {
div := Div(Span("foo")) div := Div(Span("foo"))
var b bytes.Buffer var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indentation{Indent: "\t"}, div); err != nil { if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err) t.Fatal(err)
} }

View File

@ -3,6 +3,7 @@ package html
import ( import (
"fmt" "fmt"
"io" "io"
"strings"
) )
const defaultPWidth = 112 const defaultPWidth = 112
@ -52,10 +53,24 @@ func (r *renderer) getPrintf(tagName string) func(f string, a ...any) {
func (r *renderer) renderAttributes(tagName string, a []Attributes) { func (r *renderer) renderAttributes(tagName string, a []Attributes) {
printf := r.getPrintf(tagName) printf := r.getPrintf(tagName)
isDeclaration := strings.HasPrefix(tagName, "!")
for _, ai := range a { for _, ai := range a {
for name, value := range ai { for _, name := range ai.names {
if isTrue, _ := value.(bool); isTrue { value := ai.values[name]
printf(" &s", name) isTrue, _ := value.(bool)
if isTrue && isDeclaration {
escaped := attributeEscape(name)
if escaped == name {
printf(" %s", name)
continue
}
printf(" \"%s\"", escaped)
continue
}
if isTrue {
printf(" %s", name)
continue continue
} }
@ -91,7 +106,7 @@ func (r *renderer) renderUnindented(name string, rg renderGuide, a []Attributes,
ew := newEscapeWriter(r.out) ew := newEscapeWriter(r.out)
_, r.err = io.Copy(ew, rd) _, r.err = io.Copy(ew, rd)
if r.err != nil { if r.err == nil {
ew.Flush() ew.Flush()
r.err = ew.err r.err = ew.err
} }
@ -165,7 +180,6 @@ func (r *renderer) renderInline(name string, rg renderGuide, a []Attributes, chi
if r.err == nil { if r.err == nil {
iw := newIndentWriter(r.out, r.currentIndent+r.indent.Indent) iw := newIndentWriter(r.out, r.currentIndent+r.indent.Indent)
iw.Write([]byte{'\n'}) iw.Write([]byte{'\n'})
iw.Write([]byte(r.currentIndent + r.indent.Indent))
_, r.err = io.Copy(iw, rd) _, r.err = io.Copy(iw, rd)
if r.err == nil { if r.err == nil {
iw.Flush() iw.Flush()
@ -222,7 +236,6 @@ func (r *renderer) renderInline(name string, rg renderGuide, a []Attributes, chi
if r.err == nil { if r.err == nil {
iw := newIndentWriter(r.out, r.currentIndent+r.indent.Indent) iw := newIndentWriter(r.out, r.currentIndent+r.indent.Indent)
iw.Write([]byte{'\n'}) iw.Write([]byte{'\n'})
iw.Write([]byte(r.currentIndent + r.indent.Indent))
iw.Write([]byte(s)) iw.Write([]byte(s))
iw.Flush() iw.Flush()
r.err = iw.err r.err = iw.err
@ -298,7 +311,7 @@ func (r *renderer) renderInline(name string, rg renderGuide, a []Attributes, chi
cr := new(renderer) cr := new(renderer)
*cr = *r *cr = *r
cr.currentIndent += cr.indent.Indent cr.currentIndent += cr.indent.Indent
cr.pwidth -= len([]rune(cr.indent.Indent)) cr.pwidth -= indentLen(cr.indent.Indent)
if cr.pwidth < cr.indent.MinPWidth { if cr.pwidth < cr.indent.MinPWidth {
cr.pwidth = cr.indent.MinPWidth cr.pwidth = cr.indent.MinPWidth
} }
@ -339,7 +352,7 @@ func (r *renderer) renderBlock(name string, rg renderGuide, a []Attributes, chil
lastBlock := true lastBlock := true
originalIndent, originalWidth := r.currentIndent, r.pwidth originalIndent, originalWidth := r.currentIndent, r.pwidth
r.currentIndent += r.indent.Indent r.currentIndent += r.indent.Indent
r.pwidth -= len([]rune(r.indent.Indent)) r.pwidth -= indentLen(r.indent.Indent)
if r.pwidth < r.indent.MinPWidth { if r.pwidth < r.indent.MinPWidth {
r.pwidth = r.indent.MinPWidth r.pwidth = r.indent.MinPWidth
} }
@ -348,11 +361,9 @@ func (r *renderer) renderBlock(name string, rg renderGuide, a []Attributes, chil
rd, isReader := c.(io.Reader) rd, isReader := c.(io.Reader)
if isReader && rg.verbatim { if isReader && rg.verbatim {
r.clearWrapper() r.clearWrapper()
printf("\n")
if r.err == nil { if r.err == nil {
iw := newIndentWriter(r.out, r.currentIndent+r.indent.Indent) iw := newIndentWriter(r.out, r.currentIndent)
iw.Write([]byte{'\n'}) iw.Write([]byte{'\n'})
iw.Write([]byte(r.currentIndent + r.indent.Indent))
_, r.err = io.Copy(iw, rd) _, r.err = io.Copy(iw, rd)
if r.err == nil { if r.err == nil {
iw.Flush() iw.Flush()
@ -406,7 +417,6 @@ func (r *renderer) renderBlock(name string, rg renderGuide, a []Attributes, chil
if r.err == nil { if r.err == nil {
iw := newIndentWriter(r.out, r.currentIndent) iw := newIndentWriter(r.out, r.currentIndent)
iw.Write([]byte{'\n'}) iw.Write([]byte{'\n'})
iw.Write([]byte(r.currentIndent + r.indent.Indent))
iw.Write([]byte(s)) iw.Write([]byte(s))
iw.Flush() iw.Flush()
r.err = iw.err r.err = iw.err

View File

@ -3,36 +3,18 @@ package html_test
import ( import (
"bytes" "bytes"
"code.squareroundforest.org/arpio/html" "code.squareroundforest.org/arpio/html"
. "code.squareroundforest.org/arpio/html/tags" . "code.squareroundforest.org/arpio/html/tag"
"errors"
"strings" "strings"
"testing" "testing"
) )
type failingWriter struct {
after int
}
func failWriteAfter(n int) *failingWriter {
return &failingWriter{n}
}
func (w *failingWriter) Write(p []byte) (int, error) {
w.after -= len(p)
if w.after < 0 {
return len(p) + w.after, errors.New("test error")
}
return len(p), nil
}
func TestRender(t *testing.T) { func TestRender(t *testing.T) {
t.Run("merge render guides", func(t *testing.T) { t.Run("merge render guides", func(t *testing.T) {
foo := html.Inline(html.Verbatim(Define("foo"))) foo := html.Inline(html.Verbatim(Define("foo")))
foo = foo("<bar><baz></bar>") foo = foo("<bar><baz></bar>")
var b bytes.Buffer var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indentation{Indent: "\t"}, foo); err != nil { if err := html.RenderIndent(&b, html.Indent(), foo); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -41,7 +23,8 @@ func TestRender(t *testing.T) {
} }
}) })
t.Run("attribute escaping", func(t *testing.T) { t.Run("attributes", func(t *testing.T) {
t.Run("escaping", func(t *testing.T) {
span := Span(Attr("foo", "bar=\"&\"")) span := Span(Attr("foo", "bar=\"&\""))
var b bytes.Buffer var b bytes.Buffer
@ -54,6 +37,33 @@ func TestRender(t *testing.T) {
} }
}) })
t.Run("true form", func(t *testing.T) {
span := Span(Attr("foo", true))
var b bytes.Buffer
if err := html.Render(&b, span); err != nil {
t.Fatal(err)
}
if b.String() != "<span foo></span>" {
t.Fatal(b.String())
}
})
t.Run("declaration", func(t *testing.T) {
comment := Comment("foo & bar & baz", "qux")
var b bytes.Buffer
if err := html.Render(&b, comment); err != nil {
t.Fatal(err)
}
if b.String() != "<!-- \"foo &amp; bar &amp; baz\" qux -->" {
t.Fatal(b.String())
}
})
})
t.Run("html escape", func(t *testing.T) { t.Run("html escape", func(t *testing.T) {
t.Run("basic escape", func(t *testing.T) { t.Run("basic escape", func(t *testing.T) {
span := Span("<foo>bar&baz</foo>") span := Span("<foo>bar&baz</foo>")
@ -85,32 +95,32 @@ func TestRender(t *testing.T) {
t.Run("write error", func(t *testing.T) { t.Run("write error", func(t *testing.T) {
t.Run("fail immediately", func(t *testing.T) { t.Run("fail immediately", func(t *testing.T) {
div := Div(Span("foo")) div := Div(Span("foo"))
w := failWriteAfter(0) w := &errorWriter{}
if err := html.Render(w, div); err == nil || !strings.Contains(err.Error(), "test error") { if err := html.Render(w, div); err == nil || !strings.Contains(err.Error(), "test write error") {
t.Fatal() t.Fatal()
} }
}) })
t.Run("fail in tag", func(t *testing.T) { t.Run("fail in tag", func(t *testing.T) {
div := Div(Span("foo")) div := Div(Span("foo"))
w := failWriteAfter(6) w := &errorWriter{failAfter: 6}
if err := html.Render(w, div); err == nil || !strings.Contains(err.Error(), "test error") { if err := html.Render(w, div); err == nil || !strings.Contains(err.Error(), "test write error") {
t.Fatal() t.Fatal()
} }
}) })
t.Run("partial text children", func(t *testing.T) { t.Run("partial text children", func(t *testing.T) {
div := Div("foo", Div("bar"), "baz") div := Div("foo", Div("bar"), "baz")
w := failWriteAfter(5) w := &errorWriter{failAfter: 5}
if err := html.RenderIndent(w, html.Indentation{Indent: "\t"}, div); err == nil || !strings.Contains(err.Error(), "test error") { if err := html.RenderIndent(w, html.Indent(), div); err == nil || !strings.Contains(err.Error(), "test write error") {
t.Fatal() t.Fatal()
} }
}) })
t.Run("text children", func(t *testing.T) { t.Run("text children", func(t *testing.T) {
div := Div("foo", "bar", "baz") div := Div("foo", "bar", "baz")
w := failWriteAfter(5) w := &errorWriter{failAfter: 5}
if err := html.RenderIndent(w, html.Indentation{Indent: "\t"}, div); err == nil || !strings.Contains(err.Error(), "test error") { if err := html.RenderIndent(w, html.Indent(), div); err == nil || !strings.Contains(err.Error(), "test write error") {
t.Fatal() t.Fatal()
} }
}) })
@ -121,7 +131,7 @@ func TestRender(t *testing.T) {
div := Div(Span("foo")) div := Div(Span("foo"))
var b bytes.Buffer var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indentation{Indent: "\t"}, div); err != nil { if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -131,10 +141,10 @@ func TestRender(t *testing.T) {
}) })
t.Run("empty tag", func(t *testing.T) { t.Run("empty tag", func(t *testing.T) {
div := Div(Br()) div := Div(Br)
var b bytes.Buffer var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indentation{Indent: "\t"}, div); err != nil { if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -147,7 +157,7 @@ func TestRender(t *testing.T) {
div := Div("foo bar baz", Div("qux quux"), "corge") div := Div("foo bar baz", Div("qux quux"), "corge")
var b bytes.Buffer var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indentation{Indent: "\t"}, div); err != nil { if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -160,13 +170,823 @@ func TestRender(t *testing.T) {
div := Div(Span("foo bar baz", Div("qux quux"), "corge")) div := Div(Span("foo bar baz", Div("qux quux"), "corge"))
var b bytes.Buffer var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indentation{Indent: "XYZ"}, div); err != nil { if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err) t.Fatal(err)
} }
if b.String() != "<div>\nXYZ<span>foo bar baz\nXYZXYZ<div>\nXYZXYZXYZqux quux\nXYZXYZ</div>\nXYZcorge</span>\n</div>" { if b.String() != "<div>\n\t<span>foo bar baz\n\t\t<div>\n\t\t\tqux quux\n\t\t</div>\n\tcorge</span>\n</div>" {
t.Fatal(b.String())
}
})
t.Run("verbatim", func(t *testing.T) {
foo := html.Verbatim(Define("foo"))
foo = foo("bar\nbaz\nqux")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), foo); err != nil {
t.Fatal(err)
}
if b.String() != "<foo>\n\tbar\n\tbaz\n\tqux\n</foo>" {
t.Fatal(b.String())
}
})
t.Run("script not indented", func(t *testing.T) {
script := Div(Script("foo()\nbar()\nbaz()"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), script); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\t<script>\nfoo()\nbar()\nbaz()\n\t</script>\n</div>" {
t.Fatal(b.String()) t.Fatal(b.String())
} }
}) })
}) })
t.Run("simple", func(t *testing.T) {
t.Run("unindented", func(t *testing.T) {
div := Div("foo")
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>foo</div>" {
t.Fatal(b.String())
}
})
t.Run("unindented number", func(t *testing.T) {
div := Div(42)
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>42</div>" {
t.Fatal(b.String())
}
})
t.Run("unindented after block", func(t *testing.T) {
div := Div(Div, "foo")
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div><div></div>foo</div>" {
t.Fatal(b.String())
}
})
t.Run("unindented after inline", func(t *testing.T) {
div := Div(Span, "foo")
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div><span></span>foo</div>" {
t.Fatal(b.String())
}
})
t.Run("inline", func(t *testing.T) {
span := Span("foo")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>foo</span>" {
t.Fatal(b.String())
}
})
t.Run("inline number", func(t *testing.T) {
span := Span(42)
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>42</span>" {
t.Fatal(b.String())
}
})
t.Run("inline after block", func(t *testing.T) {
span := Span(Div, "foo")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>\n\t<div></div>\nfoo</span>" {
t.Fatal(b.String())
}
})
t.Run("inline after inline", func(t *testing.T) {
span := Span(Span, "foo")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span><span></span>foo</span>" {
t.Fatal(b.String())
}
})
t.Run("inline tag after block", func(t *testing.T) {
span := Span(Div, Span("foo"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>\n\t<div></div>\n<span>foo</span></span>" {
t.Fatal(b.String())
}
})
t.Run("inline children", func(t *testing.T) {
p := P("foo")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), p); err != nil {
t.Fatal(err)
}
if b.String() != "<p>foo</p>" {
t.Fatal(b.String())
}
})
t.Run("inline children number", func(t *testing.T) {
p := P(42)
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), p); err != nil {
t.Fatal(err)
}
if b.String() != "<p>42</p>" {
t.Fatal(b.String())
}
})
t.Run("inline children after inline children", func(t *testing.T) {
p := P(P, "foo")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), p); err != nil {
t.Fatal(err)
}
if b.String() != "<p>\n\t<p></p>\nfoo</p>" {
t.Fatal(b.String())
}
})
t.Run("block", func(t *testing.T) {
div := Div("foo")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\tfoo\n</div>" {
t.Fatal(b.String())
}
})
t.Run("block number", func(t *testing.T) {
div := Div(42)
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\t42\n</div>" {
t.Fatal(b.String())
}
})
t.Run("block after block", func(t *testing.T) {
div := Div(Div, 42)
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\t<div></div>\n\t42\n</div>" {
t.Fatal(b.String())
}
})
t.Run("block after inline", func(t *testing.T) {
div := Div(Span, 42)
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\t<span></span>42\n</div>" {
t.Fatal(b.String())
}
})
})
t.Run("nil child", func(t *testing.T) {
t.Run("unindented", func(t *testing.T) {
div := Div("foo", nil, "bar")
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>foobar</div>" {
t.Fatal(b.String())
}
})
t.Run("unindented verbatim", func(t *testing.T) {
div := html.Verbatim(Div("foo &", nil, " bar"))
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>foo & bar</div>" {
t.Fatal(b.String())
}
})
t.Run("unindented script", func(t *testing.T) {
script := Script("let foo =", nil, " bar && baz")
var b bytes.Buffer
if err := html.Render(&b, script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>let foo = bar && baz</script>" {
t.Fatal(b.String())
}
})
t.Run("inline", func(t *testing.T) {
span := Span("foo", nil, "bar")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>foobar</span>" {
t.Fatal(b.String())
}
})
t.Run("inline verbatim", func(t *testing.T) {
span := html.Verbatim(Span("foo &", nil, " bar"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>\n\tfoo &\n\t bar\n</span>" {
t.Fatal(b.String())
}
})
t.Run("inline script", func(t *testing.T) {
script := html.Inline(Script("let foo =", nil, " bar && baz"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>\nlet foo =\n bar && baz\n</script>" {
t.Fatal(b.String())
}
})
t.Run("block", func(t *testing.T) {
div := Div("foo", nil, "bar")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\tfoobar\n</div>" {
t.Fatal(b.String())
}
})
t.Run("block verbatim", func(t *testing.T) {
div := html.Verbatim(Div("foo &", nil, " bar"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\tfoo &\n\t bar\n</div>" {
t.Fatal(b.String())
}
})
t.Run("block script", func(t *testing.T) {
script := Script("let foo =", nil, " bar && baz")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>\nlet foo =\n bar && baz\n</script>" {
t.Fatal(b.String())
}
})
})
t.Run("empty child", func(t *testing.T) {
t.Run("unindented", func(t *testing.T) {
div := Div("foo", "", "bar")
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>foobar</div>" {
t.Fatal(b.String())
}
})
t.Run("unindented verbatim", func(t *testing.T) {
div := html.Verbatim(Div("foo &", "", " bar"))
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>foo & bar</div>" {
t.Fatal(b.String())
}
})
t.Run("unindented script", func(t *testing.T) {
script := Script("let foo =", "", " bar && baz")
var b bytes.Buffer
if err := html.Render(&b, script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>let foo = bar && baz</script>" {
t.Fatal(b.String())
}
})
t.Run("inline", func(t *testing.T) {
span := Span("foo", "", "bar")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>foobar</span>" {
t.Fatal(b.String())
}
})
t.Run("inline verbatim", func(t *testing.T) {
span := html.Verbatim(Span("foo &", "", " bar"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>\n\tfoo &\n\t bar\n</span>" {
t.Fatal(b.String())
}
})
t.Run("inline script", func(t *testing.T) {
script := html.Inline(Script("let foo =", "", " bar && baz"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>\nlet foo =\n bar && baz\n</script>" {
t.Fatal(b.String())
}
})
t.Run("block", func(t *testing.T) {
div := Div("foo", "", "bar")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\tfoobar\n</div>" {
t.Fatal(b.String())
}
})
t.Run("block verbatim", func(t *testing.T) {
div := html.Verbatim(Div("foo &", "", " bar"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\tfoo &\n\t bar\n</div>" {
t.Fatal(b.String())
}
})
t.Run("block script", func(t *testing.T) {
script := Script("let foo =", "", " bar && baz")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>\nlet foo =\n bar && baz\n</script>" {
t.Fatal(b.String())
}
})
})
t.Run("reader child", func(t *testing.T) {
t.Run("unindented", func(t *testing.T) {
rd := bytes.NewBufferString("foo & bar & baz")
div := Div(rd)
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>foo &amp; bar &amp; baz</div>" {
t.Fatal(b.String())
}
})
t.Run("unindented verbatim", func(t *testing.T) {
rd := bytes.NewBufferString("foo & bar & baz")
div := html.Verbatim(Div(rd))
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>foo & bar & baz</div>" {
t.Fatal(b.String())
}
})
t.Run("unindented script", func(t *testing.T) {
rd := bytes.NewBufferString("let foo = bar() && baz()")
script := Script(rd)
var b bytes.Buffer
if err := html.Render(&b, script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>let foo = bar() && baz()</script>" {
t.Fatal(b.String())
}
})
t.Run("inline", func(t *testing.T) {
rd := bytes.NewBufferString("foo & bar & baz")
span := Span(rd)
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>foo &amp; bar &amp; baz</span>" {
t.Fatal(b.String())
}
})
t.Run("inline verbatim", func(t *testing.T) {
rd := bytes.NewBufferString("foo & bar & baz")
span := html.Verbatim(Span(rd))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>\n\tfoo & bar & baz\n</span>" {
t.Fatal(b.String())
}
})
t.Run("inline script", func(t *testing.T) {
rd := bytes.NewBufferString("let foo = bar() && baz()")
script := html.Inline(Script(rd))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>\nlet foo = bar() && baz()\n</script>" {
t.Fatal(b.String())
}
})
t.Run("inline after block", func(t *testing.T) {
rd := bytes.NewBufferString("foo & bar & baz")
span := Span(Div, rd)
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>\n\t<div></div>\nfoo &amp; bar &amp; baz</span>" {
t.Fatal(b.String())
}
})
t.Run("block", func(t *testing.T) {
rd := bytes.NewBufferString("foo & bar & baz")
div := Div(rd)
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\tfoo &amp; bar &amp; baz\n</div>" {
t.Fatal(b.String())
}
})
t.Run("block verbatim", func(t *testing.T) {
rd := bytes.NewBufferString("foo & bar & baz")
div := html.Verbatim(Div(rd))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\tfoo & bar & baz\n</div>" {
t.Fatal(b.String())
}
})
t.Run("block script", func(t *testing.T) {
rd := bytes.NewBufferString("let foo = bar() && baz()")
script := Script(rd)
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>\nlet foo = bar() && baz()\n</script>" {
t.Fatal(b.String())
}
})
})
t.Run("void", func(t *testing.T) {
t.Run("unindented", func(t *testing.T) {
var b bytes.Buffer
if err := html.Render(&b, Br); err != nil {
t.Fatal(err)
}
if b.String() != "<br>" {
t.Fatal(b.String())
}
})
t.Run("inline", func(t *testing.T) {
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), Br); err != nil {
t.Fatal(err)
}
if b.String() != "<br>" {
t.Fatal(b.String())
}
})
t.Run("block", func(t *testing.T) {
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), Hr); err != nil {
t.Fatal(err)
}
if b.String() != "<hr>" {
t.Fatal(b.String())
}
})
})
t.Run("no children", func(t *testing.T) {
t.Run("unindented", func(t *testing.T) {
var b bytes.Buffer
if err := html.Render(&b, Div); err != nil {
t.Fatal(err)
}
if b.String() != "<div></div>" {
t.Fatal(b.String())
}
})
t.Run("inline", func(t *testing.T) {
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), Span); err != nil {
t.Fatal(err)
}
if b.String() != "<span></span>" {
t.Fatal(b.String())
}
})
t.Run("block", func(t *testing.T) {
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), Div); err != nil {
t.Fatal(err)
}
if b.String() != "<div></div>" {
t.Fatal(b.String())
}
})
})
t.Run("verbatim", func(t *testing.T) {
t.Run("unindented", func(t *testing.T) {
div := html.Verbatim(Div("foo & bar"))
var b bytes.Buffer
if err := html.Render(&b, div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>foo & bar</div>" {
t.Fatal(b.String())
}
})
t.Run("inline", func(t *testing.T) {
span := html.Verbatim(Span("foo & bar"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), span); err != nil {
t.Fatal(err)
}
if b.String() != "<span>\n\tfoo & bar\n</span>" {
t.Fatal(b.String())
}
})
t.Run("block", func(t *testing.T) {
div := html.Verbatim(Div("foo & bar"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), div); err != nil {
t.Fatal(err)
}
if b.String() != "<div>\n\tfoo & bar\n</div>" {
t.Fatal(b.String())
}
})
})
t.Run("script", func(t *testing.T) {
t.Run("unindented", func(t *testing.T) {
script := Script("let foo = bar && baz")
var b bytes.Buffer
if err := html.Render(&b, script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>let foo = bar && baz</script>" {
t.Fatal(b.String())
}
})
t.Run("inline", func(t *testing.T) {
script := html.Inline(Script("let foo = bar && baz"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>\nlet foo = bar && baz\n</script>" {
t.Fatal(b.String())
}
})
t.Run("block", func(t *testing.T) {
script := Script("let foo = bar && baz")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indent(), script); err != nil {
t.Fatal(err)
}
if b.String() != "<script>\nlet foo = bar && baz\n</script>" {
t.Fatal(b.String())
}
})
})
t.Run("wrap", func(t *testing.T) {
t.Run("simple", func(t *testing.T) {
p := P("foo bar baz qux quux corge")
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indentation{Indent: "\t", PWidth: 15}, p); err != nil {
t.Error(err)
}
if b.String() != "<p>foo bar baz\nqux quux corge\n</p>" {
t.Error(b.String())
}
})
t.Run("min width", func(t *testing.T) {
div := Div(P("foo bar baz qux quux corge"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indentation{Indent: "\t", PWidth: 21, MinPWidth: 18}, div); err != nil {
t.Error(err)
}
if b.String() != "<div>\n\t<p>foo bar baz qux\n\tquux corge</p>\n</div>" {
t.Error(b.String())
}
})
t.Run("min width inline", func(t *testing.T) {
span := Span(P("foo bar baz qux quux corge"))
var b bytes.Buffer
if err := html.RenderIndent(&b, html.Indentation{Indent: "\t", PWidth: 21, MinPWidth: 18}, span); err != nil {
t.Error(err)
}
if b.String() != "<span>\n\t<p>foo bar baz qux\n\tquux corge</p>\n</span>" {
t.Error(b.String())
}
})
})
} }

View File

@ -47,7 +47,7 @@ func main() {
printf("// generated by ../script/generate-tags.go\n") printf("// generated by ../script/generate-tags.go\n")
printf("\n") printf("\n")
printf("package tags\n") printf("package tag\n")
printf("import \"code.squareroundforest.org/arpio/html\"\n") printf("import \"code.squareroundforest.org/arpio/html\"\n")
for _, si := range ss { for _, si := range ss {
exp := fmt.Sprintf("html.Define(\"%s\")", si) exp := fmt.Sprintf("html.Define(\"%s\")", si)

View File

@ -46,7 +46,7 @@ func main() {
printf("// generated by ../script/promote-to-tags.go\n") printf("// generated by ../script/promote-to-tags.go\n")
printf("\n") printf("\n")
printf("package tags\n") printf("package tag\n")
printf("import \"code.squareroundforest.org/arpio/html\"\n") printf("import \"code.squareroundforest.org/arpio/html\"\n")
for _, si := range ss { for _, si := range ss {
printf("var %s = html.%s\n", si, si) printf("var %s = html.%s\n", si, si)

View File

@ -1,6 +1,6 @@
// generated by ../script/generate-tags.go // generated by ../script/generate-tags.go
package tags package tag
import "code.squareroundforest.org/arpio/html" import "code.squareroundforest.org/arpio/html"

View File

@ -1,6 +1,6 @@
// generated by ../script/generate-tags.go // generated by ../script/generate-tags.go
package tags package tag
import "code.squareroundforest.org/arpio/html" import "code.squareroundforest.org/arpio/html"

View File

@ -1,6 +1,6 @@
// generated by ../script/generate-tags.go // generated by ../script/generate-tags.go
package tags package tag
import "code.squareroundforest.org/arpio/html" import "code.squareroundforest.org/arpio/html"

View File

@ -1,6 +1,6 @@
// generated by ../script/promote-to-tags.go // generated by ../script/promote-to-tags.go
package tags package tag
import "code.squareroundforest.org/arpio/html" import "code.squareroundforest.org/arpio/html"

View File

@ -1,6 +1,6 @@
// generated by ../script/generate-tags.go // generated by ../script/generate-tags.go
package tags package tag
import "code.squareroundforest.org/arpio/html" import "code.squareroundforest.org/arpio/html"

View File

@ -1,6 +1,6 @@
// generated by ../script/generate-tags.go // generated by ../script/generate-tags.go
package tags package tag
import "code.squareroundforest.org/arpio/html" import "code.squareroundforest.org/arpio/html"

View File

@ -1,6 +1,6 @@
// generated by ../script/generate-tags.go // generated by ../script/generate-tags.go
package tags package tag
import "code.squareroundforest.org/arpio/html" import "code.squareroundforest.org/arpio/html"

View File

@ -21,6 +21,10 @@ func validateSymbol(s string) error {
} }
func validateTagName(name string) error { func validateTagName(name string) error {
if strings.HasPrefix(name, "!") {
return nil
}
return validateSymbol(name) return validateSymbol(name)
} }
@ -41,7 +45,7 @@ func validate(name string, children []any) error {
isDeclaration := strings.HasPrefix(name, "!") isDeclaration := strings.HasPrefix(name, "!")
for _, ai := range a { for _, ai := range a {
for name := range ai { for _, name := range ai.names {
if isDeclaration { if isDeclaration {
continue continue
} }

View File

@ -3,7 +3,7 @@ package html_test
import ( import (
"bytes" "bytes"
"code.squareroundforest.org/arpio/html" "code.squareroundforest.org/arpio/html"
. "code.squareroundforest.org/arpio/html/tags" . "code.squareroundforest.org/arpio/html/tag"
"testing" "testing"
) )

View File

@ -11,7 +11,8 @@ type wrapper struct {
out *indentWriter out *indentWriter
width int width int
line, word *bytes.Buffer line, word *bytes.Buffer
inWord, inTag, inSingleQuote, inQuote, lastSpace, started bool inWord, inTag, inSingleQuote, inQuote bool
lastSpace, started bool
err error err error
} }

View File

@ -3,7 +3,7 @@ package html_test
import ( import (
"bytes" "bytes"
"code.squareroundforest.org/arpio/html" "code.squareroundforest.org/arpio/html"
. "code.squareroundforest.org/arpio/html/tags" . "code.squareroundforest.org/arpio/html/tag"
"testing" "testing"
) )
@ -56,7 +56,7 @@ func TestWrap(t *testing.T) {
ew := &errorWriter{} ew := &errorWriter{}
if err := html.RenderIndent( if err := html.RenderIndent(
ew, ew,
html.Indentation{Indent: "\t"}, html.Indent(),
Span(Span("foo"), Span("bar"), Span("baz")), Span(Span("foo"), Span("bar"), Span("baz")),
); err == nil { ); err == nil {
t.Fatal() t.Fatal()
@ -117,7 +117,7 @@ func TestWrap(t *testing.T) {
div := Div(Span("foo bar baz qux quux corge")) div := Div(Span("foo bar baz qux quux corge"))
var buf bytes.Buffer var buf bytes.Buffer
if err := html.RenderIndent(&buf, html.Indentation{Indent: "\t", PWidth: 9}, div); err != nil { if err := html.RenderIndent(&buf, html.Indentation{Indent: "\t", PWidth: 15}, div); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -130,7 +130,7 @@ func TestWrap(t *testing.T) {
div := Div(Span("foo"), " ", Span("bar")) div := Div(Span("foo"), " ", Span("bar"))
var buf bytes.Buffer var buf bytes.Buffer
if err := html.RenderIndent(&buf, html.Indentation{Indent: "\t"}, div); err != nil { if err := html.RenderIndent(&buf, html.Indent(), div); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -143,7 +143,7 @@ func TestWrap(t *testing.T) {
div := Div("foo\nbar\tbaz") div := Div("foo\nbar\tbaz")
var buf bytes.Buffer var buf bytes.Buffer
if err := html.RenderIndent(&buf, html.Indentation{Indent: "\t"}, div); err != nil { if err := html.RenderIndent(&buf, html.Indent(), div); err != nil {
t.Fatal(err) t.Fatal(err)
} }