///|
fn folded_bytes(bytes : BytesView) -> Bytes {
  Bytes::makei(bytes.length(), index => {
    let byte = bytes[index]
    if byte >= b'A' && byte <= b'Z' {
      (byte.to_int() + 32).to_byte()
    } else {
      byte
    }
  })
}

///|
fn byte_word(byte : Byte) -> Bool {
  byte == b'_' || byte is (b'a'..<=b'z' | b'A'..<=b'Z' | b'0'..<=b'9')
}

///|
fn fixed_span(
  patterns : Array[Bytes],
  line : BytesView,
  position : Int32,
  whole : Bool,
  word : Bool,
) -> (Int32, Int32)? {
  if position > line.length() {
    return None
  }
  let mut best = None
  for pattern in patterns {
    if whole {
      if position == 0 && line == pattern[:] {
        return Some((0, line.length()))
      }
      continue
    }
    let mut search = position
    while search <= line.length() {
      guard line[search:].find(pattern[:]) is Some(relative) else { break }
      let start = search + relative
      let end = start + pattern.length()
      if !word ||
        (
          (start == 0 || !byte_word(line[start - 1])) &&
          (end == line.length() || !byte_word(line[end]))
        ) {
        match best {
          Some((old_start, old_end)) =>
            if start < old_start || (start == old_start && end > old_end) {
              best = Some((start, end))
            }
          None => best = Some((start, end))
        }
        break
      }
      search = start + 1
    }
  }
  best
}