-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplaintext.go
180 lines (152 loc) · 5.05 KB
/
plaintext.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
package style
import (
"strconv"
"strings"
"github.com/pirmd/text"
)
var (
_ Styler = (*TextSyntax)(nil) //Makes sure that TextSyntax implements Styler
)
// NewPlaintext is a pre-defined style.TextSyntax Styler to write plain text. It
// allows for maximum 80 chars per line, indenting is made of 4 spaces and list
// bullets are made of unicode hyphen and bullet.
func NewPlaintext() *TextSyntax {
return &TextSyntax{
TextWidth: 80,
TabWidth: 4,
ListBullets: []string{"\u2043 ", "\u2022 ", "\u25E6 "},
}
}
// TextSyntax implements Styler interface to provide basic formatting to write plain
// texts. It supports text indenting and wraping as well as table but does not
// provide color nor text emphasis supports.
type TextSyntax struct {
*CoreSyntax
//TextWidth specified the maximum length of a text line
//If stx.TextWidth is null or negative, wraping is disabled (in practice, you
//still want stx.TextSyntaxwidth to be large enough to obtain a readable output).
TextWidth int
//TabWidth is the number of spaces added in front of text for each
//tabulation's level
TabWidth int
//ListBullets list the bullets added to each list items. Bullets are chosen
//in the given order following the list nested-level (if nested-level is
//greater than bullets number it restarts from 1).
//If you want some spaces between the bullet and the start of text, you
//have to include it (avoid "\t" nevertheless).
ListBullets []string
indentLvl int
nestLvl int
//If true, adds a line break before paragraphs, headers or lists. It is
//automatically set-up the first time one any of these formats is used.
needLeadingBr bool
}
// Tab increases the tabulation level for the provided text.
// Wraping is done according to stx.TextWidth value, if stx.TextWidth is null, Tab
// only indents and doesn't wrap the provided text.
func (stx *TextSyntax) Tab() func(string) string {
stx.indentLvl++
return func(s string) string {
stx.indentLvl--
return s
}
}
// Header returns text as a chapter's header.
func (stx *TextSyntax) Header(lvl int) func(s string) string {
switch {
case lvl <= 0:
return func(s string) string { return "" }
case lvl == 1:
return func(s string) string { return stx.br() + stx.Upper(s) + "\n" }
default:
return func(s string) string { return stx.br() + stx.TitleCase(s) }
}
}
// Paragraph returns text as a new paragraph
func (stx *TextSyntax) Paragraph(s string) string {
return stx.br() + stx.tab(s, stx.indentLvl, "") + "\n"
}
// BulletedList returns a new bulleted-list (each list item has a leading
// bullet).
// It automatically indents each item.
func (stx *TextSyntax) BulletedList() func(items ...string) string {
stx.nestLvl++
stx.indentLvl++
bullet := stx.ListBullets[stx.indentLvl%len(stx.ListBullets)]
return func(items ...string) string {
var s string
for i, item := range items {
if i == 0 {
s = stx.tab(item, stx.indentLvl, bullet)
} else {
s = s + "\n" + stx.tab(item, stx.indentLvl, bullet)
}
if !strings.HasSuffix(s, "\n") {
s += "\n"
}
}
stx.indentLvl--
stx.nestLvl--
return stx.br() + s
}
}
// OrderedList returns a new ordered-list (each list item has a leading
// auto-incrementing enumerator).
// It automatically indents each item.
func (stx *TextSyntax) OrderedList() func(items ...string) string {
stx.nestLvl++
stx.indentLvl++
return func(items ...string) string {
var s string
for i, item := range items {
enum := strconv.Itoa(i+1) + ". "
if i == 0 {
s = stx.tab(item, stx.indentLvl, enum)
} else {
s = s + "\n" + stx.tab(item, stx.indentLvl, enum)
}
if !strings.HasSuffix(s, "\n") {
s += "\n"
}
}
stx.indentLvl--
stx.nestLvl--
return stx.br() + s
}
}
// Define returns a term definition
func (stx *TextSyntax) Define(term string, desc string) string {
term = stx.tab(term, stx.indentLvl, "") + "\n"
desc = stx.tab(desc, stx.indentLvl+1, "") + "\n"
return stx.br() + term + desc
}
// Table draws a table out of the provided rows.
// Table column width are guessed automatically and are arranged so that the table
// fits into stx.TextWidth.
func (stx *TextSyntax) Table(rows ...[]string) string {
width := stx.TextWidth - (stx.indentLvl * stx.TabWidth)
//TODO(pirmd): introduce way to chose/define Table grid
table := text.NewTable().SetMaxWidth(width).SetGrid(" ", "-", " ").Rows(rows...).Draw()
return stx.br() + stx.tab(table, stx.indentLvl, "") + "\n"
}
func (stx *TextSyntax) br() string {
if stx.nestLvl != 0 || stx.needLeadingBr {
return "\n"
}
stx.needLeadingBr = true
return ""
}
func (stx *TextSyntax) tab(s string, lvl int, tag string) string {
prefix := strings.Repeat(" ", lvl*stx.TabWidth)
if stx.TextWidth > 0 {
if stx.nestLvl > 0 {
width := stx.TextWidth - (stx.nestLvl-1)*stx.TabWidth
prefix = strings.Repeat(" ", (lvl-stx.nestLvl)*stx.TabWidth)
return text.Tab(s, tag, prefix, width, false)
}
//Tab should not cut long "words", otherwise it might split links (that
//are seen as words) and void Markdown syntax
return text.Tab(s, tag, prefix, stx.TextWidth, false)
}
return text.Indent(s, tag, prefix)
}