@@ -1426,7 +1426,7 @@ defmodule Beacon.RuntimeRenderer do
14261426 end
14271427
14281428 # Recursively search for {:=, [], [{:dynamic, ...}, fn_def]}
1429- defp find_dynamic_fn ( { := , [ ] , [ { :dynamic , [ ] , _ } , fn_ast ] } ) , do: fn_ast
1429+ defp find_dynamic_fn ( { := , _ , [ { :dynamic , _ , _ } , fn_ast ] } ) , do: fn_ast
14301430
14311431 defp find_dynamic_fn ( { :__block__ , [ ] , children } ) when is_list ( children ) do
14321432 Enum . find_value ( children , fn child -> find_dynamic_fn ( child ) end )
@@ -1457,15 +1457,16 @@ defmodule Beacon.RuntimeRenderer do
14571457 # v1 = case ...
14581458 # [v0, v1, ...] (return list)
14591459 # end
1460- defp extract_dynamics ( { :fn , [ ] , [ { :-> , [ ] , [ [ _track_changes ] , body ] } ] } ) do
1461- { :__block__ , [ ] , [ _changed_setup | rest ] } = body
1460+ defp extract_dynamics ( { :fn , _ , [ { :-> , _ , [ [ _track_changes ] , body ] } ] } ) do
1461+ { :__block__ , _ , body_parts } = body
1462+ rest = drop_dynamic_setup ( body_parts )
14621463
14631464 { all_assigns , return_vars } =
14641465 case rest do
14651466 # No dynamic expressions (static template): empty block + empty list
1466- [ { :__block__ , [ ] , [ ] } , [ ] ] -> { [ ] , [ ] }
1467+ [ { :__block__ , _ , [ ] } , [ ] ] -> { [ ] , [ ] }
14671468 # Multiple dynamic expressions: __block__ wrapping assignments + return list
1468- [ { :__block__ , [ ] , assigns } , return_list ] when is_list ( assigns ) -> { assigns , return_list }
1469+ [ { :__block__ , _ , assigns } , return_list ] when is_list ( assigns ) -> { assigns , return_list }
14691470 # Single dynamic expression: one assignment + return list
14701471 [ { := , _ , _ } = single_assign , return_list ] -> { [ single_assign ] , return_list }
14711472 # Fallback
@@ -1498,6 +1499,16 @@ defmodule Beacon.RuntimeRenderer do
14981499 binding_irs ++ output_irs
14991500 end
15001501
1502+ defp drop_dynamic_setup ( [ { := , _ , [ { :changed , _ , Phoenix.LiveView.Engine } , _ ] } | rest ] ) do
1503+ drop_dynamic_setup ( rest )
1504+ end
1505+
1506+ defp drop_dynamic_setup ( [ { := , _ , [ { :vars_changed , _ , Phoenix.LiveView.Engine } , _ ] } | rest ] ) do
1507+ drop_dynamic_setup ( rest )
1508+ end
1509+
1510+ defp drop_dynamic_setup ( rest ) , do: rest
1511+
15011512 defp extract_one_dynamic ( { := , _meta , [ { _var , _ , _ } , case_expr ] } ) do
15021513 extract_case_expr ( case_expr )
15031514 end
@@ -1663,11 +1674,21 @@ defmodule Beacon.RuntimeRenderer do
16631674 transform_comprehension ( comp_struct , nil )
16641675 end
16651676
1677+ # Phoenix.LiveView.LiveStream.annotate_comprehension(struct, enum)
1678+ defp transform_expr ( { { :. , [ ] , [ { :__aliases__ , _ , [ :Phoenix , :LiveView , :LiveStream ] } , :annotate_comprehension ] } , [ ] , [ comp_struct , _enum ] } ) do
1679+ transform_comprehension ( comp_struct , nil )
1680+ end
1681+
16661682 # Phoenix.LiveView.Comprehension.__mark_consumable__(enum)
16671683 defp transform_expr ( { { :. , [ ] , [ { :__aliases__ , _ , [ :Phoenix , :LiveView , :Comprehension ] } , :__mark_consumable__ ] } , [ ] , [ enum_expr ] } ) do
16681684 transform_expr ( enum_expr )
16691685 end
16701686
1687+ # Phoenix.LiveView.LiveStream.mark_consumable(enum)
1688+ defp transform_expr ( { { :. , [ ] , [ { :__aliases__ , _ , [ :Phoenix , :LiveView , :LiveStream ] } , :mark_consumable ] } , [ ] , [ enum_expr ] } ) do
1689+ transform_expr ( enum_expr )
1690+ end
1691+
16711692 # List literal
16721693 defp transform_expr ( list ) when is_list ( list ) do
16731694 { :list , Enum . map ( list , & transform_expr / 1 ) }
@@ -1722,7 +1743,7 @@ defmodule Beacon.RuntimeRenderer do
17221743 # for comprehension expression
17231744 defp transform_expr ( { :for , _ , [ { :<- , _ , [ binding , enum_expr ] } | opts ] } ) do
17241745 var_name = extract_var_name ( binding )
1725- body = Keyword . get ( opts , :do , nil )
1746+ body = opts |> List . last ( ) |> Keyword . get ( :do , nil )
17261747 { :for_expr , var_name , transform_expr ( enum_expr ) , transform_expr ( body ) }
17271748 end
17281749
@@ -1920,6 +1941,9 @@ defmodule Beacon.RuntimeRenderer do
19201941 { := , [ ] , [ { :for , _ , _ } , { { :. , [ ] , [ { :__aliases__ , _ , [ :Phoenix , :LiveView , :Comprehension ] } , :__mark_consumable__ ] } , [ ] , [ enum_expr ] } ] } ->
19211942 transform_expr ( enum_expr )
19221943
1944+ { := , [ ] , [ { :for , _ , _ } , { { :. , [ ] , [ { :__aliases__ , _ , [ :Phoenix , :LiveView , :LiveStream ] } , :mark_consumable ] } , [ ] , [ enum_expr ] } ] } ->
1945+ transform_expr ( enum_expr )
1946+
19231947 _ ->
19241948 nil
19251949 end )
@@ -1928,6 +1952,9 @@ defmodule Beacon.RuntimeRenderer do
19281952 { { :. , [ ] , [ { :__aliases__ , _ , [ :Phoenix , :LiveView , :Comprehension ] } , :__annotate__ ] } , [ ] , [ comp , _ ] } ->
19291953 { :ok , transform_comprehension ( comp , enum_source ) }
19301954
1955+ { { :. , [ ] , [ { :__aliases__ , _ , [ :Phoenix , :LiveView , :LiveStream ] } , :annotate_comprehension ] } , [ ] , [ comp , _ ] } ->
1956+ { :ok , transform_comprehension ( comp , enum_source ) }
1957+
19311958 _ ->
19321959 nil
19331960 end )
@@ -1936,13 +1963,18 @@ defmodule Beacon.RuntimeRenderer do
19361963 defp transform_comprehension ( { :% , [ ] , [ _aliases , { :%{} , [ ] , fields } ] } , enum_source ) do
19371964 static = Keyword . fetch! ( fields , :static )
19381965 fingerprint = Keyword . fetch! ( fields , :fingerprint )
1939- dynamics_expr = Keyword . fetch! ( fields , :dynamics )
1966+ has_key? = Keyword . get ( fields , :has_key? , false )
1967+
1968+ dynamics_ir =
1969+ cond do
1970+ dynamics_expr = fields [ :dynamics ] ->
1971+ transform_for_dynamics ( dynamics_expr , enum_source )
19401972
1941- # The dynamics is a `for` comprehension. Extract var name and body,
1942- # but replace the enum with the actual source (from __mark_consumable__).
1943- dynamics_ir = transform_for_dynamics ( dynamics_expr , enum_source )
1973+ entries_expr = fields [ :entries ] ->
1974+ transform_for_entries ( entries_expr , enum_source )
1975+ end
19441976
1945- { :comprehension , % { static: static , fingerprint: fingerprint , dynamics: dynamics_ir } }
1977+ { :comprehension , % { static: static , fingerprint: fingerprint , dynamics: dynamics_ir , has_key?: has_key? } }
19461978 end
19471979
19481980 defp transform_for_dynamics ( { :for , _ , [ { :<- , _ , [ binding , _for_var ] } , [ do: body ] ] } , enum_source ) do
@@ -1952,6 +1984,26 @@ defmodule Beacon.RuntimeRenderer do
19521984
19531985 defp transform_for_dynamics ( other , _enum_source ) , do: transform_expr ( other )
19541986
1987+ defp transform_for_entries ( { :for , _ , [ { :<- , _ , [ binding , _for_var ] } | opts ] } , enum_source ) do
1988+ var_name = extract_var_name ( binding )
1989+ body = opts |> List . last ( ) |> Keyword . get ( :do , nil )
1990+ { :for_expr , var_name , enum_source || { :literal , [ ] } , transform_entry_body ( body ) }
1991+ end
1992+
1993+ defp transform_for_entries ( other , _enum_source ) , do: transform_expr ( other )
1994+
1995+ defp transform_entry_body ( { :{} , _ , [ _key_expr , _vars_map , { :fn , _ , [ { :-> , _ , [ _args , body ] } ] } ] } ) do
1996+ transform_entry_fn_body ( body )
1997+ end
1998+
1999+ defp transform_entry_body ( other ) , do: transform_for_body ( other )
2000+
2001+ defp transform_entry_fn_body ( { :__block__ , _meta , parts } ) do
2002+ parts |> List . last ( ) |> transform_for_body ( )
2003+ end
2004+
2005+ defp transform_entry_fn_body ( other ) , do: transform_for_body ( other )
2006+
19552007 # The for body is typically: __block__ [v0 = live_to_iodata(item), [v0]]
19562008 # We need to extract the actual expressions and return them as a list.
19572009 # Some for bodies also contain local variable assignments from <% var = expr %>.
@@ -2403,7 +2455,7 @@ defmodule Beacon.RuntimeRenderer do
24032455 end
24042456
24052457 # Comprehension: produces %Phoenix.LiveView.Comprehension{}
2406- defp eval_ir ( { :comprehension , % { static: static , fingerprint: fp , dynamics: dyn_expr } } , a , b ) do
2458+ defp eval_ir ( { :comprehension , % { static: static , fingerprint: fp , dynamics: dyn_expr } = meta } , a , b ) do
24072459 dynamics = eval_comprehension_dynamics ( dyn_expr , a , b )
24082460
24092461 # Convert dynamics (list of lists) to entries format for LiveView 1.1+
@@ -2414,7 +2466,7 @@ defmodule Beacon.RuntimeRenderer do
24142466
24152467 % Phoenix.LiveView.Comprehension {
24162468 static: static ,
2417- has_key?: false ,
2469+ has_key?: Map . get ( meta , :has_key? , false ) ,
24182470 entries: entries ,
24192471 fingerprint: fp ,
24202472 stream: nil
0 commit comments