///|
/// Normalize a browser's already-edited display after composition/autofill.
/// Selection refers to the proposed display. Prefixes are formatted by the same
/// MoonBit core to map the caret, never by host-side formatting rules.
pub fn Editor::reconcile(
  self : Editor,
  text : String,
  start : Int,
  end : Int,
) -> Result[Editor, InputError] {
  let limit = match self.mask.kind {
    DecimalKind(_, _, _) => 512
    _ => 8192
  }
  if text.length() > limit {
    return Err(TooLong(limit))
  }
  if start < 0 || end < start || end > text.length() {
    return Err(InvalidSelection)
  }
  let next = match self.set_value(text) {
    Ok(e) => e
    Err(_) => {
      // Native input may leave previously generated separators in old positions.
      // Recover the single contiguous edit instead of treating that transient
      // display as a new, incorrectly grouped value.
      let old_chars = self.display().to_array()
      let new_chars = text.to_array()
      let mut prefix = 0
      while prefix < old_chars.length() &&
            prefix < new_chars.length() &&
            old_chars[prefix] == new_chars[prefix] {
        prefix += 1
      }
      let mut suffix = 0
      while suffix < old_chars.length() - prefix &&
            suffix < new_chars.length() - prefix &&
            old_chars[old_chars.length() - 1 - suffix] ==
            new_chars[new_chars.length() - 1 - suffix] {
        suffix += 1
      }
      let mut from = 0
      for i in 0.. selected.insert(String::from_array(inserted))
        Err(e) => Err(e)
      }
    }
  }
  let before_start : Array[Char] = []
  let before_end : Array[Char] = []
  for offset, c in text.iter2() {
    if offset + c.utf16_len() <= start {
      before_start.push(c)
    }
    if offset + c.utf16_len() <= end || (start != end && offset < end) {
      before_end.push(c)
    }
  }
  fn mapped(chars : Array[Char]) -> Int {
    let prefix = String::from_array(chars)
    match self.mask.kind {
      DecimalKind(_, _, grouped) =>
        match decimal_chars(prefix, grouped) {
          Ok(raw) => next.display_offset(raw.length()).unwrap_or(0)
          Err(_) => 0
        }
      PatternKind(p) | DateKind(p) =>
        match parse_pattern(p, prefix, 0) {
          Ok(raw) => next.display_offset(raw.length()).unwrap_or(0)
          Err(_) => 0
        }
    }
  }
  next.select(mapped(before_start), mapped(before_end))
}