///|
/// A single cursor, a faithful port of Monaco's `oneCursor.ts` `Cursor`. It
/// keeps both a model-coordinate and a view-coordinate `SingleCursorState` in
/// sync through the `CursorContext`'s coordinates converter. Setting one side
/// derives the other (`set_state`), exactly as Monaco's `_setState` does.
///
/// Model validation and internal-version snapshots belong to
/// `CursorsController`; this lower owner only derives the missing coordinate
/// side. Editable marker-tracked ranges remain outside the readonly contract.
priv struct Cursor {
mut model_state : SingleCursorState
mut view_state : SingleCursorState
}
///|
/// A fresh cursor collapsed at the document origin.
fn Cursor::Cursor() -> Cursor {
let origin = @base_common.Position(1, 1)
{
model_state: SingleCursorState::collapsed(origin),
view_state: SingleCursorState::collapsed(origin),
}
}
///|
/// Captures the model and view sides together.
fn Cursor::as_cursor_state(self : Cursor) -> CursorState {
CursorState(self.model_state, self.view_state)
}
///|
/// Source-shaped nullable state entry. Exactly one side is normally supplied;
/// when both are present the model side remains authoritative and is
/// reprojected through the live converter. The source's simultaneous
/// model/view cross-validation surface is intentionally owned by the
/// controller's documented reduced seam. A double-null input is a no-op.
fn Cursor::set_state(
self : Cursor,
context : CursorContext,
model_state : SingleCursorState?,
view_state : SingleCursorState?,
) -> Bool {
match (model_state, view_state) {
(None, None) => false
(Some(model_state), _) => {
self.set_model_state(context, model_state)
true
}
(None, Some(view_state)) => {
self.set_view_state(context, view_state)
true
}
}
}
///|
/// Sets the cursor from a view-coordinate state, deriving the model state by
/// converting every endpoint through the coordinates converter. Mirrors the
/// "we only have the view state => compute the model state" branch of
/// Monaco's `_setState`.
fn Cursor::set_view_state(
self : Cursor,
context : CursorContext,
view_state : SingleCursorState,
) -> Unit {
self.view_state = view_state
let model_selection_start = context.view_range_to_model(
Range(
view_state.selection_start_start.line_number,
view_state.selection_start_start.column,
view_state.selection_start_end.line_number,
view_state.selection_start_end.column,
),
)
self.model_state = SingleCursorState(
Position(
model_selection_start.start_line_number,
model_selection_start.start_column,
),
Position(
model_selection_start.end_line_number,
model_selection_start.end_column,
),
view_state.selection_start_kind,
context.view_to_model(view_state.position),
selection_start_leftover_visible_columns=view_state.selection_start_leftover_visible_columns,
leftover_visible_columns=view_state.leftover_visible_columns,
)
}
///|
/// Sets the cursor from a model-coordinate state, deriving the view state by
/// converting every endpoint through the coordinates converter. Mirrors the
/// "we only have the model state => compute the view state" branch of
/// Monaco's `_setState`; used to re-project an existing selection after the
/// view model reflows (wrapping and injected text).
fn Cursor::set_model_state(
self : Cursor,
context : CursorContext,
model_state : SingleCursorState,
) -> Unit {
self.model_state = model_state
self.view_state = SingleCursorState(
context.model_to_view(model_state.selection_start_start),
context.model_to_view(model_state.selection_start_end),
model_state.selection_start_kind,
context.model_to_view(model_state.position),
selection_start_leftover_visible_columns=model_state.selection_start_leftover_visible_columns,
leftover_visible_columns=model_state.leftover_visible_columns,
)
}
///|
/// Re-derives the view state from the current model state through `context`.
/// Used when the view model is rebuilt so an existing selection stays anchored
/// to the same model positions after the reflow.
fn Cursor::reproject(self : Cursor, context : CursorContext) -> Unit {
self.set_model_state(context, self.model_state)
}