// Ported from `vscode/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts`
// (`DecorationToRender`, `DedupOverlay._render`) and the decoration-collection
// half of
// `vscode/src/vs/editor/browser/viewParts/linesDecorations/linesDecorations.ts`
// (`LinesDecorationsOverlay._getDecorations`). Kept DOM-free in the view-model
// tier so the dedup/expansion logic is unit-testable; the margin view part
// turns the per-line class names into `.cldr` nodes.
///|
/// A decoration to render along margin lines `start_line_number` through
/// `end_line_number` — Monaco's `DecorationToRender` (`glyphMargin.ts`).
/// `tooltip` (`linesDecorationsTooltip`) is not carried by the viewer's
/// decoration options; `z_index` is kept for parity but — like Monaco's
/// `LinesDecorationsOverlay` — does not affect the emitted order.
pub struct DecorationToRender {
start_line_number : Int32
end_line_number : Int32
class_name : String
z_index : Int32
} derive(Eq, Debug)
///|
/// `LinesDecorationsOverlay._getDecorations`: the viewport decorations that
/// carry a `linesDecorationsClassName` — plus, on their first line only, a
/// `firstLineDecorationClassName` (the folding chevron's lane) — as render
/// entries. Ranges are view-space (the viewport decoration collection
/// already converted them).
pub fn lines_decorations_to_render(
decorations : Array[ViewModelDecoration],
) -> Array[DecorationToRender] {
let result : Array[DecorationToRender] = []
for decoration in decorations {
if decoration.options.lines_decorations_class_name is Some(class_name) {
result.push({
start_line_number: decoration.range.start_line_number,
end_line_number: decoration.range.end_line_number,
class_name,
z_index: decoration.options.z_index,
})
}
if decoration.options.first_line_decoration_class_name is Some(class_name) {
result.push({
start_line_number: decoration.range.start_line_number,
end_line_number: decoration.range.start_line_number,
class_name,
z_index: decoration.options.z_index,
})
}
}
result
}
///|
/// `DedupOverlay._render`: expands the decorations into one class-name list
/// per visible line, skipping a class name already emitted on a line by an
/// earlier decoration of the same class. Sorting groups equal class names so
/// the previous-decoration dedup window works; `@base_common.compare` also
/// preserves JavaScript's UTF-16 lexicographic order for different classes.
pub fn dedup_margin_decorations(
visible_start_line_number : Int32,
visible_end_line_number : Int32,
decorations : Array[DecorationToRender],
) -> Array[Array[String]] {
let output : Array[Array[String]] = []
for _ in visible_start_line_number..<=visible_end_line_number {
output.push([])
}
if decorations.length() == 0 {
return output
}
// Sort decorations by className, then by startLineNumber and then by
// endLineNumber (`glyphMargin.ts DedupOverlay._render`).
decorations.sort_by((a, b) => {
if a.class_name == b.class_name {
if a.start_line_number == b.start_line_number {
a.end_line_number - b.end_line_number
} else {
a.start_line_number - b.start_line_number
}
} else {
@base_common.compare(a.class_name, b.class_name)
}
})
let mut prev_class_name : String? = None
let mut prev_end_line_index = 0
for decoration in decorations {
let class_name = decoration.class_name
let mut start_line_index = (decoration.start_line_number -
visible_start_line_number).max(0)
let end_line_index = (decoration.end_line_number - visible_start_line_number).min(
visible_end_line_number - visible_start_line_number,
)
if prev_class_name is Some(prev_class) && prev_class == class_name {
// Here we avoid rendering the same className multiple times on the
// same line.
start_line_index = start_line_index.max(prev_end_line_index + 1)
prev_end_line_index = prev_end_line_index.max(end_line_index)
} else {
prev_class_name = Some(class_name)
prev_end_line_index = end_line_index
}
for line_index in start_line_index..<=prev_end_line_index {
output[line_index].push(class_name)
}
}
output
}
///|
pub extend DecorationToRender with Debug::{to_repr}
///|
pub extend DecorationToRender with Eq::{equal, not_equal}