///|
fn is_mapping_separator(ch : Char) -> Bool {
ch == ',' || ch == ';'
}
///|
fn decode_segment_values(
input : StringView,
offset : Int,
) -> Result[(Array[Int], Int), SourceMapError] {
let values : Array[Int] = []
let mut index = offset
while index < input.length() {
let ch = input.unsafe_get(index).unsafe_to_char()
if is_mapping_separator(ch) {
break
}
match decode_vlq(input, index) {
Ok((value, next)) => {
values.push(value)
index = next
}
Err(err) => return Err(err)
}
}
Ok((values, index))
}
///|
fn segment_error(
line : Int,
segment : Int,
values : Int,
reason : String,
) -> SourceMapError {
InvalidSegment(line~, segment~, values~, reason~)
}
///|
pub fn decode_mappings(
input : StringView,
) -> Result[Array[MappingSegment], SourceMapError] {
let segments : Array[MappingSegment] = []
let mut generated_line = 0
let mut generated_column = 0
let mut source_index = 0
let mut original_line = 0
let mut original_column = 0
let mut name_index = 0
let mut index = 0
let mut segment_index = 0
let mut line_has_segment = false
while index < input.length() {
let ch = input.unsafe_get(index).unsafe_to_char()
if ch == ';' {
generated_line += 1
generated_column = 0
segment_index = 0
line_has_segment = false
index += 1
} else if ch == ',' {
if !line_has_segment {
return Err(
segment_error(generated_line, segment_index, 0, "empty segment"),
)
}
segment_index += 1
index += 1
} else {
match decode_segment_values(input, index) {
Ok((values, next)) => {
if values.length() == 0 {
return Err(
segment_error(generated_line, segment_index, 0, "empty segment"),
)
}
if values.length() == 1 {
generated_column += values[0]
segments.push(
MappingSegment::unmapped(generated_line, generated_column),
)
} else if values.length() == 4 || values.length() == 5 {
generated_column += values[0]
source_index += values[1]
original_line += values[2]
original_column += values[3]
if values.length() == 5 {
name_index += values[4]
segments.push(
MappingSegment::mapped(
generated_line,
generated_column,
source_index,
original_line,
original_column,
name_index~,
),
)
} else {
segments.push(
MappingSegment::mapped(
generated_line, generated_column, source_index, original_line,
original_column,
),
)
}
} else {
return Err(
segment_error(
generated_line,
segment_index,
values.length(),
"segment must contain 1, 4 or 5 VLQ fields",
),
)
}
line_has_segment = true
index = next
}
Err(err) => return Err(err)
}
}
}
Ok(segments)
}
///|
fn sorted_mapping_copy(
segments : ArrayView[MappingSegment],
) -> Array[MappingSegment] {
let copy = segments.to_owned()
copy.sort_by((a, b) => {
let by_line = a.generated_line.compare(b.generated_line)
if by_line != 0 {
by_line
} else {
a.generated_column.compare(b.generated_column)
}
})
copy
}
///|
fn encode_segment_values(values : ArrayView[Int]) -> String {
encode_vlq_values(values)
}
///|
pub fn encode_mappings(segments : ArrayView[MappingSegment]) -> String {
let ordered = sorted_mapping_copy(segments)
let buf = StringBuilder()
let mut current_line = 0
let mut previous_generated_column = 0
let mut previous_source_index = 0
let mut previous_original_line = 0
let mut previous_original_column = 0
let mut previous_name_index = 0
let mut first_in_line = true
for segment in ordered {
while current_line < segment.generated_line {
buf.write_char(';')
current_line += 1
previous_generated_column = 0
first_in_line = true
}
if !first_in_line {
buf.write_char(',')
}
let fields : Array[Int] = []
fields.push(segment.generated_column - previous_generated_column)
previous_generated_column = segment.generated_column
match
(segment.source_index, segment.original_line, segment.original_column) {
(Some(source), Some(original_line), Some(original_column)) => {
fields.push(source - previous_source_index)
fields.push(original_line - previous_original_line)
fields.push(original_column - previous_original_column)
previous_source_index = source
previous_original_line = original_line
previous_original_column = original_column
if segment.name_index is Some(name) {
fields.push(name - previous_name_index)
previous_name_index = name
}
}
_ => ()
}
buf.write_string(encode_segment_values(fields))
first_in_line = false
}
buf.to_string()
}
///|
pub fn SourceMap::encoded_mappings(self : SourceMap) -> String {
encode_mappings(self.mappings)
}