Skip to content

Commit bbdba95

Browse files
committed
Refractor
1 parent e26e4cd commit bbdba95

File tree

3 files changed

+197
-81
lines changed

3 files changed

+197
-81
lines changed

.luarc.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"runtime.special": {
3+
"typename" : "type"
4+
}
5+
}

test.lua

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
local xml_gen = require("xml-generator")
2+
3+
---Table -> Tree using &ltdetails&gt and &ltsummary&gt
4+
---@param tbl table
5+
---@return XML.Node
6+
local function tree(tbl)
7+
return xml_gen.generate_node(function(xml)
8+
return xml.div {
9+
function ()
10+
local function getval(v)
11+
local tname = xml_gen.typename(v)
12+
if tname == "XML.Node" then return v, false end
13+
14+
if tname ~= "table" or (getmetatable(v) or {}).__tostring then
15+
return xml.p(tostring(v)), false
16+
end
17+
18+
return tree(v), true
19+
end
20+
21+
for i, v in ipairs(tbl) do
22+
local val, is_tree = getval(v)
23+
if is_tree then
24+
coroutine.yield (
25+
xml.details {
26+
xml.summary(tostring(i)),
27+
val
28+
}
29+
)
30+
else
31+
coroutine.yield (
32+
xml.table {
33+
xml.tr {
34+
xml.td(tostring(i));
35+
xml.td(val);
36+
}
37+
}
38+
)
39+
end
40+
41+
tbl[i] = nil
42+
end
43+
44+
for k, v in pairs(tbl) do
45+
local val, is_tree = getval(v)
46+
if is_tree then
47+
coroutine.yield (
48+
xml.details {
49+
xml.summary(tostring(k)),
50+
val
51+
}
52+
)
53+
else
54+
coroutine.yield (
55+
xml.table {
56+
xml.tr {
57+
xml.td(tostring(k));
58+
xml.td(val);
59+
}
60+
}
61+
)
62+
end
63+
end
64+
end
65+
}
66+
end)
67+
end
68+
69+
--[[
70+
details {
71+
font-family: arial, sans-serif;
72+
margin-left: 10px; /* Reduced for less indentation */
73+
border-left: 1px solid #ccc; /* Reduced for less spacing */
74+
padding-left: 5px; /* Reduced for less spacing */
75+
}
76+
77+
summary {
78+
font-weight: bold;
79+
cursor: pointer;
80+
display: list-item; /* To keep the arrow */
81+
list-style: disclosure-closed; /* Default arrow when closed */
82+
}
83+
84+
summary::-webkit-details-marker { /* Custom arrow for Webkit browsers */
85+
display: none;
86+
}
87+
88+
details[open] summary {
89+
list-style: disclosure-open; /* Arrow when opened */
90+
}
91+
92+
div {
93+
padding: 2px; /* Reduced for less spacing */
94+
margin-top: 2px; /* Reduced for less spacing */
95+
margin-bottom: 2px; /* Reduced for less spacing */
96+
border-radius: 3px; /* Reduced for less spacing */
97+
background-color: #f2f2f2;
98+
}
99+
100+
details > div {
101+
margin-left: 10px; /* Reduced for less indentation */
102+
}
103+
104+
]]
105+
106+
107+
local doc = xml_gen.generate(function (xml)
108+
return xml.html {charset="utf8"} {
109+
xml.head {
110+
xml.title "Hello, World!";
111+
xml_gen.style {
112+
["details > div"] = {
113+
["margin-left"] = "10px"
114+
}
115+
}
116+
};
117+
118+
xml.body {
119+
xml.div {id="numbers"} {
120+
tree {
121+
key = "value";
122+
sub = {
123+
key = "value"
124+
};
125+
126+
array = { 1, 2, 3, 4, 5 }
127+
}
128+
}
129+
}
130+
}
131+
end)
132+
133+
print(doc)

xml-generator.lua

Lines changed: 59 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,18 @@ end
1818

1919

2020
---@class xml-generator
21-
local export = {}
21+
local export = {
22+
sanitize_style = false
23+
}
2224

2325
---@class XML.Children
24-
---@field [integer] XML.Node | string | fun(): XML.Node
26+
---@field [integer] XML.Node | string
2527

2628
---@class XML.AttributeTable : XML.Children
2729
---@field [string] string | boolean | number
2830

2931
---@class XML.Node
30-
---@operator call(XML.Children): XML.Node
32+
---@operator call(XML.AttributeTable): XML.Node
3133
---@field tag string
3234
---@field children XML.Children
3335
---@field attributes XML.AttributeTable
@@ -50,125 +52,100 @@ function export.sanitize_attributes(str)
5052
end
5153

5254
---@param x any
53-
---@return type | string
54-
local function typename(x)
55+
---@return type | "XML.Node" | string
56+
function export.typename(x)
5557
local mt = getmetatable(x)
5658
if mt and mt.__name then
5759
return mt.__name
5860
else
5961
return type(x)
6062
end
6163
end
64+
local typename = export.typename
6265

6366
---@param node XML.Node
6467
---@return string
6568
function export.node_to_string(node)
66-
local html = "<" .. node.tag
69+
local sanitize = (not export.sanitize_style) and node.tag ~= "style"
70+
local sanitize_text = sanitize and export.sanitize_text or function (...) return ... end
71+
72+
local html = "<"..node.tag
6773

6874
for k, v in pairs(node.attributes) do
6975
if type(v) == "boolean" then
70-
if v then html = html .. " " .. k end
76+
if v then html = html.." "..k end
7177
else
72-
html = html .. " " .. k .. "=\"" .. export.sanitize_attributes(tostring(v)) .. "\""
78+
html = html.." "..k.."=\""..export.sanitize_attributes(tostring(v)).."\""
7379
end
7480
end
7581

