1
0

refactor render

This commit is contained in:
Arpad Ryszka 2025-10-06 02:14:18 +02:00
parent 73250375c9
commit b4b05bfab4
2 changed files with 269 additions and 354 deletions

View File

@ -2,5 +2,4 @@ explain the immutability guarantee in the Go docs: for children yes, for childre
recommendation is not to mutate children. Ofc, creatively breaking the rules is always well appreciated by the recommendation is not to mutate children. Ofc, creatively breaking the rules is always well appreciated by the
right audience right audience
test wrapped templates test wrapped templates
test empty block
review which tags should be of type inline-children review which tags should be of type inline-children

498
render.go
View File

@ -51,6 +51,89 @@ func (r *renderer) getPrintf(tagName string) func(f string, a ...any) {
} }
} }
func (r *renderer) ensureWrapper() bool {
if _, ok := r.out.(*wrapper); ok {
return false
}
r.originalOut = r.out
r.out = newWrapper(r.originalOut, r.pwidth, r.currentIndent)
return true
}
func (r *renderer) clearWrapper() {
w, ok := r.out.(*wrapper)
if !ok {
return
}
if err := w.Flush(); err != nil {
r.err = err
}
r.out = r.originalOut
}
func (r *renderer) writeEscaped(s string) {
if r.err != nil {
return
}
ew := newEscapeWriter(r.out)
if _, r.err = ew.Write([]byte(s)); r.err != nil {
return
}
r.err = ew.Flush()
}
func (r *renderer) copyEscaped(rd io.Reader) {
if r.err != nil {
return
}
ew := newEscapeWriter(r.out)
if _, r.err = io.Copy(ew, rd); r.err != nil {
return
}
r.err = ew.Flush()
}
func (r *renderer) writeIndented(indent, s string) {
if r.err != nil {
return
}
iw := newIndentWriter(r.out, indent)
if _, r.err = iw.Write([]byte{'\n'}); r.err != nil {
return
}
if _, r.err = iw.Write([]byte(s)); r.err != nil {
return
}
r.err = iw.Flush()
}
func (r *renderer) copyIndented(indent string, rd io.Reader) {
if r.err != nil {
return
}
iw := newIndentWriter(r.out, indent)
if _, r.err = iw.Write([]byte{'\n'}); r.err != nil {
return
}
if _, r.err = io.Copy(iw, rd); r.err != nil {
return
}
r.err = iw.Flush()
}
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, "!") isDeclaration := strings.HasPrefix(tagName, "!")
@ -89,28 +172,26 @@ func (r *renderer) renderUnindented(name string, rg renderGuide, a []Attributes,
} }
for _, c := range children { for _, c := range children {
if ct, ok := c.(Tag); ok {
ct(r)
continue
}
if c == nil { if c == nil {
continue continue
} }
if rd, ok := c.(io.Reader); ok { rd, isReader := c.(io.Reader)
if rg.verbatim || rg.script { if isReader && (rg.verbatim || rg.script) {
if r.err == nil {
_, r.err = io.Copy(r.out, rd) _, r.err = io.Copy(r.out, rd)
}
continue continue
} }
ew := newEscapeWriter(r.out) if isReader {
_, r.err = io.Copy(ew, rd) r.copyEscaped(rd)
if r.err == nil { continue
ew.Flush()
r.err = ew.err
} }
if ct, ok := c.(Tag); ok {
ct(r)
continue continue
} }
@ -124,171 +205,46 @@ func (r *renderer) renderUnindented(name string, rg renderGuide, a []Attributes,
continue continue
} }
if r.err == nil { r.writeEscaped(s)
ew := newEscapeWriter(r.out)
ew.Write([]byte(s))
ew.Flush()
r.err = ew.err
}
} }
printf("</%s>", name) printf("</%s>", name)
} }
func (r *renderer) ensureWrapper() bool { func (r *renderer) renderReaderChild(tagName string, rg renderGuide, block, lastBlock bool, rd io.Reader) bool {
if _, ok := r.out.(*wrapper); ok { printf := r.getPrintf(tagName)
if rg.verbatim {
r.clearWrapper()
indent := r.currentIndent
if !block {
indent += r.indent.Indent
}
r.copyIndented(indent, rd)
return false return false
} }
r.originalOut = r.out if rg.script {
r.out = newWrapper(r.originalOut, r.pwidth, r.currentIndent)
return true
}
func (r *renderer) clearWrapper() {
w, ok := r.out.(*wrapper)
if !ok {
return
}
if err := w.Flush(); err != nil {
r.err = err
}
r.out = r.originalOut
}
func (r *renderer) renderInline(name string, rg renderGuide, a []Attributes, children []any) {
newWrapper := r.ensureWrapper()
printf := r.getPrintf(name)
printf("<%s", name)
r.renderAttributes(name, a)
printf(">")
if rg.void {
if newWrapper {
r.clearWrapper()
}
return
}
var lastBlock bool
for _, c := range children {
rd, isReader := c.(io.Reader)
if isReader && rg.verbatim {
r.clearWrapper()
if r.err == nil {
iw := newIndentWriter(r.out, r.currentIndent+r.indent.Indent)
iw.Write([]byte{'\n'})
_, r.err = io.Copy(iw, rd)
if r.err == nil {
iw.Flush()
r.err = iw.err
}
}
lastBlock = true
continue
}
if isReader && rg.script {
r.clearWrapper() r.clearWrapper()
printf("\n") printf("\n")
if r.err == nil {
_, r.err = io.Copy(r.out, rd) _, r.err = io.Copy(r.out, rd)
lastBlock = true
continue
} }
if isReader { return false
if lastBlock {
printf("\n%s", r.currentIndent)
}
if r.ensureWrapper() {
newWrapper = true
}
if r.err == nil {
ew := newEscapeWriter(r.out)
_, r.err = io.Copy(ew, rd)
if r.err == nil {
ew.Flush()
r.err = ew.err
}
}
lastBlock = false
continue
}
ct, isTag := c.(Tag)
if !isTag && rg.verbatim {
if c == nil {
continue
}
s := fmt.Sprint(c)
if s == "" {
continue
}
r.clearWrapper()
if r.err == nil {
iw := newIndentWriter(r.out, r.currentIndent+r.indent.Indent)
iw.Write([]byte{'\n'})
iw.Write([]byte(s))
iw.Flush()
r.err = iw.err
}
lastBlock = true
continue
}
if !isTag && rg.script {
if c == nil {
continue
}
s := fmt.Sprint(c)
if s == "" {
continue
}
r.clearWrapper()
printf("\n%s", s)
lastBlock = true
continue
}
if !isTag {
if c == nil {
continue
}
s := fmt.Sprint(c)
if s == "" {
continue
} }
if lastBlock { if lastBlock {
printf("\n%s", r.currentIndent) printf("\n%s", r.currentIndent)
} }
if r.ensureWrapper() { newWrapper := r.ensureWrapper()
newWrapper = true r.copyEscaped(rd)
return newWrapper
} }
if r.err == nil { func (r *renderer) renderChildTag(tagName string, block, lastBlock bool, ct Tag) (bool, bool) {
ew := newEscapeWriter(r.out) printf := r.getPrintf(tagName)
ew.Write([]byte(s))
ew.Flush()
r.err = ew.err
}
lastBlock = false
continue
}
var rgq renderGuidesQuery var rgq renderGuidesQuery
ct(&rgq) ct(&rgq)
@ -298,23 +254,21 @@ func (r *renderer) renderInline(name string, rg renderGuide, a []Attributes, chi
printf("\n%s", r.currentIndent) printf("\n%s", r.currentIndent)
} }
if r.ensureWrapper() { newWrapper := r.ensureWrapper()
newWrapper = true
}
ct(r) ct(r)
lastBlock = false return false, newWrapper
continue
} }
r.clearWrapper() r.clearWrapper()
cr := new(renderer) cr := new(renderer)
*cr = *r *cr = *r
if !block {
cr.currentIndent += cr.indent.Indent cr.currentIndent += cr.indent.Indent
cr.pwidth -= indentLen(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
} }
}
printf("\n%s", cr.currentIndent) printf("\n%s", cr.currentIndent)
ct(cr) ct(cr)
@ -322,181 +276,148 @@ func (r *renderer) renderInline(name string, rg renderGuide, a []Attributes, chi
r.err = cr.err r.err = cr.err
} }
lastBlock = true return true, false
}
func (r *renderer) renderVerbatimChild(block bool, c any) {
s := fmt.Sprint(c)
if s == "" {
return
}
r.clearWrapper()
indent := r.currentIndent
if !block {
indent += r.indent.Indent
}
r.writeIndented(indent, s)
}
func (r *renderer) renderChildScript(tagName string, c any) {
s := fmt.Sprint(c)
if s == "" {
return
}
r.clearWrapper()
printf := r.getPrintf(tagName)
printf("\n%s", s)
}
func (r *renderer) renderChildContent(tagName string, lastBlock bool, c any) bool {
s := fmt.Sprint(c)
if s == "" {
return false
} }
if lastBlock { if lastBlock {
printf := r.getPrintf(tagName)
printf("\n%s", r.currentIndent) printf("\n%s", r.currentIndent)
} }
printf("</%s>", name) newWrapper := r.ensureWrapper()
if newWrapper { r.writeEscaped(s)
r.clearWrapper() return newWrapper
} }
func (r *renderer) renderIndented(name string, rg renderGuide, a []Attributes, children []any) {
var newWrapper bool
if rg.inline || rg.inlineChildren {
newWrapper = r.ensureWrapper()
} }
func (r *renderer) renderBlock(name string, rg renderGuide, a []Attributes, children []any) {
printf := r.getPrintf(name) printf := r.getPrintf(name)
printf("<%s", name) printf("<%s", name)
r.renderAttributes(name, a) r.renderAttributes(name, a)
printf(">") printf(">")
if rg.void { if rg.void {
if newWrapper {
r.clearWrapper()
}
return return
} }
if len(children) == 0 { if len(children) == 0 {
printf("</%s>", name) printf("</%s>", name)
if newWrapper {
r.clearWrapper()
}
return return
} }
lastBlock := true block := !rg.inline && !rg.inlineChildren
lastBlock := block
originalIndent, originalWidth := r.currentIndent, r.pwidth originalIndent, originalWidth := r.currentIndent, r.pwidth
if block {
r.currentIndent += r.indent.Indent r.currentIndent += r.indent.Indent
r.pwidth -= indentLen(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
} }
}
for _, c := range children { for _, c := range children {
rd, isReader := c.(io.Reader)
if isReader && rg.verbatim {
r.clearWrapper()
if r.err == nil {
iw := newIndentWriter(r.out, r.currentIndent)
iw.Write([]byte{'\n'})
_, r.err = io.Copy(iw, rd)
if r.err == nil {
iw.Flush()
r.err = iw.err
}
}
lastBlock = true
continue
}
if isReader && rg.script {
r.clearWrapper()
printf("\n")
_, r.err = io.Copy(r.out, rd)
lastBlock = true
continue
}
if isReader {
if lastBlock {
printf("\n%s", r.currentIndent)
}
r.ensureWrapper()
if r.err == nil {
ew := newEscapeWriter(r.out)
_, r.err = io.Copy(ew, rd)
if r.err == nil {
ew.Flush()
r.err = ew.err
}
}
lastBlock = false
continue
}
ct, isTag := c.(Tag)
if !isTag && rg.verbatim {
if c == nil { if c == nil {
continue continue
} }
s := fmt.Sprint(c) if ct, isTag := c.(Tag); isTag {
if s == "" { var nw bool
lastBlock, nw = r.renderChildTag(name, block, lastBlock, ct)
if nw {
newWrapper = true
}
continue continue
} }
r.clearWrapper() if rd, isReader := c.(io.Reader); isReader {
if r.err == nil { if r.renderReaderChild(name, rg, block, lastBlock, rd) {
iw := newIndentWriter(r.out, r.currentIndent) newWrapper = true
iw.Write([]byte{'\n'})
iw.Write([]byte(s))
iw.Flush()
r.err = iw.err
} }
lastBlock = rg.verbatim || rg.script
continue
}
if rg.verbatim {
r.renderVerbatimChild(block, c)
lastBlock = true lastBlock = true
continue continue
} }
if !isTag && rg.script { if rg.script {
if c == nil { r.renderChildScript(name, c)
continue
}
s := fmt.Sprint(c)
if s == "" {
continue
}
r.clearWrapper()
printf("\n%s", s)
lastBlock = true lastBlock = true
continue continue
} }
if !isTag { if r.renderChildContent(name, lastBlock, c) {
if c == nil { newWrapper = true
continue
}
s := fmt.Sprint(c)
if s == "" {
continue
}
if lastBlock {
printf("\n%s", r.currentIndent)
}
r.ensureWrapper()
if r.err == nil {
ew := newEscapeWriter(r.out)
ew.Write([]byte(s))
ew.Flush()
r.err = ew.err
} }
lastBlock = false lastBlock = false
continue
} }
var rgq renderGuidesQuery if block {
ct(&rgq)
crg := mergeRenderingGuides(rgq.value)
if crg.inline {
if lastBlock {
printf("\n%s", r.currentIndent)
}
r.ensureWrapper()
ct(r)
lastBlock = false
continue
}
r.clearWrapper()
cr := new(renderer)
*cr = *r
printf("\n%s", cr.currentIndent)
ct(cr)
if cr.err != nil {
r.err = cr.err
}
lastBlock = true
}
r.clearWrapper()
r.currentIndent, r.pwidth = originalIndent, originalWidth r.currentIndent, r.pwidth = originalIndent, originalWidth
printf("\n%s</%s>", r.currentIndent, name) }
if block {
r.clearWrapper()
}
if lastBlock || block {
printf("\n%s", r.currentIndent)
}
printf("</%s>", name)
if newWrapper && !block {
r.clearWrapper()
}
} }
func (r *renderer) render(name string, children []any) { func (r *renderer) render(name string, children []any) {
@ -511,10 +432,5 @@ func (r *renderer) render(name string, children []any) {
return return
} }
if rg.inline || rg.inlineChildren { r.renderIndented(name, rg, a, c)
r.renderInline(name, rg, a, c)
return
}
r.renderBlock(name, rg, a, c)
} }