@@ -45,7 +45,7 @@ let template:String = """
45
45
import SwiftSyntax
46
46
47
47
/// The `%tagName%`%aliases% HTML element.%elementDocumentation%
48
- public struct %elementName% : HTMLElement {%variables%
48
+ public struct %elementName% : HTMLElement {%variables%%render%
49
49
}
50
50
51
51
public extension %elementName% {
@@ -64,9 +64,24 @@ let defaultVariables:[HTMLElementVariable] = [
64
64
65
65
let indent1 : String = " \n "
66
66
let indent2 : String = indent1 + " "
67
+ let indent3 : String = indent2 + " "
68
+ let indent4 : String = indent3 + " "
69
+ let indent5 : String = indent4 + " "
70
+ let indent6 : String = indent5 + " "
67
71
68
72
for (elementType, customAttributes) in attributes ( ) . filter ( { $0. key == . a } ) {
69
73
var variablesString : String = " "
74
+ var renderString : String = " \n " + indent1 + " @inlinable " + indent1 + " public var description : String { "
75
+ var renderAttributesString : String = indent2 + " func attributes() -> String { "
76
+ renderAttributesString += indent3 + " let sd:String = encoding.stringDelimiter(forMacro: fromMacro) "
77
+ renderAttributesString += indent3
78
+ renderAttributesString += """
79
+ var items:[String] = self.attributes.compactMap({
80
+ guard let v:String = $0.htmlValue(encoding: encoding, forMacro: fromMacro) else { return nil }
81
+ let d:String = $0.htmlValueDelimiter(encoding: encoding, forMacro: fromMacro)
82
+ return $0.key + ($0.htmlValueIsVoidable && v.isEmpty ? " " : " = " + d + v + d)
83
+ })
84
+ """
70
85
71
86
var variables : [ HTMLElementVariable ] = defaultVariables
72
87
variables. append ( get ( public: true , mutable: false , name: " tag " , valueType: . string, defaultValue: " \" %tagName% \" " ) )
@@ -75,23 +90,99 @@ for (elementType, customAttributes) in attributes().filter({ $0.key == .a }) {
75
90
variables. append ( attribute)
76
91
}
77
92
93
+ func separator( key: String ) -> String {
94
+ switch key {
95
+ case " accept " , " coords " , " exportparts " , " imagesizes " , " imagesrcset " , " sizes " , " srcset " :
96
+ return " , "
97
+ case " allow " :
98
+ return " ; "
99
+ default :
100
+ return " "
101
+ }
102
+ }
103
+
104
+ let excludeRendered : Set < String > = [ " attributes " , " isVoid " , " encoding " , " tag " , " fromMacro " , " trailingSlash " , " escaped " , " innerHTML " ]
78
105
for variable in variables. sorted ( by: {
79
106
guard $0. memoryLayoutAlignment == $1. memoryLayoutAlignment else { return $0. memoryLayoutAlignment > $1. memoryLayoutAlignment }
80
107
guard $0. memoryLayoutSize == $1. memoryLayoutSize else { return $0. memoryLayoutSize > $1. memoryLayoutSize }
81
108
guard $0. memoryLayoutStride == $1. memoryLayoutStride else { return $0. memoryLayoutStride > $1. memoryLayoutStride }
82
109
return $0. name < $1. name
83
110
} ) {
84
111
variablesString += indent1 + variable. description
112
+ let variableName : String = variable. name
113
+ if !excludeRendered. contains ( variableName) {
114
+ if variable. valueType. isOptional {
115
+ if variable. valueType. isAttribute {
116
+ renderAttributesString += indent3 + " if let " + variableName
117
+ renderAttributesString += " , let v:String = " + variableName + " .htmlValue(encoding: encoding, forMacro: fromMacro) { "
118
+ renderAttributesString += indent4 + " let s:String = " + variableName + " .htmlValueIsVoidable && v.isEmpty ? \" \" : \" = \" + sd + v + sd "
119
+ renderAttributesString += indent4 + " items.append( \" " + variableName. lowercased ( ) + " \" + s) "
120
+ } else if variable. valueType. isString {
121
+ renderAttributesString += indent3 + " if let " + variableName
122
+ renderAttributesString += " { "
123
+ renderAttributesString += indent4 + " items.append( \" " + variableName. lowercased ( ) + " \" + sd + " + variableName + " + sd) "
124
+ } else if variable. valueType. isArray {
125
+ let separator : String = separator ( key: variableName. lowercased ( ) )
126
+ renderAttributesString += indent3 + " if ! " + variableName + " .isEmpty { "
127
+ renderAttributesString += indent4 + " var v:String = sd "
128
+ renderAttributesString += indent4
129
+
130
+ var function : String = indent5
131
+ switch variable. valueType {
132
+ case . array( . string) , . optional( . array( . string) ) :
133
+ function += " v += e + \" \( separator) \" "
134
+ case . array( . int) , . optional( . array( . int) ) :
135
+ function += " v += String(describing: e) + \" \( separator) \" "
136
+ default :
137
+ function += " if let e:String = e.htmlValue(encoding: encoding, forMacro: fromMacro) { " + indent6 + " v += e + \" \( separator) \" " + indent5 + " } "
138
+ }
139
+
140
+ renderAttributesString += """
141
+ for e in \( variableName) { \( function)
142
+ }
143
+ """
144
+ renderAttributesString += indent4 + " v.removeLast() "
145
+ renderAttributesString += indent4 + " items.append( \" " + variableName. lowercased ( ) + " = \" + v + sd) "
146
+ } else {
147
+ renderAttributesString += indent3 + " if let " + variableName
148
+ renderAttributesString += " { "
149
+ renderAttributesString += indent4 + " // test "
150
+ }
151
+ renderAttributesString += indent3 + " } "
152
+ } else {
153
+ renderAttributesString += " \n + String(describing: " + variableName + " ) "
154
+ }
155
+ }
85
156
}
157
+ renderAttributesString += indent3 + " return (items.isEmpty ? \" \" : \" \" ) + items.joined(separator: \" \" ) "
86
158
87
159
variables = variables. sorted ( by: { $0. name <= $1. name } )
88
160
var customAttributeCases : String = " "
89
161
for variable in variables {
90
- customAttributeCases += indent2 + " case " + variable. name + " ( " + variable. valueType. annotation ( variableName: variable. name) + " = " + variable. valueType. defaultOptionalValue + " ) "
162
+ if !excludeRendered. contains ( variable. name. lowercased ( ) ) {
163
+ customAttributeCases += indent2 + " case " + variable. name + " ( " + variable. valueType. annotation ( variableName: variable. name) + " = " + variable. valueType. defaultOptionalValue + " ) "
164
+ }
91
165
}
166
+
167
+ renderAttributesString += indent2 + " } "
168
+ renderString += renderAttributesString + " \n "
169
+ renderString += """
170
+ let string:String = innerHTML.map({ String(describing: $0) }).joined()
171
+ let l:String, g:String
172
+ if escaped {
173
+ l = " < "
174
+ g = " > "
175
+ } else {
176
+ l = " < "
177
+ g = " > "
178
+ }
179
+ return l + tag + attributes() + g + string + l + " / " + tag + g
180
+ """
181
+ renderString += indent1 + " } "
92
182
93
183
var code : String = template
94
184
code. replace ( " %variables% " , with: variablesString)
185
+ code. replace ( " %render% " , with: renderString)
95
186
var elementDocumentation : [ String ] = elementType. documentation
96
187
elementDocumentation. append ( contentsOf: [ " " , " [Read more](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/%tagName%). " ] )
97
188
let elementDocumentationString : String = " \n /// \n " + elementDocumentation. map ( { " /// " + $0 } ) . joined ( separator: " \n " )
@@ -108,7 +199,8 @@ for (elementType, customAttributes) in attributes().filter({ $0.key == .a }) {
108
199
let filePath:String = writeTo + fileName
109
200
if FileManager.default.fileExists(atPath: filePath) {
110
201
try FileManager.default.removeItem(atPath: filePath)
111
- }*/
202
+ }
203
+ FileManager.default.createFile(atPath: filePath, contents: code.data(using: .utf8)!)*/
112
204
}
113
205
extension Array where Element == HTMLElementVariable {
114
206
func filterAndSort( _ predicate: ( Element ) -> Bool ) -> [ Element ] {
@@ -159,152 +251,13 @@ struct HTMLElementVariable : Hashable {
159
251
if !string. isEmpty {
160
252
string += indent1
161
253
}
162
- string += ( `public` ? " public " : " private " ) + " " + ( mutable ? " var " : " let " ) + " " + name + " : " + valueType. annotation ( variableName: name) + ( defaultValue != nil ? " = " + defaultValue! : " " )
254
+ string += ( `public` ? " public " : " @usableFromInline internal " ) + " " + ( mutable ? " var " : " let " ) + " " + name + " : " + valueType. annotation ( variableName: name) + ( defaultValue != nil ? " = " + defaultValue! : " " )
163
255
return string
164
256
}
165
257
}
166
258
167
259
// MARK: HTMLElementType
168
- enum HTMLElementType : String , CaseIterable {
169
- case html
170
-
171
- case a
172
- case abbr
173
- case address
174
- case area
175
- case article
176
- case aside
177
- case audio
178
-
179
- case b
180
- case base
181
- case bdi
182
- case bdo
183
- case blockquote
184
- case body
185
- case br
186
- case button
187
-
188
- case canvas
189
- case caption
190
- case cite
191
- case code
192
- case col
193
- case colgroup
194
-
195
- case data
196
- case datalist
197
- case dd
198
- case del
199
- case details
200
- case dfn
201
- case dialog
202
- case div
203
- case dl
204
- case dt
205
-
206
- case em
207
- case embed
208
-
209
- case fencedframe
210
- case fieldset
211
- case figcaption
212
- case figure
213
- case footer
214
- case form
215
-
216
- case h1, h2, h3, h4, h5, h6
217
- case head
218
- case header
219
- case hgroup
220
- case hr
221
-
222
- case i
223
- case iframe
224
- case img
225
- case input
226
- case ins
227
-
228
- case kbd
229
-
230
- case label
231
- case legend
232
- case li
233
- case link
234
-
235
- case main
236
- case map
237
- case mark
238
- case menu
239
- case meta
240
- case meter
241
-
242
- case nav
243
- case noscript
244
-
245
- case object
246
- case ol
247
- case optgroup
248
- case option
249
- case output
250
-
251
- case p
252
- case picture
253
- case portal
254
- case pre
255
- case progress
256
-
257
- case q
258
-
259
- case rp
260
- case rt
261
- case ruby
262
-
263
- case s
264
- case samp
265
- case script
266
- case search
267
- case section
268
- case select
269
- case slot
270
- case small
271
- case source
272
- case span
273
- case strong
274
- case style
275
- case sub
276
- case summary
277
- case sup
278
-
279
- case table
280
- case tbody
281
- case td
282
- case template
283
- case textarea
284
- case tfoot
285
- case th
286
- case thead
287
- case time
288
- case title
289
- case tr
290
- case track
291
-
292
- case u
293
- case ul
294
-
295
- case variable // var
296
- case video
297
-
298
- case wbr
299
-
300
- var isVoid : Bool {
301
- switch self {
302
- case . area, . base, . br, . col, . embed, . hr, . img, . input, . link, . meta, . source, . track, . wbr:
303
- return true
304
- default :
305
- return false
306
- }
307
- }
260
+ extension HTMLElementType {
308
261
309
262
var tagName : String {
310
263
switch self {
@@ -330,6 +283,14 @@ enum HTMLElementType : String, CaseIterable {
330
283
" " ,
331
284
" Content within each `<a>` _should_ indicate the link's destination. If the `href` attribute is present, pressing the enter key while focused on the `<a>` element will activate it. "
332
285
]
286
+ case . abbr:
287
+ return [
288
+ " Represents an abbreviation or acronym. " ,
289
+ " " ,
290
+ " When including an abbreviation or acronym, provide a full expansion of the term in plain text on first use, along with the `<abbr>` to mark up the abbreviation. This informs the user what the abbreviation or acronym means. " ,
291
+ " " ,
292
+ " The optional [`title`](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/title) attribute can provide an expansion for the abbreviation or acronym when a full expansion is not present. This provides a hint to user agents on how to announce/display the content while informing all users what the abbreviation means. If present, `title` must contain this full description and nothing else. "
293
+ ]
333
294
default : return [ ]
334
295
}
335
296
}
@@ -389,6 +350,20 @@ enum HTMLElementValueType : Hashable {
389
350
default : return false
390
351
}
391
352
}
353
+
354
+ var isString : Bool {
355
+ switch self {
356
+ case . string, . optional( . string) : return true
357
+ default : return false
358
+ }
359
+ }
360
+
361
+ var isOptional : Bool {
362
+ switch self {
363
+ case . optional( _) : return true
364
+ default : return false
365
+ }
366
+ }
392
367
393
368
var defaultOptionalValue : String {
394
369
isArray ? " [] " : isBool ? " false " : " nil "
@@ -398,9 +373,9 @@ enum HTMLElementValueType : Hashable {
398
373
// MARK: Get
399
374
func get(
400
375
_ variableName: String ,
401
- _ valueType: HTMLElementValueType ,
402
- _ documentation: HTMLElementVariableDocumentation ? = nil
376
+ _ valueType: HTMLElementValueType
403
377
) -> HTMLElementVariable {
378
+ let documentation : HTMLElementVariableDocumentation ? = HTMLElementVariableDocumentation ( rawValue: variableName. lowercased ( ) )
404
379
return get ( public: true , mutable: true , name: variableName, documentation: documentation? . value ?? [ ] , valueType: . optional( valueType) )
405
380
}
406
381
func get(
@@ -465,7 +440,7 @@ func get(
465
440
}
466
441
467
442
// MARK: Attribute Documentation
468
- enum HTMLElementVariableDocumentation {
443
+ enum HTMLElementVariableDocumentation : String {
469
444
case download
470
445
471
446
var value : [ String ] {
@@ -489,7 +464,7 @@ func attributes() -> [HTMLElementType:[HTMLElementVariable]] {
489
464
// MARK: A
490
465
. a : [
491
466
get ( " attributionsrc " , . array( of: . string) ) ,
492
- get ( " download " , . attribute, . download ) ,
467
+ get ( " download " , . attribute) ,
493
468
get ( " href " , . string) ,
494
469
get ( " hrefLang " , . string) ,
495
470
get ( " ping " , . array( of: . string) ) ,
@@ -503,7 +478,7 @@ func attributes() -> [HTMLElementType:[HTMLElementVariable]] {
503
478
. area : [
504
479
get ( " alt " , . string) ,
505
480
get ( " coords " , . array( of: . int) ) ,
506
- get ( " download " , . attribute, . download ) ,
481
+ get ( " download " , . attribute) ,
507
482
get ( " href " , . string) ,
508
483
get ( " shape " , . attribute) ,
509
484
get ( " ping " , . array( of: . string) ) ,
0 commit comments