///| Source-shaped readonly port of Monaco's `ContiguousTokensStore`.
///
/// Source: `vscode/src/vs/editor/common/tokens/contiguousTokensStore.ts` at
/// checked-in oracle.
///
/// The local representation keeps Monaco's three observable store states:
/// `Missing` is an uninitialized sparse slot, `Empty` is the compact empty-line
/// sentinel, and `Words` holds encoded `[endOffset, metadata]` pairs. MoonBit
/// has one owned `Array[UInt]` representation, so the source's typed-array
/// versus ArrayBuffer identity branches do not exist. Incremental editing and
/// sparse semantic-token application remain outside this readonly package.
///|
/// One initialized or uninitialized syntactic-token slot.
/// Source: `contiguousTokensStore.ts:20` and `contiguousTokensEditing.ts:8`.
priv enum SyntacticLineTokens {
Missing
Empty
Words(Array[UInt32])
}
///|
/// Inclusive changed-line interval produced by one contiguous batch.
/// Source: `contiguousTokensStore.ts:213-244`.
pub struct ContiguousTokensStoreChange {
from_line_number : Int32
to_line_number : Int32
}
///|
/// Ordered changed intervals from `set_multiline_tokens`.
/// Source: `contiguousTokensStore.ts:213-244`.
pub struct ContiguousTokensStoreUpdate {
changes : Array[ContiguousTokensStoreChange]
}
///|
/// Per-model contiguous syntactic token storage.
/// Source: `contiguousTokensStore.ts:19-22`.
struct ContiguousTokensStore {
line_tokens : Array[SyntacticLineTokens]
mut len : Int32
language_id_codec : @services.LanguageIdCodec
}
///|
/// Creates an empty store retaining the shared language-id codec.
/// Source: `contiguousTokensStore.ts:24-28`.
pub fn ContiguousTokensStore::ContiguousTokensStore(
language_id_codec : @services.LanguageIdCodec,
) -> ContiguousTokensStore {
{ line_tokens: [], len: 0, language_id_codec, }
}
///|
/// Drops every initialized slot and resets the logical initialized prefix.
/// Source: `contiguousTokensStore.ts:30-33`.
pub fn ContiguousTokensStore::flush(self : ContiguousTokensStore) -> Unit {
self.line_tokens.clear()
self.len = 0
}
///|
/// True when the physical per-line array is nonempty.
/// Source: `contiguousTokensStore.ts:35-37`.
#warnings("-unused_value")
fn ContiguousTokensStore::has_tokens(self : ContiguousTokensStore) -> Bool {
self.line_tokens.length() > 0
}
///|
/// Passively reads stored words or returns one top-level-language fallback
/// token. This operation never invokes a tokenizer.
/// Source: `contiguousTokensStore.ts:39-53`.
pub fn ContiguousTokensStore::get_tokens(
self : ContiguousTokensStore,
top_level_language_id : String,
line_index : Int32,
line_text : String,
) -> LineTokens {
let raw_line_tokens = if line_index < self.len {
self.line_tokens[line_index]
} else {
Missing
}
match raw_line_tokens {
Words(tokens) => LineTokens(tokens, line_text, self.language_id_codec)
Missing | Empty => {
let line_tokens : Array[UInt32] = [
line_text.length().reinterpret_as_uint(),
self.default_metadata(top_level_language_id),
]
LineTokens(line_tokens, line_text, self.language_id_codec)
}
}
}
///|
/// Builds Monaco's exact fallback metadata, including its no-grammar balanced
/// brackets guess.
/// Source: `contiguousTokensStore.ts:247-257`.
fn ContiguousTokensStore::default_metadata(
self : ContiguousTokensStore,
top_level_language_id : String,
) -> UInt32 {
let language_id = self.language_id_codec.encode_language_id(
top_level_language_id,
)
((language_id << METADATA_LANGUAGEID_OFFSET) |
(STANDARD_TOKEN_TYPE_OTHER << METADATA_TOKEN_TYPE_OFFSET) |
(FONT_STYLE_NONE << METADATA_FONT_STYLE_OFFSET) |
(COLOR_ID_DEFAULT_FOREGROUND << METADATA_FOREGROUND_OFFSET) |
(COLOR_ID_DEFAULT_BACKGROUND << METADATA_BACKGROUND_OFFSET) |
(1 << METADATA_BALANCED_BRACKETS_OFFSET)).reinterpret_as_uint()
}
///|
/// Normalizes one tokenizer result before storage. `Missing` is never returned:
/// it is reserved for slots that have not been written.
/// Source: `contiguousTokensStore.ts:55-85`.
fn ContiguousTokensStore::massage_tokens(
self : ContiguousTokensStore,
top_level_language_id : String,
line_text_length : Int32,
tokens : Array[UInt32]?,
) -> SyntacticLineTokens {
let encoded_language_id = self.language_id_codec.encode_language_id(
top_level_language_id,
)
if line_text_length == 0 {
let has_different_language_id = tokens is Some([_, second, ..]) &&
TokenMetadata(second).get_language_id() != encoded_language_id
if !has_different_language_id {
return Empty
}
}
match tokens {
None | Some([]) =>
Words([
line_text_length.reinterpret_as_uint(),
self.default_metadata(top_level_language_id),
])
Some(words) => {
words[words.length() - 2] = line_text_length.reinterpret_as_uint()
Words(words)
}
}
}
///|
/// Grows the initialized prefix through `line_index`, filling holes with
/// `Missing` exactly as Monaco fills them with null.
/// Source: `contiguousTokensStore.ts:87-92`.
fn ContiguousTokensStore::ensure_line(
self : ContiguousTokensStore,
line_index : Int32,
) -> Unit {
while line_index >= self.len {
self.line_tokens.push(Missing)
self.len += 1
}
}
///|
/// Stores one normalized line. Equality is checked only when requested; the
/// unchecked suffix lane always reports false.
/// Source: `contiguousTokensStore.ts:117-127`.
fn ContiguousTokensStore::set_tokens(
self : ContiguousTokensStore,
top_level_language_id : String,
line_index : Int32,
line_text_length : Int32,
tokens : Array[UInt32]?,
check_equality : Bool,
) -> Bool {
let new_tokens = self.massage_tokens(
top_level_language_id, line_text_length, tokens,
)
self.ensure_line(line_index)
let old_tokens = self.line_tokens[line_index]
self.line_tokens[line_index] = new_tokens
if check_equality {
!syntactic_line_tokens_equal(old_tokens, new_tokens)
} else {
false
}
}
///|
/// Preserves the source equality distinctions and compares `Words`
/// element-by-element.
/// Source: `contiguousTokensStore.ts:129-146`.
fn syntactic_line_tokens_equal(
left : SyntacticLineTokens,
right : SyntacticLineTokens,
) -> Bool {
match (left, right) {
(Missing, Missing) | (Empty, Empty) => true
(Words(left_words), Words(right_words)) => left_words == right_words
_ => false
}
}
///|
/// Applies batches in order. Within each batch it checks equality only through
/// the first changed line, then writes the remaining suffix unchecked and
/// returns one inclusive min/max interval.
/// Source: `contiguousTokensStore.ts:213-244`.
pub fn ContiguousTokensStore::set_multiline_tokens(
self : ContiguousTokensStore,
tokens : Array[ContiguousMultilineTokens],
top_level_language_id : String,
get_line_length : (Int32) -> Int32,
) -> ContiguousTokensStoreUpdate {
if tokens.length() == 0 {
return { changes: [], }
}
let ranges : Array[ContiguousTokensStoreChange] = []
for element in tokens {
let mut min_changed_line_number = 0
let mut max_changed_line_number = 0
let mut has_change = false
for line_number in element.start_line_number()..<=element.end_line_number() {
if has_change {
ignore(
self.set_tokens(
top_level_language_id,
line_number - 1,
get_line_length(line_number),
Some(element.get_line_tokens(line_number)),
false,
),
)
max_changed_line_number = line_number
} else {
let line_has_change = self.set_tokens(
top_level_language_id,
line_number - 1,
get_line_length(line_number),
Some(element.get_line_tokens(line_number)),
true,
)
if line_has_change {
has_change = true
min_changed_line_number = line_number
max_changed_line_number = line_number
}
}
}
if has_change {
ranges.push({
from_line_number: min_changed_line_number,
to_line_number: max_changed_line_number,
})
}
}
{ changes: ranges, }
}