support reader as content
This commit is contained in:
parent
7610db6e71
commit
2f496f4ca2
13
escape.go
13
escape.go
@ -1,19 +1,19 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"io"
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const unicodeNBSP = 0xa0
|
||||
const unicodeNBSP = 0xa0
|
||||
|
||||
type escapeWriter struct {
|
||||
out *bufio.Writer
|
||||
out *bufio.Writer
|
||||
spaceStarted, lastSpace bool
|
||||
err error
|
||||
err error
|
||||
}
|
||||
|
||||
func attributeEscape(value string) string {
|
||||
@ -67,6 +67,11 @@ func (w *escapeWriter) Write(p []byte) (int, error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return len(p), w.err
|
||||
}
|
||||
|
||||
if r == unicode.ReplacementChar {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
package html_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"code.squareroundforest.org/arpio/html"
|
||||
. "code.squareroundforest.org/arpio/html/tags"
|
||||
"testing"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
func TestEscape(t *testing.T) {
|
||||
|
||||
92
indent.go
Normal file
92
indent.go
Normal file
@ -0,0 +1,92 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
type indentWriter struct {
|
||||
out *bufio.Writer
|
||||
indent string
|
||||
started, lineStarted bool
|
||||
err error
|
||||
}
|
||||
|
||||
func newIndentWriter(out io.Writer, indent string) *indentWriter {
|
||||
return &indentWriter{
|
||||
out: bufio.NewWriter(out),
|
||||
indent: indent,
|
||||
}
|
||||
}
|
||||
|
||||
func (w *indentWriter) write(r ...rune) {
|
||||
if w.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ri := range r {
|
||||
if _, w.err = w.out.WriteRune(ri); w.err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *indentWriter) Write(p []byte) (int, error) {
|
||||
if w.err != nil {
|
||||
return 0, w.err
|
||||
}
|
||||
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
runes := bytes.NewBuffer(nil)
|
||||
if n, err := runes.Write(p); err != nil {
|
||||
w.err = err
|
||||
return n, w.err
|
||||
}
|
||||
|
||||
for {
|
||||
r, _, err := runes.ReadRune()
|
||||
if errors.Is(err, io.EOF) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return len(p), w.err
|
||||
}
|
||||
|
||||
if r == unicode.ReplacementChar {
|
||||
continue
|
||||
}
|
||||
|
||||
if r == '\n' {
|
||||
w.write('\n')
|
||||
w.lineStarted = false
|
||||
continue
|
||||
}
|
||||
|
||||
if w.started && !w.lineStarted {
|
||||
w.write([]rune(w.indent)...)
|
||||
}
|
||||
|
||||
w.write(r)
|
||||
w.started = true
|
||||
w.lineStarted = true
|
||||
}
|
||||
|
||||
return len(p), w.err
|
||||
}
|
||||
|
||||
func (w *indentWriter) Flush() error {
|
||||
if w.err != nil {
|
||||
return w.err
|
||||
}
|
||||
|
||||
w.err = w.out.Flush()
|
||||
return w.err
|
||||
}
|
||||
@ -4,8 +4,8 @@ import (
|
||||
"bytes"
|
||||
"code.squareroundforest.org/arpio/html"
|
||||
. "code.squareroundforest.org/arpio/html/tags"
|
||||
"testing"
|
||||
"code.squareroundforest.org/arpio/notation"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLib(t *testing.T) {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package html_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"code.squareroundforest.org/arpio/notation"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func printBytes(a ...any) {
|
||||
|
||||
143
render.go
143
render.go
@ -3,7 +3,6 @@ package html
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const defaultPWidth = 112
|
||||
@ -38,15 +37,6 @@ func mergeRenderingGuides(rgs []renderGuide) renderGuide {
|
||||
return rg
|
||||
}
|
||||
|
||||
func indentLines(indent string, s string) string {
|
||||
l := strings.Split(s, "\n")
|
||||
for i := range l {
|
||||
l[i] = fmt.Sprintf("%s%s", indent, l[i])
|
||||
}
|
||||
|
||||
return strings.Join(l, "\n")
|
||||
}
|
||||
|
||||
func (r *renderer) getPrintf(tagName string) func(f string, a ...any) {
|
||||
return func(f string, a ...any) {
|
||||
if r.err != nil {
|
||||
@ -93,6 +83,22 @@ func (r *renderer) renderUnindented(name string, rg renderGuide, a []Attributes,
|
||||
continue
|
||||
}
|
||||
|
||||
if rd, ok := c.(io.Reader); ok {
|
||||
if rg.verbatim || rg.script {
|
||||
_, 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
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
s := fmt.Sprint(c)
|
||||
if s == "" {
|
||||
continue
|
||||
@ -100,6 +106,7 @@ func (r *renderer) renderUnindented(name string, rg renderGuide, a []Attributes,
|
||||
|
||||
if rg.verbatim || rg.script {
|
||||
printf(s)
|
||||
continue
|
||||
}
|
||||
|
||||
if r.err == nil {
|
||||
@ -152,6 +159,54 @@ func (r *renderer) renderInline(name string, rg renderGuide, a []Attributes, chi
|
||||
|
||||
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'})
|
||||
iw.Write([]byte(r.currentIndent + r.indent.Indent))
|
||||
_, 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -164,8 +219,15 @@ func (r *renderer) renderInline(name string, rg renderGuide, a []Attributes, chi
|
||||
}
|
||||
|
||||
r.clearWrapper()
|
||||
s = indentLines(r.currentIndent+r.indent.Indent, s)
|
||||
printf("\n%s", s)
|
||||
if r.err == nil {
|
||||
iw := newIndentWriter(r.out, r.currentIndent+r.indent.Indent)
|
||||
iw.Write([]byte{'\n'})
|
||||
iw.Write([]byte(r.currentIndent + r.indent.Indent))
|
||||
iw.Write([]byte(s))
|
||||
iw.Flush()
|
||||
r.err = iw.err
|
||||
}
|
||||
|
||||
lastBlock = true
|
||||
continue
|
||||
}
|
||||
@ -283,6 +345,52 @@ func (r *renderer) renderBlock(name string, rg renderGuide, a []Attributes, chil
|
||||
}
|
||||
|
||||
for _, c := range children {
|
||||
rd, isReader := c.(io.Reader)
|
||||
if isReader && rg.verbatim {
|
||||
r.clearWrapper()
|
||||
printf("\n")
|
||||
if r.err == nil {
|
||||
iw := newIndentWriter(r.out, r.currentIndent+r.indent.Indent)
|
||||
iw.Write([]byte{'\n'})
|
||||
iw.Write([]byte(r.currentIndent + r.indent.Indent))
|
||||
_, 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 {
|
||||
@ -295,8 +403,15 @@ func (r *renderer) renderBlock(name string, rg renderGuide, a []Attributes, chil
|
||||
}
|
||||
|
||||
r.clearWrapper()
|
||||
s = indentLines(r.currentIndent, s)
|
||||
printf("\n%s", s)
|
||||
if r.err == nil {
|
||||
iw := newIndentWriter(r.out, r.currentIndent)
|
||||
iw.Write([]byte{'\n'})
|
||||
iw.Write([]byte(r.currentIndent + r.indent.Indent))
|
||||
iw.Write([]byte(s))
|
||||
iw.Flush()
|
||||
r.err = iw.err
|
||||
}
|
||||
|
||||
lastBlock = true
|
||||
continue
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
// generated by ../script/generate-tags.go
|
||||
|
||||
package tags
|
||||
|
||||
import "code.squareroundforest.org/arpio/html"
|
||||
|
||||
var Address = html.Define("address")
|
||||
var Article = html.Define("article")
|
||||
var Audio = html.Define("audio")
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
// generated by ../script/generate-tags.go
|
||||
|
||||
package tags
|
||||
|
||||
import "code.squareroundforest.org/arpio/html"
|
||||
|
||||
var A = html.Inline(html.Define("a"))
|
||||
var Abbr = html.Inline(html.Define("abbr"))
|
||||
var B = html.Inline(html.Define("b"))
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
// generated by ../script/generate-tags.go
|
||||
|
||||
package tags
|
||||
|
||||
import "code.squareroundforest.org/arpio/html"
|
||||
|
||||
var H1 = html.InlineChildren(html.Define("h1"))
|
||||
var H2 = html.InlineChildren(html.Define("h2"))
|
||||
var H3 = html.InlineChildren(html.Define("h3"))
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
// generated by ../script/promote-to-tags.go
|
||||
|
||||
package tags
|
||||
|
||||
import "code.squareroundforest.org/arpio/html"
|
||||
|
||||
var Attr = html.Attr
|
||||
var Define = html.Define
|
||||
var Doctype = html.Doctype
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
// generated by ../script/generate-tags.go
|
||||
|
||||
package tags
|
||||
|
||||
import "code.squareroundforest.org/arpio/html"
|
||||
|
||||
var Script = html.ScriptContent(html.Define("script"))
|
||||
var Style = html.ScriptContent(html.Define("style"))
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
// generated by ../script/generate-tags.go
|
||||
|
||||
package tags
|
||||
|
||||
import "code.squareroundforest.org/arpio/html"
|
||||
|
||||
var Area = html.Void(html.Define("area"))
|
||||
var Base = html.Void(html.Define("base"))
|
||||
var Hr = html.Void(html.Define("hr"))
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
// generated by ../script/generate-tags.go
|
||||
|
||||
package tags
|
||||
|
||||
import "code.squareroundforest.org/arpio/html"
|
||||
|
||||
var Br = html.Inline(html.Void(html.Define("br")))
|
||||
var Embed = html.Inline(html.Void(html.Define("embed")))
|
||||
var Img = html.Inline(html.Void(html.Define("img")))
|
||||
|
||||
24
wrap.go
24
wrap.go
@ -8,9 +8,8 @@ import (
|
||||
)
|
||||
|
||||
type wrapper struct {
|
||||
out io.Writer
|
||||
out *indentWriter
|
||||
width int
|
||||
indent string
|
||||
line, word *bytes.Buffer
|
||||
inWord, inTag, inSingleQuote, inQuote, lastSpace, started bool
|
||||
err error
|
||||
@ -18,11 +17,10 @@ type wrapper struct {
|
||||
|
||||
func newWrapper(out io.Writer, width int, indent string) *wrapper {
|
||||
return &wrapper{
|
||||
out: out,
|
||||
width: width,
|
||||
indent: indent,
|
||||
line: bytes.NewBuffer(nil),
|
||||
word: bytes.NewBuffer(nil),
|
||||
out: newIndentWriter(out, indent),
|
||||
width: width,
|
||||
line: bytes.NewBuffer(nil),
|
||||
word: bytes.NewBuffer(nil),
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,10 +37,6 @@ func (w *wrapper) feed() error {
|
||||
if _, err := w.out.Write([]byte{'\n'}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := w.out.Write([]byte(w.indent)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := io.Copy(w.out, w.line); err != nil {
|
||||
@ -79,6 +73,11 @@ func (w *wrapper) Write(p []byte) (int, error) {
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
w.err = err
|
||||
return len(p), w.err
|
||||
}
|
||||
|
||||
if r == unicode.ReplacementChar {
|
||||
continue
|
||||
}
|
||||
@ -165,5 +164,6 @@ func (w *wrapper) Flush() error {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
w.err = w.out.Flush()
|
||||
return w.err
|
||||
}
|
||||
|
||||
@ -5,12 +5,12 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
type errorWriter struct{
|
||||
out io.Writer
|
||||
type errorWriter struct {
|
||||
out io.Writer
|
||||
failAfter int
|
||||
}
|
||||
|
||||
func(ew *errorWriter) Write(p []byte) (int, error) {
|
||||
func (ew *errorWriter) Write(p []byte) (int, error) {
|
||||
wp := p
|
||||
if len(wp) > ew.failAfter {
|
||||
wp = wp[:ew.failAfter]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user