///|
/// What the printer knows about the source of a parsed file. Empty for an
/// AST built by hand.
priv struct SourceFacts {
  /// The start of each token, in source order.
  starts : Array[@ast.Location]
  /// The text of each token.
  lexemes : Array[String]
  /// `prefix[i]` is the number of gaps with a break among gaps `0.. Bool {
  trivia
  .iter()
  .any(t => t is Newline(_, _) || (t is Comment(c) && c.kind is Line))
}

///|
fn SourceFacts::from_tokens(tokens : Array[@scanner.Token]) -> SourceFacts {
  let starts = []
  let lexemes = []
  let prefix = [0]
  for i, t in tokens {
    starts.push(@ast.Location::{
      row: t.span.start.line,
      column: t.span.start.column,
    })
    lexemes.push(t.lexeme)
    let gap = if i == 0 {
      t.trivia_before
    } else {
      tokens[i - 1].trivia_after + t.trivia_before
    }
    prefix.push(prefix[i] + (if gap_has_break(gap) { 1 } else { 0 }))
  }
  { starts, lexemes, prefix, }
}

///|
/// How the formatter chooses line breaks.
///
/// - `ElmFormat`: as elm-format 0.8.7: a construct is multi-line when the
///   source has a line break where elm-format looks, or when a part of it
///   is multi-line. There is no line width. Literals keep the form of
///   their source text: a triple-quoted string stays triple-quoted, and a
///   float with an exponent keeps one (`1E3` gives `1.0e3`).
/// - `Width(n)`: a construct is multi-line when it does not fit in `n`
///   columns (the layout of `print_file`). Literals print from their
///   value.
///
/// Both layouts print the comments of the source.
///
/// ```mbt check
/// test {
///   let source = "module A exposing (a)\n\na = [ 1\n  , 2 ]\n"
///   inspect(
///     @printer.format(source, layout=ElmFormat),
///     content=(
///       #|module A exposing (a)
///       #|
///       #|
///       #|a =
///       #|    [ 1
///       #|    , 2
///       #|    ]
///       #|
///     ),
///   )
///   inspect(
///     @printer.format(source, layout=Width(80)),
///     content=(
///       #|module A exposing (a)
///       #|
///       #|
///       #|a =
///       #|    [ 1, 2 ]
///       #|
///     ),
///   )
/// }
/// ```
pub(all) enum Layout {
  ElmFormat
  Width(Int)
} derive(Eq, Debug)

///|
fn Layout::render_width(self : Layout) -> Int {
  match self {
    ElmFormat => @int.MAX_VALUE
    Width(n) => n
  }
}