///|
/// DOM span/character coordinate for a rendered line.
pub(all) struct DomPosition {
part_index : Int32
char_index : Int32
} derive(Eq, Debug)
///|
/// Mapping entry from one line column to rendered DOM coordinates and visible
/// monospace column.
pub struct CharacterMappingEntry {
part_index : Int32
char_index : Int32
visible_column : Int32
} derive(Eq, Debug)
///|
/// Column-to-DOM mapping produced while rendering a line.
pub struct CharacterMapping {
length : Int32
entries : Array[CharacterMappingEntry]
} derive(Eq, Debug)
///|
/// Creates a mapping with `length` column slots.
pub fn CharacterMapping::CharacterMapping(length : Int32) -> CharacterMapping {
let entries : Array[CharacterMappingEntry] = []
for _ in 0.. Unit {
let index = column - 1
if index >= 0 && index < self.entries.length() {
self.entries[index] = { part_index, char_index, visible_column, }
}
}
///|
/// Returns the DOM position nearest a one-based line column.
pub fn CharacterMapping::get_dom_position(
self : CharacterMapping,
column : Int32,
) -> DomPosition {
if self.length == 0 {
return { part_index: 0, char_index: 0, }
}
let index = (column - 1).clamp(min=0, max=self.length - 1)
let entry = self.entries[index]
{ part_index: entry.part_index, char_index: entry.char_index, }
}
///|
/// Returns the one-based line column nearest a DOM position, transcribing
/// Monaco's `CharacterMapping.getColumn`/`partDataToCharOffset`. The mapping
/// entries are laid out left-to-right, so their `(part_index, char_index)`
/// ranks increase monotonically; a binary search brackets the DOM position
/// between two columns and returns the nearer one. `part_length` bounds the
/// "sitting between parts" case, where the upper column starts a new part.
pub fn CharacterMapping::get_column(
self : CharacterMapping,
dom_position : DomPosition,
part_length : Int32,
) -> Int32 {
self.part_data_to_char_offset(
dom_position.part_index,
part_length,
dom_position.char_index,
) +
1
}
///|
fn CharacterMapping::part_data_to_char_offset(
self : CharacterMapping,
part_index : Int32,
part_length : Int32,
char_index : Int32,
) -> Int32 {
if self.length == 0 {
return 0
}
let search = mapping_rank(part_index, char_index)
let (min, max) = for min = 0, max = self.length - 1; min + 1 < max; {
let mid = (min + max) / 2
let mid_entry = mapping_rank_entry(self.entries[mid])
if mid_entry == search {
break (mid, mid)
} else if mid_entry > search {
continue min, mid
} else {
continue mid, max
}
} nobreak {
(min, max)
}
if min == max {
return min
}
let min_entry = self.entries[min]
let max_entry = self.entries[max]
if mapping_rank_entry(min_entry) == search {
return min
}
if mapping_rank_entry(max_entry) == search {
return max
}
let min_char_index = min_entry.char_index
let max_char_index = if min_entry.part_index != max_entry.part_index {
// Sitting between parts: the upper bracket starts a new part, so its
// distance is measured against the hit part's length.
part_length
} else {
max_entry.char_index
}
let min_entry_distance = char_index - min_char_index
let max_entry_distance = max_char_index - char_index
if min_entry_distance <= max_entry_distance {
min
} else {
max
}
}
///|
/// Visible monospace column for a one-based line column.
pub fn CharacterMapping::get_horizontal_offset(
self : CharacterMapping,
column : Int32,
) -> Int32 {
if self.length == 0 {
return 0
}
let index = (column - 1).clamp(min=0, max=self.length - 1)
self.entries[index].visible_column
}
///|
/// Horizontal CSS-pixel offset for a one-based line column.
pub fn CharacterMapping::column_horizontal_offset(
self : CharacterMapping,
column : Int32,
space_width~ : Double,
) -> Double {
self.get_horizontal_offset(column).to_double() * space_width
}
///|
/// Copies the raw mapping entries for diagnostics and tests.
pub fn CharacterMapping::inflate(
self : CharacterMapping,
) -> Array[CharacterMappingEntry] {
self.entries.copy()
}
///|
fn mapping_rank(part_index : Int32, char_index : Int32) -> Int32 {
part_index * 65536 + char_index
}
///|
fn mapping_rank_entry(entry : CharacterMappingEntry) -> Int32 {
mapping_rank(entry.part_index, entry.char_index)
}
///|
/// Render output when the caller owns the output builder.
pub struct RenderLineOutput {
character_mapping : CharacterMapping
contains_foreign_elements : ForeignElementType
} derive(Eq, Debug)
///|
/// Render output including generated HTML.
pub struct RenderLineOutput2 {
html : String
character_mapping : CharacterMapping
contains_foreign_elements : ForeignElementType
} derive(Eq, Debug)
///|
pub extend CharacterMapping with Debug::{to_repr}
///|
pub extend CharacterMapping with Eq::{equal, not_equal}
///|
pub extend CharacterMappingEntry with Debug::{to_repr}
///|
pub extend CharacterMappingEntry with Eq::{equal, not_equal}
///|
pub extend DomPosition with Debug::{to_repr}
///|
pub extend DomPosition with Eq::{equal, not_equal}
///|
pub extend RenderLineOutput with Debug::{to_repr}
///|
pub extend RenderLineOutput with Eq::{equal, not_equal}
///|
pub extend RenderLineOutput2 with Debug::{to_repr}
///|
pub extend RenderLineOutput2 with Eq::{equal, not_equal}