roff
This commit is contained in:
parent
59084d8292
commit
ace14e534c
8
lib.go
8
lib.go
@ -141,7 +141,7 @@ func Title(level int, text string, manInfo ...TitleInfo) Entry {
|
|||||||
return e
|
return e
|
||||||
}
|
}
|
||||||
|
|
||||||
func ManualSection(s int) TitleInfo {
|
func ManSection(s int) TitleInfo {
|
||||||
return TitleInfo{section: s}
|
return TitleInfo{section: s}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ func ReleaseVersion(v string) TitleInfo {
|
|||||||
return TitleInfo{version: v}
|
return TitleInfo{version: v}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ManualCategory(c string) TitleInfo {
|
func ManCategory(c string) TitleInfo {
|
||||||
return TitleInfo{category: c}
|
return TitleInfo{category: c}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,8 +275,8 @@ func Teletype(out io.Writer, d Document) error {
|
|||||||
//
|
//
|
||||||
// Text is always wrapped, or as controlled by the roff processor, except for tables. The Wrap instrunction has
|
// Text is always wrapped, or as controlled by the roff processor, except for tables. The Wrap instrunction has
|
||||||
// no effect, except for tables.
|
// no effect, except for tables.
|
||||||
func Runoff(io.Writer, Document) error {
|
func Runoff(out io.Writer, d Document) error {
|
||||||
return nil
|
return renderRoff(out, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Markdown(io.Writer, Document) error {
|
func Markdown(io.Writer, Document) error {
|
||||||
|
|||||||
5
notes.txt
Normal file
5
notes.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
indentation for syntax in tty and roff
|
||||||
|
verify if empty document doesn't print even an empty line. E.g. empty table, empty paragraph. What should those
|
||||||
|
print?
|
||||||
|
does the table need the non-breaking space for the filling in roff?
|
||||||
|
indentation for syntax may not require non-break spaces
|
||||||
236
runoff.go
236
runoff.go
@ -5,6 +5,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func escapeRoff(s string, additional ...string) string {
|
func escapeRoff(s string, additional ...string) string {
|
||||||
@ -98,6 +100,22 @@ func roffDefinitionNames(d []DefinitionItem) []string {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func roffCellTexts(r []TableRow) ([][]string, error) {
|
||||||
|
var cellTexts [][]string
|
||||||
|
for _, row := range r {
|
||||||
|
var c []string
|
||||||
|
for _, cell := range row.cells {
|
||||||
|
var b bytes.Buffer
|
||||||
|
renderRoffText(&roffWriter{w: &b, internal: true}, cell.text)
|
||||||
|
c = append(c, b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
cellTexts = append(cellTexts, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cellTexts, nil
|
||||||
|
}
|
||||||
|
|
||||||
func renderRoffText(w writer, text Txt, additionalEscape ...string) {
|
func renderRoffText(w writer, text Txt, additionalEscape ...string) {
|
||||||
if len(text.cat) > 0 {
|
if len(text.cat) > 0 {
|
||||||
for i, tc := range text.cat {
|
for i, tc := range text.cat {
|
||||||
@ -125,10 +143,8 @@ func renderRoffText(w writer, text Txt, additionalEscape ...string) {
|
|||||||
|
|
||||||
if text.link != "" {
|
if text.link != "" {
|
||||||
if text.text != "" {
|
if text.text != "" {
|
||||||
w.write(text.text)
|
|
||||||
renderRoffString(w, text.text, additionalEscape...)
|
renderRoffString(w, text.text, additionalEscape...)
|
||||||
w.write(" (")
|
w.write(" (")
|
||||||
w.write(text.link)
|
|
||||||
renderRoffString(w, text.link, additionalEscape...)
|
renderRoffString(w, text.link, additionalEscape...)
|
||||||
w.write(")")
|
w.write(")")
|
||||||
return
|
return
|
||||||
@ -143,12 +159,12 @@ func renderRoffText(w writer, text Txt, additionalEscape ...string) {
|
|||||||
|
|
||||||
func renderRoffTitle(w writer, e Entry) {
|
func renderRoffTitle(w writer, e Entry) {
|
||||||
if e.titleLevel != 0 || e.man.section == 0 {
|
if e.titleLevel != 0 || e.man.section == 0 {
|
||||||
|
e.text.bold = true
|
||||||
p := Entry{
|
p := Entry{
|
||||||
typ: paragraph,
|
typ: paragraph,
|
||||||
text: e.text,
|
text: e.text,
|
||||||
indent: e.indent,
|
indent: e.indent,
|
||||||
indentFirst: e.indentFirst,
|
indentFirst: e.indentFirst,
|
||||||
bold: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderRoffParagraph(w, p)
|
renderRoffParagraph(w, p)
|
||||||
@ -172,17 +188,17 @@ func renderRoffTitle(w writer, e Entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func renderRoffParagraph(w writer, e Entry) {
|
func renderRoffParagraph(w writer, e Entry) {
|
||||||
w.write(".in ", e.indent, "\n.tin ", e.indent + e.indentFirst, "\n")
|
w.write(".in ", e.indent, "\n.ti ", e.indent + e.indentFirst, "\n")
|
||||||
renderRoffText(w, e.text)
|
renderRoffText(w, e.text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderRoffList(w writer, e Entry) {
|
func renderRoffList(w writer, e Entry) {
|
||||||
for i, item := range e.items {
|
for i, item := range e.items {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
w.write(".br\n")
|
w.write("\n.br\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
w.write(".in ", e.indent + 2, "\n.tin ", e.indent + e.indentFirst, "\n")
|
w.write(".in ", e.indent + 2, "\n.ti ", e.indent + e.indentFirst, "\n")
|
||||||
w.write("\\(bu ")
|
w.write("\\(bu ")
|
||||||
renderRoffText(w, item.text)
|
renderRoffText(w, item.text)
|
||||||
}
|
}
|
||||||
@ -192,10 +208,10 @@ func renderRoffNumberedList(w writer, e Entry) {
|
|||||||
maxDigits := numDigits(len(e.items))
|
maxDigits := numDigits(len(e.items))
|
||||||
for i, item := range e.items {
|
for i, item := range e.items {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
w.write(".br\n")
|
w.write("\n.br\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
w.write(".in ", e.indent + maxDigits + 2, "\n.tin ", e.indent + e.indentFirst, "\n")
|
w.write(".in ", e.indent + maxDigits + 2, "\n.ti ", e.indent + e.indentFirst, "\n")
|
||||||
w.write(padRight(fmt.Sprintf("%d.", i + 1), maxDigits + 2))
|
w.write(padRight(fmt.Sprintf("%d.", i + 1), maxDigits + 2))
|
||||||
renderRoffText(w, item.text)
|
renderRoffText(w, item.text)
|
||||||
}
|
}
|
||||||
@ -206,10 +222,10 @@ func renderRoffDefinitions(w writer, e Entry) {
|
|||||||
maxNameLength := maxLength(names)
|
maxNameLength := maxLength(names)
|
||||||
for i, definition := range e.definitions {
|
for i, definition := range e.definitions {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
w.write(".br\n")
|
w.write("\n.br\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
w.write(".in ", e.indent + maxNameLength + 4, "\n.tin ", e.indent + e.indentFirst, "\n")
|
w.write(".in ", e.indent + maxNameLength + 4, "\n.ti ", e.indent + e.indentFirst, "\n")
|
||||||
w.write("\\(bu ")
|
w.write("\\(bu ")
|
||||||
renderRoffText(w, definition.name)
|
renderRoffText(w, definition.name)
|
||||||
w.write(":", timesn("\\~", maxNameLength - len([]rune(names[i])) + 1))
|
w.write(":", timesn("\\~", maxNameLength - len([]rune(names[i])) + 1))
|
||||||
@ -223,10 +239,10 @@ func renderRoffNumberedDefinitions(w writer, e Entry) {
|
|||||||
maxNameLength := maxLength(names)
|
maxNameLength := maxLength(names)
|
||||||
for i, definition := range e.definitions {
|
for i, definition := range e.definitions {
|
||||||
if i > 0 {
|
if i > 0 {
|
||||||
w.write(".br\n")
|
w.write("\n.br\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
w.write(".in ", e.indent + maxDigits + maxNameLength + 4, "\n.tin ", e.indent + e.indentFirst, "\n")
|
w.write(".in ", e.indent + maxDigits + maxNameLength + 4, "\n.ti ", e.indent + e.indentFirst, "\n")
|
||||||
w.write(padRight(fmt.Sprintf("%d.", i + 1), maxDigits + 2))
|
w.write(padRight(fmt.Sprintf("%d.", i + 1), maxDigits + 2))
|
||||||
renderRoffText(w, definition.name)
|
renderRoffText(w, definition.name)
|
||||||
w.write(":", timesn("\\~", maxNameLength - len([]rune(names[i])) + 1))
|
w.write(":", timesn("\\~", maxNameLength - len([]rune(names[i])) + 1))
|
||||||
@ -235,12 +251,208 @@ func renderRoffNumberedDefinitions(w writer, e Entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func renderRoffTable(w writer, e Entry) {
|
func renderRoffTable(w writer, e Entry) {
|
||||||
|
if len(e.rows) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e.rows = normalizeTable(e.rows)
|
||||||
|
if len(e.rows[0].cells) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.write(".nf\n")
|
||||||
|
defer w.write("\n.fi")
|
||||||
|
|
||||||
|
cellTexts, err := roffCellTexts(e.rows)
|
||||||
|
if err != nil {
|
||||||
|
w.setErr(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
totalSeparatorWidth := (len(cellTexts[0]) - 1) * 3
|
||||||
|
if e.wrapWidth > 0 {
|
||||||
|
allocatedWidth := e.wrapWidth - e.indent - totalSeparatorWidth
|
||||||
|
columnWeights := columnWeights(cellTexts)
|
||||||
|
targetColumnWidths := targetColumnWidths(allocatedWidth, columnWeights)
|
||||||
|
for i := range cellTexts {
|
||||||
|
for j := range cellTexts[i] {
|
||||||
|
cellTexts[i][j] = wrap(cellTexts[i][j], targetColumnWidths[j], 0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
columnWidths := columnWidths(cellTexts)
|
||||||
|
totalWidth := totalSeparatorWidth
|
||||||
|
for i := range columnWidths {
|
||||||
|
totalWidth += columnWidths[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
hasHeader := e.rows[0].header
|
||||||
|
for i := range cellTexts {
|
||||||
|
if i > 0 {
|
||||||
|
sep := "-"
|
||||||
|
if hasHeader && i == 1 {
|
||||||
|
sep = "="
|
||||||
|
}
|
||||||
|
|
||||||
|
w.write("\n")
|
||||||
|
w.write(timesn(" ", e.indent), timesn(sep, totalWidth))
|
||||||
|
w.write("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := make([][]string, len(cellTexts[i]))
|
||||||
|
for j := range cellTexts[i] {
|
||||||
|
lines[j] = strings.Split(cellTexts[i][j], "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxLines int
|
||||||
|
for j := range lines {
|
||||||
|
if len(lines[j]) > maxLines {
|
||||||
|
maxLines = len(lines[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := 0; k < maxLines; k++ {
|
||||||
|
if k > 0 {
|
||||||
|
w.write("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
for j := range lines {
|
||||||
|
if j == 0 {
|
||||||
|
w.write(timesn(" ", e.indent))
|
||||||
|
} else {
|
||||||
|
w.write(" | ")
|
||||||
|
}
|
||||||
|
|
||||||
|
var l string
|
||||||
|
if k < len(lines[j]) {
|
||||||
|
l = lines[j][k]
|
||||||
|
}
|
||||||
|
|
||||||
|
w.write(padRight(l, columnWidths[j]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasHeader && len(cellTexts) == 1 {
|
||||||
|
w.write("\n", timesn("=", totalWidth))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderRoffCode(w writer, e Entry) {
|
func renderRoffCode(w writer, e Entry) {
|
||||||
|
w.write(".nf\n")
|
||||||
|
defer w.write("\n.fi")
|
||||||
|
e.text.text = escapeRoff(e.text.text)
|
||||||
|
writeLines(w, e.text.text, e.indent, e.indent)
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderRoffMultiple(w writer, s SyntaxItem) {
|
||||||
|
s.topLevel = false
|
||||||
|
s.multiple = false
|
||||||
|
renderRoffSyntaxItem(w, s)
|
||||||
|
w.write("...")
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderRoffRequired(w writer, s SyntaxItem) {
|
||||||
|
s.delimited = true
|
||||||
|
s.topLevel = false
|
||||||
|
s.required = false
|
||||||
|
w.write("<")
|
||||||
|
renderRoffSyntaxItem(w, s)
|
||||||
|
w.write(">")
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderRoffOptional(w writer, s SyntaxItem) {
|
||||||
|
s.delimited = true
|
||||||
|
s.topLevel = false
|
||||||
|
s.optional = false
|
||||||
|
w.write("[")
|
||||||
|
renderRoffSyntaxItem(w, s)
|
||||||
|
w.write("]")
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderRoffSequence(w writer, s SyntaxItem) {
|
||||||
|
if !s.delimited && !s.topLevel {
|
||||||
|
w.write("(")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, item := range s.sequence {
|
||||||
|
if i > 0 {
|
||||||
|
w.write(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
item.delimited = false
|
||||||
|
renderRoffSyntaxItem(w, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.delimited && !s.topLevel {
|
||||||
|
w.write(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderRoffChoice(w writer, s SyntaxItem) {
|
||||||
|
if !s.delimited && !s.topLevel {
|
||||||
|
w.write("(")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, item := range s.choice {
|
||||||
|
if i > 0 {
|
||||||
|
separator := "|"
|
||||||
|
if s.topLevel {
|
||||||
|
separator = "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
w.write(separator)
|
||||||
|
}
|
||||||
|
|
||||||
|
item.delimited = false
|
||||||
|
renderRoffSyntaxItem(w, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.delimited && !s.topLevel {
|
||||||
|
w.write(")")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderRoffSymbol(w writer, s SyntaxItem) {
|
||||||
|
w.write(escapeRoff(s.symbol))
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderRoffSyntaxItem(w writer, s SyntaxItem) {
|
||||||
|
switch {
|
||||||
|
|
||||||
|
// foo...
|
||||||
|
case s.multiple:
|
||||||
|
renderRoffMultiple(w, s)
|
||||||
|
|
||||||
|
// <foo>
|
||||||
|
case s.required:
|
||||||
|
renderRoffRequired(w, s)
|
||||||
|
|
||||||
|
// [foo]
|
||||||
|
case s.optional:
|
||||||
|
renderRoffOptional(w, s)
|
||||||
|
|
||||||
|
// foo bar baz or (foo bar baz)
|
||||||
|
case len(s.sequence) > 0:
|
||||||
|
renderRoffSequence(w, s)
|
||||||
|
|
||||||
|
// foo|bar|baz or (foo|bar|baz)
|
||||||
|
case len(s.choice) > 0:
|
||||||
|
renderRoffChoice(w, s)
|
||||||
|
|
||||||
|
// foo
|
||||||
|
default:
|
||||||
|
renderRoffSymbol(w, s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderRoffSyntax(w writer, e Entry) {
|
func renderRoffSyntax(w writer, e Entry) {
|
||||||
|
s := e.syntax
|
||||||
|
s.topLevel = true
|
||||||
|
w.write(".nf\n")
|
||||||
|
defer w.write("\n.fi")
|
||||||
|
w.write(timesn("\u00a0", e.indent + e.indentFirst))
|
||||||
|
renderRoffSyntaxItem(w, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderRoff(out io.Writer, d Document) error {
|
func renderRoff(out io.Writer, d Document) error {
|
||||||
|
|||||||
2300
runoff_test.go
Normal file
2300
runoff_test.go
Normal file
File diff suppressed because it is too large
Load Diff
@ -400,6 +400,7 @@ func renderTTYSyntaxItem(w writer, s SyntaxItem) {
|
|||||||
func renderTTYSyntax(w writer, e Entry) {
|
func renderTTYSyntax(w writer, e Entry) {
|
||||||
s := e.syntax
|
s := e.syntax
|
||||||
s.topLevel = true
|
s.topLevel = true
|
||||||
|
w.write(timesn(" ", e.indent + e.indentFirst))
|
||||||
renderTTYSyntaxItem(w, s)
|
renderTTYSyntaxItem(w, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -537,8 +537,8 @@ Entry explanations:
|
|||||||
textfmt.Item(textfmt.Text("this is another item")),
|
textfmt.Item(textfmt.Text("this is another item")),
|
||||||
textfmt.Item(textfmt.Text("this is a third item")),
|
textfmt.Item(textfmt.Text("this is a third item")),
|
||||||
),
|
),
|
||||||
0,
|
|
||||||
4,
|
4,
|
||||||
|
0,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -688,8 +688,8 @@ third item
|
|||||||
textfmt.Item(textfmt.Text("this is another item")),
|
textfmt.Item(textfmt.Text("this is another item")),
|
||||||
textfmt.Item(textfmt.Text("this is a third item")),
|
textfmt.Item(textfmt.Text("this is a third item")),
|
||||||
),
|
),
|
||||||
0,
|
|
||||||
4,
|
4,
|
||||||
|
0,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -880,8 +880,8 @@ third item
|
|||||||
textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")),
|
textfmt.Definition(textfmt.Text("green"), textfmt.Text("looks like grass")),
|
||||||
textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")),
|
textfmt.Definition(textfmt.Text("blue"), textfmt.Text("looks like sky")),
|
||||||
),
|
),
|
||||||
0,
|
|
||||||
4,
|
4,
|
||||||
|
0,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1953,5 +1953,33 @@ and silver birch | their canopies creating | and shadow on the
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("example indented", func(t *testing.T) {
|
||||||
|
doc := textfmt.Doc(
|
||||||
|
textfmt.Indent(
|
||||||
|
textfmt.Syntax(
|
||||||
|
textfmt.Symbol("foo"),
|
||||||
|
textfmt.ZeroOrMore(textfmt.Symbol("options")),
|
||||||
|
textfmt.Required(textfmt.Symbol("filename")),
|
||||||
|
textfmt.ZeroOrMore(
|
||||||
|
textfmt.Choice(
|
||||||
|
textfmt.Symbol("string"),
|
||||||
|
textfmt.Symbol("number"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
if err := textfmt.Teletype(&b, doc); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if b.String() != " foo [options]... <filename> [string|number]...\n" {
|
||||||
|
t.Fatal(b.String())
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
2
text.go
2
text.go
@ -3,6 +3,8 @@ package textfmt
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
"fmt"
|
||||||
|
"bytes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func timesn(s string, n int) string {
|
func timesn(s string, n int) string {
|
||||||
|
|||||||
6
write.go
6
write.go
@ -19,7 +19,7 @@ type ttyWriter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type roffWriter struct {
|
type roffWriter struct {
|
||||||
w *bufio.Writer
|
w io.Writer
|
||||||
internal bool
|
internal bool
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
@ -63,6 +63,9 @@ func (w *roffWriter) write(a ...any) {
|
|||||||
var rr []rune
|
var rr []rune
|
||||||
s := fmt.Sprint(ai)
|
s := fmt.Sprint(ai)
|
||||||
r := []rune(s)
|
r := []rune(s)
|
||||||
|
if w.internal {
|
||||||
|
rr = r
|
||||||
|
} else {
|
||||||
for i := range r {
|
for i := range r {
|
||||||
if r[i] == '\u00a0' {
|
if r[i] == '\u00a0' {
|
||||||
rr = append(rr, []rune("\\~")...)
|
rr = append(rr, []rune("\\~")...)
|
||||||
@ -71,6 +74,7 @@ func (w *roffWriter) write(a ...any) {
|
|||||||
|
|
||||||
rr = append(rr, r[i])
|
rr = append(rr, r[i])
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := w.w.Write([]byte(string(rr))); err != nil {
|
if _, err := w.w.Write([]byte(string(rr))); err != nil {
|
||||||
w.err = err
|
w.err = err
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user