Path: blob/main/src/vs/editor/common/tokens/contiguousMultilineTokens.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as arrays from '../../../base/common/arrays.js';6import { readUInt32BE, writeUInt32BE } from '../../../base/common/buffer.js';7import { Position } from '../core/position.js';8import { IRange } from '../core/range.js';9import { countEOL } from '../core/misc/eolCounter.js';10import { ContiguousTokensEditing } from './contiguousTokensEditing.js';11import { LineRange } from '../core/ranges/lineRange.js';1213/**14* Represents contiguous tokens over a contiguous range of lines.15*/16export class ContiguousMultilineTokens {17public static deserialize(buff: Uint8Array, offset: number, result: ContiguousMultilineTokens[]): number {18const view32 = new Uint32Array(buff.buffer);19const startLineNumber = readUInt32BE(buff, offset); offset += 4;20const count = readUInt32BE(buff, offset); offset += 4;21const tokens: Uint32Array[] = [];22for (let i = 0; i < count; i++) {23const byteCount = readUInt32BE(buff, offset); offset += 4;24tokens.push(view32.subarray(offset / 4, offset / 4 + byteCount / 4));25offset += byteCount;26}27result.push(new ContiguousMultilineTokens(startLineNumber, tokens));28return offset;29}3031/**32* The start line number for this block of tokens.33*/34private _startLineNumber: number;3536/**37* The tokens are stored in a binary format. There is an element for each line,38* so `tokens[index]` contains all tokens on line `startLineNumber + index`.39*40* On a specific line, each token occupies two array indices. For token i:41* - at offset 2*i => endOffset42* - at offset 2*i + 1 => metadata43*44*/45private _tokens: (Uint32Array | ArrayBuffer | null)[];4647/**48* (Inclusive) start line number for these tokens.49*/50public get startLineNumber(): number {51return this._startLineNumber;52}5354/**55* (Inclusive) end line number for these tokens.56*/57public get endLineNumber(): number {58return this._startLineNumber + this._tokens.length - 1;59}6061constructor(startLineNumber: number, tokens: Uint32Array[]) {62this._startLineNumber = startLineNumber;63this._tokens = tokens;64}6566getLineRange(): LineRange {67return new LineRange(this._startLineNumber, this._startLineNumber + this._tokens.length);68}6970/**71* @see {@link _tokens}72*/73public getLineTokens(lineNumber: number): Uint32Array | ArrayBuffer | null {74return this._tokens[lineNumber - this._startLineNumber];75}7677public appendLineTokens(lineTokens: Uint32Array): void {78this._tokens.push(lineTokens);79}8081public serializeSize(): number {82let result = 0;83result += 4; // 4 bytes for the start line number84result += 4; // 4 bytes for the line count85for (let i = 0; i < this._tokens.length; i++) {86const lineTokens = this._tokens[i];87if (!(lineTokens instanceof Uint32Array)) {88throw new Error(`Not supported!`);89}90result += 4; // 4 bytes for the byte count91result += lineTokens.byteLength;92}93return result;94}9596public serialize(destination: Uint8Array, offset: number): number {97writeUInt32BE(destination, this._startLineNumber, offset); offset += 4;98writeUInt32BE(destination, this._tokens.length, offset); offset += 4;99for (let i = 0; i < this._tokens.length; i++) {100const lineTokens = this._tokens[i];101if (!(lineTokens instanceof Uint32Array)) {102throw new Error(`Not supported!`);103}104writeUInt32BE(destination, lineTokens.byteLength, offset); offset += 4;105destination.set(new Uint8Array(lineTokens.buffer), offset); offset += lineTokens.byteLength;106}107return offset;108}109110public applyEdit(range: IRange, text: string): void {111const [eolCount, firstLineLength] = countEOL(text);112this._acceptDeleteRange(range);113this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength);114}115116private _acceptDeleteRange(range: IRange): void {117if (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn) {118// Nothing to delete119return;120}121122const firstLineIndex = range.startLineNumber - this._startLineNumber;123const lastLineIndex = range.endLineNumber - this._startLineNumber;124125if (lastLineIndex < 0) {126// this deletion occurs entirely before this block, so we only need to adjust line numbers127const deletedLinesCount = lastLineIndex - firstLineIndex;128this._startLineNumber -= deletedLinesCount;129return;130}131132if (firstLineIndex >= this._tokens.length) {133// this deletion occurs entirely after this block, so there is nothing to do134return;135}136137if (firstLineIndex < 0 && lastLineIndex >= this._tokens.length) {138// this deletion completely encompasses this block139this._startLineNumber = 0;140this._tokens = [];141return;142}143144if (firstLineIndex === lastLineIndex) {145// a delete on a single line146this._tokens[firstLineIndex] = ContiguousTokensEditing.delete(this._tokens[firstLineIndex], range.startColumn - 1, range.endColumn - 1);147return;148}149150if (firstLineIndex >= 0) {151// The first line survives152this._tokens[firstLineIndex] = ContiguousTokensEditing.deleteEnding(this._tokens[firstLineIndex], range.startColumn - 1);153154if (lastLineIndex < this._tokens.length) {155// The last line survives156const lastLineTokens = ContiguousTokensEditing.deleteBeginning(this._tokens[lastLineIndex], range.endColumn - 1);157158// Take remaining text on last line and append it to remaining text on first line159this._tokens[firstLineIndex] = ContiguousTokensEditing.append(this._tokens[firstLineIndex], lastLineTokens);160161// Delete middle lines162this._tokens.splice(firstLineIndex + 1, lastLineIndex - firstLineIndex);163} else {164// The last line does not survive165166// Take remaining text on last line and append it to remaining text on first line167this._tokens[firstLineIndex] = ContiguousTokensEditing.append(this._tokens[firstLineIndex], null);168169// Delete lines170this._tokens = this._tokens.slice(0, firstLineIndex + 1);171}172} else {173// The first line does not survive174175const deletedBefore = -firstLineIndex;176this._startLineNumber -= deletedBefore;177178// Remove beginning from last line179this._tokens[lastLineIndex] = ContiguousTokensEditing.deleteBeginning(this._tokens[lastLineIndex], range.endColumn - 1);180181// Delete lines182this._tokens = this._tokens.slice(lastLineIndex);183}184}185186private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number): void {187188if (eolCount === 0 && firstLineLength === 0) {189// Nothing to insert190return;191}192193const lineIndex = position.lineNumber - this._startLineNumber;194195if (lineIndex < 0) {196// this insertion occurs before this block, so we only need to adjust line numbers197this._startLineNumber += eolCount;198return;199}200201if (lineIndex >= this._tokens.length) {202// this insertion occurs after this block, so there is nothing to do203return;204}205206if (eolCount === 0) {207// Inserting text on one line208this._tokens[lineIndex] = ContiguousTokensEditing.insert(this._tokens[lineIndex], position.column - 1, firstLineLength);209return;210}211212this._tokens[lineIndex] = ContiguousTokensEditing.deleteEnding(this._tokens[lineIndex], position.column - 1);213this._tokens[lineIndex] = ContiguousTokensEditing.insert(this._tokens[lineIndex], position.column - 1, firstLineLength);214215this._insertLines(position.lineNumber, eolCount);216}217218private _insertLines(insertIndex: number, insertCount: number): void {219if (insertCount === 0) {220return;221}222const lineTokens: (Uint32Array | ArrayBuffer | null)[] = [];223for (let i = 0; i < insertCount; i++) {224lineTokens[i] = null;225}226this._tokens = arrays.arrayInsert(this._tokens, insertIndex, lineTokens);227}228}229230231