Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#### :nail_care: Polish

- Avoid running `rescript-schema-ppx` and `sury-ppx` on source files without an `@schema` annotation. https://github.com/rescript-lang/rescript/pull/8662
- Format JSX expression children and standalone comments with braces to prepare for future literal text support. https://github.com/rescript-lang/rescript/pull/8666

#### :house: Internal

Expand Down
2 changes: 2 additions & 0 deletions compiler/syntax/JSX.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ rewrites those nodes as calls to the configured JSX module:
directly, which supports external components;
- a lowercase tag is emitted through the configured host-element module;
- fragments use the configured `jsxFragment` value;
- comment-only child containers are retained as comments by the parser and do
not contribute a child; bare `{}` still denotes an empty record expression;
- one child becomes a `children` prop and multiple children become an array;
- keyed elements select the keyed runtime entry point;
- at most one props spread is accepted, and it must precede explicit props.
Expand Down
85 changes: 31 additions & 54 deletions compiler/syntax/src/res_comments_table.ml
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ type node =
| CoreType of Parsetree.core_type
| ExprArgument of {expr: Parsetree.expression; loc: Location.t}
| Expression of Parsetree.expression
| JsxChild of Parsetree.expression
| ExprRecordRow of Longident.t Asttypes.loc * Parsetree.expression
| ExtensionConstructor of Parsetree.extension_constructor
| LabelDeclaration of Parsetree.label_declaration
Expand Down Expand Up @@ -448,7 +449,7 @@ let get_loc node =
}
| CoreType ct -> ct.ptyp_loc
| ExprArgument {loc} -> loc
| Expression e -> (
| Expression e | JsxChild e -> (
match e.pexp_attributes with
| ({txt = "res.braces" | "ns.braces"; loc}, _) :: _ -> loc
| _ -> e.pexp_loc)
Expand Down Expand Up @@ -640,6 +641,13 @@ and walk_node node tbl comments =
| CoreType ct -> walk_core_type ct tbl comments
| ExprArgument ea -> walk_expr_argument ea.expr ea.loc tbl comments
| Expression e -> walk_expression e tbl comments
| JsxChild e ->
(* Braces may contain comments outside the expression itself. Keep those
on the child, rather than moving them into record fields or call args. *)
let leading, inside, trailing = partition_by_loc comments e.pexp_loc in
attach tbl.leading e.pexp_loc leading;
walk_expression e tbl inside;
attach tbl.trailing e.pexp_loc trailing
| ExprRecordRow (ri, e) -> walk_expr_record_row (ri, e) tbl comments
| ExtensionConstructor ec -> walk_extension_constructor ec tbl comments
| LabelDeclaration ld -> walk_label_declaration ld tbl comments
Expand All @@ -658,23 +666,29 @@ and walk_node node tbl comments =
| ValueBinding vb -> walk_value_binding vb tbl comments
| JsxProp prop -> walk_jsx_prop prop tbl comments

and walk_list : ?prev_loc:Location.t -> node list -> t -> Comment.t list -> unit
=
fun ?prev_loc l t comments ->
and walk_list : ?prev_node:node -> node list -> t -> Comment.t list -> unit =
fun ?prev_node l t comments ->
match l with
| _ when comments = [] -> ()
| [] -> (
match prev_loc with
| Some loc -> attach t.trailing loc comments
match prev_node with
| Some node -> attach t.trailing (get_loc node) comments
| None -> ())
| node :: rest ->
let curr_loc = get_loc node in
let leading, inside, trailing = partition_by_loc comments curr_loc in
(match prev_loc with
(match prev_node with
| None ->
(* first node, all leading comments attach here *)
attach t.leading curr_loc leading
| Some prev_loc ->
| Some (JsxChild ({pexp_desc = Pexp_jsx_element _} as child))
when Res_parens.jsx_child_expr child = Nothing ->
(* Standalone containers put their comments on separate lines. Always
attach comments between bare JSX elements to the next child so the
first format and subsequent parses agree. *)
attach t.leading curr_loc leading
| Some prev_node ->
let prev_loc = get_loc prev_node in
(* Same line *)
if prev_loc.loc_end.pos_lnum == curr_loc.loc_start.pos_lnum then (
let after_prev, before_curr =
Expand All @@ -692,7 +706,7 @@ and walk_list : ?prev_loc:Location.t -> node list -> t -> Comment.t list -> unit
in
attach t.leading curr_loc leading);
walk_node node t inside;
walk_list ~prev_loc:curr_loc rest t trailing
walk_list ~prev_node:node rest t trailing

(* The parsetree doesn't always contain location info about the opening or
* closing token of a "list-of-things". This routine visits the whole list,
Expand Down Expand Up @@ -1599,15 +1613,14 @@ and walk_expression expr t comments =
| Pexp_jsx_element
(Jsx_fragment
{
jsx_fragment_opening = opening_greater_than;
jsx_fragment_opening = _opening_greater_than;
jsx_fragment_children = children;
jsx_fragment_closing = _closing_lesser_than;
}) ->
let opening_token = {expr.pexp_loc with loc_end = opening_greater_than} in
let on_same_line, rest = partition_by_on_same_line opening_token comments in
attach t.trailing opening_token on_same_line;
let xs = children |> List.map (fun e -> Expression e) in
walk_list xs t rest
}) -> (
match children with
| [] -> attach t.inside expr.pexp_loc comments
| children -> walk_list (List.map (fun e -> JsxChild e) children) t comments
)
| Pexp_jsx_element
(Jsx_unary_element
{
Expand Down Expand Up @@ -1696,12 +1709,6 @@ and walk_expression expr t comments =
rest
in

(* comments after '>' on the same line should be attached to '>' *)
let after_opening_greater_than, rest =
partition_by_on_same_line opening_greater_than_loc rest
in
attach t.trailing opening_greater_than_loc after_opening_greater_than;

let comments_for_children, _rest =
match closing_tag with
| None -> (rest, [])
Expand All @@ -1712,39 +1719,9 @@ and walk_expression expr t comments =
partition_leading_trailing rest closing_tag_loc
in
match children with
| [] -> (
(* attach all comments to the closing tag if there are no children *)
match closing_tag with
| None ->
(* if there is no closing tag, the comments will attached after the expression *)
()
| Some closing_tag ->
let closing_tag_loc =
Parsetree_viewer.container_element_closing_tag_loc closing_tag
in
if
opening_greater_than_loc.loc_end.pos_lnum
< closing_tag_loc.loc_start.pos_lnum + 1
then (
(* In this case, there are no children but there are comments between the opening and closing tag,
We can attach these the inside table, to easily print them later as indented comments
For example:
<div>
// comment 1
// comment 2
</div>
*)
let inside_comments, leading_for_closing_tag =
partition_between_lines opening_greater_than_loc.loc_end.pos_lnum
closing_tag_loc.loc_start.pos_lnum comments_for_children
in
attach t.inside expr.pexp_loc inside_comments;
attach t.leading closing_tag_loc leading_for_closing_tag)
else
(* if the closing tag is on the same line, attach comments to the opening tag *)
attach t.leading closing_tag_loc comments_for_children)
| [] -> attach t.inside expr.pexp_loc comments_for_children
| children ->
let children_nodes = List.map (fun e -> Expression e) children in
let children_nodes = List.map (fun e -> JsxChild e) children in

walk_list children_nodes t comments_for_children
(* It is less likely that there are comments inside the closing tag,
Expand Down
16 changes: 16 additions & 0 deletions compiler/syntax/src/res_core.ml
Original file line number Diff line number Diff line change
Expand Up @@ -2964,6 +2964,22 @@ and parse_jsx_children p : Parsetree.jsx_children =
parse_primary_expr ~operand:(parse_atomic_expr p) ~no_call:true p
in
loop p (child :: children)
| Lbrace when Parser.peek2 p = Rbrace ->
let start_pos = Parser.start_pos p in
Parser.next p;
let comments_before = p.comments in
Parser.next p;
(* Only comment-containing containers are trivia. Keep bare {} as an
empty record, including when nested inside an expression container. *)
if p.comments != comments_before then loop p children
else
let loc = mk_loc start_pos (Parser.position p) in
let child =
parse_primary_expr
~operand:(Ast_helper.Exp.record ~loc [] None)
~no_call:true p
in
loop p (child :: children)
| token when Grammar.is_jsx_child_start token ->
let child =
parse_primary_expr ~operand:(parse_atomic_expr p) ~no_call:true p
Expand Down
32 changes: 5 additions & 27 deletions compiler/syntax/src/res_parens.ml
Original file line number Diff line number Diff line change
Expand Up @@ -348,39 +348,17 @@ let jsx_child_expr expr =
match expr.Parsetree.pexp_desc with
| Parsetree.Pexp_let _ | Pexp_sequence _ | Pexp_letexception _
| Pexp_letmodule _ | Pexp_open _ ->
(* These expressions already print as braced blocks. *)
Nothing
| _ -> (
let opt_braces, _ = Parsetree_viewer.process_braces_attr expr in
match opt_braces with
| Some ({Location.loc = braces_loc}, _) -> Braced braces_loc
| _ -> (
match expr with
| {
Parsetree.pexp_desc =
Pexp_constant (Pconst_integer (x, _) | Pconst_float (x, _));
pexp_attributes = [];
}
when starts_with_minus x ->
Parenthesized
| _ when Parsetree_viewer.expr_is_await expr -> Parenthesized
| {
Parsetree.pexp_desc =
( Pexp_ident _ | Pexp_constant _ | Pexp_regexp _ | Pexp_field _
| Pexp_construct _ | Pexp_variant _ | Pexp_array _ | Pexp_pack _
| Pexp_record _ | Pexp_object_literal _ | Pexp_extension _
| Pexp_letmodule _ | Pexp_letexception _ | Pexp_open _
| Pexp_sequence _ | Pexp_let _ | Pexp_jsx_element _ );
pexp_attributes = [];
} ->
Nothing
| {
Parsetree.pexp_desc =
Pexp_constraint
({pexp_desc = Pexp_pack _}, {ptyp_desc = Ptyp_package _});
pexp_attributes = [];
} ->
Nothing
| {pexp_desc = Pexp_jsx_element _} -> Nothing
match expr.pexp_desc with
| Pexp_jsx_element _ -> Nothing
(* JSX child expressions use braces even when the legacy grammar accepts
them bare. Records need an outer pair around their own braces. *)
| _ -> Parenthesized))

let binary_expr expr =
Expand Down
3 changes: 3 additions & 0 deletions compiler/syntax/src/res_parens.mli
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ val field_expr : Parsetree.expression -> kind
val ternary_operand : Parsetree.expression -> kind

val jsx_prop_expr : Parsetree.expression -> kind

(* JSX children use braces for [Parenthesized]; [Nothing] is reserved for JSX
elements and expressions that already print as braced blocks. *)
val jsx_child_expr : Parsetree.expression -> kind

val binary_expr : Parsetree.expression -> kind
Expand Down
Loading
Loading