diff --git a/formatTest/typeCheckedTests/expected_output/attributes.re b/formatTest/typeCheckedTests/expected_output/attributes.re
index dac3648a8..2aee626b8 100644
--- a/formatTest/typeCheckedTests/expected_output/attributes.re
+++ b/formatTest/typeCheckedTests/expected_output/attributes.re
@@ -120,7 +120,11 @@ let x = [@attrEverything] (true && false);
/**
* How attribute parsings respond to other syntactic constructs.
*/
-let add = a => [@onRet] a;
+let add = a =>
+ [@onRet]
+ {
+ a;
+ };
let add = a => [@onRet] a;
@@ -382,8 +386,18 @@ type classAttributesOnKeys = {
.
[@bs.set] key1: string,
/* The follow two are the same */
- [@bs.get null] key2: [@onType2] Js.t(int),
- [@bs.get null] key3: [@onType2] Js.t(int),
+ [@bs.get
+ {
+ null;
+ }
+ ]
+ key2: [@onType2] Js.t(int),
+ [@bs.get
+ {
+ null;
+ }
+ ]
+ key3: [@onType2] Js.t(int),
key4: Js.t([@justOnInt] int),
};
@@ -533,7 +547,13 @@ let res =
let res =
switch (x) {
- | _ => [@attr] String.(Array.(concat))
+ | _ =>
+ [@attr]
+ {
+ open String;
+ open Array;
+ concat;
+ }
};
/* GADT */
@@ -602,10 +622,6 @@ type tttttt = {
mutable x: int,
};
-let tmp =
- /** On if statement */
- (if (true) {true} else {false});
-
type foo =
option(
[@foo
diff --git a/formatTest/typeCheckedTests/expected_output/braces.re b/formatTest/typeCheckedTests/expected_output/braces.re
new file mode 100644
index 000000000..17d14888a
--- /dev/null
+++ b/formatTest/typeCheckedTests/expected_output/braces.re
@@ -0,0 +1,181 @@
+let preserveThis = "preserveThis";
+
+let x = a => {
+ preserveThis;
+};
+
+let x = a => {
+ /* Even with comment */
+ preserveThis;
+};
+
+let res = {
+ /**
+ * doc comment.
+ */
+ (if (true) {()} else {()});
+ ();
+};
+
+let x = a => {
+ /* It would be okay for this comment to get formatted into
+ * the braces but ideally it wouldn't. */
+ preserveThis;
+};
+
+type recordType = {
+ recordTypeFieldOne: int,
+ recordTypeFieldTwo: string,
+};
+
+let x =
+ {
+ recordTypeFieldOne: 0,
+ recordTypeFieldTwo: "two",
+ }.recordTypeFieldOne;
+
+/**
+ * Use a pointless wrapping.
+ */
+let x =
+ {
+ {
+ recordTypeFieldOne: 0,
+ recordTypeFieldTwo: "two",
+ };
+ }.recordTypeFieldOne;
+
+let createRecord = () => {
+ recordTypeFieldOne: 0,
+ recordTypeFieldTwo: "two",
+};
+
+let x =
+ {
+ createRecord();
+ }.recordTypeFieldOne;
+
+let y = [@attr] 3;
+
+/* The attribute on the explicit braces should also be preserved */
+let y =
+ [@attr]
+ {
+ 3;
+ };
+
+let myFun = (. a, b, c) => a + b + c;
+
+let result = {
+ myFun(. 0, 1, 2);
+};
+
+let result =
+ [@attr]
+ {
+ myFun(. 0, 1, 2);
+ };
+
+let tmp =
+ /** On if statement */
+ {
+ if (true) {true} else {false};
+ };
+
+let tmp = {
+ let tmp = false;
+ /** On if statement */
+ (if (tmp) {true} else {false});
+};
+
+let tmp = {
+ let tmp = false;
+ /** On if statement */
+ (if (tmp) {"true"} else {"false"});
+ "but the if statement wasn't the last";
+};
+
+module Callbacks = {
+ let runThisCallback = (s, next) => {
+ s ++ next("!");
+ ();
+ };
+};
+
+let result =
+ Callbacks.runThisCallback("hi", s =>
+ s ++ "concatThis!"
+ );
+
+let result =
+ Callbacks.runThisCallback("hi", s => {
+ s ++ "!";
+ });
+
+Callbacks.runThisCallback("hi", s =>
+ s ++ "concatThis!"
+);
+
+Callbacks.runThisCallback("hi", s => {
+ s ++ "concatThis!";
+});
+
+Callbacks.runThisCallback("hi", s => {
+ let s = s ++ s;
+ s ++ "concatThis!";
+});
+
+let test = {
+ open Callbacks;
+ ();
+ 34;
+};
+
+/* Even though these braces aren't needed we will preserve them because they
+ * were requested */
+let test = {
+ open Callbacks;
+ open String;
+ 34;
+};
+
+/* You can have unecessary braces around inline opens even */
+let test = {
+ Callbacks.(String.(34));
+};
+
+/* Even though these braces aren't needed we will preserve them because they
+ * were requested */
+let test = {
+ open Callbacks;
+ String.("hello" ++ "!");
+};
+
+let test = {
+ open Callbacks;
+ [@attr] String.("hello" ++ "!");
+};
+
+/* Doesn't currently parse.
+ let test = {
+ [@attr1]
+ open Callbacks;
+ String.("hello" ++ "!");
+ };
+ */
+let test = {
+ Callbacks.(
+ {
+ let f = 0;
+ f;
+ }
+ );
+};
+
+let test =
+ Callbacks.(
+ {
+ let f = 0;
+ f;
+ }
+ );
diff --git a/formatTest/typeCheckedTests/expected_output/jsx.re b/formatTest/typeCheckedTests/expected_output/jsx.re
index 5a8acc968..3137d7dc4 100644
--- a/formatTest/typeCheckedTests/expected_output/jsx.re
+++ b/formatTest/typeCheckedTests/expected_output/jsx.re
@@ -101,29 +101,32 @@ module Namespace = {
};
module Optional1 = {
- let createElement = (~required, ~children, ()) =>
+ let createElement = (~required, ~children, ()) => {
switch (required) {
| Some(a) => {displayName: a}
| None => {displayName: "nope"}
};
+ };
};
module Optional2 = {
let createElement =
- (~optional=?, ~children, ()) =>
+ (~optional=?, ~children, ()) => {
switch (optional) {
| Some(a) => {displayName: a}
| None => {displayName: "nope"}
};
+ };
};
module DefaultArg = {
let createElement =
- (~default=Some("foo"), ~children, ()) =>
+ (~default=Some("foo"), ~children, ()) => {
switch (default) {
| Some(a) => {displayName: a}
| None => {displayName: "nope"}
};
+ };
};
module LotsOfArguments = {
@@ -174,8 +177,9 @@ let notReallyJSX = (~foo, ~bar, children) => {
displayName: "test",
};
-let fakeRender = (el: component) =>
+let fakeRender = (el: component) => {
el.displayName;
+};
/* end of setup */
let (/><) = (a, b) => a + b;
@@ -465,7 +469,7 @@ let div = (~children) => 1;
[@JSX] ((() => div)())(~children=[]);
-let myFun = () =>
+let myFun = () => {
<>
>;
+};
-let myFun = () => <> >;
+let myFun = () => {
+ <> >;
+};
-let myFun = () =>
+let myFun = () => {
<>
>;
+};
/**
* Children should wrap without forcing attributes to.
diff --git a/formatTest/typeCheckedTests/expected_output/oo.re b/formatTest/typeCheckedTests/expected_output/oo.re
index 821648ade..78f39a4c0 100644
--- a/formatTest/typeCheckedTests/expected_output/oo.re
+++ b/formatTest/typeCheckedTests/expected_output/oo.re
@@ -13,12 +13,18 @@ class virtual stack ('a) (init) = {
Some(hd);
| [] => None
};
- pub push = hd => v = [hd, ...v];
- initializer (
- print_string("initializing object")
- );
- pub explicitOverrideTest = a => a + 1;
- pri explicitOverrideTest2 = a => a + 1;
+ pub push = hd => {
+ v = [hd, ...v];
+ };
+ initializer {
+ print_string("initializing object");
+ };
+ pub explicitOverrideTest = a => {
+ a + 1;
+ };
+ pri explicitOverrideTest2 = a => {
+ a + 1;
+ };
};
let tmp = {
@@ -49,10 +55,12 @@ class virtual stackWithAttributes ('a) (init) = {
Some(hd);
| [] => None
};
- pub push = hd => v = [hd, ...v];
- initializer (
- print_string("initializing object")
- );
+ pub push = hd => {
+ v = [hd, ...v];
+ };
+ initializer {
+ print_string("initializing object");
+ };
};
class extendedStack ('a) (init) = {
@@ -66,9 +74,15 @@ class extendedStackAcknowledgeOverride
(init) = {
inherit (class stack('a))(init);
val dummy = ();
- pub implementMe = i => i + 1;
- pub! explicitOverrideTest = a => a + 2;
- pri! explicitOverrideTest2 = a => a + 2;
+ pub implementMe = i => {
+ i + 1;
+ };
+ pub! explicitOverrideTest = a => {
+ a + 2;
+ };
+ pri! explicitOverrideTest2 = a => {
+ a + 2;
+ };
};
let inst = (new extendedStack)([1, 2]);
@@ -134,8 +148,12 @@ let anonClosedObject: {
x: int,
y: int,
} = {
- pub x = 0;
- pub y = 0
+ pub x = {
+ 0;
+ };
+ pub y = {
+ 0;
+ }
};
let onlyHasX = {pub x = 0};
diff --git a/formatTest/typeCheckedTests/expected_output/reasonComments.re b/formatTest/typeCheckedTests/expected_output/reasonComments.re
index e92e6e2d7..fba79b9e5 100644
--- a/formatTest/typeCheckedTests/expected_output/reasonComments.re
+++ b/formatTest/typeCheckedTests/expected_output/reasonComments.re
@@ -96,8 +96,9 @@ let myFunction = /* First arg */
withFirstArg,
/* Second Arg */
andSecondArg,
- ) =>
- withFirstArg + andSecondArg; /* After Semi */
+ ) => {
+ withFirstArg + andSecondArg;
+}; /* After Semi */
type point = {
x: string, /* x field */
@@ -303,7 +304,11 @@ type intPair2 = (
int,
);
-let result = /**/ (2 + 3);
+let result =
+ /**/
+ {
+ 2 + 3;
+ };
/* This is not yet idempotent */
/* { */
@@ -336,7 +341,9 @@ let blahCurriedX = x =>
| Black(x) => 0 /* After black */
| Green(x) => 0; /* After second green */ /* On next line after blahCurriedX def */
-let name_equal = (x, y) => x == y;
+let name_equal = (x, y) => {
+ x == y;
+};
let equal = (i1, i2) =>
i1.contents === i2.contents && true; /* most unlikely first */
diff --git a/formatTest/typeCheckedTests/expected_output/sequences.re b/formatTest/typeCheckedTests/expected_output/sequences.re
index 18e466168..54f2e3743 100644
--- a/formatTest/typeCheckedTests/expected_output/sequences.re
+++ b/formatTest/typeCheckedTests/expected_output/sequences.re
@@ -23,9 +23,17 @@ let twenty = 20;
* printed in reduced form because sequences are a *parse* time construct.
* To ensure these are parsed correctly, adding to an integer.
*/
-let result = 0 + twenty;
-
-let result = 0 + twenty;
+let result =
+ 0
+ + {
+ twenty;
+ };
+
+let result =
+ 0
+ + {
+ twenty;
+ };
let result = 0 + twenty;
@@ -66,7 +74,9 @@ let cannotPunASingleFieldRecord = {
let fourty =
20 + cannotPunASingleFieldRecord.number;
-let thisIsASequenceNotPunedRecord = number;
+let thisIsASequenceNotPunedRecord = {
+ number;
+};
let fourty = 20 + thisIsASequenceNotPunedRecord;
diff --git a/formatTest/typeCheckedTests/expected_output/trailing.re b/formatTest/typeCheckedTests/expected_output/trailing.re
index 9f9fee544..3522c72fc 100644
--- a/formatTest/typeCheckedTests/expected_output/trailing.re
+++ b/formatTest/typeCheckedTests/expected_output/trailing.re
@@ -189,9 +189,9 @@ class virtual
];
pub virtual implementMe:
(int, int) => (int, int);
- initializer (
- print_string("initializing object")
- );
+ initializer {
+ print_string("initializing object");
+ };
};
class extendedStack
diff --git a/formatTest/typeCheckedTests/input/attributes.re b/formatTest/typeCheckedTests/input/attributes.re
index 366e86ca0..fce968b1f 100644
--- a/formatTest/typeCheckedTests/input/attributes.re
+++ b/formatTest/typeCheckedTests/input/attributes.re
@@ -459,16 +459,6 @@ type tttttt = {
mutable x: int
};
-
-let tmp = {
- /** On if statement */
- if (true) {
- true
- } else {
- false
- };
-};
-
type foo =
option(
[@foo ["how does this break", "when long enough"]] (
diff --git a/formatTest/typeCheckedTests/input/braces.re b/formatTest/typeCheckedTests/input/braces.re
new file mode 100644
index 000000000..fd599fab1
--- /dev/null
+++ b/formatTest/typeCheckedTests/input/braces.re
@@ -0,0 +1,181 @@
+
+
+let preserveThis = "preserveThis";
+
+let x = (a) => {
+ preserveThis;
+};
+
+let x = (a) => {
+ /* Even with comment */
+ preserveThis;
+};
+
+let res = {
+ /**
+ * doc comment.
+ */
+ if (true) {
+ ()
+ } else {
+ ()
+ };
+ ();
+};
+
+let x = (a) =>
+/* It would be okay for this comment to get formatted into
+ * the braces but ideally it wouldn't. */
+{
+ preserveThis;
+};
+
+type recordType = {
+ recordTypeFieldOne: int,
+ recordTypeFieldTwo: string
+};
+let x = {
+ recordTypeFieldOne: 0,
+ recordTypeFieldTwo: "two",
+}.recordTypeFieldOne;
+
+/**
+ * Use a pointless wrapping.
+ */
+let x = {
+ {
+ recordTypeFieldOne: 0,
+ recordTypeFieldTwo: "two",
+ }
+}.recordTypeFieldOne;
+
+let createRecord = () => {
+ recordTypeFieldOne: 0,
+ recordTypeFieldTwo: "two",
+};
+
+let x = {
+ createRecord();
+}.recordTypeFieldOne;
+
+
+let y = [@attr] 3;
+
+/* The attribute on the explicit braces should also be preserved */
+let y = [@attr] {
+ 3;
+};
+
+let myFun = (.a, b, c) => a + b + c;
+
+let result = {
+ myFun(. 0, 1, 2);
+};
+
+let result = [@attr] {
+ myFun(. 0, 1, 2);
+};
+
+let tmp = {
+ /** On if statement */
+ if (true) {
+ true
+ } else {
+ false
+ };
+};
+
+let tmp = {
+ let tmp = false;
+ /** On if statement */
+ if (tmp) {
+ true
+ } else {
+ false
+ };
+};
+
+let tmp = {
+ let tmp = false;
+ /** On if statement */
+ if (tmp) {
+ "true"
+ } else {
+ "false"
+ };
+ "but the if statement wasn't the last";
+};
+
+
+module Callbacks = {
+ let runThisCallback = (s, next) => {s ++ next("!"); ()};
+};
+
+let result = Callbacks.runThisCallback("hi", (s) => s ++ "concatThis!");
+let result = Callbacks.runThisCallback("hi", (s) => {
+ s ++ "!"
+});
+
+Callbacks.runThisCallback("hi", (s) => s ++ "concatThis!");
+Callbacks.runThisCallback("hi", (s) => {
+ s ++ "concatThis!"
+});
+
+Callbacks.runThisCallback("hi", (s) => {
+ let s = s ++ s;
+ s ++ "concatThis!"
+});
+
+
+let test = {
+ open Callbacks;
+ ();
+ 34;
+};
+
+/* Even though these braces aren't needed we will preserve them because they
+ * were requested */
+let test = {
+ open Callbacks;
+ open String;
+ 34;
+};
+
+/* You can have unecessary braces around inline opens even */
+let test = {
+ Callbacks.(String.(34));
+};
+
+/* Even though these braces aren't needed we will preserve them because they
+ * were requested */
+let test = {
+ open Callbacks;
+ String.("hello" ++ "!");
+};
+
+let test = {
+ open Callbacks;
+ [@attr]
+ String.("hello" ++ "!");
+};
+
+/* Doesn't currently parse.
+ let test = {
+ [@attr1]
+ open Callbacks;
+ String.("hello" ++ "!");
+ };
+*/
+
+let test = {
+ Callbacks.({
+ let f = 0;
+ f;
+ });
+};
+
+let test =
+ Callbacks.({
+ let f = 0;
+ f;
+ });
diff --git a/formatTest/unit_tests/expected_output/basicStructures.re b/formatTest/unit_tests/expected_output/basicStructures.re
index 064014ea8..0112a1418 100644
--- a/formatTest/unit_tests/expected_output/basicStructures.re
+++ b/formatTest/unit_tests/expected_output/basicStructures.re
@@ -1,6 +1,7 @@
/* Copyright (c) 2015-present, Facebook, Inc. All rights reserved. */
-let run = () =>
+let run = () => {
TestUtils.printSection("Basic Structures");
+};
while (something) {
print_string("You're in a while loop");
@@ -247,12 +248,14 @@ let result =
} else {
print_string("b >= a");
};
- } else if ((a, b) =>
- if (a > b) {
- print_string("b < a");
- } else {
- print_string("a <= b");
- }) {
+ } else if ({
+ (a, b) =>
+ if (a > b) {
+ print_string("b < a");
+ } else {
+ print_string("a <= b");
+ };
+ }) {
print_string(
"That could never possibly type check",
);
@@ -564,9 +567,13 @@ let myTuple: myTupleType = myTuple;
let myTuple: myTupleType = (one: int, two: int);
/* Now functions that accept a single argument being a tuple look familiar */
-let addValues = (a: int, b: int) => a + b;
+let addValues = (a: int, b: int) => {
+ a + b;
+};
-let addValues = (a: int, b: int) => a + b;
+let addValues = (a: int, b: int) => {
+ a + b;
+};
let myFunction = (a: int, b: int) : int =>
a + b;
diff --git a/formatTest/unit_tests/expected_output/extensions.re b/formatTest/unit_tests/expected_output/extensions.re
index 4b88e9ba1..189daf9b3 100644
--- a/formatTest/unit_tests/expected_output/extensions.re
+++ b/formatTest/unit_tests/expected_output/extensions.re
@@ -28,25 +28,37 @@ let x = {
| Some(x) => assert false
| None => ()
};
- try%extend (raise(Not_found)) {
+ try%extend (
+ {
+ raise(Not_found);
+ }
+ ) {
| Not_found => ()
| Invalid_argument(msg) => prerr_endline(msg)
};
};
-let x = if%extend (true) {1} else {2};
+let x = {
+ if%extend (true) {1} else {2};
+};
-let x =
+let x = {
switch%extend (None) {
| Some(x) => assert false
| None => ()
};
+};
-let x =
- try%extend (raise(Not_found)) {
+let x = {
+ try%extend (
+ {
+ raise(Not_found);
+ }
+ ) {
| Not_found => ()
| Invalid_argument(msg) => prerr_endline(msg)
};
+};
/* At structure level */
try%extend () {
@@ -104,41 +116,50 @@ let x =
| Some(1) => ();
/* With two extensions, alone */
-let x = [%extend1
+let x = {
+ %extend1
try%extend2 () {
| _ => ()
- }
-];
+ };
+};
-let x = [%extend1
+let x = {
+ %extend1
switch%extend2 () {
| _ => ()
- }
-];
+ };
+};
-let x = [%extend1
- if%extend2 (true) {1} else {2}
-];
+let x = {
+ %extend1
+ if%extend2 (true) {1} else {2};
+};
-let x = [%extend1
+let x = {
+ %extend1
for%extend2 (i in 1 to 10) {
();
- }
-];
+ };
+};
-let x = [%extend1
+let x = {
+ %extend1
while%extend2 (false) {
();
- }
-];
+ };
+};
-let x = [%extend1 fun%extend2 () => ()];
+let x = {
+ %extend1
+ fun%extend2 () => ();
+};
-let x = [%extend1
+let x = {
+ %extend1
fun%extend2
| None => ()
- | Some(1) => ()
-];
+ | Some(1) => ();
+};
/* With two extensions, first in sequence */
let x = {
diff --git a/formatTest/unit_tests/expected_output/infix.re b/formatTest/unit_tests/expected_output/infix.re
index f23599aee..ea1f27307 100644
--- a/formatTest/unit_tests/expected_output/infix.re
+++ b/formatTest/unit_tests/expected_output/infix.re
@@ -1075,14 +1075,15 @@ let server = {
body
|> Cohttp_lwt_body.to_string
>|= (
- body =>
+ body => {
Printf.sprintf(
"okokok",
uri,
meth,
headers,
body,
- )
+ );
+ }
)
>>= (
body =>
diff --git a/formatTest/unit_tests/expected_output/modules.re b/formatTest/unit_tests/expected_output/modules.re
index 11dbf9f74..263d4cc58 100644
--- a/formatTest/unit_tests/expected_output/modules.re
+++ b/formatTest/unit_tests/expected_output/modules.re
@@ -1,6 +1,7 @@
/* Copyright (c) 2015-present, Facebook, Inc. All rights reserved. */
-let run = () =>
+let run = () => {
TestUtils.printSection("Modules");
+};
/**
* Modules:
@@ -531,24 +532,52 @@ module M = {
module N = {
open M;
- let z = M.(34);
+ let z = {
+ open M;
+ 34;
+ };
let z = {
open M;
34;
35;
};
+ let z = {
+ open M;
+ (34, 35);
+ };
let z = M.(34, 35);
let z = M.(34, 35);
- let z = M.(34, 35);
- let z = M.{};
+ let z = {
+ open M;
+ {};
+ };
let z = M.{};
let z = M.{};
- let z = M.{x: 10};
- let z = M.[foo, bar];
- let z = M.[foo, bar];
- let z = M.{x: 10, y: 20};
- let z = M.(M2.(value));
- let z = M.(M2.value);
+ let z = {
+ open M;
+ {x: 10};
+ };
+ let z = {
+ open M;
+ [foo, bar];
+ };
+ let z = {
+ open M;
+ [foo, bar];
+ };
+ let z = {
+ open M;
+ {x: 10, y: 20};
+ };
+ let z = {
+ open M;
+ open M2;
+ value;
+ };
+ let z = {
+ open M;
+ M2.value;
+ };
let z = {
open! M;
34;
diff --git a/formatTest/unit_tests/expected_output/polymorphism.re b/formatTest/unit_tests/expected_output/polymorphism.re
index 462b697b0..95801eb28 100644
--- a/formatTest/unit_tests/expected_output/polymorphism.re
+++ b/formatTest/unit_tests/expected_output/polymorphism.re
@@ -1,6 +1,7 @@
/* Copyright (c) 2015-present, Facebook, Inc. All rights reserved. */
-let run = () =>
+let run = () => {
TestUtils.printSection("Polymorphism");
+};
type myType('a) = list('a);
diff --git a/formatTest/unit_tests/expected_output/syntax.re b/formatTest/unit_tests/expected_output/syntax.re
index b7bbcb77c..9e5ba1a90 100644
--- a/formatTest/unit_tests/expected_output/syntax.re
+++ b/formatTest/unit_tests/expected_output/syntax.re
@@ -411,9 +411,13 @@ type hasA = {a: int};
let a = 10;
let returnsASequenceExpressionWithASingleIdentifier =
- () => a;
+ () => {
+ a;
+};
-let thisReturnsA = () => a;
+let thisReturnsA = () => {
+ a;
+};
let thisReturnsAAsWell = () => a;
@@ -1048,8 +1052,9 @@ let match = "match";
let method = "method";
let foo =
- (x, ~x as bar, ~z, ~foo as bar, ~foo as z) =>
+ (x, ~x as bar, ~z, ~foo as bar, ~foo as z) => {
bar + 2;
+};
let zzz = myFunc(1, 2, [||]);
@@ -1201,17 +1206,17 @@ test(~desc=?[@attr] "my test", ~f=?[@attr] () => {
x + y;
});
-describe("App", () =>
- test("math", () =>
- Expect.expect(1 + 2) |> toBe(3)
- )
-);
+describe("App", () => {
+ test("math", () => {
+ Expect.expect(1 + 2) |> toBe(3);
+ });
+});
-describe([@attr] "App", [@attr] () =>
- test([@attr] "math", [@attr] () =>
- Expect.expect(1 + 2) |> toBe(3)
- )
-);
+describe([@attr] "App", [@attr] () => {
+ test([@attr] "math", [@attr] () => {
+ Expect.expect(1 + 2) |> toBe(3);
+ });
+});
describe(~text="App", ~f=() =>
test(~text="math", ~f=() =>
diff --git a/src/reason-parser/jbuild b/src/reason-parser/jbuild
index f2052971b..cac00e9f6 100644
--- a/src/reason-parser/jbuild
+++ b/src/reason-parser/jbuild
@@ -56,6 +56,7 @@
syntax_util
reason_comment
reason_layout
+ reason_attrs
reason_heuristics
reason_toolchain
reason_config
diff --git a/src/reason-parser/reason_attrs.ml b/src/reason-parser/reason_attrs.ml
new file mode 100644
index 000000000..17418810c
--- /dev/null
+++ b/src/reason-parser/reason_attrs.ml
@@ -0,0 +1,93 @@
+(**
+ * Kinds of attributes.
+ * TODO: Deprecate this in favor of `partition`.
+ *)
+open Ast_404.Parsetree
+open Ast_404.Asttypes
+type attributesPartition = {
+ arityAttrs : attributes;
+ docAttrs : attributes;
+ stdAttrs : attributes;
+ jsxAttrs : attributes;
+ refmtAttrs : attributes;
+ (* Sometimes these are printed, and sometimes not! *)
+ uncurriedAttrs : attributes;
+}
+
+
+let isArity = function
+ | ({txt="explicit_arity"; loc}, _)
+ | ({txt="implicit_arity"; loc}, _) -> true
+ | _ -> false
+
+let isJsx = function
+ | ({txt="JSX"; loc}, _) -> true
+ | _ -> false
+
+let isUncurried ?(bsIsUncurried=true) = function
+ | ({txt = "bs"}, PStr []) -> bsIsUncurried
+ | _ -> false
+
+let isRefmt ~filter attr =
+ match attr with
+ | (
+ {txt="refmt"; loc},
+ PStr [{pstr_desc=Pstr_eval({pexp_desc=Pexp_constant(Pconst_string(tag, None))}, _)}]
+ ) -> (
+ match filter with
+ | None -> true
+ | Some style -> String.compare tag style == 0
+ )
+ | _ -> false
+
+let isRefmtExplicitBraces = isRefmt ~filter:(Some "explicitBraces")
+
+let isRefmtInlineOpen = isRefmt ~filter:(Some "inlineOpen")
+
+let isStandard ?(bsIsUncurried=true) attr = not (
+ isArity attr ||
+ isJsx attr ||
+ isRefmt ~filter:None attr ||
+ isUncurried ~bsIsUncurried attr
+)
+
+(** Partition attributes into kinds:
+ NOTE: Use the better `partition` version which helps preserve
+ ordering by only pulling off the attributes you need.
+ bsIsUncurried: Whether or not uncurried attribute should be treated as a
+ special non "standard" attribute. Another valid name would have been
+ "considerUncurriedArgumentsSpecial" *)
+let rec partitionAttributes ?(bsIsUncurried=true) attrs : attributesPartition =
+ match attrs with
+ | [] ->
+ {arityAttrs=[]; docAttrs=[]; stdAttrs=[]; jsxAttrs=[]; refmtAttrs=[]; uncurriedAttrs=[]}
+ | attr::atTl when isUncurried ~bsIsUncurried attr ->
+ let partition = partitionAttributes atTl in
+ {partition with uncurriedAttrs=attr::partition.uncurriedAttrs}
+ | attr::atTl when isJsx attr ->
+ let partition = partitionAttributes atTl in
+ {partition with jsxAttrs=attr::partition.jsxAttrs}
+ | attr::atTl when (isRefmt ~filter:None) attr ->
+ let partition = partitionAttributes atTl in
+ {partition with refmtAttrs=attr::partition.refmtAttrs}
+ | attr::atTl when isArity attr ->
+ let partition = partitionAttributes atTl in
+ {partition with arityAttrs=attr::partition.arityAttrs}
+ (*| (({txt="ocaml.text"; loc}, _) as doc)::atTl
+ | (({txt="ocaml.doc"; loc}, _) as doc)::atTl ->
+ let partition = partitionAttributes atTl in
+ {partition with docAttrs=doc::partition.docAttrs}*)
+ | atHd::atTl ->
+ let partition = partitionAttributes atTl in
+ {partition with stdAttrs=atHd::partition.stdAttrs}
+
+(* Returns (selected, remaining) *)
+let rec partition fn attrs : attribute list * attribute list =
+ match attrs with
+ | [] -> ([], [])
+ | attr::atTl when fn attr ->
+ let (selectedRec, remainingRec) = partition fn atTl in
+ (attr::selectedRec, remainingRec)
+ | attr::atTl ->
+ let (selectedRec, remainingRec) = partition fn atTl in
+ (selectedRec, attr::remainingRec)
diff --git a/src/reason-parser/reason_heuristics.ml b/src/reason-parser/reason_heuristics.ml
index 018089563..8a18bfd3d 100644
--- a/src/reason-parser/reason_heuristics.ml
+++ b/src/reason-parser/reason_heuristics.ml
@@ -1,3 +1,4 @@
+
let is_punned_labelled_expression e lbl =
let open Ast_404.Parsetree in
match e.pexp_desc with
@@ -75,3 +76,25 @@ let funAppCallbackExceedsWidth ~printWidth ~args ~funExpr () =
* | X(y) =>
*)
let singleTokenPatternOmmitTrail txt = String.length txt < 4
+
+(* There may be other reasons why braces are rendered even if they weren't
+ * required. The user might have requested them! *)
+let rec astRequiresSequenceBraces expr =
+ let open Ast_404.Parsetree in
+ let open Ast_404.Asttypes in
+ match expr.pexp_desc with
+ | Pexp_let (rf, l, e) -> true
+ | Pexp_sequence _ -> true
+ | Pexp_letmodule (s, me, e) -> true
+ | Pexp_open (Fresh, lid, e) -> not (List.exists Reason_attrs.isRefmtInlineOpen expr.pexp_attributes)
+ | Pexp_open (Override, lid, e) -> true
+ | Pexp_letexception _ -> true
+ | _ -> false
+
+(* In the event they weren't required - did the user request braces *)
+let rec userRequestedSequenceBraces expr =
+ let open Ast_404.Parsetree in
+ let open Ast_404.Asttypes in
+ List.exists Reason_attrs.isRefmtExplicitBraces expr.pexp_attributes
+
+let requiresSequenceBracesBecauseUserRequested x = false
diff --git a/src/reason-parser/reason_parser.mly b/src/reason-parser/reason_parser.mly
index d270ba008..f791c658f 100644
--- a/src/reason-parser/reason_parser.mly
+++ b/src/reason-parser/reason_parser.mly
@@ -254,6 +254,15 @@ let mkcf ?(loc=dummy_loc()) ?(ghost=false) d =
let loc = set_loc_state ghost loc in
Cf.mk ~loc d
+(* used for storing stylistic information in the AST *)
+(* As if you had written [@refmt "txt"] *)
+let simple_ghost_refmt_text_attr ?(loc=dummy_loc ()) txt =
+ let loc = set_loc_state true loc in
+ (
+ {txt="refmt"; loc},
+ PStr [{pstr_desc=Pstr_eval(mkexp ~loc (Pexp_constant(Pconst_string(txt, None))), []); pstr_loc=loc}]
+ )
+
let simple_ghost_text_attr ?(loc=dummy_loc ()) txt =
let loc = set_loc_state true loc in
[({txt; loc}, PStr [])]
@@ -2314,8 +2323,17 @@ class_type_declaration_details:
*/
braced_expr:
mark_position_exp
- ( LBRACE seq_expr RBRACE
- { $2 }
+ ( LBRACE seq_expr RBRACE {
+ (* Among other expressions, a series of open statements without any
+ * let/letmodule/letexception/sequence does not actually need braces, although
+ * you may request them. *)
+ if Reason_heuristics.astRequiresSequenceBraces $2 then $2
+ else {
+ (* Braces weren't required - interpret this as an explicit request for them *)
+ $2 with
+ pexp_attributes=(simple_ghost_refmt_text_attr "explicitBraces") :: $2.pexp_attributes
+ }
+ }
| LBRACE as_loc(seq_expr) error
{ syntax_error_exp $2.loc "SyntaxError in block" }
| LBRACE DOTDOTDOT expr_optional_constraint COMMA? RBRACE
@@ -2840,7 +2858,10 @@ parenthesized_expr:
| E as_loc(POSTFIXOP)
{ mkexp(Pexp_apply(mkoperator $2, [Nolabel, $1])) }
| as_loc(mod_longident) DOT LPAREN expr_list RPAREN
- { mkexp(Pexp_open(Fresh, $1, may_tuple $startpos($3) $endpos($5) $4)) }
+ { mkexp
+ ~attrs:[simple_ghost_refmt_text_attr "inlineOpen"]
+ (Pexp_open(Fresh, $1, may_tuple $startpos($3) $endpos($5) $4))
+ }
| mod_longident DOT as_loc(LPAREN) expr_list as_loc(error)
{ unclosed_exp (with_txt $3 "(") (with_txt $5 ")") }
| E DOT as_loc(label_longident)
@@ -2848,8 +2869,9 @@ parenthesized_expr:
| as_loc(mod_longident) DOT LBRACE RBRACE
{ let loc = mklocation $symbolstartpos $endpos in
let pat = mkpat (Ppat_var (mkloc "this" loc)) in
- mkexp(Pexp_open (Fresh, $1,
- mkexp(Pexp_object(Cstr.mk pat []))))
+ mkexp
+ ~attrs:[simple_ghost_refmt_text_attr "inlineOpen"]
+ (Pexp_open (Fresh, $1, mkexp(Pexp_object(Cstr.mk pat []))))
}
| E LBRACKET expr RBRACKET
{ let loc = mklocation $symbolstartpos $endpos in
@@ -2873,14 +2895,18 @@ parenthesized_expr:
{ let (exten, fields) = $4 in
let loc = mklocation $symbolstartpos $endpos in
let rec_exp = mkexp ~loc (Pexp_record (fields, exten)) in
- mkexp(Pexp_open(Fresh, $1, rec_exp))
+ mkexp
+ ~attrs:[simple_ghost_refmt_text_attr "inlineOpen"]
+ (Pexp_open(Fresh, $1, rec_exp))
}
| mod_longident DOT as_loc(LBRACE) record_expr as_loc(error)
{ unclosed_exp (with_txt $3 "{") (with_txt $5 "}") }
| as_loc(mod_longident) DOT LBRACKETBAR expr_list BARRBRACKET
{ let loc = mklocation $symbolstartpos $endpos in
let rec_exp = Exp.mk ~loc ~attrs:[] (Pexp_array $4) in
- mkexp(Pexp_open(Fresh, $1, rec_exp))
+ mkexp
+ ~attrs:[simple_ghost_refmt_text_attr "inlineOpen"]
+ (Pexp_open(Fresh, $1, rec_exp))
}
| mod_longident DOT as_loc(LBRACKETBAR) expr_list as_loc(error)
{ unclosed_exp (with_txt $3 "[|") (with_txt $5 "|]") }
@@ -2889,7 +2915,9 @@ parenthesized_expr:
let loc = mklocation $startpos($4) $endpos($4) in
let list_exp = make_real_exp (mktailexp_extension loc seq ext_opt) in
let list_exp = { list_exp with pexp_loc = loc } in
- mkexp (Pexp_open (Fresh, $1, list_exp))
+ mkexp
+ ~attrs:[simple_ghost_refmt_text_attr "inlineOpen"]
+ (Pexp_open (Fresh, $1, list_exp))
}
| as_loc(PREFIXOP) E %prec below_DOT_AND_SHARP
{ mkexp(Pexp_apply(mkoperator $1, [Nolabel, $2])) }
@@ -2906,7 +2934,9 @@ parenthesized_expr:
| as_loc(mod_longident) DOT LBRACELESS field_expr_list COMMA? GREATERRBRACE
{ let loc = mklocation $symbolstartpos $endpos in
let exp = Exp.mk ~loc ~attrs:[] (Pexp_override $4) in
- mkexp (Pexp_open(Fresh, $1, exp))
+ mkexp
+ ~attrs:[simple_ghost_refmt_text_attr "inlineOpen"]
+ (Pexp_open(Fresh, $1, exp))
}
| mod_longident DOT as_loc(LBRACELESS) field_expr_list COMMA? as_loc(error)
{ unclosed_exp (with_txt $3 "{<") (with_txt $6 ">}") }
@@ -2916,9 +2946,11 @@ parenthesized_expr:
{ mkinfixop $1 (mkoperator $2) $3 }
| as_loc(mod_longident) DOT LPAREN MODULE module_expr COLON package_type RPAREN
{ let loc = mklocation $symbolstartpos $endpos in
- mkexp (Pexp_open(Fresh, $1,
- mkexp ~loc (Pexp_constraint (mkexp ~ghost:true ~loc (Pexp_pack $5),
- mktyp ~ghost:true ~loc (Ptyp_package $7)))))
+ mkexp
+ ~attrs:[simple_ghost_refmt_text_attr "inlineOpen"]
+ (Pexp_open(Fresh, $1,
+ mkexp ~loc (Pexp_constraint (mkexp ~ghost:true ~loc (Pexp_pack $5),
+ mktyp ~ghost:true ~loc (Ptyp_package $7)))))
}
| mod_longident DOT as_loc(LPAREN) MODULE module_expr COLON as_loc(error)
{ unclosed_exp (with_txt $3 "(") (with_txt $7 ")")}
diff --git a/src/reason-parser/reason_pprint_ast.ml b/src/reason-parser/reason_pprint_ast.ml
index b306bc5e5..ac4010ce1 100644
--- a/src/reason-parser/reason_pprint_ast.ml
+++ b/src/reason-parser/reason_pprint_ast.ml
@@ -279,44 +279,6 @@ let expandLocation pos ~expand:(startPos, endPos) =
}
}
-(** Kinds of attributes *)
-type attributesPartition = {
- arityAttrs : attributes;
- docAttrs : attributes;
- stdAttrs : attributes;
- jsxAttrs : attributes;
- uncurried : bool
-}
-
-(** Partition attributes into kinds *)
-let rec partitionAttributes ?(allowUncurry=true) attrs : attributesPartition =
- match attrs with
- | [] ->
- {arityAttrs=[]; docAttrs=[]; stdAttrs=[]; jsxAttrs=[]; uncurried = false}
- | (({txt = "bs"}, PStr []) as attr)::atTl ->
- let partition = partitionAttributes atTl in
- if allowUncurry then
- {partition with uncurried = true}
- else begin
- {partition with stdAttrs=attr::partition.stdAttrs}
- end
- | (({txt="JSX"; loc}, _) as jsx)::atTl ->
- let partition = partitionAttributes atTl in
- {partition with jsxAttrs=jsx::partition.jsxAttrs}
- | (({txt="explicit_arity"; loc}, _) as arity_attr)::atTl
- | (({txt="implicit_arity"; loc}, _) as arity_attr)::atTl ->
- let partition = partitionAttributes atTl in
- {partition with arityAttrs=arity_attr::partition.arityAttrs}
- (*| (({txt="ocaml.text"; loc}, _) as doc)::atTl
- | (({txt="ocaml.doc"; loc}, _) as doc)::atTl ->
- let partition = partitionAttributes atTl in
- {partition with docAttrs=doc::partition.docAttrs}*)
- | atHd::atTl ->
- let partition = partitionAttributes atTl in
- {partition with stdAttrs=atHd::partition.stdAttrs}
-
-let extractStdAttrs attrs =
- (partitionAttributes attrs).stdAttrs
let rec sequentialIfBlocks x =
match x with
@@ -2035,8 +1997,9 @@ let printer = object(self:'self)
method non_arrowed_core_type x = self#non_arrowed_non_simple_core_type x
method core_type2 x =
- let {stdAttrs; uncurried} = partitionAttributes x.ptyp_attributes in
- let uncurried = uncurried || try Hashtbl.find uncurriedTable x.ptyp_loc with | Not_found -> false in
+ let {Reason_attrs.stdAttrs; uncurriedAttrs;} = Reason_attrs.partitionAttributes x.ptyp_attributes in
+ let uncurried =
+ (uncurriedAttrs != []) || try Hashtbl.find uncurriedTable x.ptyp_loc with | Not_found -> false in
if stdAttrs <> [] then
formatAttributed
(self#non_arrowed_simple_core_type {x with ptyp_attributes = []})
@@ -2085,8 +2048,8 @@ let printer = object(self:'self)
(* Same as core_type2 but can be aliased *)
method core_type x =
- let {stdAttrs; uncurried} = partitionAttributes x.ptyp_attributes in
- let () = if uncurried then Hashtbl.add uncurriedTable x.ptyp_loc true in
+ let {Reason_attrs.stdAttrs; uncurriedAttrs} = Reason_attrs.partitionAttributes x.ptyp_attributes in
+ let () = if (uncurriedAttrs != []) then Hashtbl.add uncurriedTable x.ptyp_loc true in
if stdAttrs <> [] then
formatAttributed
(self#non_arrowed_simple_core_type {x with ptyp_attributes = []})
@@ -2314,7 +2277,7 @@ let printer = object(self:'self)
* not parsed or printed correctly. *)
method type_variant_leaf1 opt_ampersand polymorphic print_bar x =
let {pcd_name; pcd_args; pcd_res; pcd_loc; pcd_attributes} = x in
- let {stdAttrs} = partitionAttributes pcd_attributes in
+ let {Reason_attrs.stdAttrs} = Reason_attrs.partitionAttributes pcd_attributes in
let ampersand_helper i arg =
let ct = self#core_type arg in
let ct = match arg.ptyp_desc with
@@ -2566,7 +2529,7 @@ let printer = object(self:'self)
"hello", None.
*)
method non_arrowed_non_simple_core_type x =
- let {stdAttrs} = partitionAttributes x.ptyp_attributes in
+ let {Reason_attrs.stdAttrs} = Reason_attrs.partitionAttributes x.ptyp_attributes in
if stdAttrs <> [] then
formatAttributed
(self#non_arrowed_simple_core_type {x with ptyp_attributes=[]})
@@ -2578,7 +2541,7 @@ let printer = object(self:'self)
| _ -> self#non_arrowed_simple_core_type x
method non_arrowed_simple_core_type x =
- let {stdAttrs} = partitionAttributes x.ptyp_attributes in
+ let {Reason_attrs.stdAttrs} = Reason_attrs.partitionAttributes x.ptyp_attributes in
if stdAttrs <> [] then
formatSimpleAttributed
(self#non_arrowed_simple_core_type {x with ptyp_attributes=[]})
@@ -2742,8 +2705,7 @@ let printer = object(self:'self)
[left; right]
method pattern_without_or x =
- (* TODOATTRIBUTES: Handle the stdAttrs here *)
- let {arityAttrs} = partitionAttributes x.ppat_attributes in
+ let {Reason_attrs.arityAttrs} = Reason_attrs.partitionAttributes x.ppat_attributes in
match x.ppat_desc with
| Ppat_alias (p, s) ->
let raw_pattern = (self#pattern p) in
@@ -2784,7 +2746,7 @@ let printer = object(self:'self)
| _ -> self#simple_pattern x
method pattern x =
- let {arityAttrs; stdAttrs} = partitionAttributes x.ppat_attributes in
+ let {Reason_attrs.arityAttrs; stdAttrs} = Reason_attrs.partitionAttributes x.ppat_attributes in
if stdAttrs <> [] then
formatAttributed
(* Doesn't need to be simple_pattern because attributes are parse as
@@ -2814,7 +2776,7 @@ let printer = object(self:'self)
| _ -> self#pattern x
method simple_pattern x =
- let {arityAttrs; stdAttrs} = partitionAttributes x.ppat_attributes in
+ let {Reason_attrs.arityAttrs; stdAttrs} = Reason_attrs.partitionAttributes x.ppat_attributes in
if stdAttrs <> [] then
formatSimpleAttributed
(self#simple_pattern {x with ppat_attributes=arityAttrs})
@@ -2920,7 +2882,7 @@ let printer = object(self:'self)
method simple_get_application x =
- let {stdAttrs; jsxAttrs} = partitionAttributes x.pexp_attributes in
+ let {Reason_attrs.stdAttrs; jsxAttrs} = Reason_attrs.partitionAttributes x.pexp_attributes in
match (x.pexp_desc, stdAttrs, jsxAttrs) with
| (_, attrHd::attrTl, []) -> None (* Has some printed attributes - not simple *)
| (Pexp_apply ({pexp_desc=Pexp_ident loc}, l), [], _jsx::_) -> (
@@ -3149,8 +3111,9 @@ let printer = object(self:'self)
(* This method may not even be needed *)
method unparseUnattributedExpr x =
- match partitionAttributes x.pexp_attributes with
- | {docAttrs = []; stdAttrs = []; _} -> self#unparseExpr x
+ match Reason_attrs.partitionAttributes x.pexp_attributes with
+ (* Not sure the docAttrs is necessary here *)
+ | {Reason_attrs.docAttrs = []; stdAttrs = []; _} -> self#unparseExpr x
| _ -> makeList ~wrap:("(",")") [self#unparseExpr x]
(* ensureExpr ensures that the expression is wrapped in parens
@@ -3247,13 +3210,14 @@ let printer = object(self:'self)
method unparseExprRecurse x =
let x = self#process_underscore_application x in
(* If there are any attributes, render unary like `(~-) x [@ppx]`, and infix like `(+) x y [@attr]` *)
- let {arityAttrs; stdAttrs; jsxAttrs; uncurried} = partitionAttributes x.pexp_attributes in
- let () = if uncurried then Hashtbl.add uncurriedTable x.pexp_loc true in
- let x = {x with pexp_attributes = (arityAttrs @ stdAttrs @ jsxAttrs) } in
+ let {Reason_attrs.arityAttrs; stdAttrs; jsxAttrs; refmtAttrs; uncurriedAttrs} =
+ Reason_attrs.partitionAttributes x.pexp_attributes in
+ let () = if (uncurriedAttrs != []) then Hashtbl.add uncurriedTable x.pexp_loc true in
+ let x = {x with pexp_attributes = (refmtAttrs @ arityAttrs @ stdAttrs @ jsxAttrs) } in
(* If there's any attributes, recurse without them, then apply them to
the ends of functions, or simplify infix printings then append. *)
if stdAttrs <> [] then
- let withoutVisibleAttrs = {x with pexp_attributes=(arityAttrs @ jsxAttrs)} in
+ let withoutVisibleAttrs = {x with pexp_attributes=(refmtAttrs @ arityAttrs @ jsxAttrs)} in
let attributesAsList = (List.map self#attribute stdAttrs) in
let itms = match self#unparseExprRecurse withoutVisibleAttrs with
| SpecificInfixPrecedence ({reducePrecedence; shiftPrecedence}, wrappedRule) ->
@@ -3569,8 +3533,7 @@ let printer = object(self:'self)
match expression.pexp_desc with
| Pexp_ident ident when isPunnedJsxArg lbl ident -> atom lbl
| Pexp_apply ({pexp_desc=Pexp_ident loc}, l) when isJSXComponent loc l ->
- label (atom (lbl ^ "="))
- (makeList ~break:IfNeed ~wrap:("{", "}") [(self#simplifyUnparseExpr expression)])
+ label (atom (lbl ^ "=")) (makeLetSequenceSingleLine (self#letList expression))
| Pexp_record _
| Pexp_construct _
| Pexp_array _
@@ -4185,7 +4148,12 @@ let printer = object(self:'self)
(first :: List.map (self#binding "and") rest)
method letList expr =
- match (expr.pexp_attributes, expr.pexp_desc) with
+ (* bsIsUncurried should probably be false, as long as it is wherever
+ * calling into this *)
+ let (stdAttrs, remaining) =
+ Reason_attrs.partition (Reason_attrs.isStandard ~bsIsUncurried:true)
+ expr.pexp_attributes in
+ match (stdAttrs, expr.pexp_desc) with
| ([], Pexp_let (rf, l, e)) ->
(* For "letList" bindings, the start/end isn't as simple as with
* module value bindings. For "let lists", the sequences were formed
@@ -4196,7 +4164,7 @@ let printer = object(self:'self)
(source_map ~loc:bindingsLoc bindingsLayout :: self#letList e)
| ([], Pexp_open (ovf, lid, e))
(* Add this when check to make sure these are handled as regular "simple expressions" *)
- when not (self#isSeriesOfOpensFollowedByNonSequencyExpression expr) ->
+ when not (List.exists Reason_attrs.isRefmtInlineOpen expr.pexp_attributes) ->
let overrideStr = match ovf with | Override -> "!" | Fresh -> "" in
let openLayout = label ~space:true
(atom ("open" ^ overrideStr))
@@ -4239,6 +4207,12 @@ let printer = object(self:'self)
* deeply inspecting the leftmost token/term in the expression. *)
(source_map ~loc:e1.pexp_loc e1Layout :: self#letList e2)
| _ ->
+ (* letList is used to render something with braces ensured. Even if they weren't
+ required (such as with if/while loops. The result *will* be wrapped in braces.
+ Want to avoid rendering an extra set of braces here. *)
+ let (_explicitBracesAttrs, remaining) =
+ Reason_attrs.partition Reason_attrs.isRefmtExplicitBraces expr.pexp_attributes in
+ let expr = {expr with pexp_attributes=remaining} in
match expression_not_immediate_extension_sugar expr with
| Some (extension, {pexp_attributes = []; pexp_desc = Pexp_let (rf, l, e)}) ->
let bindingsLayout = self#bindings ~extension (rf, l) in
@@ -4418,7 +4392,7 @@ let printer = object(self:'self)
(* Expressions requiring parens, in most contexts such as separated by infix *)
method expression_requiring_parens_in_infix x =
- let {stdAttrs} = partitionAttributes x.pexp_attributes in
+ let {Reason_attrs.stdAttrs} = Reason_attrs.partitionAttributes x.pexp_attributes in
assert (stdAttrs == []);
let extension, x = expression_immediate_extension_sugar x in
match x.pexp_desc with
@@ -4679,9 +4653,14 @@ let printer = object(self:'self)
| UnaryPostfix _
| Infix _ -> self#simplifyUnparseExpr x
| Normal ->
- if x.pexp_attributes = [] then
+ (* Typically attributes are printed and make an expression not
+ simple, but explicit braces attributes don't contribute to the un-simpleness.
+ They don't help though. We want to ignore them when making the decision. *)
+ let (_explicitBracesAttrs, remaining) =
+ Reason_attrs.partition Reason_attrs.isRefmtExplicitBraces x.pexp_attributes in
(* `let a = foo().bar` instead of `let a = (foo()).bar *)
(* same for foo()##bar, foo()#=bar, etc. *)
+ if remaining == [] then
self#unparseExpr x
else
self#simplifyUnparseExpr x
@@ -4793,19 +4772,11 @@ let printer = object(self:'self)
~postSpace:true
allRows
- method isSeriesOfOpensFollowedByNonSequencyExpression expr =
- match (expr.pexp_attributes, expr.pexp_desc) with
- | ([], Pexp_let (rf, l, e)) -> false
- | ([], Pexp_sequence _) -> false
- | ([], Pexp_letmodule (s, me, e)) -> false
- | ([], Pexp_open (ovf, lid, e)) ->
- ovf == Fresh && self#isSeriesOfOpensFollowedByNonSequencyExpression e
- | ([], Pexp_letexception _) -> false
- | _ -> true
-
method unparseObject ?wrap:((lwrap,rwrap)=("", "")) ?(withStringKeys=false) l o =
let core_field_type (s, attrs, ct) =
- let l = extractStdAttrs attrs in
+ let (stdAttrs, remaining) =
+ Reason_attrs.partition (Reason_attrs.isStandard ~bsIsUncurried:true) attrs in
+ let l = stdAttrs in
let row =
let rowKey = if withStringKeys then
(makeList ~wrap:("\"", "\"") [atom s])
@@ -4883,9 +4854,14 @@ let printer = object(self:'self)
| _ -> assert false
method simplest_expression x =
- let {stdAttrs; jsxAttrs} = partitionAttributes x.pexp_attributes in
- if stdAttrs <> [] then
+ let hasStdAttrs =
+ List.exists (Reason_attrs.isStandard ~bsIsUncurried:true) x.pexp_attributes in
+ let sequenceBracesRequired = Reason_heuristics.astRequiresSequenceBraces x in
+ let sequenceBracesRequested = Reason_heuristics.userRequestedSequenceBraces x in
+ if hasStdAttrs then
None
+ else if (sequenceBracesRequested || sequenceBracesRequired) then
+ Some (makeLetSequence (self#letList x))
else
let item =
match x.pexp_desc with
@@ -4912,6 +4888,8 @@ let printer = object(self:'self)
(List.map string_x_expression l)
)
| Pexp_construct _ when is_simple_construct (view_expr x) ->
+ let (jsxAttrs, remaining) =
+ Reason_attrs.partition Reason_attrs.isJsx x.pexp_attributes in
let hasJsxAttribute = jsxAttrs != [] in
Some (
match view_expr x with
@@ -4981,6 +4959,7 @@ let printer = object(self:'self)
Some (self#unparseSequence ~construct:`Array l)
| Pexp_let _ | Pexp_sequence _
| Pexp_letmodule _ | Pexp_letexception _ ->
+ (* Would have already been handled by astRequiresSequenceBraces *)
Some (makeLetSequence (self#letList x))
| Pexp_extension e ->
begin match expression_immediate_extension_sugar x with
@@ -4997,8 +4976,7 @@ let printer = object(self:'self)
(self#reset#patternFunction ~extension x'.pexp_loc l))
| _ -> Some (self#extension e)
end
- | Pexp_open (ovf, lid, e) ->
- if self#isSeriesOfOpensFollowedByNonSequencyExpression x then
+ | Pexp_open (ovf, lid, e) -> (
(*
* Instead of printing:
* let result = { open Fmt; strf(foo);}
@@ -5020,10 +4998,9 @@ let printer = object(self:'self)
| _ -> makeList ~wrap:("(",")") ~break:IfNeed [self#unparseExpr e]
in
Some (label (label (self#longident_loc lid) (atom ("."))) expression)
- else
- Some (makeLetSequence (self#letList x))
+ )
| Pexp_field (e, li) ->
- Some (label (makeList [self#simple_enough_to_be_lhs_dot_send e; atom "."]) (self#longident_loc li))
+ Some (label ~indent:0 (self#simple_enough_to_be_lhs_dot_send e) (makeList([atom "."; self#longident_loc li])))
| Pexp_send (e, s) ->
let needparens = match e.pexp_desc with
| Pexp_apply (ee, _) ->
@@ -5127,13 +5104,15 @@ let printer = object(self:'self)
method attributes l = List.map self#attribute l
method attach_std_attrs l toThis =
- let l = extractStdAttrs l in
+ let (stdAttrs, _remaining) =
+ Reason_attrs.partition (Reason_attrs.isStandard ~bsIsUncurried:true) l in
+ let l = stdAttrs in
match l with
| [] -> toThis
| _::_ -> makeList ~postSpace:true (List.concat [self#attributes l; [toThis]])
- method attach_std_item_attrs ?(allowUncurry=true) ?extension l toThis =
- let l = (partitionAttributes ~allowUncurry l).stdAttrs in
+ method attach_std_item_attrs ?(bsIsUncurried=true) ?extension l toThis =
+ let l = (Reason_attrs.partitionAttributes ~bsIsUncurried l).stdAttrs in
match extension, l with
| None, [] -> toThis
| _, _ ->
@@ -5270,7 +5249,7 @@ let printer = object(self:'self)
label ~space:true (atom "as") (self#core_type ct) ::
instTypeFields
in
- self#attach_std_item_attrs ~allowUncurry:false x.pcty_attributes (
+ self#attach_std_item_attrs ~bsIsUncurried:false x.pcty_attributes (
makeList
~wrap:("{", "}")
~postSpace:true
@@ -5529,7 +5508,7 @@ let printer = object(self:'self)
source_map ~loc:p.ppat_loc field :: fields
method simple_class_expr x =
- let {stdAttrs} = partitionAttributes x.pcl_attributes in
+ let {Reason_attrs.stdAttrs} = Reason_attrs.partitionAttributes x.pcl_attributes in
if stdAttrs <> [] then
formatSimpleAttributed
(self#simple_class_expr {x with pcl_attributes=[]})
@@ -5574,7 +5553,7 @@ let printer = object(self:'self)
| _ -> [self#class_expr x]
method class_expr x =
- let {stdAttrs} = partitionAttributes x.pcl_attributes in
+ let {Reason_attrs.stdAttrs} = Reason_attrs.partitionAttributes x.pcl_attributes in
(* We cannot handle the attributes here. Must handle them in each item *)
if stdAttrs <> [] then
(* Do not need a "simple" attributes precedence wrapper. *)
@@ -5989,8 +5968,9 @@ let printer = object(self:'self)
let item = (
match term.pstr_desc with
| Pstr_eval (e, attrs) ->
- let {stdAttrs; jsxAttrs; uncurried} = partitionAttributes attrs in
- if uncurried then Hashtbl.add uncurriedTable e.pexp_loc true;
+ let {Reason_attrs.stdAttrs; jsxAttrs; uncurriedAttrs} =
+ Reason_attrs.partitionAttributes attrs in
+ if (uncurriedAttrs != []) then Hashtbl.add uncurriedTable e.pexp_loc true;
let layout = self#attach_std_item_attrs stdAttrs (self#unparseUnattributedExpr e) in
(* If there was a JSX attribute BUT JSX component wasn't detected,
that JSX attribute needs to be pretty printed so it doesn't get
@@ -6221,8 +6201,9 @@ let printer = object(self:'self)
method label_x_expression_param (l, e) =
let (uncurried, e) =
- let {uncurried; stdAttrs} = partitionAttributes e.pexp_attributes in
- if uncurried then
+ let {uncurriedAttrs; Reason_attrs.stdAttrs} =
+ Reason_attrs.partitionAttributes e.pexp_attributes in
+ if uncurriedAttrs != [] then
(true, {e with pexp_attributes = stdAttrs})
else (false, e)
in
@@ -6292,9 +6273,9 @@ let printer = object(self:'self)
* MyModuleBlah.toList(argument)
*)
let (argLbl, cb) = callbackArg in
- let {stdAttrs; uncurried} = partitionAttributes cb.pexp_attributes in
+ let {Reason_attrs.stdAttrs; uncurriedAttrs} = Reason_attrs.partitionAttributes cb.pexp_attributes in
let cbAttrs = stdAttrs in
- if uncurried then Hashtbl.add uncurriedTable cb.pexp_loc true;
+ if uncurriedAttrs != [] then Hashtbl.add uncurriedTable cb.pexp_loc true;
let (cbArgs, retCb) = self#curriedPatternsAndReturnVal {cb with pexp_attributes = []} in
let cbArgs = if List.length cbAttrs > 0 then
makeList ~break:IfNeed ~inline:(true, true) ~postSpace:true
@@ -6309,8 +6290,10 @@ let printer = object(self:'self)
source_map ~loc:funExpr.pexp_loc
(makeList ~wrap:("", "(") [self#simplifyUnparseExpr funExpr])
in
- let formattedFunAppl = begin match self#letList retCb with
- | [x] ->
+ let cbSequenceBracesRequired = Reason_heuristics.astRequiresSequenceBraces retCb in
+ let cbSequenceBracesRequested = Reason_heuristics.userRequestedSequenceBraces retCb in
+ let formattedFunAppl = begin match (cbSequenceBracesRequested || cbSequenceBracesRequired, self#letList retCb) with
+ | (false, [x]) ->
(* force breaks for test assertion style callbacks, e.g.
* describe("App", () => test("math", () => Expect.expect(1 + 2) |> toBe(3)));
* should always break for readability of the tests:
@@ -6335,7 +6318,7 @@ let printer = object(self:'self)
argsWithCallbackArgs)
in
label left returnValueCallback
- | xs ->
+ | (_, xs) ->
let printWidthExceeded = Reason_heuristics.funAppCallbackExceedsWidth ~printWidth:settings.width ~args ~funExpr () in
if printWidthExceeded = false then
(*
diff --git a/src/reason-parser/syntax_util.ml b/src/reason-parser/syntax_util.ml
index bd07037ec..6a6d1dabd 100644
--- a/src/reason-parser/syntax_util.ml
+++ b/src/reason-parser/syntax_util.ml
@@ -5,6 +5,18 @@ open Ast_mapper
open Parsetree
open Longident
+(*
+ UTF-8 characters are encoded like this (most editors are UTF-8)
+ 0xxxxxxx (length 1)
+ 110xxxxx 10xxxxxx (length 2)
+ 1110xxxx 10xxxxxx 10xxxxxx (length 3)
+ 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (length 4)
+
+ Numbers over 127 cannot be encoded in UTF in a single byte, so they use two
+ bytes. That means we can use any characters between 128-255 to encode special
+ characters that would never be written by the user and thus never be confused
+ for our special formatting characters.
+*)
(* Logic for handling special behavior that only happens if things break. We
use characters that will never appear in the printed output if actually
written in source code. The OCaml formatter will replace them with the escaped
@@ -20,6 +32,7 @@ module TrailingCommaMarker = struct
let char = Char.chr 249 (* ˘ *)
let string = String.make 1 char
end
+
module OpenBraceMarker = struct
(* An open brace marker will only be rendered if it is immediately
* followed by a newline. This should NOT BE USED WITH