1
0
textfmt/lib.go
2025-11-02 22:15:31 +01:00

299 lines
5.3 KiB
Go

package textfmt
import (
"io"
"time"
)
const (
invalid = iota
title
paragraph
list
numberedList
definitions
numberedDefinitions
table
code
syntax
)
type Txt struct {
text string
link string
bold, italic bool
cat []Txt
}
type ListItem struct {
text Txt
}
type DefinitionItem struct {
name, value Txt
}
type TableCell struct {
text Txt
}
type TableRow struct {
cells []TableCell
header bool
}
type SyntaxItem struct {
symbol string
multiple bool
required bool
optional bool
sequence []SyntaxItem
choice []SyntaxItem
topLevel bool
delimited bool
}
type Entry struct {
typ int
text Txt
titleLevel int
items []ListItem
definitions []DefinitionItem
rows []TableRow
syntax SyntaxItem
wrapWidth int
wrapWidthFirst int
indent int
indentFirst int
man struct {
section int
date time.Time
version string
category string
}
}
type TitleInfo struct {
section int
date time.Time
version string
category string
}
type Document struct {
entries []Entry
}
func Text(text string) Txt {
return Txt{text: text}
}
func Link(label, uri string) Txt {
return Txt{text: label, link: uri}
}
func Bold(t Txt) Txt {
t.bold = true
return t
}
func Italic(t Txt) Txt {
t.italic = true
return t
}
func Cat(t ...Txt) Txt {
return Txt{cat: t}
}
func Title(level int, text string, manInfo ...TitleInfo) Entry {
if level != 0 {
return Entry{
typ: title,
titleLevel: level,
text: Text(text),
}
}
e := Entry{
typ: title,
titleLevel: level,
text: Text(text),
}
for _, m := range manInfo {
if m.section != 0 {
e.man.section = m.section
}
if !m.date.IsZero() {
e.man.date = m.date
}
if m.version != "" {
e.man.version = m.version
}
if m.category != "" {
e.man.category = m.category
}
}
return e
}
func ManSection(s int) TitleInfo {
return TitleInfo{section: s}
}
func ReleaseDate(d time.Time) TitleInfo {
return TitleInfo{date: d}
}
func ReleaseVersion(v string) TitleInfo {
return TitleInfo{version: v}
}
func ManCategory(c string) TitleInfo {
return TitleInfo{category: c}
}
func Paragraph(t Txt) Entry {
return Entry{typ: paragraph, text: t}
}
func Item(text Txt) ListItem {
return ListItem{text: text}
}
func List(items ...ListItem) Entry {
return Entry{typ: list, items: items}
}
func NumberedList(items ...ListItem) Entry {
return Entry{typ: numberedList, items: items}
}
func Definition(name, value Txt) DefinitionItem {
return DefinitionItem{name: name, value: value}
}
func DefinitionList(items ...DefinitionItem) Entry {
return Entry{typ: definitions, definitions: items}
}
func NumberedDefinitionList(items ...DefinitionItem) Entry {
return Entry{typ: numberedDefinitions, definitions: items}
}
func Cell(text Txt) TableCell {
return TableCell{text: text}
}
func Header(cells ...TableCell) TableRow {
return TableRow{cells: cells, header: true}
}
func Row(cells ...TableCell) TableRow {
return TableRow{cells: cells}
}
// when no header and markdown, header automatic
func Table(rows ...TableRow) Entry {
return Entry{typ: table, rows: rows}
}
func CodeBlock(codeBlock string) Entry {
return Entry{typ: code, text: Text(codeBlock)}
}
func Symbol(text string) SyntaxItem {
return SyntaxItem{symbol: text}
}
func OneOrMore(item SyntaxItem) SyntaxItem {
item.required = true
item.optional = false
item.multiple = true
return item
}
func ZeroOrMore(item SyntaxItem) SyntaxItem {
item.required = false
item.optional = true
item.multiple = true
return item
}
func Required(item SyntaxItem) SyntaxItem {
item.required = true
item.optional = false
return item
}
func Optional(item SyntaxItem) SyntaxItem {
item.required = false
item.optional = true
return item
}
func Sequence(items ...SyntaxItem) SyntaxItem {
return SyntaxItem{sequence: items}
}
// top level on separate lines without delimiter
func Choice(items ...SyntaxItem) SyntaxItem {
return SyntaxItem{choice: items}
}
func Syntax(items ...SyntaxItem) Entry {
if len(items) == 1 {
return Entry{typ: syntax, syntax: items[0]}
}
return Entry{typ: syntax, syntax: Sequence(items...)}
}
func Wrap(e Entry, width int) Entry {
e.wrapWidth = width
return e
}
// indentFirst is relative to indent
func Indent(e Entry, indent, indentFirst int) Entry {
e.indent, e.indentFirst = indent, indentFirst
return e
}
func Doc(e ...Entry) Document {
return Document{entries: e}
}
func Teletype(out io.Writer, d Document) error {
return renderTeletype(out, d)
}
// Runoff is an attempt to render roff format. It is primarily targeting man pages. While it may be possible to
// use for other purposes, the man macro will likely be required to render the output.
//
// Text is always wrapped, or as controlled by the roff processor, except for tables. The Wrap instrunction has
// no effect, except for tables.
func Runoff(out io.Writer, d Document) error {
return renderRoff(out, d)
}
func Markdown(out io.Writer, d Document) error {
return renderMarkdown(out, d)
}
func HTMLFragment(out io.Writer, doc Document) error {
// with the won HTML library
return renderHTMLFragment(out, doc)
}
// if lang is empty, the lang attribute will be omitted
func HTML(out io.Writer, doc Document, lang string) error {
// with the won HTML library
return renderHTML(out, doc, lang)
}