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
};

// whitespace interleaving

// comment1
// comment2

// whitespace above & below

let r = {
  fieldOne: (identifier : string), //eol1
  // c1

  // c2

  // c3
  // c4

  // c5
  fieldTwo: (identifier : string), // eol2 with trailing comma
};
// trailing

// trailing whitespace above
// attach

// last comment
