// comment 1
let /* a */ x /* b */ = /* c */ 1 /* comment 2 */

// comment 3

// comment 4
let x = 1 /* 5 */
/* 6 */

let f = (
  // 1
  a, //2
  // 3
  b,
) => {
  // 5
  x: 1, // 6
  // 7
  y: 2, // 8
}
/* **** comment */
/* ** comment */
/* comment */
/* ** comment */
/* *** comment */
/* **** comment */

/* ** */
/* *** */

/* ** */

/* (** comment *) */
/* (*** comment *) */
/* *(*** comment *) */

/* comment * */
/* comment ** */
/* comment *** */
/* comment **** */

let testingNotQuiteEndOfLineComments = list{
  "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 no semi */,
  /* Comment after last item in list. */
} /* Comment after list bracket */

let testingEndOfLineComments = list{
  "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 semi */,
  /* Comment after last item in list. */
} /* Comment after list bracket */

/* This time no space between bracket and comment */
let testingEndOfLineComments = list{} /* Comment after list bracket */

type t = (int, int) /* End of line on t */

type t22 = /* End of t22 line on type t22 = */
(int, int)

type variant =
  /* Comment above X */
  | X(int) /* End of line on X */
  /* Comment above Y */
  | Y(int) /* End of line on Y */
/* Comment on entire type def for variant */

type rec x = {
  /* not attached *above* x */
  fieldOne: int,
} /* Attached end of line after x */
and y = {
  /* not attached *above* y */
  fieldTwo: int,
} /* Attached end of line after y */

let result = switch X(3) {
| X(x) =>
  /* Where does this comment go? */
  let tmp = x
  x + tmp
| Y(x) =>
  /* How about this one */
  let tmp = x
  x + tmp
}

let result = switch None {
| Some({fieldOne: 20}) =>
  /* Where does this comment go? */
  let tmp = 0
  2 + tmp
| Some({fieldOne: n}) =>
  /* How about this one */
  let tmp = n
  n + tmp
| None => 20
}

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 */
}

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 */

module Temp = {
  let v = true
  let logIt = (str, ()) => print_string(str)
}

let store_attributes = arg => {
  let attributes_file = "test"
  let proc_name = attributes_file ++ ".proc"
  let should_write =
    /* only overwrite defined procedures */
    Temp.v || !Temp.v
  if should_write {
    Temp.logIt(proc_name, ())
  }
}
3 // -
3 //-

3 //-

3 /* - */
// **** comment
/* ** comment */
@ocaml.doc(" docstring ")
@ocaml.doc(" docstring ")
@ocaml.doc(" ")
@ocaml.doc("")
@ocaml.doc(" (** comment *) ")
@ocaml.doc(" (*** comment *) ")
@ocaml.doc("
 * Multiline
 ")
@ocaml.doc(" Multiline
 *
 ")
@ocaml.doc("
 **
 ")
module // comment

/* ** comment */
/* *** comment */
/* **** comment */

/* ** */
/* *** */

/* ** */

// (** comment *)
// (*** comment *)
// *(*** comment *)
// comment *
// comment **
// comment ***
// comment ****

JustString = {
  include Map.Make(Int32) // Comment eol include
}

let testingEndOfLineComments = list{
  "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 = list{
  "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 = list{
  "Item 0",
  // Comment after last item in list.
} // Comment after semi

// The space between ; and comment shoudn't matter
let testPlacementOfTrailingComment = list{
  "Item 0",
  // Comment after last item in list.
} // Comment after semi

// Try again but without other things in the list
let testPlacementOfTrailingComment = list{"Item 0"} // Comment after semi

// The space between ; and comment shoudn't matter
let testPlacementOfTrailingComment = list{
  "Item 0",
  // Comment after last item in list.
} // Comment after semi

let testingEndOfLineComments = list{} // 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<
  '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 rec 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 rec 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, fieldA: a}) =>
  let tmp = 0
  2 + tmp
| Some({fieldOne: n, fieldA: a}) =>
  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 X(2, 3) {
// Above X line
| X(_, _) => "result of X" // retain this // retain this

// Above Y line
| Y(_) => "result of Y" // End of arrow and Y line
}

/* type optionalTuple = */
/* | OptTup( */
/* option( */
/* ( */
/* int, */
/* int */
/* ), */
/* ), */
/* ); */

type optionTuple = option<(int, int)> // First int // Second int

type intPair = (int, int) // First int // Second int

type intPair2 = (
  // First int
  int,
  // Second int
  int,
)

let result = @ocaml.doc("") (2 + 3)

// This is not yet idempotent
// {
//   /**/
//   (+) 2 3
// };

let a = ()
for i in 0 to 10 {
  // bla
  a
}

if true {
  ()
}

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, x) =>
  switch x {
  | 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))

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 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()
} else {
  // Comment before print
  print_newline()
}

// 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 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()
}

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()
}

// 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()
} 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()
}

// 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()
}

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, 2) //eol1 // eol2

let y = callFunctionTwoArgs(1, 2) //eol1 // eol2

let y = OneTupleArgConstructor((1, 2)) //eol1 // eol2

let y = callFunctionOneTuple((1, 2)) //eol1 // 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", "2") //eol1 // eol2

let y = callFunctionTwoArgs("1", "2") //eol1 // eol2

let y = OneTupleArgConstructor(("1", "2")) //eol1 // eol2

let y = callFunctionOneTuple(("1", "2")) //eol1 // 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, identifier) //eol1 // eol2

let y = callFunctionTwoArgs(identifier, identifier) //eol1 // eol2

let y = OneTupleArgConstructor((identifier, identifier)) //eol1 // eol2

let y = callFunctionOneTuple((identifier, identifier)) //eol1 // 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), (identifier: string)) //eol1 // eol2

let y = callFunctionTwoArgs((identifier: string), (identifier: string)) //eol1 // eol2

let y = OneTupleArgConstructor(((identifier: string), (identifier: string))) //eol1 // eol2

let y = callFunctionOneTuple(((identifier: string), (identifier: string))) //eol1 // 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
