///|
fn markdown_editor_backspace_footnote_marker(
  chars : Array[Char],
  caret : Int,
) -> RichTextInputTransform? {
  if caret <= 0 {
    return None
  }
  if caret >= 2 && chars[caret - 2] == '[' && chars[caret - 1] == '^' {
    match markdown_editor_footnote_close_after(chars, caret) {
      Some(close_start) =>
        return Some(
          markdown_editor_remove_reference_marker(
            chars,
            caret - 2,
            caret,
            close_start,
            close_start,
          ),
        )
      None => ()
    }
  }
  if chars[caret - 1] == ']' {
    match markdown_editor_footnote_open_before(chars, caret - 1) {
      Some(open_start) =>
        return Some(
          markdown_editor_remove_reference_marker(
            chars,
            open_start,
            open_start + 2,
            caret - 1,
            caret - 1,
          ),
        )
      None => ()
    }
  }
  None
}

///|
fn markdown_editor_delete_footnote_marker(
  chars : Array[Char],
  caret : Int,
) -> RichTextInputTransform? {
  if caret >= chars.length() {
    return None
  }
  if chars[caret] == '[' &&
    caret + 1 < chars.length() &&
    chars[caret + 1] == '^' {
    match markdown_editor_footnote_close_after(chars, caret + 2) {
      Some(close_start) =>
        return Some(
          markdown_editor_remove_reference_marker(
            chars,
            caret,
            caret + 2,
            close_start,
            close_start,
          ),
        )
      None => ()
    }
  }
  if chars[caret] == ']' {
    match markdown_editor_footnote_open_before(chars, caret) {
      Some(open_start) =>
        return Some(
          markdown_editor_remove_reference_marker(
            chars,
            open_start,
            open_start + 2,
            caret,
            caret,
          ),
        )
      None => ()
    }
  }
  None
}

///|
fn markdown_editor_footnote_close_after(
  chars : Array[Char],
  content_start : Int,
) -> Int? {
  let mut close_start = content_start
  while close_start < chars.length() &&
        chars[close_start] != ']' &&
        chars[close_start] != '\n' {
    close_start = close_start + 1
  }
  if close_start <= content_start ||
    close_start >= chars.length() ||
    chars[close_start] != ']' {
    None
  } else {
    Some(close_start)
  }
}

///|
fn markdown_editor_footnote_open_before(
  chars : Array[Char],
  close_start : Int,
) -> Int? {
  if close_start >= chars.length() || chars[close_start] != ']' {
    return None
  }
  let mut content_start = close_start
  while content_start > 0 &&
        chars[content_start - 1] != '[' &&
        chars[content_start - 1] != '\n' {
    content_start = content_start - 1
  }
  if content_start <= 0 || chars[content_start - 1] != '[' {
    return None
  }
  let open_start = content_start - 1
  if content_start >= close_start ||
    content_start >= chars.length() ||
    chars[content_start] != '^' ||
    content_start + 1 >= close_start {
    None
  } else {
    Some(open_start)
  }
}

///|
fn markdown_editor_backspace_autolink_marker(
  chars : Array[Char],
  caret : Int,
) -> RichTextInputTransform? {
  if caret <= 0 {
    return None
  }
  if chars[caret - 1] == '>' {
    match markdown_editor_autolink_open_before(chars, caret - 1) {
      Some(open_start) =>
        return Some(
          markdown_editor_remove_reference_marker(
            chars,
            open_start,
            open_start + 1,
            caret - 1,
            caret - 1,
          ),
        )
      None => ()
    }
  }
  if chars[caret - 1] == '<' {
    match markdown_editor_autolink_close_after(chars, caret) {
      Some(close_start) =>
        return Some(
          markdown_editor_remove_reference_marker(
            chars,
            caret - 1,
            caret,
            close_start,
            close_start,
          ),
        )
      None => ()
    }
  }
  None
}

///|
fn markdown_editor_delete_autolink_marker(
  chars : Array[Char],
  caret : Int,
) -> RichTextInputTransform? {
  if caret >= chars.length() {
    return None
  }
  if chars[caret] == '<' {
    match markdown_editor_autolink_close_after(chars, caret + 1) {
      Some(close_start) =>
        return Some(
          markdown_editor_remove_reference_marker(
            chars,
            caret,
            caret + 1,
            close_start,
            close_start,
          ),
        )
      None => ()
    }
  }
  if chars[caret] == '>' {
    match markdown_editor_autolink_open_before(chars, caret) {
      Some(open_start) =>
        return Some(
          markdown_editor_remove_reference_marker(
            chars,
            open_start,
            open_start + 1,
            caret,
            caret,
          ),
        )
      None => ()
    }
  }
  None
}

///|
fn markdown_editor_autolink_close_after(
  chars : Array[Char],
  content_start : Int,
) -> Int? {
  let mut close = content_start
  while close < chars.length() && chars[close] != '>' && chars[close] != '\n' {
    close = close + 1
  }
  if close >= chars.length() || chars[close] != '>' {
    return None
  }
  let target = String::from_array(chars[content_start:close])
  if markdown_editor_is_autolink_target(target) {
    Some(close)
  } else {
    None
  }
}

///|
fn markdown_editor_autolink_open_before(
  chars : Array[Char],
  close_start : Int,
) -> Int? {
  let mut open = close_start
  while open > 0 && chars[open - 1] != '<' && chars[open - 1] != '\n' {
    open = open - 1
  }
  if open <= 0 || chars[open - 1] != '<' {
    return None
  }
  let target = String::from_array(chars[open:close_start])
  if markdown_editor_is_autolink_target(target) {
    Some(open - 1)
  } else {
    None
  }
}