///|
fn Prompt::run_paste_handler(
  self : Prompt,
  state : InputState,
  key_bindings : KeyBindings,
  info : @console.ConsoleKeyInfo,
) -> KeyProcessingResult {
  if !key_bindings.paste.matches(info) {
    return NotHandled
  }
  let text = self.clipboard.read_text() catch {
    BackendFailure(_) => return Handled
    _ => return NotHandled
  }
  if text.length() == 0 {
    return Handled
  }
  let filtered = dedent_and_filter_paste(text)
  self.pane_state.completion_state = None
  self.pane_state.overload_state = None
  self.pane_state.search_state = None
  self.delete_selection(state.left, state.right)
  state.left.push_iter(filtered.iter())
  self.track_undo(state.left, state.right)
  state.request_render()
  state.last_was_yank = false
  Handled
}

///|
fn dedent_and_filter_paste(text : String) -> String {
  let lines = text.split("\n").collect()
  let result = StringBuilder::new()
  let has_cr = text.contains("\r")
  let line_count = lines.length()
  if line_count > 1 {
    let non_empty_lines = lines.filter(line => {
      let line_without_cr = if has_cr && line.has_suffix("\r") {
        line[:line.length() - 1]
      } else {
        line
      }
      !line_without_cr.is_blank()
    })
    if non_empty_lines.length() <= 1 {
      append_filtered_lines(result, lines, has_cr)
    } else {
      let min_indent = compute_min_indent(non_empty_lines, has_cr)
      if min_indent == 0 {
        append_filtered_lines(result, lines, has_cr)
      } else {
        for i, line in lines {
          if i > 0 {
            result.write_char('\n')
          }
          let line_without_cr = if has_cr && line.has_suffix("\r") {
            line[:line.length() - 1]
          } else {
            line
          }
          let dedented = if line_without_cr.length() > min_indent {
            line_without_cr[min_indent:]
          } else {
            line_without_cr
          }
          append_filtered(result, dedented)
        }
      }
    }
  } else {
    append_filtered(result, text)
  }
  result.to_string()
}

///|
fn compute_min_indent(lines : Array[StringView], has_cr : Bool) -> Int {
  let mut min_indent = 0
  for line in lines {
    let line_without_cr = if has_cr && line.has_suffix("\r") {
      line[:line.length() - 1]
    } else {
      line
    }
    let indent = count_leading_whitespace(line_without_cr)
    if min_indent == 0 || indent < min_indent {
      min_indent = indent
    }
  }
  min_indent
}

///|
fn count_leading_whitespace(s : StringView) -> Int {
  let mut count = 0
  for ch in s {
    if ch.is_whitespace() {
      count += 1
    } else {
      break
    }
  }
  count
}

///|
fn append_filtered(sb : StringBuilder, s : StringView) -> Unit {
  for ch in s {
    if ch == '\t' {
      sb.write_view("    ")
    } else if ch != '\r' {
      sb.write_char(ch)
    }
  }
}

///|
fn append_filtered_lines(
  sb : StringBuilder,
  lines : Array[StringView],
  has_cr : Bool,
) -> Unit {
  for i, line in lines {
    if i > 0 {
      sb.write_char('\n')
    }
    let line_without_cr = if has_cr && line.has_suffix("\r") {
      line[:line.length() - 1]
    } else {
      line
    }
    append_filtered(sb, line_without_cr)
  }
}