Skip to content

Commit e9842d0

Browse files
HTML element generation updates
1 parent 3c6f31a commit e9842d0

File tree

7 files changed

+236
-94
lines changed

7 files changed

+236
-94
lines changed

.gitignore

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,4 @@ DerivedData
77
.netrc
88
.vscode
99
Package.resolved
10-
__BenchmarkBoilerplate.*
11-
Aria.*
12-
Attributes.*
13-
ChildOf.*
14-
DebugRender.*
15-
DebugXmlRender.*
16-
Elements.*
17-
Events.*
18-
EncodingTests.d
19-
EncodingTests.o
20-
EncodingTests.swiftdeps*
21-
EscapeHTMLTests.d
22-
EscapeHTMLTests.o
23-
EscapeHTMLTests.swiftdeps*
24-
HTMX.d
25-
HTMX.o
26-
HTMX.swiftdeps*
27-
HTMXTests.d
28-
HTMXTests.o
29-
HTMXTests.swiftdeps*
30-
Html4.*
31-
HtmlRender.*
32-
MediaType.*
33-
Node.*
34-
Tag.*
35-
Tags.*
36-
XmlRender.*
37-
InterpolationLookup.d
38-
InterpolationLookup.o
39-
InterpolationLookup.swiftdeps*
40-
LiteralElements.d
41-
LiteralElements.o
42-
LiteralElements.swiftdeps*
10+
main

Sources/GenerateElements/main.swift

Lines changed: 142 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,18 @@
66
//
77

88
// generate the html element files using the following command:
9-
//
10-
// swiftc main.swift -D GENERATE_ELEMENTS && ./main
9+
/*
10+
swiftc main.swift ../HTMLKitUtilities/HTMLEncoding.swift \
11+
../HTMLKitUtilities/attributes/HTMLElementAttribute.swift \
12+
../HTMLKitUtilities/attributes/HTMLElementAttributeExtra.swift \
13+
../HTMLKitUtilities/attributes/HTMX.swift \
14+
../HTMLKitUtilities/attributes/HTMXAttributes.swift \
15+
-D GENERATE_ELEMENTS && ./main
16+
*/
1117

1218
#if canImport(Foundation) && GENERATE_ELEMENTS
1319

14-
// We do we do it this way?
20+
// Why do we do it this way?
1521
// - The documentation doesn't link correctly if we generate from a macro
1622

1723
import Foundation
@@ -22,31 +28,36 @@ writeTo = "/home/paradigm/Desktop/GitProjects" + suffix
2228
#elseif os(macOS)
2329
writeTo = "/Users/randomhashtags/GitProjects" + suffix
2430
#else
25-
#errorget("no write path declared for platform")
31+
#error("no write path declared for platform")
2632
#endif
2733

28-
let now:Date = Date()
34+
let now:String = Date.now.formatted(date: .abbreviated, time: .complete)
2935
let template:String = """
3036
//
3137
// %elementName%.swift
3238
//
3339
//
34-
// Generated on \(now).
40+
// Generated \(now).
3541
//
3642
3743
import SwiftSyntax
3844
39-
/// The `%tagName%` HTML element.%elementDocumentation%
45+
/// The `%tagName%`%aliases% HTML element.%elementDocumentation%
4046
public struct %elementName% : HTMLElement {%variables%
4147
}
48+
49+
public extension %elementName% {
50+
enum AttributeKeys {%customAttributeCases%
51+
}
52+
}
4253
"""
4354
let defaultVariables:[HTMLElementVariable] = [
44-
.init(public: true, mutable: true, name: "trailingSlash", valueType: .bool, defaultValue: "false"),
45-
.init(public: true, mutable: true, name: "escaped", valueType: .bool, defaultValue: "false"),
46-
.init(public: false, mutable: true, name: "fromMacro", valueType: .bool, defaultValue: "false"),
47-
.init(public: false, mutable: true, name: "encoding", valueType: .custom("HTMLEncoding"), defaultValue: ".string"),
48-
.init(public: true, mutable: true, name: "innerHTML", valueType: .array(of: .custom("CustomStringConvertible"))),
49-
.init(public: true, mutable: true, name: "attributes", valueType: .array(of: .custom("HTMLElementAttribute"))),
55+
get(public: true, mutable: true, name: "trailingSlash", valueType: .bool, defaultValue: "false"),
56+
get(public: true, mutable: true, name: "escaped", valueType: .bool, defaultValue: "false"),
57+
get(public: false, mutable: true, name: "fromMacro", valueType: .bool, defaultValue: "false"),
58+
get(public: false, mutable: true, name: "encoding", valueType: .custom("HTMLEncoding"), defaultValue: ".string"),
59+
get(public: true, mutable: true, name: "innerHTML", valueType: .array(of: .custom("CustomStringConvertible"))),
60+
get(public: true, mutable: true, name: "attributes", valueType: .array(of: .custom("HTMLElementAttribute"))),
5061
]
5162

