///|
/// A 1-based line and a 1-based column counted in code points.
pub(all) struct Position {
  line : Int
  column : Int
} derive(Debug, Eq)

///|
fn is_high_surrogate(c : Int) -> Bool {
  c >= 0xD800 && c <= 0xDBFF
}

///|
/// The position of UTF-16 `offset` in `source`. `\r\n` ends one line.
pub fn position(source : String, offset : Int) -> Position {
  let end = if offset < source.length() { offset } else { source.length() }
  let mut line = 1
  let mut column = 1
  let mut i = 0
  while i < end {
    let c = source.at(i).to_int()
    if c == '\n'.to_int() {
      line += 1
      column = 1
      i += 1
    } else if c == '\r'.to_int() &&
      i + 1 < source.length() &&
      source.at(i + 1).to_int() == '\n'.to_int() {
      i += 1
    } else {
      column += 1
      i += if is_high_surrogate(c) && i + 1 < end { 2 } else { 1 }
    }
  }
  { line, column, }
}

///|
/// The text of 1-based `line`, without its line break.
fn line_text(source : String, line : Int) -> String {
  let lines = source.split("\n").map(v => v.to_owned()).collect()
  if line - 1 < lines.length() {
    let text = lines[line - 1]
    if text.has_suffix("\r") {
      text.view(end_offset=text.length() - 1).to_owned()
    } else {
      text
    }
  } else {
    ""
  }
}