-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathboxer.lua
143 lines (115 loc) · 3.6 KB
/
boxer.lua
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
--- Module boxer wraps constructors that return renderers to provide CSS-like
-- box model margin, border, and padding. boxer satisfies the renderer interface
-- so it can be passed into other rows and columns.
-- @module boxer
--- getWidestLine returns the widest line (highest len()) for a table of
-- strings.
-- @table t A table of strings.
-- @treturn number
local function getWidestLine(t)
local w = 0
for _, v in ipairs(t) do
w = math.max(v:len(), w)
end
return w
end
--- wrap surrounds a table of strings in a given character. This includes a full
-- bar of c as the first line and last elements of the returned table, as well
-- as identical on each side of each line.
-- @table t A table of strings.
-- @string c The character to wrap the table with.
-- @treturn table
local function wrap(t, c)
local out = {}
do
local w = getWidestLine(t)
for i, v in ipairs(t) do
local s = w - v:len()
out[i] = string.format("%s%s%s%s", c, v, string.rep(" ", s), c)
end
end
local w = getWidestLine(out)
table.insert(out, 1, string.rep(c, w))
out[#out+1] = string.rep(c, w)
return out
end
--- boxer wraps a ctor for a renderer, adding functionality for rendering box
-- model style margins, borders, and padding. boxer satisfies the renderer
-- interface so it can be used a renderer in rows and columns.
-- @function ctor The wrapped renderer constructor.
-- @treturn boxer
local function boxer(ctor)
assert(type(ctor) == "function", "invalid input, expected function")
return function(content)
local renderer = ctor(content)
assert(renderer.render and type(renderer.render) == "function",
"invalid return from wrapped constructor, expected renderer")
assert(renderer.width and type(renderer.width) == "function",
"invalid return from wrapped constructor, expected renderer")
--- @type boxer
local B = {}
local border
local margin
local padding
--- border sets the border string to be applied during render.
-- @string b
-- @treturn boxer
function B.border(b)
assert(type(b) == "string", "invalid input, expected string")
assert(b:len() == 1, "invalid input, expected single character")
border = b
return B
end
--- margin sets the margin string to be applied during render.
-- @string m
-- @treturn boxer
function B.margin(m)
assert(type(m) == "string", "invalid input, expected string")
assert(m:len() == 1, "invalid input, expected single character")
margin = m
return B
end
--- padding sets the padding string to be applied during render.
-- @string p
-- @treturn boxer
function B.padding(p)
assert(type(p) == "string", "invalid input, expected string")
assert(p:len() == 1, "invalid input, expected single character")
padding = p
return B
end
--- render wraps the output of the embedded renderer in strings as given by
-- the border, margin, and padding fields.
-- @treturn table
function B.render()
local r = renderer.render()
-- add these inside out
if padding then
r = wrap(r, padding)
end
if border then
r = wrap(r, border)
end
if margin then
r = wrap(r, margin)
end
return r
end
--- width calculates the width of the inner content based on the length of
-- the border, margin, and padding previously given. This width gets set as
-- the width of the wrapped renderer.
-- @number w
-- @treturn boxer
function B.width(w)
local mw = margin and 2 or 0
local bw = border and 2 or 0
local pw = padding and 2 or 0
w = w - (mw + bw + pw)
assert(w > 0, "inner renderer width reduced to 0 or less")
renderer.width(w)
return B
end
return B
end
end
return boxer