///|
fn rich_text_block_selection_visual_range(
  block : RichTextBlock,
  selection : TextRange,
) -> TextRange? {
  match (block.source_range, block.content_range) {
    (Some(source), Some(content)) =>
      if selection.end <= source.start || selection.start >= source.end {
        None
      } else {
        let start = rich_text_block_source_offset_to_visual(
          block,
          clamp_int(selection.start, source.start, source.end),
          content,
        )
        let end = rich_text_block_source_offset_to_visual(
          block,
          clamp_int(selection.end, source.start, source.end),
          content,
        )
        if end > start {
          Some(TextRange::new(start~, end~))
        } else {
          None
        }
      }
    (_, Some(content)) =>
      if selection.end <= content.start || selection.start >= content.end {
        None
      } else {
        let start = rich_text_block_source_offset_to_visual(
          block,
          clamp_int(selection.start, content.start, content.end),
          content,
        )
        let end = rich_text_block_source_offset_to_visual(
          block,
          clamp_int(selection.end, content.start, content.end),
          content,
        )
        if end > start {
          Some(TextRange::new(start~, end~))
        } else {
          None
        }
      }
    _ => None
  }
}

///|
fn append_rich_text_selection_commands(
  commands : Array[DrawCommand],
  document : RichTextDocument,
  content : Rect,
  base_font : FontSpec,
  text_system : TextSystem,
  selection : TextRange?,
) -> Unit {
  match active_text_selection(selection) {
    Some(selection) => {
      let mut y = content.origin.y
      for block in document.blocks {
        let line_height = rich_text_block_line_height(block, base_font)
        let block_height = line_height + block.inset.top + block.inset.bottom
        let frame = Rect::new(
          x=content.origin.x + block.inset.left,
          y=y + block.inset.top,
          width=max_double(
            0.0,
            content.size.width - block.inset.left - block.inset.right,
          ),
          height=line_height,
        )
        match rich_text_block_selection_visual_range(block, selection) {
          Some(range) =>
            append_rich_text_block_selection_rect(
              commands, block, range, frame, base_font, text_system,
            )
          None => ()
        }
        y = y + block_height
      }
    }
    None => ()
  }
}

///|
fn append_rich_text_block_selection_rect(
  commands : Array[DrawCommand],
  block : RichTextBlock,
  range : TextRange,
  frame : Rect,
  base_font : FontSpec,
  text_system : TextSystem,
) -> Unit {
  let start_rect = rich_text_block_caret_rect(
    block,
    range.start,
    frame,
    base_font,
    text_system,
  )
  let end_rect = rich_text_block_caret_rect(
    block,
    range.end,
    frame,
    base_font,
    text_system,
  )
  if end_rect.origin.x > start_rect.origin.x {
    commands.push(
      FillRect(
        Rect::new(
          x=start_rect.origin.x,
          y=frame.origin.y + 4.0,
          width=end_rect.origin.x - start_rect.origin.x,
          height=max_double(10.0, frame.size.height - 8.0),
        ),
        Color::blue().with_alpha(0.18),
      ),
    )
  }
}