Skip to content

Commit 32f8e02

Browse files
committed
Update
1 parent b94e2aa commit 32f8e02

File tree

3 files changed

+128
-30
lines changed

3 files changed

+128
-30
lines changed

Sources/FunctionalViewComponent/FunctionalViewComponent.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,45 @@
44
@attached(body)
55
public macro ViewComponent() = #externalMacro(module: "FunctionalViewComponentMacros", type: "ViewComponentMacro")
66

7+
#if DEBUG
8+
import SwiftUI
9+
@ViewComponent
10+
@MainActor
11+
func basic(
12+
arg1: Int,
13+
arg2: String,
14+
arg3: @escaping () -> Void
15+
) -> some View {
16+
17+
@State var count: Int = 0
18+
19+
VStack {
20+
Text("\(count), \(arg1), \(arg2)")
21+
Button("Click me") {
22+
count += 1
23+
}
24+
}
25+
26+
}
727

28+
@ViewComponent
29+
@MainActor
30+
func objcct(
31+
obj: AnyObject
32+
) -> some View {
33+
34+
// unowned let obj = obj
35+
36+
@State var count: Int = 0
37+
38+
VStack {
39+
}
40+
41+
}
42+
43+
#Preview {
44+
basic(arg1: 1, arg2: "hoge", arg3: {
45+
print("hit")
46+
})
47+
}
48+
#endif

Sources/FunctionalViewComponentMacros/FunctionalViewComponentMacro.swift

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import SwiftSyntaxMacros
55

