Skip to content
This repository was archived by the owner on Jun 15, 2023. It is now read-only.

Implement jsx spread printing #368

Merged
merged 2 commits into from
Apr 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 51 additions & 28 deletions src/res_printer.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3834,7 +3834,11 @@ and printJsxExpression lident args cmtTbl =
let name = printJsxName lident in
let (formattedProps, children) = printJsxProps args cmtTbl in
(* <div className="test" /> *)
let isSelfClosing = match children with | [] -> true | _ -> false in
let isSelfClosing =
match children with
| Some ({Parsetree.pexp_desc = Pexp_construct ({txt = Longident.Lident "[]"}, None)}) -> true
| _ -> false
in
Doc.group (
Doc.concat [
Doc.group (
Expand All @@ -3851,7 +3855,10 @@ and printJsxExpression lident args cmtTbl =
Doc.indent (
Doc.concat [
Doc.line;
printJsxChildren children cmtTbl;
(match children with
| Some childrenExpression -> printJsxChildren childrenExpression cmtTbl
| None -> Doc.nil
);
]
);
Doc.line;
Expand All @@ -3865,17 +3872,17 @@ and printJsxExpression lident args cmtTbl =
and printJsxFragment expr cmtTbl =
let opening = Doc.text "<>" in
let closing = Doc.text "</>" in
let (children, _) = ParsetreeViewer.collectListExpressions expr in
(* let (children, _) = ParsetreeViewer.collectListExpressions expr in *)
Doc.group (
Doc.concat [
opening;
begin match children with
| [] -> Doc.nil
| children ->
begin match expr.pexp_desc with
| Pexp_construct ({txt = Longident.Lident "[]"}, None) -> Doc.nil
| _ ->
Doc.indent (
Doc.concat [
Doc.line;
printJsxChildren children cmtTbl;
printJsxChildren expr cmtTbl;
]
)
end;
Expand All @@ -3884,29 +3891,46 @@ and printJsxFragment expr cmtTbl =
]
)

and printJsxChildren (children: Parsetree.expression list) cmtTbl =
Doc.group (
Doc.join ~sep:Doc.line (
List.map (fun (expr : Parsetree.expression) ->
let leadingLineCommentPresent = hasLeadingLineComment cmtTbl expr.pexp_loc in
let exprDoc = printExpressionWithComments expr cmtTbl in
match Parens.jsxChildExpr expr with
| Parenthesized | Braced _ ->
(* {(20: int)} make sure that we also protect the expression inside *)
let innerDoc = if Parens.bracedExpr expr then addParens exprDoc else exprDoc in
if leadingLineCommentPresent then
addBraces innerDoc
else
Doc.concat [Doc.lbrace; innerDoc; Doc.rbrace]
| Nothing -> exprDoc
) children
and printJsxChildren (childrenExpr : Parsetree.expression) cmtTbl =
match childrenExpr.pexp_desc with
| Pexp_construct ({txt = Longident.Lident "::"}, _) ->
let (children, _) = ParsetreeViewer.collectListExpressions childrenExpr in
Doc.group (
Doc.join ~sep:Doc.line (
List.map (fun (expr : Parsetree.expression) ->
let leadingLineCommentPresent = hasLeadingLineComment cmtTbl expr.pexp_loc in
let exprDoc = printExpressionWithComments expr cmtTbl in
match Parens.jsxChildExpr expr with
| Parenthesized | Braced _ ->
(* {(20: int)} make sure that we also protect the expression inside *)
let innerDoc = if Parens.bracedExpr expr then addParens exprDoc else exprDoc in
if leadingLineCommentPresent then
addBraces innerDoc
else
Doc.concat [Doc.lbrace; innerDoc; Doc.rbrace]
| Nothing -> exprDoc
) children
)
)
)
| _ ->
let leadingLineCommentPresent = hasLeadingLineComment cmtTbl childrenExpr.pexp_loc in
let exprDoc = printExpressionWithComments childrenExpr cmtTbl in
Doc.concat [
Doc.dotdotdot;
match Parens.jsxChildExpr childrenExpr with
| Parenthesized | Braced _ ->
let innerDoc = if Parens.bracedExpr childrenExpr then addParens exprDoc else exprDoc in
if leadingLineCommentPresent then
addBraces innerDoc
else
Doc.concat [Doc.lbrace; innerDoc; Doc.rbrace]
| Nothing -> exprDoc
]

and printJsxProps args cmtTbl =
and printJsxProps args cmtTbl :(Doc.t * Parsetree.expression option) =
let rec loop props args =
match args with
| [] -> (Doc.nil, [])
| [] -> (Doc.nil, None)
| [
(Asttypes.Labelled "children", children);
(
Expand All @@ -3925,8 +3949,7 @@ and printJsxProps args cmtTbl =
)
]
) in
let (children, _) = ParsetreeViewer.collectListExpressions children in
(formattedProps, children)
(formattedProps, Some children)
| arg::args ->
let propDoc = printJsxProp arg cmtTbl in
loop (propDoc::props) args
Expand Down
8 changes: 8 additions & 0 deletions tests/conversion/reason/expected/jsxProps.re.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ let handleClick = (href, event) =>
@react.component
let make = (~href, ~className="", ~children) =>
<a href className onClick={event => handleClick(href, event)}> children </a>

<Animated> ...{x => <div />} </Animated>

<div> ...element </div>
<div> ...{a => 1} </div>
<div> ...<span /> </div>
<div> ...[a, b] </div>
<div> ...{(1, 2)} </div>
8 changes: 8 additions & 0 deletions tests/conversion/reason/jsxProps.re
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,11 @@ let make = (~href, ~className="", ~children) =>
<a href className onClick={event => handleClick(href, event)}>
children
</a>;

<Animated> ...{x => <div />} </Animated>;

<div> ...element </div>;
<div> ...{(a) => 1} </div>;
<div> ...<span /> </div>;
<div> ...[|a, b|] </div>;
<div> ...(1, 2) </div>;
12 changes: 11 additions & 1 deletion tests/parsing/grammar/expressions/expected/jsx.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -560,4 +560,14 @@ let _ =
~children:[((Js.log (a <= 10))
[@ns.braces ])] ())
[@JSX ])] ())
[@JSX ])] ())[@JSX ])
[@JSX ])] ())[@JSX ])
;;((div ~children:element ())[@JSX ])
;;((div ~children:((fun a -> 1)[@ns.braces ]) ())[@JSX ])
;;((div ~children:((span ~children:[] ())[@JSX ]) ())[@JSX ])
;;((div ~children:[|a|] ())[@JSX ])
;;((div ~children:(1, 2) ())[@JSX ])
;;(([element])[@JSX ])
;;(([(((fun a -> 1))[@ns.braces ])])[@JSX ])
;;(([((span ~children:[] ())[@JSX ])])[@JSX ])
;;(([[|a|]])[@JSX ])
;;(([(1, 2)])[@JSX ])
13 changes: 13 additions & 0 deletions tests/parsing/grammar/expressions/jsx.res
Original file line number Diff line number Diff line change
Expand Up @@ -500,3 +500,16 @@ let _ = <View style=styles["backgroundImageWrapper"]>
<div> {Js.log(a <= 10)} </div>
<div> <div> {Js.log(a <= 10)} </div> </div>
<div> <div onClick={_ => Js.log(a <= 10) }> <div> {Js.log(a <= 10)} </div> </div> </div>


