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