5263
let indent1:String = "\n "
@@ -56,44 +67,25 @@ for (elementType, customAttributes) in attributes().filter({ $0.key == .a }) {
5667
var variablesString:String = ""
5768

5869
var variables:[HTMLElementVariable] = defaultVariables
59-
variables.append(.init(public: true, mutable: false, name: "tag", valueType: .string, defaultValue: "\"%tagName%\""))
60-
variables.append(.init(public: true, mutable: false, name: "isVoid", valueType: .bool, defaultValue: "\(elementType.isVoid)"))
70+
variables.append(get(public: true, mutable: false, name: "tag", valueType: .string, defaultValue: "\"%tagName%\""))
71+
variables.append(get(public: true, mutable: false, name: "isVoid", valueType: .bool, defaultValue: "\(elementType.isVoid)"))
6172
for attribute in customAttributes {
6273
variables.append(attribute)
6374
}
64-
65-
let booleans:[HTMLElementVariable] = variables.filter({ $0.valueType.isBool })
66-
for bool in booleans {
67-
variablesString += indent1 + bool.description
68-
}
69-
70-
let integers:[HTMLElementVariable] = variables.filter({ $0.valueType == .int })
71-
for int in integers {
72-
variablesString += indent1 + int.description
73-
}
74-
75-
let floats:[HTMLElementVariable] = variables.filter({ $0.valueType == .float })
76-
for float in floats {
77-
variablesString += indent1 + float.description
78-
}
79-
80-
let attributes:[HTMLElementVariable] = variables.filter({ $0.valueType.isAttribute })
81-
for attribute in attributes {
82-
variablesString += indent1 + attribute.description
83-
}
84-
85-
let strings:[HTMLElementVariable] = variables.filter({ $0.valueType == .string })
86-
for string in strings {
87-
variablesString += indent1 + string.description
88-
}
89-
90-
let arrays:[HTMLElementVariable] = variables.filter({ $0.valueType.isArray })
91-
for array in arrays {
92-
variablesString += indent1 + array.description
75+
76+
for variable in variables.sorted(by: {
77+
guard $0.memoryLayoutAlignment == $1.memoryLayoutAlignment else { return $0.memoryLayoutAlignment > $1.memoryLayoutAlignment }
78+
guard $0.memoryLayoutSize == $1.memoryLayoutSize else { return $0.memoryLayoutSize > $1.memoryLayoutSize }
79+
guard $0.memoryLayoutStride == $1.memoryLayoutStride else { return $0.memoryLayoutStride > $1.memoryLayoutStride }
80+
return $0.name < $1.name
81+
}) {
82+
variablesString += indent1 + variable.description
9383
}
9484

9585
variables = variables.sorted(by: { $0.name <= $1.name })
86+
var customAttributeCases:String = ""
9687
for variable in variables {
88+
customAttributeCases += indent2 + "case " + variable.name + "(" + variable.valueType.annotation(variableName: variable.name) + " = " + variable.valueType.defaultOptionalValue + ")"
9789
}
9890

9991
var code:String = template
@@ -103,7 +95,11 @@ for (elementType, customAttributes) in attributes().filter({ $0.key == .a }) {
10395
let elementDocumentationString:String = "\n/// \n" + elementDocumentation.map({ "/// " + $0 }).joined(separator: "\n")
10496
code.replace("%elementDocumentation%", with: elementDocumentationString)
10597
code.replace("%tagName%", with: elementType.tagName)
98+
99+
let aliases:String = elementType.aliases.isEmpty ? "" : " (" + elementType.aliases.map({ "_" + $0 + "_" }).joined(separator: ", ") + ")"
100+
code.replace("%aliases%", with: aliases)
106101
code.replace("%elementName%", with: elementType.rawValue)
102+
code.replace("%customAttributeCases%", with: customAttributeCases)
107103
print(code)
108104

109105
/*let fileName:String = elementType.rawValue + ".swift"
@@ -112,17 +108,33 @@ for (elementType, customAttributes) in attributes().filter({ $0.key == .a }) {
112108
try FileManager.default.removeItem(atPath: filePath)
113109
}*/
114110
}
111+
extension Array where Element == HTMLElementVariable {
112+
func filterAndSort(_ predicate: (Element) -> Bool) -> [Element] {
113+
return filter(predicate).sorted(by: { $0.mutable == $1.mutable ? $0.public == $1.public ? $0.name < $1.name : !$0.public : !$0.mutable })
114+
}
115+
}
115116

116117
// MARK: HTMLElementVariable
117-
struct HTMLElementVariable {
118+
struct HTMLElementVariable : Hashable {
118119
let name:String
119120
let documentation:[String]
120121
let defaultValue:String?
121122
let `public`:Bool
122123
let mutable:Bool
123124
let valueType:HTMLElementValueType
125+
let memoryLayoutAlignment:Int
126+
let memoryLayoutSize:Int
127+
let memoryLayoutStride:Int
124128

125-
init(public: Bool, mutable: Bool, name: String, documentation: [String] = [], valueType: HTMLElementValueType, defaultValue: String? = nil) {
129+
init(
130+
public: Bool,
131+
mutable: Bool,
132+
name: String,
133+
documentation: [String] = [],
134+
valueType: HTMLElementValueType,
135+
defaultValue: String? = nil,
136+
memoryLayout: (alignment: Int, size: Int, stride: Int)
137+
) {
126138
switch name {
127139
case "for", "default", "defer", "as":
128140
self.name = "`" + name + "`"
@@ -134,12 +146,16 @@ struct HTMLElementVariable {
134146
self.public = `public`
135147
self.mutable = mutable
136148
self.valueType = valueType
149+
(memoryLayoutAlignment, memoryLayoutSize, memoryLayoutStride) = (memoryLayout.alignment, memoryLayout.size, memoryLayout.stride)
137150
}
138151

139152
var description : String {
140153
var string:String = ""
141154
for documentation in documentation {
142-
string += "/// " + documentation
155+
string += indent1 + "/// " + documentation
156+
}
157+
if !string.isEmpty {
158+
string += indent1
143159
}
144160
string += (`public` ? "public" : "private") + " " + (mutable ? "var" : "let") + " " + name + ":" + valueType.annotation(variableName: name) + (defaultValue != nil ? " = " + defaultValue! : "")
145161
return string
@@ -294,6 +310,15 @@ enum HTMLElementType : String, CaseIterable {
294310
default: return rawValue
295311
}
296312
}
313+
314+
var aliases : [String] {
315+
var aliases:Set<String>
316+
switch self {
317+
case .a: aliases = ["anchor"]
318+
default: aliases = []
319+
}
320+
return aliases.sorted(by: { $0 <= $1 })
321+
}
297322

298323
var documentation : [String] {
299324
switch self {
@@ -357,8 +382,7 @@ enum HTMLElementValueType : Hashable {
357382

358383
var isAttribute : Bool {
359384
switch self {
360-
case .attribute: return true
361-
case .otherAttribute(_): return true
385+
case .attribute, .otherAttribute(_): return true
362386
case .optional(let item): return item.isAttribute
363387
default: return false
364388
}
@@ -369,9 +393,73 @@ enum HTMLElementValueType : Hashable {
369393
}
370394
}
371395

372-
373-
func get(_ variableName: String, _ valueType: HTMLElementValueType, _ documentation: HTMLElementVariableDocumentation? = nil) -> HTMLElementVariable {
374-
return HTMLElementVariable(public: true, mutable: true, name: variableName, documentation: documentation?.value ?? [], valueType: .optional(valueType), defaultValue: valueType.defaultOptionalValue)
396+
// MARK: Get
397+
func get(
398+
_ variableName: String,
399+
_ valueType: HTMLElementValueType,
400+
_ documentation: HTMLElementVariableDocumentation? = nil
401+
) -> HTMLElementVariable {
402+
return get(public: true, mutable: true, name: variableName, documentation: documentation?.value ?? [], valueType: .optional(valueType))
403+
}
404+
func get(
405+
public: Bool,
406+
mutable: Bool,
407+
name: String,
408+
documentation: [String] = [],
409+
valueType: HTMLElementValueType,
410+
defaultValue: String? = nil
411+
) -> HTMLElementVariable {
412+
func get<T>(_ dude: T.Type) -> (Int, Int, Int) {
413+
return (MemoryLayout<T>.alignment, MemoryLayout<T>.size, MemoryLayout<T>.stride)
414+
}
415+
var (alignment, size, stride):(Int, Int, Int) = (-1, -1, -1)
416+
func layout(vt: HTMLElementValueType) -> (Int, Int, Int) {
417+
switch vt {
418+
case .bool, .booleanDefaultValue(_): return get(Bool.self)
419+
case .string: return get(String.self)
420+
case .int: return get(Int.self)
421+
case .float: return get(Float.self)
422+
case .cssUnit: return get(HTMLElementAttribute.CSSUnit.self)
423+
case .attribute: return HTMLElementAttribute.Extra.memoryLayout(for: name.lowercased()) ?? (-1, -1, -1)
424+
case .otherAttribute(let item): return HTMLElementAttribute.Extra.memoryLayout(for: item.lowercased()) ?? (-1, -1, -1)
425+
case .custom(let s):
426+
switch s {
427+
case "HTMLEncoding": return get(HTMLEncoding.self)
428+
default: break
429+
}
430+
431+
default: break
432+
}
433+
return (-1, -1, -1)
434+
}
435+
switch valueType {
436+
case .bool, .string, .int, .float, .cssUnit, .attribute, .custom(_): (alignment, size, stride) = layout(vt: valueType)
437+
case .optional(let innerVT):
438+
switch innerVT {
439+
case .bool, .booleanDefaultValue(_): (alignment, size, stride) = get(Bool.self)
440+
case .string: (alignment, size, stride) = get(String?.self)
441+
case .int: (alignment, size, stride) = get(Int?.self)
442+
case .float: (alignment, size, stride) = get(Float?.self)
443+
case .cssUnit: (alignment, size, stride) = get(HTMLElementAttribute.CSSUnit?.self)
444+
case .attribute: (alignment, size, stride) = HTMLElementAttribute.Extra.memoryLayout(for: name.lowercased()) ?? (-1, -1, -1)
445+
case .otherAttribute(let item): (alignment, size, stride) = HTMLElementAttribute.Extra.memoryLayout(for: item.lowercased()) ?? (-1, -1, -1)
446+
case .array(_): (alignment, size, stride) = (8, 8, 8)
447+
default: break
448+
}
449+
case .array(_): (alignment, size, stride) = (8, 8, 8)
450+
default: break
451+
}
452+
//var documentation:[String] = documentation
453+
//documentation.append(contentsOf: ["- Memory Layout:", " - Alignment: \(alignment)", " - Size: \(size)", " - Stride: \(stride)"])
454+
return HTMLElementVariable(
455+
public: `public`,
456+
mutable: mutable,
457+
name: name,
458+
documentation: documentation,
459+
valueType: valueType,
460+
defaultValue: defaultValue ?? valueType.defaultOptionalValue,
461+
memoryLayout: (alignment, size, stride)
462+
)
375463
}
376464

377465
// MARK: Attribute Documentation
@@ -399,7 +487,7 @@ func attributes() -> [HTMLElementType:[HTMLElementVariable]] {
399487
// MARK: A
400488
.a : [
401489
get("attributionsrc", .array(of: .string)),
402-
get("download", .attribute),
490+
get("download", .attribute, .download),
403491
get("href", .string),
404492
get("hrefLang", .string),
405493
get("ping", .array(of: .string)),
@@ -413,7 +501,7 @@ func attributes() -> [HTMLElementType:[HTMLElementVariable]] {
413501
.area : [
414502
get("alt", .string),
415503
get("coords", .array(of: .int)),
416-
get("download", .attribute),
504+
get("download", .attribute, .download),
417505
get("href", .string),
418506
get("shape", .attribute),
419507
get("ping", .array(of: .string)),

Sources/HTMLKitUtilities/attributes/HTMLElementAttribute.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
// Created by Evan Anderson on 11/19/24.
66
//
77

8+
#if canImport(SwiftSyntax)
89
import SwiftSyntax
910
import SwiftSyntaxMacros
11+
#endif
1012

1113
// MARK: HTMLElementAttribute
1214
public enum HTMLElementAttribute : HTMLInitializable {
@@ -59,6 +61,7 @@ public enum HTMLElementAttribute : HTMLInitializable {
5961
@available(*, deprecated, message: "General consensus considers this \"bad practice\" and you shouldn't mix your HTML and JavaScript. This will never be removed and remains deprecated to encourage use of other techniques. Learn more at https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#inline_event_handlers_—_dont_use_these.")
6062
case event(Extra.event, _ value: String? = nil)
6163

64+
#if canImport(SwiftSyntax)
6265
// MARK: init rawValue
6366
public init?(context: some MacroExpansionContext, key: String, arguments: LabeledExprListSyntax) {
6467
let expression:ExprSyntax = arguments.first!.expression
@@ -120,6 +123,7 @@ public enum HTMLElementAttribute : HTMLInitializable {
120123
default: return nil
121124
}
122125
}
126+
#endif
123127

124128
// MARK: key
125129
public var key : String {
@@ -283,6 +287,7 @@ public extension HTMLElementAttribute {
283287
/// Relative to the parent element
284288
case percent(_ value: Float?)
285289

290+
#if canImport(SwiftSyntax)
286291
public init?(context: some MacroExpansionContext, key: String, arguments: LabeledExprListSyntax) {
287292
let expression:ExprSyntax = arguments.first!.expression
288293
func float() -> Float? {
@@ -309,6 +314,7 @@ public extension HTMLElementAttribute {
309314
default: return nil
310315
}
311316
}
317+
#endif
312318

313319
public var key : String {
314320
switch self {

0 commit comments

Comments
 (0)