66
extension FunctionDeclSyntax {
77
struct BodyComponents {
8-
let stateDeclarations: [String]
9-
let bindingDeclarations: [String]
10-
let temporaryDeclarations: [String]
8+
let stateDeclarations: [VariableDeclSyntax]
9+
let bindingDeclarations: [VariableDeclSyntax]
10+
let temporaryDeclarations: [VariableDeclSyntax]
1111
let viewBody: [String]
1212
}
1313

@@ -22,24 +22,27 @@ extension FunctionDeclSyntax {
2222
fatalError("Function body is required")
2323
}
2424

25-
var stateDeclarations: [String] = []
26-
var bindingDeclarations: [String] = []
27-
var temporaryDeclarations: [String] = []
25+
var stateDeclarations: [VariableDeclSyntax] = []
26+
var bindingDeclarations: [VariableDeclSyntax] = []
27+
var temporaryDeclarations: [VariableDeclSyntax] = []
2828
var viewBody: [String] = []
2929

3030
for statement in body.statements {
31-
let statementText = statement.trimmed.description
32-
33-
if statementText.contains("@State") {
34-
stateDeclarations.append(statementText)
35-
} else if statementText.contains("@Binding") {
36-
bindingDeclarations.append(statementText)
37-
} else if statementText.contains("var ") || statementText.contains("let ")
38-
|| statementText.contains("unowned let") || statementText.contains("weak var")
39-
{
40-
temporaryDeclarations.append(statementText)
31+
32+
if let variableDecl = statement.item.as(VariableDeclSyntax.self) {
33+
34+
for attribute in variableDecl.attributes {
35+
if attribute.as(AttributeSyntax.self)?.kind == AttributeSyntax.init(stringLiteral: "@State").kind {
36+
stateDeclarations.append(variableDecl)
37+
} else if attribute.as(AttributeSyntax.self)?.attributeName.description == "Binding" {
38+
bindingDeclarations.append(variableDecl)
39+
} else {
40+
temporaryDeclarations.append(variableDecl)
41+
}
42+
}
43+
4144
} else {
42-
viewBody.append(statementText)
45+
viewBody.append(statement.trimmed.description)
4346
}
4447
}
4548

@@ -51,7 +54,7 @@ extension FunctionDeclSyntax {
5154
)
5255
}
5356

54-
var initializerComponents: [InitializerComponent] {
57+
func initializerComponents() -> [InitializerComponent] {
5558
let parameters = self.signature.parameterClause.parameters
5659
return parameters.map { param in
5760
let name = param.secondName?.text ?? param.firstName.text
@@ -81,11 +84,9 @@ public struct ViewComponentMacro: BodyMacro {
8184
fatalError("ViewComponentMacro can only be applied to function declarations.")
8285
}
8386

84-
// イニシャライザのコンポーネントを取得
85-
let initComponents = functionDecl.initializerComponents
87+
let initComponents = functionDecl.initializerComponents()
8688

87-
// ボディのコンポーネントを取得
88-
let components = functionDecl.extractBodyComponents()
89+
let bodyComponents = functionDecl.extractBodyComponents()
8990

9091
let initBlock = """
9192
init(\(initComponents.map { "\($0.name): \($0.type)" }.joined(separator: ", "))) {
@@ -95,22 +96,23 @@ public struct ViewComponentMacro: BodyMacro {
9596

9697
let bodyBlock = """
9798
var body: some View {
98-
\(components.viewBody.joined(separator: "\n").indented(1))
99+
\(bodyComponents.viewBody.joined(separator: "\n").indented(1))
99100
}
100101
"""
101-
102+
102103
let component = """
103104
struct Component: View {
104105
105-
\(initComponents.map { "let \($0.name): \($0.type.replacingOccurrences(of: "@escaping", with: "").trimmingCharacters(in: .whitespaces))" }.joined(separator: "\n").indented(1))
106+
\(bodyComponents.temporaryDeclarations.map { $0.trimmed.description }.joined(separator: "\n"))
106107
107-
\(components.stateDeclarations.joined(separator: "\n").indented(1))
108+
\(initComponents.filter { c in bodyComponents.temporaryDeclarations.contains { $0.bindings.contains { $0.pattern == c.name } } }.map { "let \($0.name): \($0.type.replacingOccurrences(of: "@escaping", with: "").trimmingCharacters(in: .whitespaces))" }.joined(separator: "\n").indented(1))
108109
109-
\(components.bindingDeclarations.joined(separator: "\n").indented(1))
110+
\(bodyComponents.stateDeclarations.map { $0.trimmed.description }.joined(separator: "\n").indented(1))
111+
112+
\(bodyComponents.bindingDeclarations.map { $0.trimmed.description }.joined(separator: "\n").indented(1))
110113
111114
\(initBlock.indented(1))
112115
113-
\(components.temporaryDeclarations.joined(separator: "\n"))
114116
115117
\(bodyBlock.indented(1))
116118
@@ -152,3 +154,7 @@ struct FunctionalViewComponentPlugin: CompilerPlugin {
152154
ViewComponentMacro.self
153155
]
154156
}
157+
158+
extension VariableDeclSyntax {
159+
160+
}

Tests/FunctionalViewComponentTests/FunctionalViewComponentTests.swift

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,59 @@ final class TrackingMacroTests: XCTestCase {
3434
}
3535
"""#
3636
} expansion: {
37-
"""
38-
"""
37+
#"""
38+
@MainActor
39+
func hoge(arg1: Int, arg2: String, arg3: @escaping () -> Void) -> some View {
40+
41+
struct Component: View {
42+
43+
44+
45+
let arg1: Int
46+
47+
let arg2: String
48+
49+
let arg3: () -> Void
50+
51+
@State var count: Int = 0
52+
53+
54+
55+
init(arg1: Int, arg2: String, arg3: @escaping () -> Void) {
56+
57+
self.arg1 = arg1
58+
59+
self.arg2 = arg2
60+
61+
self.arg3 = arg3
62+
63+
}
64+
65+
66+
67+
var body: some View {
68+
69+
VStack {
70+
71+
Text("\(count), \(arg1), \(arg2)")
72+
73+
Button("Click me") {
74+
75+
count += 1
76+
77+
}
78+
79+
}
80+
81+
}
82+
83+
84+
85+
}
86+
87+
return Component(arg1: arg1, arg2: arg2, arg3: arg3)
88+
}
89+
"""#
3990
}
4091

4192
}

0 commit comments

Comments
 (0)