///|
pub struct GeneratedLineInfo {
line : Int
start_offset : Int
end_offset : Int
length : Int
has_line_break : Bool
} derive(Eq, Debug, ToJson)
///|
fn is_line_break(ch : Char) -> Bool {
ch == '\n' || ch == '\r'
}
///|
fn line_break_width(input : StringView, index : Int) -> Int {
let ch = input.unsafe_get(index).unsafe_to_char()
if ch == '\r' && index + 1 < input.length() {
let next = input.unsafe_get(index + 1).unsafe_to_char()
if next == '\n' {
2
} else {
1
}
} else {
1
}
}
///|
pub fn line_table(input : StringView) -> Array[GeneratedLineInfo] {
let lines : Array[GeneratedLineInfo] = []
let mut line = 0
let mut start_offset = 0
let mut column = 0
let mut index = 0
while index < input.length() {
let ch = input.unsafe_get(index).unsafe_to_char()
if is_line_break(ch) {
lines.push({
line,
start_offset,
end_offset: index,
length: column,
has_line_break: true,
})
let width = line_break_width(input, index)
index += width
start_offset = index
column = 0
line += 1
} else {
column += 1
index += 1
}
}
lines.push({
line,
start_offset,
end_offset: input.length(),
length: column,
has_line_break: false,
})
lines
}
///|
pub fn generated_line_lengths(input : StringView) -> Array[Int] {
line_table(input).map(item => item.length)
}
///|
pub fn generated_line_count(input : StringView) -> Int {
line_table(input).length()
}
///|
pub fn generated_offset_to_position(
input : StringView,
offset : Int,
) -> GeneratedPosition? {
if offset < 0 || offset > input.length() {
return None
}
for line in line_table(input) {
if offset >= line.start_offset && offset <= line.end_offset {
return Some({ line: line.line, column: offset - line.start_offset })
}
}
None
}
///|
pub fn generated_position_to_offset(
input : StringView,
line : Int,
column : Int,
) -> Int? {
if column < 0 {
return None
}
for item in line_table(input) {
if item.line == line && column <= item.length {
return Some(item.start_offset + column)
}
}
None
}
///|
pub fn SourceMap::coverage_for_text(
self : SourceMap,
generated : StringView,
) -> CoverageReport {
self.coverage_for_lengths(generated_line_lengths(generated))
}
///|
pub fn SourceMap::coverage_text_report_for_text(
self : SourceMap,
generated : StringView,
) -> String {
self.coverage_for_text(generated).render()
}
///|
pub fn GeneratedLineInfo::summary(self : GeneratedLineInfo) -> String {
let suffix = if self.has_line_break { " break" } else { "" }
"line=\{self.line} offsets=\{self.start_offset}..\{self.end_offset} length=\{self.length}\{suffix}"
}
///|
pub fn line_table_report(input : StringView) -> String {
let buf = StringBuilder()
buf.write_string("Generated line table\n")
for item in line_table(input) {
buf.write_string(item.summary())
buf.write_char('\n')
}
buf.to_string()
}