@@ -27,23 +27,74 @@ class HookMacros(val c: Context) extends MacroUtils {
2727
2828 private implicit def autoTagToType [A ](t : c.WeakTypeTag [A ]): Type = t.tpe
2929
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"
3739
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+ // -------------------------------------------------------------------------------------------------------------------
4091
4192 def render [P , C <: Children , Ctx , CtxFn [_], Step <: SubsequentStep [Ctx , CtxFn ]]
4293 (f : c.Tree )(step : c.Tree , s : c.Tree )
4394 (implicit P : c.WeakTypeTag [P ], C : c.WeakTypeTag [C ]): c.Tree = {
4495
4596 implicit val log = MacroLogger ()
46- // log.enabled = showCode(c.macroApplication).contains("counter.value")
97+ log.enabled = showCode(c.macroApplication).contains(" DEBUG " ) // TODO: DELETE
4798 log.header()
4899 log(" macroApplication" , showRaw(c.macroApplication))
49100
@@ -103,72 +154,90 @@ class HookMacros(val c: Context) extends MacroUtils {
103154 Left (() => " Don't know how to parse " + showRaw(tree))
104155 }
105156
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 ] = {
110158 val it = h.steps.iterator
111- var stepId = 0
112159 var renderStep : HookStep = null
113- var hooks = List .empty[TermName ]
160+ var hooks = Vector .empty[HookRewriter => TermName ]
161+ var withPropsChildren = false
114162 while (it.hasNext) {
115163 val step = it.next()
116164 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+ }
122172 } else
123173 renderStep = step
124174 }
125- hooks = hooks.reverse
126175
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)
132182 }
133183 }
134184
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 ] = {
136186 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+
137196 step.name match {
197+
138198 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+ }
146219
147220 case _ =>
148221 Left (() => s " Inlining of hook method ' ${step.name}' not yet supported. " )
149222 }
150223 }
151224
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 ] = {
153226 log(" inlineHookRender." + step.name, step)
154227 step.name match {
155228 case " render" =>
156229 @ 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()))
161231
162232 case _ =>
163233 Left (() => s " Inlining of hook render method ' ${step.name}' not yet supported. " )
164234 }
165235 }
166236
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 " )
172241 q " (props => $render2): $JsFn.RawComponent[ ${Box (P )}] "
173242 }
174243
@@ -178,4 +247,35 @@ class HookMacros(val c: Context) extends MacroUtils {
178247 $ScalaFn.fromBoxed( $JsFn.fromJsFn[ ${Box (P )}, $C](rawComponent)( $summoner))
179248 """ )
180249 }
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+ }
181281}
0 commit comments