diff --git a/src/res_printer.ml b/src/res_printer.ml index 60066d09..f581cec6 100644 --- a/src/res_printer.ml +++ b/src/res_printer.ml @@ -113,6 +113,21 @@ let hasNestedJsxOrMoreThanOneChild expr = in loop false expr +let hasTailSingleLineComment tbl loc = + let rec getLastElement elements = + match elements with + | [] -> None + | [element] -> Some element + | _ :: rest -> getLastElement rest + in + match Hashtbl.find_opt tbl.CommentTable.trailing loc with + | None -> false + | Some comments -> ( + let lastComment = getLastElement comments in + match lastComment with + | None -> false + | Some comment -> Comment.isSingleLineComment comment) + let hasCommentsInside tbl loc = match Hashtbl.find_opt tbl.CommentTable.inside loc with | None -> false @@ -4041,8 +4056,20 @@ and printJsxExpression ~customLayout lident args cmtTbl = Pexp_construct ({txt = Longident.Lident "[]"}, None); } when isSelfClosing -> - Doc.concat [Doc.line; Doc.text "/>"] - | _ -> Doc.concat [Doc.softLine; Doc.greaterThan]); + Doc.text "/>" + | _ -> + (* if last trailing comment of tag is single line comment then put > on the next line + + + *) + if hasTailSingleLineComment cmtTbl lident.Asttypes.loc then + Doc.concat [Doc.softLine; Doc.greaterThan] + else + Doc.ifBreaks + (Doc.lineSuffix Doc.greaterThan) + Doc.greaterThan); ]); (if isSelfClosing then Doc.nil else @@ -4140,6 +4167,27 @@ and printJsxChildren ~customLayout (childrenExpr : Parsetree.expression) ~sep and printJsxProps ~customLayout args cmtTbl : Doc.t * Parsetree.expression option = + (* This function was introduced because we have different formatting behavior for self-closing tags and other tags + we always put /> on a new line for self-closing tag when it breaks + + + + + + we should remove this function once the format is unified + *) + let isSelfClosing children = + match children with + | { + Parsetree.pexp_desc = Pexp_construct ({txt = Longident.Lident "[]"}, None); + pexp_loc = loc; + } -> + not (hasCommentsInside cmtTbl loc) + | _ -> false + in let rec loop props args = match args with | [] -> (Doc.nil, None) @@ -4151,13 +4199,42 @@ and printJsxProps ~customLayout args cmtTbl : Pexp_construct ({txt = Longident.Lident "()"}, None); } ); ] -> + let doc = if isSelfClosing children then Doc.line else Doc.nil in + (doc, Some children) + | ((_, expr) as lastProp) + :: [ + (Asttypes.Labelled "children", children); + ( Asttypes.Nolabel, + { + Parsetree.pexp_desc = + Pexp_construct ({txt = Longident.Lident "()"}, None); + } ); + ] -> + let loc = + match expr.Parsetree.pexp_attributes with + | ({Location.txt = "ns.namedArgLoc"; loc}, _) :: _attrs -> + {loc with loc_end = expr.pexp_loc.loc_end} + | _ -> expr.pexp_loc + in + let tailSingleLineCommentPresent = hasTailSingleLineComment cmtTbl loc in + let propDoc = printJsxProp ~customLayout lastProp cmtTbl in let formattedProps = - Doc.indent - (match props with - | [] -> Doc.nil - | props -> - Doc.concat - [Doc.line; Doc.group (Doc.join ~sep:Doc.line (props |> List.rev))]) + Doc.concat + [ + Doc.indent + (Doc.concat + [ + Doc.line; + Doc.group + (Doc.join ~sep:Doc.line (propDoc :: props |> List.rev)); + ]); + (* print > on new line if last comment is single line comment *) + (match (isSelfClosing children, tailSingleLineCommentPresent) with + (* we always put /> on a new line when a self-closing tag breaks *) + | true, _ -> Doc.line + | false, true -> Doc.softLine + | false, false -> Doc.nil); + ] in (formattedProps, Some children) | arg :: args -> diff --git a/tests/conversion/reason/expected/bracedJsx.res.txt b/tests/conversion/reason/expected/bracedJsx.res.txt index 17d75e3d..b49e82fe 100644 --- a/tests/conversion/reason/expected/bracedJsx.res.txt +++ b/tests/conversion/reason/expected/bracedJsx.res.txt @@ -111,8 +111,7 @@ let make = () => {
(event->ReactEvent.Mouse.target)["querySelector"]("input")["focus"]()} - ref={containerRef->ReactDOMRe.Ref.domRef} - > + ref={containerRef->ReactDOMRe.Ref.domRef}> {state.history ->Array.mapWithIndex((index, item) =>
diff --git a/tests/printer/comments/expected/jsx.res.txt b/tests/printer/comments/expected/jsx.res.txt index 6bd7d439..8fbf20d4 100644 --- a/tests/printer/comments/expected/jsx.res.txt +++ b/tests/printer/comments/expected/jsx.res.txt @@ -31,6 +31,27 @@ module Cite = { + + + + + + + + + + + + + + + +
// Must not jump inside braces {React.string("Hello, World!")} diff --git a/tests/printer/comments/jsx.res b/tests/printer/comments/jsx.res index c7f70082..d554e249 100644 --- a/tests/printer/comments/jsx.res +++ b/tests/printer/comments/jsx.res @@ -33,6 +33,29 @@ value="" + + + + + + + + + + + + + + + +
// Must not jump inside braces {React.string("Hello, World!")} diff --git a/tests/printer/expr/expected/jsx.res.txt b/tests/printer/expr/expected/jsx.res.txt index 326cbe3c..bdc104ff 100644 --- a/tests/printer/expr/expected/jsx.res.txt +++ b/tests/printer/expr/expected/jsx.res.txt @@ -104,8 +104,7 @@ let avatarSection = onMouseLeave={_ => setHoveringAdmin(false)} onClick={_e => { stopImpersonating(csrfToken) - }} - > + }}>
: React.nullElement} diff --git a/tests/printer/other/expected/signaturePicker.res.txt b/tests/printer/other/expected/signaturePicker.res.txt index fe32b18e..f73d98b8 100644 --- a/tests/printer/other/expected/signaturePicker.res.txt +++ b/tests/printer/other/expected/signaturePicker.res.txt @@ -28,8 +28,7 @@ let make = () => {
{"Signature"->string}