///|
/// Physical lines include their exact terminator, excluding a leading BOM.
pub(all) struct Line {
start : Int
content_end : Int
end : Int
number : Int
} derive(Eq, Debug)
///|
pub fn scan_lines(source : String) -> Array[Line] {
let lines : Array[Line] = []
let mut i = if source.length() > 0 && source[0] == '\uFEFF' { 1 } else { 0 }
let mut number = 1
while i < source.length() {
let start = i
while i < source.length() && source[i] != '\r' && source[i] != '\n' {
i += 1
}
let content_end = i
if i < source.length() {
if source[i] == '\r' && i + 1 < source.length() && source[i + 1] == '\n' {
i += 2
} else {
i += 1
}
}
lines.push({ start, content_end, end: i, number })
number += 1
}
lines
}
///|
fn horizontal(c : UInt16) -> Bool {
c == ' ' || c == '\t'
}
///|
fn trim_bounds(source : String, start : Int, end : Int) -> (Int, Int) {
let mut a = start
let mut b = end
while a < b && horizontal(source[a]) {
a += 1
}
while b > a && horizontal(source[b - 1]) {
b -= 1
}
(a, b)
}
///|
fn text(source : String, start : Int, end : Int) -> String {
source[start:end].to_owned()
}
///|
fn line_span(line : Line, start : Int, end : Int) -> Span {
span(start, end, line.number, start - line.start + 1)
}