| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- import * as arrays from '../../../base/common/arrays.js';
- import { LineTokens } from './lineTokens.js';
- /**
- * Represents sparse tokens in a text model.
- */
- export class SparseTokensStore {
- constructor(languageIdCodec) {
- this._pieces = [];
- this._isComplete = false;
- this._languageIdCodec = languageIdCodec;
- }
- flush() {
- this._pieces = [];
- this._isComplete = false;
- }
- isEmpty() {
- return (this._pieces.length === 0);
- }
- set(pieces, isComplete) {
- this._pieces = pieces || [];
- this._isComplete = isComplete;
- }
- setPartial(_range, pieces) {
- // console.log(`setPartial ${_range} ${pieces.map(p => p.toString()).join(', ')}`);
- let range = _range;
- if (pieces.length > 0) {
- const _firstRange = pieces[0].getRange();
- const _lastRange = pieces[pieces.length - 1].getRange();
- if (!_firstRange || !_lastRange) {
- return _range;
- }
- range = _range.plusRange(_firstRange).plusRange(_lastRange);
- }
- let insertPosition = null;
- for (let i = 0, len = this._pieces.length; i < len; i++) {
- const piece = this._pieces[i];
- if (piece.endLineNumber < range.startLineNumber) {
- // this piece is before the range
- continue;
- }
- if (piece.startLineNumber > range.endLineNumber) {
- // this piece is after the range, so mark the spot before this piece
- // as a good insertion position and stop looping
- insertPosition = insertPosition || { index: i };
- break;
- }
- // this piece might intersect with the range
- piece.removeTokens(range);
- if (piece.isEmpty()) {
- // remove the piece if it became empty
- this._pieces.splice(i, 1);
- i--;
- len--;
- continue;
- }
- if (piece.endLineNumber < range.startLineNumber) {
- // after removal, this piece is before the range
- continue;
- }
- if (piece.startLineNumber > range.endLineNumber) {
- // after removal, this piece is after the range
- insertPosition = insertPosition || { index: i };
- continue;
- }
- // after removal, this piece contains the range
- const [a, b] = piece.split(range);
- if (a.isEmpty()) {
- // this piece is actually after the range
- insertPosition = insertPosition || { index: i };
- continue;
- }
- if (b.isEmpty()) {
- // this piece is actually before the range
- continue;
- }
- this._pieces.splice(i, 1, a, b);
- i++;
- len++;
- insertPosition = insertPosition || { index: i };
- }
- insertPosition = insertPosition || { index: this._pieces.length };
- if (pieces.length > 0) {
- this._pieces = arrays.arrayInsert(this._pieces, insertPosition.index, pieces);
- }
- // console.log(`I HAVE ${this._pieces.length} pieces`);
- // console.log(`${this._pieces.map(p => p.toString()).join('\n')}`);
- return range;
- }
- isComplete() {
- return this._isComplete;
- }
- addSparseTokens(lineNumber, aTokens) {
- if (aTokens.getLineContent().length === 0) {
- // Don't do anything for empty lines
- return aTokens;
- }
- const pieces = this._pieces;
- if (pieces.length === 0) {
- return aTokens;
- }
- const pieceIndex = SparseTokensStore._findFirstPieceWithLine(pieces, lineNumber);
- const bTokens = pieces[pieceIndex].getLineTokens(lineNumber);
- if (!bTokens) {
- return aTokens;
- }
- const aLen = aTokens.getCount();
- const bLen = bTokens.getCount();
- let aIndex = 0;
- const result = [];
- let resultLen = 0;
- let lastEndOffset = 0;
- const emitToken = (endOffset, metadata) => {
- if (endOffset === lastEndOffset) {
- return;
- }
- lastEndOffset = endOffset;
- result[resultLen++] = endOffset;
- result[resultLen++] = metadata;
- };
- for (let bIndex = 0; bIndex < bLen; bIndex++) {
- const bStartCharacter = bTokens.getStartCharacter(bIndex);
- const bEndCharacter = bTokens.getEndCharacter(bIndex);
- const bMetadata = bTokens.getMetadata(bIndex);
- const bMask = (((bMetadata & 1 /* MetadataConsts.SEMANTIC_USE_ITALIC */) ? 2048 /* MetadataConsts.ITALIC_MASK */ : 0)
- | ((bMetadata & 2 /* MetadataConsts.SEMANTIC_USE_BOLD */) ? 4096 /* MetadataConsts.BOLD_MASK */ : 0)
- | ((bMetadata & 4 /* MetadataConsts.SEMANTIC_USE_UNDERLINE */) ? 8192 /* MetadataConsts.UNDERLINE_MASK */ : 0)
- | ((bMetadata & 8 /* MetadataConsts.SEMANTIC_USE_STRIKETHROUGH */) ? 16384 /* MetadataConsts.STRIKETHROUGH_MASK */ : 0)
- | ((bMetadata & 16 /* MetadataConsts.SEMANTIC_USE_FOREGROUND */) ? 16744448 /* MetadataConsts.FOREGROUND_MASK */ : 0)
- | ((bMetadata & 32 /* MetadataConsts.SEMANTIC_USE_BACKGROUND */) ? 4278190080 /* MetadataConsts.BACKGROUND_MASK */ : 0)) >>> 0;
- const aMask = (~bMask) >>> 0;
- // push any token from `a` that is before `b`
- while (aIndex < aLen && aTokens.getEndOffset(aIndex) <= bStartCharacter) {
- emitToken(aTokens.getEndOffset(aIndex), aTokens.getMetadata(aIndex));
- aIndex++;
- }
- // push the token from `a` if it intersects the token from `b`
- if (aIndex < aLen && aTokens.getStartOffset(aIndex) < bStartCharacter) {
- emitToken(bStartCharacter, aTokens.getMetadata(aIndex));
- }
- // skip any tokens from `a` that are contained inside `b`
- while (aIndex < aLen && aTokens.getEndOffset(aIndex) < bEndCharacter) {
- emitToken(aTokens.getEndOffset(aIndex), (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask));
- aIndex++;
- }
- if (aIndex < aLen) {
- emitToken(bEndCharacter, (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask));
- if (aTokens.getEndOffset(aIndex) === bEndCharacter) {
- // `a` ends exactly at the same spot as `b`!
- aIndex++;
- }
- }
- else {
- const aMergeIndex = Math.min(Math.max(0, aIndex - 1), aLen - 1);
- // push the token from `b`
- emitToken(bEndCharacter, (aTokens.getMetadata(aMergeIndex) & aMask) | (bMetadata & bMask));
- }
- }
- // push the remaining tokens from `a`
- while (aIndex < aLen) {
- emitToken(aTokens.getEndOffset(aIndex), aTokens.getMetadata(aIndex));
- aIndex++;
- }
- return new LineTokens(new Uint32Array(result), aTokens.getLineContent(), this._languageIdCodec);
- }
- static _findFirstPieceWithLine(pieces, lineNumber) {
- let low = 0;
- let high = pieces.length - 1;
- while (low < high) {
- let mid = low + Math.floor((high - low) / 2);
- if (pieces[mid].endLineNumber < lineNumber) {
- low = mid + 1;
- }
- else if (pieces[mid].startLineNumber > lineNumber) {
- high = mid - 1;
- }
- else {
- while (mid > low && pieces[mid - 1].startLineNumber <= lineNumber && lineNumber <= pieces[mid - 1].endLineNumber) {
- mid--;
- }
- return mid;
- }
- }
- return low;
- }
- acceptEdit(range, eolCount, firstLineLength, lastLineLength, firstCharCode) {
- for (const piece of this._pieces) {
- piece.acceptEdit(range, eolCount, firstLineLength, lastLineLength, firstCharCode);
- }
- }
- }
|