refactor render
This commit is contained in:
parent
73250375c9
commit
b4b05bfab4
@ -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
|
||||
|
||||
500
render.go
500
render.go
@ -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) renderBlock(name string, rg renderGuide, a []Attributes, children []any) {
|
||||
func (r *renderer) renderIndented(name string, rg renderGuide, a []Attributes, children []any) {
|
||||
var newWrapper bool
|
||||
if rg.inline || rg.inlineChildren {
|
||||
newWrapper = r.ensureWrapper()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user