Skip to content

Commit 2a21bf8

Browse files
more progress on using a closure for the innerHTML
1 parent c562730 commit 2a21bf8

File tree

6 files changed

+98
-66
lines changed

6 files changed

+98
-66
lines changed

Sources/HTMLElements/CustomElement.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ public struct custom : HTMLElement {
1717
public var attributes:[HTMLAttribute]
1818
public var innerHTML:[CustomStringConvertible & Sendable]
1919

20-
@usableFromInline internal var encoding:HTMLEncoding = .string
20+
public private(set) var encoding:HTMLEncoding = .string
2121
public var isVoid:Bool
2222
public var trailingSlash:Bool
2323
public var escaped:Bool = false
2424

25-
@usableFromInline internal var fromMacro:Bool = false
25+
public private(set) var fromMacro:Bool = false
2626

2727
public init(_ encoding: HTMLEncoding, _ data: HTMLKitUtilities.ElementData) {
2828
self.encoding = encoding

Sources/HTMLElements/HTMLElement.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public protocol HTMLElement : CustomStringConvertible, Sendable {
1313
/// Remapped attribute names.
1414
static var otherAttributes : [String:String] { get }
1515

16+
var encoding : HTMLEncoding { get }
17+
var fromMacro : Bool { get }
18+
1619
/// Whether or not this element is a void element.
1720
var isVoid : Bool { get }
1821

@@ -38,4 +41,44 @@ extension HTMLElement {
3841
public static var otherAttributes : [String:String] {
3942
return [:]
4043
}
44+
@inlinable
45+
func render(
46+
prefix: String = "",
47+
suffix: String = "",
48+
items: [String]
49+
) -> String {
50+
let l:String, g:String
51+
if escaped {
52+
l = "<"
53+
g = ">"
54+
} else {
55+
l = "<"
56+
g = ">"
57+
}
58+
var s:String = ""
59+
if !prefix.isEmpty {
60+
s += l + prefix + g
61+
}
62+
s += l + tag
63+
for attr in self.attributes {
64+
if let v = attr.htmlValue(encoding: encoding, forMacro: fromMacro) {
65+
let d = attr.htmlValueDelimiter(encoding: encoding, forMacro: fromMacro)
66+
s += " " + attr.key + (attr.htmlValueIsVoidable && v.isEmpty ? "" : "=" + d + v + d)
67+
}
68+
}
69+
for item in items {
70+
s += " " + item
71+
}
72+
if isVoid && trailingSlash {
73+
s += " /"
74+
}
75+
s += g
76+
for i in innerHTML {
77+
s += String(describing: i)
78+
}
79+
if !suffix.isEmpty {
80+
s += l + suffix + g
81+
}
82+
return s
83+
}
4184
}

Sources/HTMLKitParse/ParseData.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ extension HTMLKitUtilities {
6464
let (string, encoding):(String, HTMLEncoding) = expandMacro(context: context)
6565
return "\(raw: encodingResult(context: context, node: context.expansion, string: string, for: encoding))"
6666
}
67-
private static func encodingResult(context: HTMLExpansionContext, node: FreestandingMacroExpansionSyntax, string: String, for encoding: HTMLEncoding) -> String {
67+
private static func encodingResult(context: HTMLExpansionContext, node: MacroExpansionExprSyntax, string: String, for encoding: HTMLEncoding) -> String {
6868
func hasNoInterpolation() -> Bool {
6969
let hasInterpolation:Bool = !string.ranges(of: try! Regex("\\((.*)\\)")).isEmpty
7070
guard !hasInterpolation else {
@@ -237,6 +237,7 @@ extension HTMLKitUtilities {
237237
) -> (CustomStringConvertible & Sendable)? {
238238
if let expansion = expr.macroExpansion {
239239
var c = context
240+
c.expansion = expansion
240241
c.arguments = expansion.arguments
241242
switch expansion.macroName.text {
242243
case "html", "anyHTML", "uncheckedHTML":

Sources/HTMLKitUtilities/HTMLExpansionContext.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import SwiftSyntaxMacros
1414
public struct HTMLExpansionContext : @unchecked Sendable {
1515
#if canImport(SwiftSyntax) && canImport(SwiftSyntaxMacros)
1616
public let context:MacroExpansionContext
17-
public package(set) var expansion:FreestandingMacroExpansionSyntax
17+
public var expansion:MacroExpansionExprSyntax
1818
public var arguments:LabeledExprListSyntax
1919
#endif
2020

@@ -46,7 +46,7 @@ public struct HTMLExpansionContext : @unchecked Sendable {
4646
elementsRequireEscaping: Bool = true
4747
) {
4848
self.context = context
49-
self.expansion = expansion
49+
self.expansion = expansion.as(ExprSyntax.self)!.macroExpansion!
5050
self.ignoresCompilerWarnings = ignoresCompilerWarnings
5151
self.encoding = encoding
5252
self.key = key

Sources/HTMLKitUtilityMacros/HTMLElements.swift

Lines changed: 47 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ enum HTMLElements : DeclarationMacro {
4343
public let tag:String = "\(tag)"
4444
public var attributes:[HTMLAttribute]
4545
public var innerHTML:[CustomStringConvertible & Sendable]
46-
private var encoding:HTMLEncoding = .string
47-
private var fromMacro:Bool = false
46+
public private(set) var encoding:HTMLEncoding = .string
47+
public private(set) var fromMacro:Bool = false
4848
public let isVoid:Bool = \(isVoid)
4949
public var trailingSlash:Bool = false
5050
public var escaped:Bool = false
@@ -100,12 +100,12 @@ enum HTMLElements : DeclarationMacro {
100100
initializers += "_ innerHTML: CustomStringConvertible & Sendable...\n) {\n"
101101
initializers += "self.attributes = attributes\n"
102102
for (key, _, _) in attributes {
103-
var key_literal = key
104-
if key_literal.first == "`" {
105-
key_literal.removeFirst()
106-
key_literal.removeLast()
103+
var keyLiteral = key
104+
if keyLiteral.first == "`" {
105+
keyLiteral.removeFirst()
106+
keyLiteral.removeLast()
107107
}
108-
initializers += "self.\(key_literal) = \(key)\n"
108+
initializers += "self.\(keyLiteral) = \(key)\n"
109109
}
110110
initializers += "self.innerHTML = innerHTML\n}\n"
111111

@@ -117,10 +117,10 @@ enum HTMLElements : DeclarationMacro {
117117
}
118118
initializers += "self.attributes = data.globalAttributes\n"
119119
for (key, value_type, _) in attributes {
120-
var key_literal = key
121-
if key_literal.first == "`" {
122-
key_literal.removeFirst()
123-
key_literal.removeLast()
120+
var keyLiteral = key
121+
if keyLiteral.first == "`" {
122+
keyLiteral.removeFirst()
123+
keyLiteral.removeLast()
124124
}
125125
var value = "as? \(value_type)"
126126
switch value_type {
@@ -129,80 +129,68 @@ enum HTMLElements : DeclarationMacro {
129129
default:
130130
break
131131
}
132-
initializers += "self.\(key) = data.attributes[\"\(key_literal)\"] " + value + "\n"
132+
initializers += "self.\(key) = data.attributes[\"\(keyLiteral)\"] " + value + "\n"
133133
}
134134
initializers += "self.innerHTML = data.innerHTML\n"
135135
initializers += "}"
136136
string += initializers
137137

138138
var render = "\npublic var description : String {\n"
139-
var attributes_func = "func attributes() -> String {\n"
139+
var attributes_func = ""
140+
var itemsArray:String = ""
140141
if !attributes.isEmpty {
141142
attributes_func += "let sd = encoding.stringDelimiter(forMacro: fromMacro)\n"
142-
attributes_func += "var"
143-
} else {
144-
attributes_func += "let"
143+
itemsArray += "var items:[String] = []\n"
145144
}
146-
attributes_func += " items:[String] = self.attributes.compactMap({\n"
147-
attributes_func += "guard let v = $0.htmlValue(encoding: encoding, forMacro: fromMacro) else { return nil }\n"
148-
attributes_func += "let d = $0.htmlValueDelimiter(encoding: encoding, forMacro: fromMacro)\n"
149-
attributes_func += #"return $0.key + ($0.htmlValueIsVoidable && v.isEmpty ? "" : "=" + d + v + d)"#
150-
attributes_func += "\n})\n"
151145
for (key, value_type, _) in attributes {
152-
var key_literal = key
153-
if key_literal.first == "`" {
154-
key_literal.removeFirst()
155-
key_literal.removeLast()
146+
var keyLiteral = key
147+
if keyLiteral.first == "`" {
148+
keyLiteral.removeFirst()
149+
keyLiteral.removeLast()
156150
}
157-
let variable_name = key_literal
158-
if key_literal == "httpEquiv" {
159-
key_literal = "http-equiv"
160-
} else if key_literal == "acceptCharset" {
161-
key_literal = "accept-charset"
151+
let variable_name = keyLiteral
152+
if keyLiteral == "httpEquiv" {
153+
keyLiteral = "http-equiv"
154+
} else if keyLiteral == "acceptCharset" {
155+
keyLiteral = "accept-charset"
162156
}
163157
if value_type == "Bool" {
164-
attributes_func += "if \(key) { items.append(\"\(key_literal)\") }\n"
158+
itemsArray += "if \(key) { items.append(\"\(keyLiteral)\") }\n"
165159
} else if value_type.first == "[" {
166-
attributes_func += "if let _\(variable_name):String = "
160+
itemsArray += "if let _\(variable_name):String = "
167161
let separator = separator(key: key)
168162
switch value_type {
169163
case "[String]":
170-
attributes_func += "\(key)?"
164+
itemsArray += "\(key)?"
171165
case "[Int]", "[Float]":
172-
attributes_func += "\(key)?.map({ \"\\($0)\" })"
166+
itemsArray += "\(key)?.map({ \"\\($0)\" })"
173167
default:
174-
attributes_func += "\(key)?.compactMap({ return $0.htmlValue(encoding: encoding, forMacro: fromMacro) })"
168+
itemsArray += "\(key)?.compactMap({ return $0.htmlValue(encoding: encoding, forMacro: fromMacro) })"
175169
}
176-
attributes_func += ".joined(separator: \"\(separator)\") {\n"
177-
attributes_func += #"let k:String = _\#(variable_name).isEmpty ? "" : "=" + sd + _\#(variable_name) + sd"#
178-
attributes_func += "\nitems.append(\"\(key_literal)\" + k)"
179-
attributes_func += "\n}\n"
170+
itemsArray += ".joined(separator: \"\(separator)\") {\n"
171+
itemsArray += #"let k:String = _\#(variable_name).isEmpty ? "" : "=" + sd + _\#(variable_name) + sd"#
172+
itemsArray += "\nitems.append(\"\(keyLiteral)\" + k)"
173+
itemsArray += "\n}\n"
180174
} else if value_type == "String" || value_type == "Int" || value_type == "Float" || value_type == "Double" {
181175
let value = value_type == "String" ? key : "String(describing: \(key))"
182-
attributes_func += #"if let \#(key) { items.append("\#(key_literal)=" + sd + \#(value) + sd) }"#
183-
attributes_func += "\n"
176+
itemsArray += #"if let \#(key) { items.append("\#(keyLiteral)=" + sd + \#(value) + sd) }"#
177+
itemsArray += "\n"
184178
} else {
185-
attributes_func += "if let \(key), let v = \(key).htmlValue(encoding: encoding, forMacro: fromMacro) {\n"
186-
attributes_func += #"let s = \#(key).htmlValueIsVoidable && v.isEmpty ? "" : "=" + sd + v + sd"#
187-
attributes_func += "\nitems.append(\"\(key_literal)\" + s)"
188-
attributes_func += "\n}\n"
179+
itemsArray += "if let \(key), let v = \(key).htmlValue(encoding: encoding, forMacro: fromMacro) {\n"
180+
itemsArray += #"let s = \#(key).htmlValueIsVoidable && v.isEmpty ? "" : "=" + sd + v + sd"#
181+
itemsArray += "\nitems.append(\"\(keyLiteral)\" + s)"
182+
itemsArray += "\n}\n"
189183
}
190184
}
191-
attributes_func += "return (items.isEmpty ? \"\" : \" \") + items.joined(separator: \" \")\n}\n"
192-
render += attributes_func
193-
render += "let string:String = innerHTML.map({ String(describing: $0) }).joined()\n"
194-
let trailing_slash = isVoid ? " + (trailingSlash ? \" /\" : \"\")" : ""
195-
render += """
196-
let l:String, g:String
197-
if escaped {
198-
l = "&lt;"
199-
g = "&gt;"
200-
} else {
201-
l = "<"
202-
g = ">"
185+
render += attributes_func + itemsArray
186+
render += "return render("
187+
if tag == "html" {
188+
render += "prefix: \"!DOCTYPE html\", "
203189
}
204-
"""
205-
render += "return \(tag == "html" ? "l + \"!DOCTYPE html\" + g + " : "")l + tag + attributes()\(trailing_slash) + g + string" + (isVoid ? "" : " + l + \"/\" + tag + g")
190+
if !isVoid {
191+
render += "suffix: \"/\" + tag, "
192+
}
193+
render += "items: \(itemsArray.isEmpty ? "[]" : "items"))"
206194
render += "}"
207195

208196
string += render

Tests/HTMLKitTests/HTMLKitTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ struct HTMLKitTests {
3939
public init(_ encoding: HTMLEncoding, _ data: HTMLKitUtilities.ElementData) {
4040
}
4141

42-
private var encoding:HTMLEncoding = .string
42+
public private(set) var encoding:HTMLEncoding = .string
4343

4444
/// Causes the browser to treat the linked URL as a download. Can be used with or without a `filename` value.
4545
///
@@ -58,7 +58,7 @@ struct HTMLKitTests {
5858
public var ping:[String] = []
5959
public var rel:[HTMLAttribute.Extra.rel] = []
6060
public var escaped:Bool = false
61-
private var fromMacro:Bool = false
61+
public private(set) var fromMacro:Bool = false
6262
public let isVoid:Bool = false
6363
public var referrerPolicy:HTMLAttribute.Extra.referrerpolicy? = nil
6464
public var target:HTMLAttribute.Extra.target? = nil

0 commit comments

Comments
 (0)