@@ -27,23 +27,74 @@ class HookMacros(val c: Context) extends MacroUtils {
27
27
28
28
private implicit def autoTagToType [A ](t : c.WeakTypeTag [A ]): Type = t.tpe
29
29
30
- private def Box : Tree = q " _root_.japgolly.scalajs.react.internal.Box "
31
- private def Box (t : Type ): Type = appliedType(c.typeOf[Box [_]], t)
32
- private def Hooks : Tree = q " _root_.japgolly.scalajs.react.hooks.Hooks "
33
- private def JsFn : Tree = q " _root_.japgolly.scalajs.react.component.JsFn "
34
- private def React : Tree = q " _root_.japgolly.scalajs.react.facade.React "
35
- private def ScalaFn : Tree = q " _root_.japgolly.scalajs.react.component.ScalaFn "
36
- private def withHooks = " withHooks"
30
+ private def Box : Tree = q " _root_.japgolly.scalajs.react.internal.Box "
31
+ private def Box (t : Type ) : Type = appliedType(c.typeOf[Box [_]], t)
32
+ private def HookCtx : Tree = q " _root_.japgolly.scalajs.react.hooks.HookCtx "
33
+ private def Hooks : Tree = q " _root_.japgolly.scalajs.react.hooks.Hooks "
34
+ private def JsFn : Tree = q " _root_.japgolly.scalajs.react.component.JsFn "
35
+ private def PropsChildren : Tree = q " _root_.japgolly.scalajs.react.PropsChildren "
36
+ private def React : Tree = q " _root_.japgolly.scalajs.react.facade.React "
37
+ private def ScalaFn : Tree = q " _root_.japgolly.scalajs.react.component.ScalaFn "
38
+ private def withHooks = " withHooks"
37
39
38
- case class HookDefn (steps : List [HookStep ])
39
- case class HookStep (name : String , targs : List [Tree ], args : List [List [Tree ]])
40
+ private case class HookDefn (steps : List [HookStep ])
41
+
42
+ private case class HookStep (name : String , targs : List [Tree ], args : List [List [Tree ]])
43
+
44
+ private class HookRewriter (props : Tree , initChildren : Tree , propsChildren : Tree ) {
45
+ private var stmts = Vector .empty[Tree ]
46
+ private var hooks = List .empty[Ident ]
47
+ private var _hookCount = 0
48
+ private var _usesChildren = false
49
+
50
+ def usesChildren () =
51
+ _usesChildren
52
+
53
+ def useChildren (): Unit = {
54
+ _usesChildren = true
55
+ this += initChildren
56
+ }
57
+
58
+ def += (stmt : Tree ): Unit =
59
+ stmts :+= stmt
60
+
61
+ def hookCount (): Int =
62
+ _hookCount
63
+
64
+ def nextHookName (suffix : String = " " ): TermName =
65
+ TermName (" hook" + (hookCount() + 1 ) + suffix)
66
+
67
+ def registerHook (h : TermName ): Unit = {
68
+ hooks :+= Ident (h)
69
+ _hookCount += 1
70
+ }
71
+
72
+ def args (): List [Tree ] =
73
+ if (usesChildren())
74
+ props :: propsChildren :: hooks
75
+ else
76
+ props :: hooks
77
+
78
+ def ctxArg (): Tree = {
79
+ val hookCtxObj = if (usesChildren()) q " $HookCtx.withChildren " else HookCtx
80
+ val create = Apply (hookCtxObj, args())
81
+ val name = nextHookName(" _ctx" )
82
+ this += q " val $name = $create"
83
+ Ident (name)
84
+ }
85
+
86
+ def wrap (body : Tree ): Tree =
87
+ q " .. $stmts; $body"
88
+ }
89
+
90
+ // -------------------------------------------------------------------------------------------------------------------
40
91
41
92
def render [P , C <: Children , Ctx , CtxFn [_], Step <: SubsequentStep [Ctx , CtxFn ]]
42
93
(f : c.Tree )(step : c.Tree , s : c.Tree )
43
94
(implicit P : c.WeakTypeTag [P ], C : c.WeakTypeTag [C ]): c.Tree = {
44
95
45
96
implicit val log = MacroLogger ()
46
- // log.enabled = showCode(c.macroApplication).contains("counter.value")
97
+ log.enabled = showCode(c.macroApplication).contains(" DEBUG " ) // TODO: DELETE
47
98
log.header()
48
99
log(" macroApplication" , showRaw(c.macroApplication))
49
100
@@ -103,72 +154,90 @@ class HookMacros(val c: Context) extends MacroUtils {
103
154
Left (() => " Don't know how to parse " + showRaw(tree))
104
155
}
105
156
106
- private type RenderInliner = (Tree , Init ) => Tree
107
-
108
- private def inlineHookDefn (h : HookDefn )(implicit log : MacroLogger ): Either [() => String , RenderInliner ] = {
109
- val init = new Init (" hook" + _, lazyVals = false )
157
+ private def inlineHookDefn (h : HookDefn )(implicit log : MacroLogger ): Either [() => String , HookRewriter => Tree ] = {
110
158
val it = h.steps.iterator
111
- var stepId = 0
112
159
var renderStep : HookStep = null
113
- var hooks = List .empty[TermName ]
160
+ var hooks = Vector .empty[HookRewriter => TermName ]
161
+ var withPropsChildren = false
114
162
while (it.hasNext) {
115
163
val step = it.next()
116
164
if (it.hasNext) {
117
- stepId += 1
118
- inlineHookStep(stepId, step, init) match {
119
- case Right (termName) => hooks ::= termName
120
- case Left (e) => return Left (e)
121
- }
165
+ if (hooks.isEmpty && step.name == " withPropsChildren" )
166
+ withPropsChildren = true
167
+ else
168
+ inlineHookStep(step) match {
169
+ case Right (h) => hooks :+= h
170
+ case Left (e) => return Left (e)
171
+ }
122
172
} else
123
173
renderStep = step
124
174
}
125
- hooks = hooks.reverse
126
175
127
- hookRenderInliner(renderStep, hooks.map(Ident (_))).map { f =>
128
- (props, init2) => {
129
- init2 ++= init.stmts
130
- f(props, init2)
131
- }
176
+ hookRenderInliner(renderStep).map { buildRender => b =>
177
+ if (withPropsChildren)
178
+ b.useChildren()
179
+ for (h <- hooks)
180
+ b registerHook h(b)
181
+ buildRender(b)
132
182
}
133
183
}
134
184
135
- private def inlineHookStep (stepId : Int , step : HookStep , init : Init )(implicit log : MacroLogger ): Either [() => String , TermName ] = {
185
+ private def inlineHookStep (step : HookStep )(implicit log : MacroLogger ): Either [() => String , HookRewriter => TermName ] = {
136
186
log(" inlineHookStep." + step.name, step)
187
+
188
+ def useState (b : HookRewriter , tpe : Tree , body : Tree ) = {
189
+ val rawName = b.nextHookName(" _raw" )
190
+ val name = b.nextHookName()
191
+ b += q " val $rawName = $React.useStateFn(() => $Box[ $tpe]( $body)) "
192
+ b += q " val $name = $Hooks.UseState.fromJsBoxed[ $tpe]( $rawName) "
193
+ name
194
+ }
195
+
137
196
step.name match {
197
+
138
198
case " useState" =>
139
- val stateType = step.targs.head
140
- val arg = step.args.head.head
141
- val rawName = TermName (" hook" + stepId + " _raw" )
142
- val name = TermName (" hook" + stepId)
143
- init += q " val $rawName = $React.useStateFn(() => $Box[ $stateType]( $arg)) "
144
- init += q " val $name = $Hooks.UseState.fromJsBoxed[ $stateType]( $rawName) "
145
- Right (name)
199
+ val targ = step.targs.head
200
+ val arg = step.args.head.head
201
+ Right (useState(_, targ, arg))
202
+
203
+ case " useStateBy" =>
204
+ val targ = step.targs.head
205
+ val arg = step.args.head.head
206
+ arg match {
207
+ case f@ Function (params, _) =>
208
+ if (params.sizeIs == 1 )
209
+ Right { b =>
210
+ val ctxArg = b.ctxArg()
211
+ useState(b, targ, call(f, ctxArg :: Nil ))
212
+ }
213
+ else
214
+ Right (b => useState(b, targ, call(f, b.args())))
215
+
216
+ case _ =>
217
+ Left (() => s " Expected a function, found: ${showRaw(arg)}" )
218
+ }
146
219
147
220
case _ =>
148
221
Left (() => s " Inlining of hook method ' ${step.name}' not yet supported. " )
149
222
}
150
223
}
151
224
152
- private def hookRenderInliner (step : HookStep , hooks : List [ Tree ] )(implicit log : MacroLogger ): Either [() => String , RenderInliner ] = {
225
+ private def hookRenderInliner (step : HookStep )(implicit log : MacroLogger ): Either [() => String , HookRewriter => Tree ] = {
153
226
log(" inlineHookRender." + step.name, step)
154
227
step.name match {
155
228
case " render" =>
156
229
@ nowarn(" msg=exhaustive" ) val List (List (renderFn), _) = step.args
157
- Right { (props, _) =>
158
- val args = props :: hooks
159
- Apply (Select (renderFn, TermName (" apply" )), args)
160
- }
230
+ Right (b => call(renderFn, b.args()))
161
231
162
232
case _ =>
163
233
Left (() => s " Inlining of hook render method ' ${step.name}' not yet supported. " )
164
234
}
165
235
}
166
236
167
- private def inlineHookRawComponent [P ](renderInliner : RenderInliner )(implicit P : c.WeakTypeTag [P ]): Tree = {
168
- val props_unbox = q " props.unbox "
169
- val init = new Init (" _i" + _)
170
- val render1 = renderInliner(props_unbox, init)
171
- val render2 = init.wrap(q " $render1.rawNode " )
237
+ private def inlineHookRawComponent [P ](rewrite : HookRewriter => Tree )(implicit P : c.WeakTypeTag [P ]): Tree = {
238
+ val b = new HookRewriter (q " props.unbox " , q " val children = $PropsChildren.fromRawProps(props) " , q " children " )
239
+ val render1 = rewrite(b)
240
+ val render2 = b.wrap(q " $render1.rawNode " )
172
241
q " (props => $render2): $JsFn.RawComponent[ ${Box (P )}] "
173
242
}
174
243
@@ -178,4 +247,35 @@ class HookMacros(val c: Context) extends MacroUtils {
178
247
$ScalaFn.fromBoxed( $JsFn.fromJsFn[ ${Box (P )}, $C](rawComponent)( $summoner))
179
248
""" )
180
249
}
250
+
251
+ // -------------------------------------------------------------------------------------------------------------------
252
+
253
+ private def call (function : Tree , args : List [Tree ]): Tree = {
254
+ import internal ._
255
+
256
+ function match {
257
+ case Function (params, body) =>
258
+
259
+ // From scala/test/files/run/macro-range/Common_1.scala
260
+ class TreeSubstituter (from : List [Symbol ], to : List [Tree ]) extends Transformer {
261
+ override def transform (tree : Tree ): Tree = tree match {
262
+ case Ident (_) =>
263
+ def subst (from : List [Symbol ], to : List [Tree ]): Tree =
264
+ if (from.isEmpty) tree
265
+ else if (tree.symbol == from.head) to.head.duplicate
266
+ else subst(from.tail, to.tail);
267
+ subst(from, to)
268
+ case _ =>
269
+ val tree1 = super .transform(tree)
270
+ if (tree1 ne tree) setType(tree1, null )
271
+ tree1
272
+ }
273
+ }
274
+ val t = new TreeSubstituter (params.map(_.symbol), args)
275
+ t.transform(body)
276
+
277
+ case _ =>
278
+ Apply (Select (function, TermName (" apply" )), args)
279
+ }
280
+ }
181
281
}
0 commit comments