///|
fn format_boundary(text : String, offset : Int) -> Bool {
  if offset < 0 || offset > text.length() {
    return false
  }
  if offset == 0 || offset == text.length() {
    return true
  }
  let previous = text.unsafe_get(offset - 1)
  let next = text.unsafe_get(offset)
  !(previous >= 0xD800 && previous <= 0xDBFF && next >= 0xDC00 && next <= 0xDFFF)
}

///|
fn format_line_break(code : UInt16) -> Bool {
  code == 10 || code == 13
}

///|
/// A range selects complete original source lines. CRLF is one line break.
fn format_line_range(text : String, range : TextRange) -> TextRange {
  let mut start = range.offset
  if start > 0 &&
    start < text.length() &&
    text.unsafe_get(start - 1) == 13 &&
    text.unsafe_get(start) == 10 {
    start -= 1
  }
  while start > 0 && !format_line_break(text.unsafe_get(start - 1)) {
    start -= 1
  }
  let mut end = if range.length > 0 {
    range.offset + range.length - 1
  } else {
    range.offset
  }
  while end < text.length() && !format_line_break(text.unsafe_get(end)) {
    end += 1
  }
  if end < text.length() {
    if text.unsafe_get(end) == 13 &&
      end + 1 < text.length() &&
      text.unsafe_get(end + 1) == 10 {
      end += 2
    } else {
      end += 1
    }
  }
  { offset: start, length: end - start, }
}

///|
fn format_indent(depth : Int, options : FormatOptions) -> String {
  if options.use_tabs {
    String::make(depth, '\t')
  } else {
    String::make(depth * options.indent_size, ' ')
  }
}

///|
fn format_has_break(text : StringView) -> Bool {
  for c in text {
    if c == '\r' || c == '\n' {
      return true
    }
  }
  false
}

///|
fn format_comment(kind : TokenKind) -> Bool {
  kind == LineComment || kind == BlockComment
}

///|
/// Plan only whitespace changes, trimming unchanged prefixes and suffixes.
fn format_gap(
  text : String,
  start : Int,
  end : Int,
  content : String,
  range : TextRange,
  edits : Array[TextEdit],
) -> Unit {
  let old = text.view(start_offset=start, end_offset=end)
  let mut prefix = 0
  while prefix < old.length() &&
        prefix < content.length() &&
        old.unsafe_get(prefix) == content.unsafe_get(prefix) {
    prefix += 1
  }
  let mut suffix = 0
  while suffix < old.length() - prefix &&
        suffix < content.length() - prefix &&
        old.unsafe_get(old.length() - 1 - suffix) ==
        content.unsafe_get(content.length() - 1 - suffix) {
    suffix += 1
  }
  let offset = start + prefix
  let length = old.length() - prefix - suffix
  let replacement = content
    .view(start_offset=prefix, end_offset=content.length() - suffix)
    .to_owned()
  if length == 0 && replacement == "" {
    return
  }
  let range_end = range.offset + range.length
  // A zero-length insertion at the next line's start belongs to that next line.
  if offset >= range.offset &&
    offset + length <= range_end &&
    (offset < range_end || range_end == text.length()) {
    edits.push({ offset, length, content: replacement, })
  }
}

///| Format valid JSONC using edits that change only whitespace between tokens.

///| String, number and comment token spelling is preserved exactly. Options

///| accept indentation 0..16 and LF/CRLF/CR. Empty documents are rejected.

///| A range uses original UTF-16 coordinates and expands to complete source

///| lines. Only minimized edits wholly within those lines are returned; a gap

///| requiring a change across their boundary is left untouched. Formatting

///|
/// context is nevertheless computed from the complete document.
pub fn format(
  text : String,
  options? : FormatOptions = FormatOptions::default(),
  range? : TextRange? = None,
) -> Result[Array[TextEdit], EditError] {
  if options.indent_size < 0 || options.indent_size > 16 {
    return Err(InvalidOptions("indent_size must be between 0 and 16"))
  }
  if options.eol != "\n" && options.eol != "\r\n" && options.eol != "\r" {
    return Err(InvalidOptions("eol must be LF, CRLF or CR"))
  }
  let selected = match range {
    None => { offset: 0, length: text.length(), }
    Some(range) => {
      if range.offset < 0 ||
        range.length < 0 ||
        range.offset > text.length() ||
        range.length > text.length() - range.offset ||
        !format_boundary(text, range.offset) ||
        !format_boundary(text, range.offset + range.length) {
        return Err(
          InvalidOptions("range must use valid UTF-16 source boundaries"),
        )
      }
      format_line_range(text, range)
    }
  }
  let document = parse(text)
  if !document.diagnostics.is_empty() {
    return Err(InvalidDocument(document.diagnostics))
  }
  let tokens = document.tokens.filter(fn(token) {
    token.kind != Whitespace && token.kind != LineBreak && token.kind != Eof
  })
  let edits : Array[TextEdit] = []
  let mut depth = 0
  let mut previous : Token? = None
  let mut end = 0
  for token in tokens {
    if token.kind == CloseBrace || token.kind == CloseBracket {
      depth -= 1
    }
    let newline = options.eol + format_indent(depth, options)
    let original = text.view(start_offset=end, end_offset=token.offset)
    let gap = match previous {
      None => ""
      Some(previous) => {
        let empty = (previous.kind == OpenBrace && token.kind == CloseBrace) ||
          (previous.kind == OpenBracket && token.kind == CloseBracket)
        if empty {
          ""
        } else if previous.kind == LineComment {
          newline
        } else if token.kind == CloseBrace || token.kind == CloseBracket {
          newline
        } else if previous.kind == OpenBrace || previous.kind == OpenBracket {
          newline
        } else if format_comment(token.kind) {
          if format_has_break(original) {
            newline
          } else {
            " "
          }
        } else if previous.kind == Comma {
          newline
        } else if previous.kind == Colon {
          " "
        } else if previous.kind == BlockComment {
          let body = text.view(
            start_offset=previous.offset,
            end_offset=previous.offset + previous.length,
          )
          if format_has_break(original) || format_has_break(body) {
            newline
          } else if token.kind == Colon || token.kind == Comma {
            ""
          } else {
            " "
          }
        } else if token.kind == Colon || token.kind == Comma {
          ""
        } else if format_has_break(original) {
          newline
        } else {
          " "
        }
      }
    }
    format_gap(text, end, token.offset, gap, selected, edits)
    if token.kind == OpenBrace || token.kind == OpenBracket {
      depth += 1
    }
    end = token.offset + token.length
    previous = Some(token)
  }
  format_gap(
    text,
    end,
    text.length(),
    if options.final_newline {
      options.eol
    } else {
      ""
    },
    selected,
    edits,
  )
  Ok(edits)
}