<div> ...element </div>
<div> ...{(a) => 1} </div>
<div> ...<span /> </div>
<div> ...[a] </div>
<div> ...(1, 2) </div>

<> ...element </>
<> ...{(a) => 1} </>
<> ...<span /> </>
<> ...[a] </>
<> ...(1, 2) </>
60 changes: 60 additions & 0 deletions tests/printer/expr/expected/jsx.res.txt
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,63 @@ module App = {
}
}
</div>

<Animated> ...{x => <div />} </Animated>
<div> ...c </div>

<Animated initialValue=0.0 value>
...{ReactDOMRe.Style.make(
~width="20px",
~height="20px",
~borderRadius="100%",
~backgroundColor="red",
)}
</Animated>

<Animated initialValue=0.0 value>
...{value =>
<div
style={ReactDOMRe.Style.make(
~width="20px",
~height="20px",
~borderRadius="100%",
~backgroundColor="red",
)}
/>}
</Animated>

<Animated initialValue=0.0 value>
...{(value): ReasonReact.element =>
<div
style={ReactDOMRe.Style.make(
~width="20px",
~height="20px",
~borderRadius="100%",
~backgroundColor="red",
)}
/>}
</Animated>

<Animated initialValue=0.0 value>
...{value => {
let width = "20px"
let height = "20px"

<div
style={ReactDOMRe.Style.make(~width, ~height, ~borderRadius="100%", ~backgroundColor="red")}
/>
}}
</Animated>

let v =
<A>
<B>
...{_ => {
let renderX = x => {
let y = x ++ x
<div key=y />
}
renderX("foo")
}}
</B>
</A>
79 changes: 79 additions & 0 deletions tests/printer/expr/jsx.res
Original file line number Diff line number Diff line change
Expand Up @@ -316,3 +316,82 @@ module App = {
}
}
</div>

<Animated> ...{x => <div />} </Animated>
<div>...c</div>;

<Animated initialValue=0.0 value>
...{
ReactDOMRe.Style.make(
~width="20px",
~height="20px",
~borderRadius="100%",
~backgroundColor="red",
)
}
</Animated>;

<Animated initialValue=0.0 value>
...{
value =>
<div
style={
ReactDOMRe.Style.make(
~width="20px",
~height="20px",
~borderRadius="100%",
~backgroundColor="red",
)
}
/>
}
</Animated>

<Animated initialValue=0.0 value>
...{
(value) :ReasonReact.element =>
<div
style={
ReactDOMRe.Style.make(
~width="20px",
~height="20px",
~borderRadius="100%",
~backgroundColor="red",
)
}
/>
}
</Animated>;

<Animated initialValue=0.0 value>
...{value => {
let width = "20px"
let height = "20px"

<div
style={
ReactDOMRe.Style.make(
~width,
~height,
~borderRadius="100%",
~backgroundColor="red",
)
}
/>
}
}
</Animated>


let v =
<A>
<B>
...{_ => {
let renderX = x => {
let y = x ++ x;
<div key=y />;
}
renderX("foo")
}}
</B>
</A>