diff --git a/formatTest/typeCheckedTests/expected_output/comments.rei b/formatTest/typeCheckedTests/expected_output/comments.rei index f309135bc..c93f6fcc1 100644 --- a/formatTest/typeCheckedTests/expected_output/comments.rei +++ b/formatTest/typeCheckedTests/expected_output/comments.rei @@ -34,13 +34,13 @@ /* comment ****/ /* comment *****/ /** - * Multiline - */; + * Multiline + */; /** Multiline - * - */; + * + */; /** - ** - */; + ** + */; diff --git a/formatTest/typeCheckedTests/expected_output/reasonLineComments.re b/formatTest/typeCheckedTests/expected_output/reasonLineComments.re new file mode 100644 index 000000000..c48c1c429 --- /dev/null +++ b/formatTest/typeCheckedTests/expected_output/reasonLineComments.re @@ -0,0 +1,734 @@ +3; // - + +3; //- + +3; //- + +3 /*-*/; + +// **** comment + +/*** comment */ +/** docstring */ +// comment +/** docstring */ +/*** comment */ +/**** comment */ +/***** comment */ +/** */ +/*** */ +/**** */ +/**/ +/***/ +/****/ +/** (** comment *) */ +/** (*** comment *) */ +// (** comment *) +// (*** comment *) +// *(*** comment *) +// comment * +// comment ** +// comment *** +// comment **** +/** + * Multiline + */ +/** Multiline + * + */ +/** + ** + */ +module JustString = { + include Map.Make(Int32); // Comment eol include +}; + +let testingEndOfLineComments = [ + "Item 1" /* Comment For First Item */, + "Item 2" /* Comment For Second Item */, + "Item 3" /* Comment For Third Item */, + "Item 4" /* Comment For Fourth Item - but before trailing comma */, + // Comment after last item in list. +] /* Comment after rbracket */; + +// But if you place them after the comma at eol, they're preserved as such +let testingEndOfLineComments = [ + "Item 1", // Comment For First Item + "Item 2", // Comment For Second Item + "Item 3", // Comment For Third Item + "Item 4" /* Comment For Fourth Item - but before trailing comma */, + // Comment after last item in list. +] /* Comment after rbracket */; + +// The space between ; and comment shoudn't matter +let testPlacementOfTrailingComment = [ + "Item 0" // + // Comment after last item in list. +]; // Comment after semi + +// The space between ; and comment shoudn't matter +let testPlacementOfTrailingComment = [ + "Item 0" // + // Comment after last item in list. +]; // Comment after semi + +// Try again but without other things in the list +let testPlacementOfTrailingComment = [ + "Item 0" // +]; // Comment after semi + +// The space between ; and comment shoudn't matter +let testPlacementOfTrailingComment = [ + "Item 0" // + // Comment after last item in list. +]; // Comment after semi + +let testingEndOfLineComments = []; // Comment after entire let binding + +// The following is not yet idempotent +// let myFunction +// withFirstArg // First arg +// andSecondArg => { // Second Arg +// withFirstArg + andSecondArg /* before semi */ ; +// }; +let myFunction = // First arg + ( + withFirstArg, + // Second Arg + andSecondArg, + ) => + withFirstArg + andSecondArg; // After Semi + +type point = { + x: string, // x field + y: string // y field +}; + +type pointWithManyKindsOfComments = { + // Line before x + x: string, // x field + // Line before y + y: string // y field + // Final row of record +}; + +type typeParamPointWithComments('a) = { + // Line before x + x: 'a, // x field + // Line before y + y: 'a // y field + // Final row of record +}; + +// Now, interleaving comments in type params +// Type name +type typeParamPointWithComments2 + // The a type param + ( + 'a, + // The b type apram + 'b, + ) = { + // Line before x + x: 'a, // x field + // Line before y + y: 'a // y field + // Final row of record +}; + +/* The way the last row comment is formatted is suboptimal becuase + * record type definitions do not include enough location information */ +type anotherpoint = { + x: string, // x field + y: string // y field + // comment as last row of record +}; + +type t = (int, int); // End of line on t + +type t2 = (int, int); // End of line on (int, int) + +type t3 = (int, int); // End of line on (int, int) + +type variant = + | X(int, int) // End of line on X + | Y(int, int); // End of line on Y // Comment on entire type def for variant + +// Before let +let res = + // Before switch + switch (X(2, 3)) { + // Above X line + | X(_) => "result of X" // End of arrow and X line + // Above Y line + | Y(_) => "result of Y" // End of arrow and Y line + }; // After final semi in switch + +let res = + switch (X(2, 3)) { + | X(0, 0) => + // After X arrow + "result of X" // End of X body line + | X(1, 0) /* Before X's arrow */ => "result of X" // End of X body line + | X(_) => + // After X _ arrow + "result of X" // End of X body line + // Above Y line + | Y(_) => + // Comment above Y body + "result of Y" + }; + +type variant2 = + // Comment above X + | X(int, int) // End of line on X + // Comment above Y + | Y(int, int); + +type variant3 = + // Comment above X + | X(int, int) // End of line on X + // Comment above Y + | Y(int, int); // End of line on Y + +type x = { + // not attached *above* x + fieldOne: int, + fieldA: int, +} // Attached end of line after x +and y = { + // not attached *above* y + fieldTwo: int, +}; // Attached end of line after y + +type x2 = { + // not attached *above* x2 + fieldOne: int, + fieldA: int, +} // Attached end of line after x2 +and y2 = { + // not attached *above* y2 + fieldTwo: int, +}; + +let result = + switch (None) { + | Some({fieldOne: 20, fieldA: a}) => + // Where does this comment go? + let tmp = 0; + 2 + tmp; + | Some({fieldOne: n, fieldA: a}) => + // How about this one + let tmp = n; + n + tmp; + | None => 20 + }; + +let res = + // Before switch + switch (X(2, 3)) { + // Above X line + | X(_) => "result of X" // End of arrow and X line + // Above Y line + | Y(_) => "result of Y" // End of arrow and Y line + }; + +/* + * Now these end of line comments *should* be retained. + */ +let result = + switch (None) { + | Some({ + fieldOne: 20, // end of line + fieldA: a // end of line + }) => + let tmp = 0; + 2 + tmp; + | Some({ + fieldOne: n, // end of line + fieldA: a // end of line + }) => + let tmp = n; + n + tmp; + | None => 20 + }; + +/* + * These end of line comments *should* be retained. + * To get the simple expression eol comment to be retained, we just need to + * implement label breaking eol behavior much like we did with sequences. + * Otherwise, right now they are not idempotent. + */ +let res = + switch ( + // Retain this + X( + 2, + 3, + ) + ) { + // Above X line + | X( + _, // retain this + _ // retain this + ) => "result of X" + // Above Y line + | Y(_) => "result of Y" // End of arrow and Y line + }; + +type optionalTuple = + | OptTup( + option( + ( + int, // First int + int // Second int + ), + ), + ); + +type optionTuple = + option( + ( + int, // First int + int // Second int + ), + ); + +type intPair = ( + int, // First int + int // Second int +); + +type intPair2 = ( + // First int + int, + // Second int + int, +); + +let result = /**/ (2 + 3); + +// This is not yet idempotent +// { +// /**/ +// (+) 2 3 +// }; +let a = (); + +for (i in 0 to 10) { + // bla + a; +}; + +if (true) { + // hello + () +}; + +type color = + | Red(int) // After red end of line + | Black(int) // After black end of line + | Green(int); // After green end of line // On next line after color type def + +let blahCurriedX = x => + fun + | Red(10) + | Black(20) + | Green(10) => 1 // After or pattern green + | Red(x) => 0 // After red + | 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 equal = (i1, i2) => + i1.contents === i2.contents && true; // most unlikely first + +let equal = (i1, i2) => + compare(compare(0, 0), compare(1, 1)); // END OF LINE HERE + +let tuple_equal = ((i1, i2)) => i1 == i2; + +let tuple_equal = ((csu, mgd)) => + // Some really long comments, see https://github.com/facebook/reason/issues/811 + tuple_equal((csu, mgd)); + +/** Comments inside empty function bodies + * See https://github.com/facebook/reason/issues/860 + */ +let fun_def_comment_inline = () => {/* */}; + +let fun_def_comment_newline = () => { + // +}; + +let fun_def_comment_long = () => { + /* longer comment inside empty function body */ +}; + +let trueThing = true; + +for (i in 0 to 1) { + // comment + print_newline(); +}; + +while (trueThing) { + // comment + print_newline(); +}; + +if (trueThing) { + // comment + print_newline(); +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); + // Comment before print + print_newline(); + // Comment after final print +} else { + // Comment before print + print_newline(); + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); + // Comment after final print +} else { + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before while test +while (trueThing) { + // Comment before print + print_newline(); + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before while test +while (trueThing) { + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before for test +for (i in 0 to 100) { + // Comment before print + print_newline(); + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before for test +for (i in 0 to 100) { + // Comment before print + print_newline(); + // Comment after final print +}; + +if (trueThing) { + // Comment before print + print_newline(); // eol print + // Comment before print + print_newline(); // eol print + // Comment after print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); // eol print + // Comment after print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); // eol print + // Comment before print + print_newline(); // eol print + // Comment after print +} else { + // Comment before print + print_newline(); // eol print + // Comment before print + print_newline(); // eol print + // Comment after print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); // eol print + // Comment before print +} else { + // Comment before print + print_newline(); // eol print + // Comment before print + print_newline(); // eol print + // Comment after print +}; + +// Comment before while test +while (trueThing) { + // Comment before print + print_newline(); // eol + // Comment before print + print_newline(); // eol + // Comment after final print +}; + +// Comment before while test +while (trueThing) { + // Comment before print + print_newline(); // eol + // Comment after final print +}; + +// Comment before for test +for (i in 0 to 100) { + // Comment before print + print_newline(); // eol + // Comment before print + print_newline(); // eol + // Comment after final print +}; + +// Comment before for test +for (i in 0 to 100) { + // Comment before print + print_newline(); // eol + // Comment after final print +}; + +let f = (a, b, c, d) => a + b + c + d; + +while (trueThing) { + f( + // a + 1, + // b + 2, + // c + 3, + // d + 4, + // does work + ); +}; + +while (trueThing) { + f( + // a + 1, + // b + 2, + // c + 3, + // d + 4 // does work + ); +}; + +ignore( + ( + _really, + _long, + _printWidth, + _exceeded, + _here, + ) => { + // First comment + let x = 0; + x + x; + // Closing comment +}); + +ignore((_xxx, _yyy) => { + // First comment + let x = 0; + x + x; + // Closing comment +}); + +type tester('a, 'b) = + | TwoArgsConstructor('a, 'b) + | OneTupleArgConstructor(('a, 'b)); + +let callFunctionTwoArgs = (a, b) => (); + +let callFunctionOneTuple = tuple => (); + +let y = + TwoArgsConstructor( + 1, //eol1 + 2 // eol2 + ); + +let y = + callFunctionTwoArgs( + 1, //eol1 + 2 // eol2 + ); + +let y = + OneTupleArgConstructor(( + 1, //eol1 + 2 // eol2 + )); + +let y = + callFunctionOneTuple(( + 1, //eol1 + 2 // eol2 + )); + +type polyRecord('a, 'b) = { + fieldOne: 'a, + fieldTwo: 'b, +}; + +let r = { + fieldOne: 1, //eol1 + fieldTwo: 2 // eol2 +}; + +let r = { + fieldOne: 1, //eol1 + fieldTwo: 2 // eol2 with trailing comma +}; + +let y = + TwoArgsConstructor( + "1", //eol1 + "2" // eol2 + ); + +let y = + callFunctionTwoArgs( + "1", //eol1 + "2" // eol2 + ); + +let y = + OneTupleArgConstructor(( + "1", //eol1 + "2" // eol2 + )); + +let y = + callFunctionOneTuple(( + "1", //eol1 + "2" // eol2 + )); + +let r = { + fieldOne: "1", //eol1 + fieldTwo: "2" // eol2 +}; + +let r = { + fieldOne: "1", //eol1 + fieldTwo: "2" // eol2 with trailing comma +}; + +let identifier = "hello"; + +let y = + TwoArgsConstructor( + identifier, //eol1 + identifier // eol2 + ); + +let y = + callFunctionTwoArgs( + identifier, //eol1 + identifier // eol2 + ); + +let y = + OneTupleArgConstructor(( + identifier, //eol1 + identifier // eol2 + )); + +let y = + callFunctionOneTuple(( + identifier, //eol1 + identifier // eol2 + )); + +let r = { + fieldOne: identifier, //eol1 + fieldTwo: identifier // eol2 +}; + +let r = { + fieldOne: identifier, //eol1 + fieldTwo: identifier // eol2 with trailing comma +}; + +let y = + TwoArgsConstructor( + identifier: string, //eol1 + identifier: string // eol2 + ); + +let y = + callFunctionTwoArgs( + identifier: string, //eol1 + identifier: string // eol2 + ); + +let y = + OneTupleArgConstructor(( + identifier: string, //eol1 + identifier: string // eol2 + )); + +let y = + callFunctionOneTuple(( + identifier: string, //eol1 + identifier: string // eol2 + )); + +let r = { + fieldOne: (identifier: string), //eol1 + fieldTwo: (identifier: string) // eol2 +}; + +let r = { + fieldOne: (identifier: string), //eol1 + fieldTwo: (identifier: string) // eol2 with trailing comma +}; diff --git a/formatTest/typeCheckedTests/expected_output/reasonLineComments.rei b/formatTest/typeCheckedTests/expected_output/reasonLineComments.rei new file mode 100644 index 000000000..c76133bf9 --- /dev/null +++ b/formatTest/typeCheckedTests/expected_output/reasonLineComments.rei @@ -0,0 +1,3 @@ +module JustString: { + include Map.S; // Comment eol include +}; diff --git a/formatTest/typeCheckedTests/input/reasonLineComments.re b/formatTest/typeCheckedTests/input/reasonLineComments.re new file mode 100644 index 000000000..4bca11833 --- /dev/null +++ b/formatTest/typeCheckedTests/input/reasonLineComments.re @@ -0,0 +1,713 @@ +3; // - +3 //- +; +3//- +; +3/*-*/; +// **** comment +/*** comment */ +/** docstring */ +// comment +/** docstring */ +/*** comment */ +/**** comment */ +/***** comment */ +/** */ +/*** */ +/**** */ +/**/ +/***/ +/****/ +/** (** comment *) */ +/** (*** comment *) */ +// (** comment *) +// (*** comment *) +// *(*** comment *) +// comment * +// comment ** +// comment *** +// comment **** +/** + * Multiline + */ +/** Multiline + * + */ +/** + ** + */ + +module JustString = { + include Map.Make(Int32); // Comment eol include +}; + +let testingEndOfLineComments = + [ + "Item 1" /* Comment For First Item */, + "Item 2" /* Comment For Second Item */, + "Item 3" /* Comment For Third Item */, + "Item 4" /* Comment For Fourth Item - but before trailing comma */, + // Comment after last item in list. + ] /* Comment after rbracket */; + +// But if you place them after the comma at eol, they're preserved as such +let testingEndOfLineComments = + [ + "Item 1", // Comment For First Item + "Item 2", // Comment For Second Item + "Item 3", // Comment For Third Item + "Item 4" /* Comment For Fourth Item - but before trailing comma */, + // Comment after last item in list. + ] /* Comment after rbracket */ ; + + +// The space between ; and comment shoudn't matter +let testPlacementOfTrailingComment = [ + "Item 0" // + // Comment after last item in list. +]; // Comment after semi + +// The space between ; and comment shoudn't matter +let testPlacementOfTrailingComment = [ + "Item 0" // + // Comment after last item in list. +];// Comment after semi + +// Try again but without other things in the list +let testPlacementOfTrailingComment = [ + "Item 0" // +]; // Comment after semi + +// The space between ; and comment shoudn't matter +let testPlacementOfTrailingComment = [ + "Item 0" // + // Comment after last item in list. +];// Comment after semi + +let testingEndOfLineComments = [];// Comment after entire let binding + + +// The following is not yet idempotent +// let myFunction +// withFirstArg // First arg +// andSecondArg => { // Second Arg +// withFirstArg + andSecondArg /* before semi */ ; +// }; + +let myFunction + (// First arg + withFirstArg, + // Second Arg + andSecondArg) { + withFirstArg + andSecondArg +}; // After Semi + +type point = { + x: string, // x field + y: string, // y field +}; + +type pointWithManyKindsOfComments = { + // Line before x + x: string, // x field + // Line before y + y: string, // y field + // Final row of record +}; + +type typeParamPointWithComments('a) = { + // Line before x + x: 'a, // x field + // Line before y + y: 'a // y field + // Final row of record +}; + +// Now, interleaving comments in type params +type + // Type name + typeParamPointWithComments2( + // The a type param + 'a, + // The b type apram + 'b) = { + // Line before x + x: 'a, // x field + // Line before y + y: 'a // y field + // Final row of record +}; + +/* The way the last row comment is formatted is suboptimal becuase + * record type definitions do not include enough location information */ +type anotherpoint = { + x: string, // x field + y: string, // y field + // comment as last row of record +}; + +type t = (int, int); // End of line on t +type t2 = + (int, int) // End of line on (int, int) + ; + +type t3 = + (int, int); // End of line on (int, int) + + +type variant = + | X (int, int) // End of line on X + | Y (int, int) // End of line on Y +; // Comment on entire type def for variant + +// Before let +let res = + // Before switch + switch (X (2, 3)) { + // Above X line + | X(_) => "result of X" // End of arrow and X line + // Above Y line + | Y(_) => "result of Y" // End of arrow and Y line + }; // After final semi in switch + +let res = + switch (X (2, 3)) { + | X (0, 0) => // After X arrow + "result of X" // End of X body line + | X (1, 0) /* Before X's arrow */ => + "result of X" // End of X body line + | X (_) => // After X _ arrow + "result of X" // End of X body line + // Above Y line + | Y (_) => + // Comment above Y body + "result of Y" + }; + +type variant2 = + // Comment above X + | X (int, int) // End of line on X + // Comment above Y + | Y (int, int); + +type variant3 = + // Comment above X + | X (int, int) // End of line on X + // Comment above Y + | Y (int, int) // End of line on Y +; + + +type x = { // not attached *above* x + fieldOne : int, + fieldA : int +} // Attached end of line after x +and y = { // not attached *above* y + fieldTwo : int +} // Attached end of line after y +; + +type x2 = { // not attached *above* x2 + fieldOne : int, + fieldA : int +} // Attached end of line after x2 +and y2 = { // not attached *above* y2 + fieldTwo : int +}; + + +let result = + switch (None) { + | Some({fieldOne: 20, fieldA:a})=> // Where does this comment go? + let tmp = 0; + 2 + tmp + | Some {fieldOne: n, fieldA:a} => + // How about this one + let tmp = n; + n + tmp + | None => 20 + }; + +let res = + // Before switch + switch (X (2, 3)) { + // Above X line + | X(_) => "result of X" // End of arrow and X line + // Above Y line + | Y(_) => "result of Y" // End of arrow and Y line + }; + +/* + * Now these end of line comments *should* be retained. + */ +let result = switch (None) { + | Some { + fieldOne: 20, // end of line + fieldA:a // end of line + } => + let tmp = 0; + 2 + tmp + | Some { + fieldOne: n, // end of line + fieldA:a // end of line + } => + let tmp = n; + n + tmp + | None => 20 + }; + +/* + * These end of line comments *should* be retained. + * To get the simple expression eol comment to be retained, we just need to + * implement label breaking eol behavior much like we did with sequences. + * Otherwise, right now they are not idempotent. + */ +let res = + switch ( // Retain this + X (2, 3) + ) + { + // Above X line + | X ( + _, // retain this + _ // retain this + ) => "result of X" + + // Above Y line + | Y(_) => "result of Y" // End of arrow and Y line + }; + + +type optionalTuple = + | OptTup ( + option (( + int, // First int + int // Second int + )) + ); + +type optionTuple = + option (( + int, // First int + int // Second int + )); + +type intPair = ( + int, // First int + int // Second int +); +type intPair2 = ( + // First int + int, + // Second int + int +); + +let result = { + /**/ + (+)(2,3) +}; + +// This is not yet idempotent +// { +// /**/ +// (+) 2 3 +// }; + +let a = (); +for (i in 0 to 10) { + // bla + a +}; + +if (true) { + // hello + () +}; + +type color = + | Red(int) // After red end of line + | Black(int) // After black end of line + | Green(int) // After green end of line +; // On next line after color type def + +let blahCurriedX(x) = + fun + | Red(10) + | Black(20) + | Green(10) => 1 // After or pattern green + | Red(x) => 0 // After red + | 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 equal(i1,i2) = + i1.contents === i2.contents && true; // most unlikely first + +let equal(i1,i2) = + compare(compare(0,0),compare(1,1)); // END OF LINE HERE + +let tuple_equal((i1, i2)) = i1 == i2; + +let tuple_equal((csu, mgd)) = + // Some really long comments, see https://github.com/facebook/reason/issues/811 + tuple_equal((csu, mgd)); + +/** Comments inside empty function bodies + * See https://github.com/facebook/reason/issues/860 + */ +let fun_def_comment_inline = () => { /* */ }; + +let fun_def_comment_newline = () => { + // +}; + +let fun_def_comment_long = () => { /* longer comment inside empty function body */}; + + +let trueThing = true; + +for (i in 0 to 1) { + // comment + print_newline(); +}; + +while (trueThing) { + // comment + print_newline(); +}; + +if (trueThing) { + // comment + print_newline() +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); + // Comment before print + print_newline(); + // Comment after final print +} else { + // Comment before print + print_newline(); + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); + // Comment after final print +} else { + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before while test +while (trueThing) { + // Comment before print + print_newline(); + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before while test +while (trueThing) { + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before for test +for (i in 0 to 100) { + // Comment before print + print_newline(); + // Comment before print + print_newline(); + // Comment after final print +}; + +// Comment before for test +for (i in 0 to 100) { + // Comment before print + print_newline(); + // Comment after final print +}; + + + +if (trueThing) { + // Comment before print + print_newline(); // eol print + // Comment before print + print_newline(); // eol print + // Comment after print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); // eol print + // Comment after print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); // eol print + // Comment before print + print_newline(); // eol print + // Comment after print +} else { + // Comment before print + print_newline(); // eol print + // Comment before print + print_newline(); // eol print + // Comment after print +}; + +// Comment before if test +if (trueThing) { + // Comment before print + print_newline(); // eol print + // Comment before print +} else { + // Comment before print + print_newline(); // eol print + // Comment before print + print_newline(); // eol print + // Comment after print +}; + +// Comment before while test +while (trueThing) { + // Comment before print + print_newline(); // eol + // Comment before print + print_newline(); // eol + // Comment after final print +}; + +// Comment before while test +while (trueThing) { + // Comment before print + print_newline(); // eol + // Comment after final print +}; + +// Comment before for test +for (i in 0 to 100) { + // Comment before print + print_newline(); // eol + // Comment before print + print_newline(); // eol + // Comment after final print +}; + +// Comment before for test +for (i in 0 to 100) { + // Comment before print + print_newline(); // eol + // Comment after final print +}; + + +let f = (a, b, c, d) => a + b + c + d; + +while(trueThing) { + f( + // a + 1, + // b + 2, + // c + 3, + // d + 4 + // does work + ); +}; +while(trueThing) { + f( + // a + 1, + // b + 2, + // c + 3, + // d + 4 // does work + ); +}; + +ignore((_really, _long, _printWidth, _exceeded, _here) => { + // First comment + let x = 0; + x + x; + // Closing comment +}); + +ignore((_xxx, _yyy) => { + // First comment + let x = 0; + x + x; + // Closing comment +}); + +type tester('a, 'b) = | TwoArgsConstructor('a, 'b) | OneTupleArgConstructor(('a, 'b)); +let callFunctionTwoArgs = (a, b) => (); +let callFunctionOneTuple = (tuple) => (); + +let y = TwoArgsConstructor( + 1, //eol1 + 2 // eol2 +); + +let y = callFunctionTwoArgs( + 1, //eol1 + 2 // eol2 +); + +let y = OneTupleArgConstructor(( + 1, //eol1 + 2 // eol2 +)); + +let y = callFunctionOneTuple(( + 1, //eol1 + 2 // eol2 +)); + + +type polyRecord('a, 'b) = {fieldOne: 'a, fieldTwo: 'b}; + +let r = { + fieldOne: 1, //eol1 + fieldTwo: 2 // eol2 +}; + +let r = { + fieldOne: 1, //eol1 + fieldTwo: 2, // eol2 with trailing comma +}; + + +let y = TwoArgsConstructor( + "1", //eol1 + "2" // eol2 +); + +let y = callFunctionTwoArgs( + "1", //eol1 + "2" // eol2 +); + +let y = OneTupleArgConstructor(( + "1", //eol1 + "2" // eol2 +)); + +let y = callFunctionOneTuple(( + "1", //eol1 + "2" // eol2 +)); + + +let r = { + fieldOne: "1", //eol1 + fieldTwo: "2" // eol2 +}; + +let r = { + fieldOne: "1", //eol1 + fieldTwo: "2", // eol2 with trailing comma +}; + +let identifier = "hello"; + +let y = TwoArgsConstructor( + identifier, //eol1 + identifier // eol2 +); + +let y = callFunctionTwoArgs( + identifier , //eol1 + identifier // eol2 +); + +let y = OneTupleArgConstructor(( + identifier , //eol1 + identifier // eol2 +)); + +let y = callFunctionOneTuple(( + identifier , //eol1 + identifier // eol2 +)); + + +let r = { + fieldOne: identifier, //eol1 + fieldTwo: identifier // eol2 +}; + +let r = { + fieldOne: identifier, //eol1 + fieldTwo: identifier, // eol2 with trailing comma +}; + + +let y = TwoArgsConstructor( + identifier : string, //eol1 + identifier : string// eol2 +); + +let y = callFunctionTwoArgs( + identifier : string , //eol1 + identifier : string // eol2 +); + +let y = OneTupleArgConstructor(( + identifier : string , //eol1 + identifier : string // eol2 +)); + +let y = callFunctionOneTuple(( + identifier : string , //eol1 + identifier : string // eol2 +)); + + +let r = { + fieldOne: (identifier : string), //eol1 + fieldTwo: (identifier : string) // eol2 +}; + +let r = { + fieldOne: (identifier : string), //eol1 + fieldTwo: (identifier : string), // eol2 with trailing comma +}; diff --git a/formatTest/typeCheckedTests/input/reasonLineComments.rei b/formatTest/typeCheckedTests/input/reasonLineComments.rei new file mode 100644 index 000000000..087516744 --- /dev/null +++ b/formatTest/typeCheckedTests/input/reasonLineComments.rei @@ -0,0 +1,3 @@ +module JustString : { + include Map.S; // Comment eol include +}; diff --git a/formatTest/unit_tests/expected_output/basicStructures.re b/formatTest/unit_tests/expected_output/basicStructures.re index 064014ea8..8f8fb07fa 100644 --- a/formatTest/unit_tests/expected_output/basicStructures.re +++ b/formatTest/unit_tests/expected_output/basicStructures.re @@ -155,8 +155,6 @@ let x = arr^[0] = 1; *============================================================================ */; -let (/\/) = (+); /* // is not a comment */ - let something = if (self.ext.logSuccess) { print_string("Did tap"); diff --git a/formatTest/unit_tests/expected_output/infix.re b/formatTest/unit_tests/expected_output/infix.re index f23599aee..2f39d16c2 100644 --- a/formatTest/unit_tests/expected_output/infix.re +++ b/formatTest/unit_tests/expected_output/infix.re @@ -588,7 +588,8 @@ let containingObject = { x + (something := y); /** * Try with || - */ x.contents + */ + x.contents || something + 1 ? hello : goodbye; @@ -613,7 +614,8 @@ let containingObject = { str.[0] || (something + 1 ? hello : goodbye); /** * Try with && - */ x.contents + */ + x.contents && something + 1 ? hello : goodbye; diff --git a/formatTest/unit_tests/expected_output/syntax.re b/formatTest/unit_tests/expected_output/syntax.re index b7bbcb77c..c410b0c77 100644 --- a/formatTest/unit_tests/expected_output/syntax.re +++ b/formatTest/unit_tests/expected_output/syntax.re @@ -736,7 +736,7 @@ type yourThing = myOtherThing(int, int); */ /** Current OCaml Named Arguments. Any aliasing is more than just aliasing! -OCaml allows full on pattern matching of named args. */ + OCaml allows full on pattern matching of named args. */ /* A: let named ~a ~b = aa + bb in B: let namedAlias ~a:aa ~b:bb = aa + bb in diff --git a/formatTest/unit_tests/expected_output/wrappingTest.re b/formatTest/unit_tests/expected_output/wrappingTest.re index 46d0c58e6..720cbc2fb 100644 --- a/formatTest/unit_tests/expected_output/wrappingTest.re +++ b/formatTest/unit_tests/expected_output/wrappingTest.re @@ -35,7 +35,7 @@ let test = 10; let test = 10; /** This comment will be corrected. - when printed. */ + when printed. */ let test = 10; /** Comments with text on line zero @@ -2900,7 +2900,7 @@ let test = 10; let test = 10; /** This comment will be corrected. - when printed. */ + when printed. */ let test = 10; /** Comments with text on line zero diff --git a/formatTest/unit_tests/expected_output/wrappingTest.rei b/formatTest/unit_tests/expected_output/wrappingTest.rei index bb4a1f6ce..58a712cdc 100644 --- a/formatTest/unit_tests/expected_output/wrappingTest.rei +++ b/formatTest/unit_tests/expected_output/wrappingTest.rei @@ -55,7 +55,7 @@ let test: int; let test: int; /** This comment will be corrected. - when printed. */ + when printed. */ let test: int; /** Comments with text on line zero diff --git a/formatTest/unit_tests/input/basicStructures.re b/formatTest/unit_tests/input/basicStructures.re index d011f5688..ae6629f0e 100644 --- a/formatTest/unit_tests/input/basicStructures.re +++ b/formatTest/unit_tests/input/basicStructures.re @@ -134,7 +134,6 @@ let x = arr^[0] = 1; *============================================================================ */; -let (//) = (+); /* // is not a comment */ let something = if (self.ext.logSuccess) { print_string("Did tap"); diff --git a/src/reason-parser/reason_comment.ml b/src/reason-parser/reason_comment.ml index db4873732..17230badc 100644 --- a/src/reason-parser/reason_comment.ml +++ b/src/reason-parser/reason_comment.ml @@ -35,14 +35,58 @@ let dump ppf t = let dump_list ppf list = List.iter (Format.fprintf ppf "%a\n" dump) list -let wrap t = - match t.text with - | "" | "*" -> "/***/" - | txt when txt.[0] = '*' && txt.[1] <> '*' -> "/**" ^ txt ^ "*/" - | txt -> "/*" ^ txt ^ "*/" - let is_doc t = String.length t.text > 0 && t.text.[0] == '*' let make ~location category text = { text; category; location } + +let align_lines text = + match Syntax_util.split_by ~keep_empty:true (fun x -> x = '\n') text with + | [] -> "" + | [text] -> text + | first :: (second :: _ as rest) -> + let leading_spaces str = + let len = String.length str and i = ref 0 in + while !i < len && str.[!i] = ' ' do incr i done; + !i + in + let smallest_leading_spaces lines = + let rec loop count = function + | [] -> count + | "" :: rest -> loop count rest + | str :: rest -> loop (min (leading_spaces str) count) rest + in + loop max_int lines + in + let begins_with_star str = + let len = String.length str and i = ref 0 in + while !i < len && (str.[!i] = ' ' || str.[!i] = '\t') do incr i done; + (!i < len && str.[!i] = '*') + in + let first_indent = + let len = String.length first and i = ref 1 in + let pred = function ' ' | '\t' | '*' -> true | _ -> false in + while !i < len && pred first.[!i] do incr i done; + if !i = len then 1 else !i + in + let attempt_remove = smallest_leading_spaces rest in + let left_pad = + String.make (if begins_with_star second then 1 else first_indent) ' ' + in + let pad_line = function + | "" -> "" + | s -> + let offset = min attempt_remove (leading_spaces s) in + left_pad ^ String.sub s offset (String.length s - offset) + in + String.concat "\n" (first :: List.map pad_line rest) + +let wrap t = + match t.text with + | "" | "*" -> "/***/" + | txt when Syntax_util.is_line_comment txt -> + "//" ^ txt + | txt when txt.[0] = '*' && txt.[1] <> '*' -> + align_lines ("/**" ^ txt ^ "*/") + | txt -> align_lines ("/*" ^ txt ^ "*/") diff --git a/src/reason-parser/reason_lexer.mll b/src/reason-parser/reason_lexer.mll index f56d92931..94940ac76 100644 --- a/src/reason-parser/reason_lexer.mll +++ b/src/reason-parser/reason_lexer.mll @@ -195,7 +195,7 @@ let set_lexeme_length buf n = ( ) (* This cut comment characters of the current buffer. - * Operators (including "/*" and "*/") are lexed with the same rule, and this + * Operators (including "/*" and "//") are lexed with the same rule, and this * function cuts the lexeme at the beginning of an operator. *) let lexeme_without_comment buf = ( let lexeme = Lexing.lexeme buf in @@ -203,7 +203,7 @@ let lexeme_without_comment buf = ( let found = ref (-1) in while !i < len && !found = -1 do begin match lexeme.[!i], lexeme.[!i+1] with - | ('/', '*') | ('*', '/') -> + | ('/', '*') | ('/', '/') | ('*', '/') -> found := !i; | _ -> () end; @@ -636,6 +636,11 @@ rule token = parse } and enter_comment = parse + | "//" ([^'\010']* newline as line) + { update_loc lexbuf None 1 false 0; + COMMENT (line, Location.curr lexbuf) } + | "//" ([^'\010']* eof as line) + { COMMENT (line ^ "\n", Location.curr lexbuf) } | "/*" ("*" "*"+)? { set_lexeme_length lexbuf 2; let start_loc = Location.curr lexbuf in diff --git a/src/reason-parser/reason_pprint_ast.ml b/src/reason-parser/reason_pprint_ast.ml index 2f7a02956..18a7b0442 100644 --- a/src/reason-parser/reason_pprint_ast.ml +++ b/src/reason-parser/reason_pprint_ast.ml @@ -1182,48 +1182,8 @@ let insertBlankLines n term = else makeList ~inline:(true, true) ~indent:0 ~break:Always_rec (Array.to_list (Array.make n (atom "")) @ [term]) -let string_after s n = String.sub s n (String.length s - n) - -(* This is a special-purpose functions only used by `formatComment_`. Notice we -skip a char below during usage because we know the comment starts with `/*` *) -let rec lineZeroMeaningfulContent_ line length idx accum = - if idx = length then None - else - let ch = String.get line idx in - if ch = '\t' || ch = ' ' || ch = '*' then - lineZeroMeaningfulContent_ line length (idx + 1) (accum + 1) - else Some accum - -let lineZeroMeaningfulContent line = - lineZeroMeaningfulContent_ line (String.length line) 1 0 - -let formatComment_ txt = - let commLines = - Syntax_util.split_by ~keep_empty:true (fun x -> x = '\n') - (Comment.wrap txt) - in - match commLines with - | [] -> atom "" - | [hd] -> - atom hd - | zero::one::tl -> - let attemptRemoveCount = (smallestLeadingSpaces (one::tl)) in - let leftPad = - if beginsWithStar one then 1 - else match lineZeroMeaningfulContent zero with - | None -> 1 - | Some num -> num + 1 - in - let padNonOpeningLine s = - let numLeadingSpaceForThisLine = numLeadingSpace s in - if String.length s == 0 then "" - else (String.make leftPad ' ') ^ - (string_after s (min attemptRemoveCount numLeadingSpaceForThisLine)) in - let lines = zero :: List.map padNonOpeningLine (one::tl) in - makeList ~inline:(true, true) ~indent:0 ~break:Always_rec (List.map atom lines) - -let formatComment comment = - source_map ~loc:(Comment.location comment) (formatComment_ comment) +let format_comment comment = + atom ~loc:(Comment.location comment) (Comment.wrap comment) let rec append ?(space=false) txt = function | Layout.SourceMap (loc, sub) -> @@ -1333,7 +1293,7 @@ let rec prependSingleLineComment ?newlinesAboveDocComments:(newlinesAboveDocComm | Sequence (config, hd::tl) when config.break = Always_rec-> Sequence(config, (prependSingleLineComment ~newlinesAboveDocComments comment hd)::tl) | layout -> - let withComment = breakline (formatComment comment) layout in + let withComment = breakline (format_comment comment) layout in if Comment.is_doc comment then insertBlankLines newlinesAboveDocComments withComment else @@ -1359,7 +1319,7 @@ let rec looselyAttachComment ~breakAncestors layout comment = | Layout.SourceMap (loc, sub) -> Layout.SourceMap (loc, looselyAttachComment ~breakAncestors sub comment) | Easy e -> - inline ~postSpace:true layout (formatComment comment) + inline ~postSpace:true layout (format_comment comment) | Sequence (listConfig, subLayouts) when List.exists (Layout.contains_location ~location) subLayouts -> (* If any of the subLayout strictly contains this comment, recurse into to it *) @@ -1371,7 +1331,7 @@ let rec looselyAttachComment ~breakAncestors layout comment = Sequence (listConfig, List.map recurse_sublayout subLayouts) | Sequence (listConfig, subLayouts) when subLayouts == [] -> (* If there are no subLayouts (empty body), create a Sequence of just the comment *) - Sequence (listConfig, [formatComment comment]) + Sequence (listConfig, [format_comment comment]) | Sequence (listConfig, subLayouts) -> let (beforeComment, afterComment) = Syntax_util.pick_while (Layout.is_before ~location) subLayouts in @@ -1413,7 +1373,7 @@ let rec insertSingleLineComment layout comment = prependSingleLineComment comment layout | Sequence (listConfig, subLayouts) when subLayouts = [] -> (* If there are no subLayouts (empty body), create a Sequence of just the comment *) - Sequence (listConfig, [formatComment comment]) + Sequence (listConfig, [format_comment comment]) | Sequence (listConfig, subLayouts) -> let newlinesAboveDocComments = listConfig.newlinesAboveDocComments in let (beforeComment, afterComment) = @@ -1421,7 +1381,7 @@ let rec insertSingleLineComment layout comment = begin match afterComment with (* Nothing in the list is after comment, attach comment to the statement before the comment *) | [] -> - let break sublayout = breakline sublayout (formatComment comment) in + let break sublayout = breakline sublayout (format_comment comment) in Sequence (listConfig, Syntax_util.map_last break beforeComment) | hd::tl -> let afterComment = @@ -1450,7 +1410,7 @@ let rec insertSingleLineComment layout comment = | (Some loc1, Some loc2) when location_is_before location loc2 -> (left, prependSingleLineComment comment right) | _ -> - (left, breakline right (formatComment comment)) + (left, breakline right (format_comment comment)) in Label (formatter, newLeft, newRight) @@ -1463,7 +1423,7 @@ let rec attachCommentToNodeRight layout comment = Layout.Sequence ({config with wrap=(lwrap, rwrap)}, sub) | Layout.SourceMap (loc, sub) -> Layout.SourceMap (loc, attachCommentToNodeRight sub comment) - | layout -> inline ~postSpace:true layout (formatComment comment) + | layout -> inline ~postSpace:true layout (format_comment comment) let rec attachCommentToNodeLeft comment layout = match layout with @@ -1474,7 +1434,7 @@ let rec attachCommentToNodeLeft comment layout = | Layout.SourceMap (loc, sub) -> Layout.SourceMap (loc, attachCommentToNodeLeft comment sub ) | layout -> - Layout.Label (inlineLabel, formatComment comment, layout) + Layout.Label (inlineLabel, format_comment comment, layout) (** [tryPerfectlyAttachComment layout comment] postorderly walk the [layout] and tries * to perfectly attach a comment with a layout node. @@ -5101,7 +5061,7 @@ let printer = object(self:'self) PStr [{ pstr_desc = Pstr_eval ({ pexp_desc = Pexp_constant (Pconst_string(text, None)); _ } , _); pstr_loc; _ }] -> let text = if text = "" then "/**/" else "/**" ^ text ^ "*/" in - makeList ~inline:(true, true) ~postSpace:true ~preSpace:true ~indent:0 ~break:IfNeed [atom ~loc:pstr_loc text] + atom ~loc:pstr_loc (Comment.align_lines text) | (s, e) -> self#payload "@" s e (* [@@ ... ] Attributes that occur after a major item in a structure/class *) diff --git a/src/reason-parser/reason_toolchain.ml b/src/reason-parser/reason_toolchain.ml index d7a709c36..790d6a128 100644 --- a/src/reason-parser/reason_toolchain.ml +++ b/src/reason-parser/reason_toolchain.ml @@ -219,6 +219,11 @@ module Create_parse_entrypoint (Toolchain_impl: Toolchain_spec) :Toolchain = str else String.unsafe_get contents (virtual_start_pos - 2) in + if Syntax_util.is_line_comment str then + Comment.make ~location:physical_loc + (if eol_start then Comment.SingleLine else Comment.EndOfLine) + str :: classifiedTail + else (* * * The following logic are designed for cases like: diff --git a/src/reason-parser/syntax_util.ml b/src/reason-parser/syntax_util.ml index bd07037ec..ba0bda0db 100644 --- a/src/reason-parser/syntax_util.ml +++ b/src/reason-parser/syntax_util.ml @@ -419,6 +419,14 @@ let apply_mapper_to_toplevel_phrase toplevel_phrase mapper = let apply_mapper_to_use_file use_file mapper = List.map (fun x -> apply_mapper_to_toplevel_phrase x mapper) use_file +(* A line comment is a comment that ends with a newline and doesn't contain + any other newline characters *) +let is_line_comment str = + (* true iff the first \n is the last character *) + match String.index str '\n' with + | exception Not_found -> false + | n -> n = String.length str - 1 + (* The following logic defines our own Error object * and register it with ocaml so it knows how to print it *) diff --git a/src/reason-parser/vendor/easy_format/easy_format.ml b/src/reason-parser/vendor/easy_format/easy_format.ml index 24e6a2890..37ef3dadc 100644 --- a/src/reason-parser/vendor/easy_format/easy_format.ml +++ b/src/reason-parser/vendor/easy_format/easy_format.ml @@ -92,6 +92,42 @@ type escape = type styles = (style_name * style) list +let pp_print_multiline ppf str = + let last = String.length str - 1 in + let rec loop i = + match String.index_from str i '\n' with + | exception Not_found -> + (* Lie a bit for the last line: fake it being very long to avoid + the formatted sticking stuff after. + For instance, without this: + /* line 1 + line 2 */ stuff + And with: + /* line 1 + line 2 */ + stuff + *) + pp_print_as ppf + (Format.pp_get_margin ppf () + String.length str) + (String.sub str i (String.length str - i)); + pp_close_box ppf () + | j when j = last -> + (* Same lie but for single line comments *) + pp_print_as ppf + (Format.pp_get_margin ppf () + String.length str) + (String.sub str i (j - i)); + pp_close_box ppf () + | j -> + pp_print_string ppf (String.sub str i (j - i)); + pp_force_newline ppf (); + loop (j + 1) + in + if String.contains str '\n' then ( + pp_open_vbox ppf 0; + loop 0; + ) else + pp_print_string ppf str + (* Transform a tree starting from the leaves, propagating and merging accumulators until reaching the root. @@ -319,10 +355,10 @@ struct let tag_string fmt o s = match o with - None -> pp_print_string fmt s + None -> pp_print_multiline fmt s | Some tag -> pp_open_tag fmt tag; - pp_print_string fmt s; + pp_print_multiline fmt s; pp_close_tag fmt () let rec fprint_t fmt = function