76-
html = html .. ">"
82+
html = html..">"
7783

7884
for i, v in ipairs(node.children) do
7985
if type(v) ~= "table" then
80-
html = html .. export.sanitize_text(tostring(v))
86+
html = html..sanitize_text(tostring(v))
8187
else
82-
html = html .. export.node_to_string(v)
88+
html = html..export.node_to_string(v)
8389
end
8490
end
8591

86-
html = html .. "</" .. node.tag .. ">"
92+
html = html.."</"..node.tag..">"
8793

8894
return html
8995
end
9096

9197
---@class XML.GeneratorTable
92-
---@field [string] fun(attributes: XML.AttributeTable | string | XML.Node): XML.Node
98+
---@field lua _G
99+
---@field [string] XML.Node
93100

94101
---@type XML.GeneratorTable
95102
export.generator_metatable = setmetatable({}, {
96103
---@param _ XML.GeneratorTable
97104
---@param tag_name string
98105
__index = function(_, tag_name)
99-
---@param attributes { [string] : string, [integer] : (XML.Node | string | fun(): XML.Node) } | string
100-
---@return table | fun(children: (XML.Node | string | fun(): XML.Node)[]): XML.Node
101-
return function(attributes)
102-
---@type XML.Node
103-
local node = {
104-
tag = tag_name,
105-
children = {},
106-
attributes = {}
107-
}
108-
109-
--if we have a situation such as
110-
---```lua
111-
---tag "string"
112-
---```
113-
--
114-
--then the content is the `string`
115-
local tname = typename(attributes)
116-
if tname ~= "table" and tname ~= "XML.Node" then
117-
node.attributes = attributes and { tostring(attributes) } or {}
118-
elseif tname == "XML.Node" then
119-
120-
---```lua
121-
---local tag = div { p "hi" }
122-
---div(tag)
123-
---```
124-
node.children = { attributes }
125-
else
126-
node.attributes = attributes --[[@as any]]
127-
end
106+
--When used
107+
if tag_name == "lua" then return _G end
108+
109+
---@type XML.Node
110+
local node = {
111+
tag = tag_name,
112+
children = {},
113+
attributes = {}
114+
}
115+
return setmetatable(node, {
116+
---@param self XML.Node
117+
---@param attribs XML.AttributeTable | string | XML.Node
118+
---@return XML.Node
119+
__call = function (self, attribs)
120+
local tname = typename(attribs)
121+
if tname == "table" then
122+
for i, v in ipairs(attribs --[[@as (string | XML.Node | fun(): XML.Node)[] ]]) do
123+
local tname = typename(v)
124+
if tname == "function" then
125+
---@type fun(): XML.Node | string
126+
v = coroutine.wrap(v)
127+
for elem in v do self.children[#self.children+1] = elem end
128+
else
129+
self.children[#self.children+1] = v
130+
end
128131

129-
for i, v in ipairs(node.attributes) do
130-
if type(v) == "function" then
131-
v = coroutine.wrap(v)
132-
for sub in v do
133-
node.children[#node.children + 1] = sub
132+
attribs[i] = nil
134133
end
135-
else
136-
node.children[#node.children + 1] = v
137-
end
138-
139-
node.attributes[i] = nil
140-
end
141134

142-
return setmetatable(node, {
143-
__name = "XML.Node",
144-
145-
__tostring = export.node_to_string,
146-
147-
---@param self XML.Node
148-
---@param children XML.Children
149-
__call = function(self, children)
150-
if type(children) ~= "table" then
151-
children = { tostring(children) }
135+
for key, value in pairs(attribs --[[@as { [string] : string | boolean | number }]]) do
136+
self.attributes[key] = value
152137
end
138+
else self.children[#self.children+1] = tname == "XML.Node" and attribs or tostring(attribs) end
153139

154-
for _, v in ipairs(children) do
155-
if type(v) == "function" then
156-
v = coroutine.wrap(v)
157-
for sub in v do
158-
self.children[#self.children + 1] = sub
159-
end
160-
else
161-
self.children[#self.children + 1] = v
162-
end
163-
end
140+
return self
141+
end;
164142

165-
return self
166-
end
167-
})
168-
end
143+
__tostring = export.node_to_string
144+
})
169145
end
170146
})
171147

148+
172149
---Usage:
173150
--[=[
174151
```lua
@@ -198,7 +175,7 @@ function export.generate_node(ctx) return ctx(export.generator_metatable) end
198175
---@generic T
199176
---@param func fun(...: T): XML.Node
200177
---@return fun(...: T): XML.Node
201-
function export.declare_generator(func) return setfenv(func, table) end
178+
function export.declare_generator(func) return setfenv(func, export.generator_metatable) end
202179

203180
---@param ctx fun(html: XML.GeneratorTable): table
204181
---@return string
@@ -254,16 +231,17 @@ function export.style(css)
254231
for selector, properties in pairs(css) do
255232
if type(selector) == "table" then selector = table.concat(selector, ", ") end
256233

257-
css_str = css_str .. selector .. " {\n"
234+
css_str = css_str..selector.." {\n"
258235
for property, value in pairs(properties) do
259236
if type(value) == "table" then value = table.concat(value, ", ") end
260237

261-
css_str = css_str .. " " .. property .. ": " .. value .. ";\n"
238+
css_str = css_str.." "..property..": "..value..";\n"
262239
end
263-
css_str = css_str .. "}\n"
240+
css_str = css_str.."}\n"
264241
end
265242

266243
return export.generate_node(function(xml) return xml.style(css_str) end)
267244
end
268245

246+
269247
return export

0 commit comments

Comments
 (0)