///|
/// The compiler's `imports`: consecutive imports form groups, split at an
/// empty line or a comment; each group is sorted; groups are kept apart
/// by an empty line.
fn Formatter::imports(
  self : Formatter,
  imports : Array[@ast.TargetedDefinition],
) -> Array[@pretty.Doc] raise FormatError {
  let groups : Array[@pretty.Doc] = []
  let current : Array[@ast.TargetedDefinition] = []
  let mut delimiter = @pretty.empty()
  for import_ in imports {
    let start = import_.definition.location().start
    let start_new_group = self.any_comments(start) ||
      self.any_empty_lines(start)
    if start_new_group {
      if !current.is_empty() {
        groups.push(delimiter + self.sorted_import_group(current))
        current.clear()
      }
      let comments = self.pop_comments(start)
      let _ = self.pop_empty_lines(start)
      delimiter = printed_comments(comments[:], true).unwrap_or(@pretty.empty())
    }
    current.push(import_)
  }
  if !current.is_empty() {
    groups.push(delimiter + self.sorted_import_group(current))
  }
  // intersperse TWO_LINES_DOCUMENT
  let out : Array[@pretty.Doc] = []
  for i, g in groups {
    if i > 0 {
      out.push(two_lines_doc)
    }
    out.push(g)
  }
  out
}

///|
/// The compiler's `sorted_import_group`.
fn Formatter::sorted_import_group(
  self : Formatter,
  imports : Array[@ast.TargetedDefinition],
) -> @pretty.Doc raise FormatError {
  let sorted = stable_sort_by(imports, (a, b) => {
    match (a.definition, b.definition) {
      (Import(x), Import(y)) => lexicographic(x.module_, y.module_)
      _ => 0
    }
  })
  let docs = []
  for i in sorted {
    docs.push(self.targeted_definition(i))
  }
  @pretty.join(docs, line_doc)
}

///|
/// The compiler's `unqualified_import`.
fn unqualified_import(import_ : @ast.UnqualifiedImport) -> @pretty.Doc {
  t(import_.name) +
  (match import_.as_name {
    None => @pretty.empty()
    Some(s) if s == import_.name => @pretty.empty()
    Some(s) => t(" as ") + t(s)
  })
}

///|
/// The import arm of the compiler's `definition`.
fn import_(import_ : @ast.Import) -> @pretty.Doc {
  let second = if import_.unqualified_values.is_empty() &&
    import_.unqualified_types.is_empty() {
    @pretty.empty()
  } else {
    let by_name = (a : @ast.UnqualifiedImport, b : @ast.UnqualifiedImport) => {
      lexicographic(a.name, b.name)
    }
    let types = stable_sort_by(import_.unqualified_types.to_owned(), by_name)
    let values = stable_sort_by(import_.unqualified_values.to_owned(), by_name)
    let docs = []
    for i in types {
      docs.push(t("type ") + unqualified_import(i))
    }
    for i in values {
      docs.push(unqualified_import(i))
    }
    let unqualified = @pretty.join(docs, flex_comma)
    let unqualified = group(
      nest(empty_break + unqualified) + trailing_comma_break,
    )
    t(".{") + unqualified + t("}")
  }
  let doc = t("import ") + t(import_.module_) + second
  let default_name = match import_.module_.rev_find("/") {
    Some(i) => import_.module_.view(start_offset=i + 1).to_owned()
    None => import_.module_
  }
  match import_.as_name {
    None => doc
    Some({ name: Variable(name), .. }) if name == default_name => doc
    Some({ name: Variable(name) | Discard(name), .. }) =>
      doc + t(" as ") + t(name)
  }
}