mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-14 03:58:33 -05:00
Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 (#6381)
* Merge from vscode 8e0f348413f4f616c23a88ae30030efa85811973 * disable strict null check
This commit is contained in:
@@ -153,8 +153,8 @@ export abstract class CommonEditorConfiguration extends Disposable implements ed
|
||||
return true;
|
||||
}
|
||||
|
||||
private static _subsetEquals(base: object, subset: object): boolean {
|
||||
for (let key in subset) {
|
||||
private static _subsetEquals(base: { [key: string]: any }, subset: { [key: string]: any }): boolean {
|
||||
for (const key in subset) {
|
||||
if (hasOwnProperty.call(subset, key)) {
|
||||
const subsetValue = subset[key];
|
||||
const baseValue = base[key];
|
||||
@@ -839,7 +839,7 @@ const editorConfiguration: IConfigurationNode = {
|
||||
enumDescriptions: [
|
||||
nls.localize('editor.gotoLocation.multiple.peek', 'Show peek view of the results (default)'),
|
||||
nls.localize('editor.gotoLocation.multiple.gotoAndPeek', 'Go to the primary result and show a peek view'),
|
||||
nls.localize('editor.gotoLocation.multiple.goto', 'Go to the primary result and ignore others')
|
||||
nls.localize('editor.gotoLocation.multiple.goto', 'Go to the primary result and enable peek-less navigation to others')
|
||||
]
|
||||
},
|
||||
'editor.selectionHighlight': {
|
||||
|
||||
@@ -36,7 +36,7 @@ export class Position {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new postion from this position.
|
||||
* Create a new position from this position.
|
||||
*
|
||||
* @param newLineNumber new line number
|
||||
* @param newColumn new column
|
||||
|
||||
@@ -10,7 +10,7 @@ export class Uint8Matrix {
|
||||
public readonly cols: number;
|
||||
|
||||
constructor(rows: number, cols: number, defaultValue: number) {
|
||||
let data = new Uint8Array(rows * cols);
|
||||
const data = new Uint8Array(rows * cols);
|
||||
for (let i = 0, len = rows * cols; i < len; i++) {
|
||||
data[i] = defaultValue;
|
||||
}
|
||||
@@ -85,8 +85,8 @@ export function toUint32(v: number): number {
|
||||
}
|
||||
|
||||
export function toUint32Array(arr: number[]): Uint32Array {
|
||||
let len = arr.length;
|
||||
let r = new Uint32Array(len);
|
||||
const len = arr.length;
|
||||
const r = new Uint32Array(len);
|
||||
for (let i = 0; i < len; i++) {
|
||||
r[i] = toUint32(arr[i]);
|
||||
}
|
||||
|
||||
@@ -26,25 +26,45 @@ export enum OverviewRulerLane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for rendering a model decoration in the overview ruler.
|
||||
* Position in the minimap to render the decoration.
|
||||
*/
|
||||
export interface IModelDecorationOverviewRulerOptions {
|
||||
export enum MinimapPosition {
|
||||
Inline = 1
|
||||
}
|
||||
|
||||
export interface IDecorationOptions {
|
||||
/**
|
||||
* CSS color to render in the overview ruler.
|
||||
* CSS color to render.
|
||||
* e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry
|
||||
*/
|
||||
color: string | ThemeColor | undefined;
|
||||
/**
|
||||
* CSS color to render in the overview ruler.
|
||||
* CSS color to render.
|
||||
* e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry
|
||||
*/
|
||||
darkColor?: string | ThemeColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for rendering a model decoration in the overview ruler.
|
||||
*/
|
||||
export interface IModelDecorationOverviewRulerOptions extends IDecorationOptions {
|
||||
/**
|
||||
* The position in the overview ruler.
|
||||
*/
|
||||
position: OverviewRulerLane;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for rendering a model decoration in the overview ruler.
|
||||
*/
|
||||
export interface IModelDecorationMinimapOptions extends IDecorationOptions {
|
||||
/**
|
||||
* The position in the overview ruler.
|
||||
*/
|
||||
position: MinimapPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for a model decoration.
|
||||
*/
|
||||
@@ -89,6 +109,10 @@ export interface IModelDecorationOptions {
|
||||
* If set, render this decoration in the overview ruler.
|
||||
*/
|
||||
overviewRuler?: IModelDecorationOverviewRulerOptions | null;
|
||||
/**
|
||||
* If set, render this decoration in the minimap.
|
||||
*/
|
||||
minimap?: IModelDecorationMinimapOptions | null;
|
||||
/**
|
||||
* If set, the decoration will be rendered in the glyph margin with this CSS class name.
|
||||
*/
|
||||
@@ -759,7 +783,7 @@ export interface ITextModel {
|
||||
* Flush all tokenization state.
|
||||
* @internal
|
||||
*/
|
||||
flushTokens(): void;
|
||||
resetTokenization(): void;
|
||||
|
||||
/**
|
||||
* Force tokenization information for `lineNumber` to be accurate.
|
||||
@@ -1092,6 +1116,12 @@ export interface ITextModel {
|
||||
* @internal
|
||||
*/
|
||||
onDidChangeTokens(listener: (e: IModelTokensChangedEvent) => void): IDisposable;
|
||||
/**
|
||||
* An event emitted when the model has been attached to the first editor or detached from the last editor.
|
||||
* @event
|
||||
* @internal
|
||||
*/
|
||||
onDidChangeAttached(listener: () => void): IDisposable;
|
||||
/**
|
||||
* An event emitted right before disposing the model.
|
||||
* @event
|
||||
|
||||
@@ -177,22 +177,26 @@ export function guessIndentation(source: ITextBuffer, defaultTabSize: number, de
|
||||
}
|
||||
|
||||
let tabSize = defaultTabSize;
|
||||
let tabSizeScore = (insertSpaces ? 0 : 0.1 * linesCount);
|
||||
|
||||
// console.log("score threshold: " + tabSizeScore);
|
||||
// Guess tabSize only if inserting spaces...
|
||||
if (insertSpaces) {
|
||||
let tabSizeScore = (insertSpaces ? 0 : 0.1 * linesCount);
|
||||
|
||||
ALLOWED_TAB_SIZE_GUESSES.forEach((possibleTabSize) => {
|
||||
let possibleTabSizeScore = spacesDiffCount[possibleTabSize];
|
||||
if (possibleTabSizeScore > tabSizeScore) {
|
||||
tabSizeScore = possibleTabSizeScore;
|
||||
tabSize = possibleTabSize;
|
||||
// console.log("score threshold: " + tabSizeScore);
|
||||
|
||||
ALLOWED_TAB_SIZE_GUESSES.forEach((possibleTabSize) => {
|
||||
let possibleTabSizeScore = spacesDiffCount[possibleTabSize];
|
||||
if (possibleTabSizeScore > tabSizeScore) {
|
||||
tabSizeScore = possibleTabSizeScore;
|
||||
tabSize = possibleTabSize;
|
||||
}
|
||||
});
|
||||
|
||||
// Let a tabSize of 2 win even if it is not the maximum
|
||||
// (only in case 4 was guessed)
|
||||
if (tabSize === 4 && spacesDiffCount[4] > 0 && spacesDiffCount[2] > 0 && spacesDiffCount[2] >= spacesDiffCount[4] / 2) {
|
||||
tabSize = 2;
|
||||
}
|
||||
});
|
||||
|
||||
// Let a tabSize of 2 win even if it is not the maximum
|
||||
// (only in case 4 was guessed)
|
||||
if (tabSize === 4 && spacesDiffCount[4] > 0 && spacesDiffCount[2] > 0 && spacesDiffCount[2] >= spacesDiffCount[4] / 2) {
|
||||
tabSize = 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -611,25 +611,25 @@ export class PieceTreeBase {
|
||||
let resultLen = 0;
|
||||
const searcher = new Searcher(searchData.wordSeparators, searchData.regex);
|
||||
|
||||
let startPostion = this.nodeAt2(searchRange.startLineNumber, searchRange.startColumn);
|
||||
if (startPostion === null) {
|
||||
let startPosition = this.nodeAt2(searchRange.startLineNumber, searchRange.startColumn);
|
||||
if (startPosition === null) {
|
||||
return [];
|
||||
}
|
||||
let endPosition = this.nodeAt2(searchRange.endLineNumber, searchRange.endColumn);
|
||||
if (endPosition === null) {
|
||||
return [];
|
||||
}
|
||||
let start = this.positionInBuffer(startPostion.node, startPostion.remainder);
|
||||
let start = this.positionInBuffer(startPosition.node, startPosition.remainder);
|
||||
let end = this.positionInBuffer(endPosition.node, endPosition.remainder);
|
||||
|
||||
if (startPostion.node === endPosition.node) {
|
||||
this.findMatchesInNode(startPostion.node, searcher, searchRange.startLineNumber, searchRange.startColumn, start, end, searchData, captureMatches, limitResultCount, resultLen, result);
|
||||
if (startPosition.node === endPosition.node) {
|
||||
this.findMatchesInNode(startPosition.node, searcher, searchRange.startLineNumber, searchRange.startColumn, start, end, searchData, captureMatches, limitResultCount, resultLen, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
let startLineNumber = searchRange.startLineNumber;
|
||||
|
||||
let currentNode = startPostion.node;
|
||||
let currentNode = startPosition.node;
|
||||
while (currentNode !== endPosition.node) {
|
||||
let lineBreakCnt = this.getLineFeedCnt(currentNode.piece.bufferIndex, start, currentNode.piece.end);
|
||||
|
||||
@@ -663,9 +663,9 @@ export class PieceTreeBase {
|
||||
}
|
||||
|
||||
startLineNumber++;
|
||||
startPostion = this.nodeAt2(startLineNumber, 1);
|
||||
currentNode = startPostion.node;
|
||||
start = this.positionInBuffer(startPostion.node, startPostion.remainder);
|
||||
startPosition = this.nodeAt2(startLineNumber, 1);
|
||||
currentNode = startPosition.node;
|
||||
start = this.positionInBuffer(startPosition.node, startPosition.remainder);
|
||||
}
|
||||
|
||||
if (startLineNumber === searchRange.endLineNumber) {
|
||||
|
||||
@@ -8,7 +8,6 @@ import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||
@@ -23,9 +22,9 @@ import { IntervalNode, IntervalTree, getNodeIsInOverviewRuler, recomputeMaxEnd }
|
||||
import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
|
||||
import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, InternalModelContentChangeEvent, ModelRawChange, ModelRawContentChangedEvent, ModelRawEOLChanged, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from 'vs/editor/common/model/textModelEvents';
|
||||
import { SearchData, SearchParams, TextModelSearch } from 'vs/editor/common/model/textModelSearch';
|
||||
import { ModelLinesTokens, ModelTokensChangedEventBuilder } from 'vs/editor/common/model/textModelTokens';
|
||||
import { TextModelTokenization, countEOL } from 'vs/editor/common/model/textModelTokens';
|
||||
import { getWordAtText } from 'vs/editor/common/model/wordHelper';
|
||||
import { IState, LanguageId, LanguageIdentifier, TokenizationRegistry, FormattingOptions } from 'vs/editor/common/modes';
|
||||
import { LanguageId, LanguageIdentifier, FormattingOptions } from 'vs/editor/common/modes';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode';
|
||||
import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports';
|
||||
@@ -33,8 +32,8 @@ import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/comm
|
||||
import { ITheme, ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer';
|
||||
|
||||
const CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048;
|
||||
import { TokensStore, MultilineTokens } from 'vs/editor/common/model/tokensStore';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
function createTextBufferBuilder() {
|
||||
return new PieceTreeTextBufferBuilder();
|
||||
@@ -235,6 +234,9 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
private readonly _onDidChangeOptions: Emitter<IModelOptionsChangedEvent> = this._register(new Emitter<IModelOptionsChangedEvent>());
|
||||
public readonly onDidChangeOptions: Event<IModelOptionsChangedEvent> = this._onDidChangeOptions.event;
|
||||
|
||||
private readonly _onDidChangeAttached: Emitter<void> = this._register(new Emitter<void>());
|
||||
public readonly onDidChangeAttached: Event<void> = this._onDidChangeAttached.event;
|
||||
|
||||
private readonly _eventEmitter: DidChangeContentEmitter = this._register(new DidChangeContentEmitter());
|
||||
public onDidChangeRawContentFast(listener: (e: ModelRawContentChangedEvent) => void): IDisposable {
|
||||
return this._eventEmitter.fastEvent((e: InternalModelContentChangeEvent) => listener(e.rawContentChangedEvent));
|
||||
@@ -242,6 +244,9 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
public onDidChangeRawContent(listener: (e: ModelRawContentChangedEvent) => void): IDisposable {
|
||||
return this._eventEmitter.slowEvent((e: InternalModelContentChangeEvent) => listener(e.rawContentChangedEvent));
|
||||
}
|
||||
public onDidChangeContentFast(listener: (e: IModelContentChangedEvent) => void): IDisposable {
|
||||
return this._eventEmitter.fastEvent((e: InternalModelContentChangeEvent) => listener(e.contentChangedEvent));
|
||||
}
|
||||
public onDidChangeContent(listener: (e: IModelContentChangedEvent) => void): IDisposable {
|
||||
return this._eventEmitter.slowEvent((e: InternalModelContentChangeEvent) => listener(e.contentChangedEvent));
|
||||
}
|
||||
@@ -284,10 +289,9 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
//#region Tokenization
|
||||
private _languageIdentifier: LanguageIdentifier;
|
||||
private readonly _tokenizationListener: IDisposable;
|
||||
private readonly _languageRegistryListener: IDisposable;
|
||||
private _revalidateTokensTimeout: any;
|
||||
/*private*/_tokens: ModelLinesTokens;
|
||||
private readonly _tokens: TokensStore;
|
||||
private readonly _tokenization: TextModelTokenization;
|
||||
//#endregion
|
||||
|
||||
constructor(source: string | model.ITextBufferFactory, creationOptions: model.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier | null, associatedResource: URI | null = null) {
|
||||
@@ -330,31 +334,12 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
this._isDisposing = false;
|
||||
|
||||
this._languageIdentifier = languageIdentifier || NULL_LANGUAGE_IDENTIFIER;
|
||||
this._tokenizationListener = TokenizationRegistry.onDidChange((e) => {
|
||||
if (e.changedLanguages.indexOf(this._languageIdentifier.language) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._resetTokenizationState();
|
||||
this.emitModelTokensChangedEvent({
|
||||
tokenizationSupportChanged: true,
|
||||
ranges: [{
|
||||
fromLineNumber: 1,
|
||||
toLineNumber: this.getLineCount()
|
||||
}]
|
||||
});
|
||||
|
||||
if (this._shouldAutoTokenize()) {
|
||||
this._warmUpTokens();
|
||||
}
|
||||
});
|
||||
this._revalidateTokensTimeout = -1;
|
||||
this._languageRegistryListener = LanguageConfigurationRegistry.onDidChange((e) => {
|
||||
if (e.languageIdentifier.id === this._languageIdentifier.id) {
|
||||
this._onDidChangeLanguageConfiguration.fire({});
|
||||
}
|
||||
});
|
||||
this._resetTokenizationState();
|
||||
|
||||
this._instanceId = singleLetter(MODEL_ID);
|
||||
this._lastDecorationId = 0;
|
||||
@@ -365,16 +350,17 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
this._isUndoing = false;
|
||||
this._isRedoing = false;
|
||||
this._trimAutoWhitespaceLines = null;
|
||||
|
||||
this._tokens = new TokensStore();
|
||||
this._tokenization = new TextModelTokenization(this);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._isDisposing = true;
|
||||
this._onWillDispose.fire();
|
||||
this._tokenizationListener.dispose();
|
||||
this._languageRegistryListener.dispose();
|
||||
this._clearTimers();
|
||||
this._tokenization.dispose();
|
||||
this._isDisposed = true;
|
||||
// Null out members, such that any use of a disposed model will throw exceptions sooner rather than later
|
||||
super.dispose();
|
||||
this._isDisposing = false;
|
||||
}
|
||||
@@ -439,8 +425,8 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
this._buffer = textBuffer;
|
||||
this._increaseVersionId();
|
||||
|
||||
// Cancel tokenization, clear all tokens and begin tokenizing
|
||||
this._resetTokenizationState();
|
||||
// Flush all tokens
|
||||
this._tokens.flush();
|
||||
|
||||
// Destroy all my decorations
|
||||
this._decorations = Object.create(null);
|
||||
@@ -524,36 +510,18 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
}
|
||||
|
||||
private _resetTokenizationState(): void {
|
||||
this._clearTimers();
|
||||
let tokenizationSupport = (
|
||||
this._isTooLargeForTokenization
|
||||
? null
|
||||
: TokenizationRegistry.get(this._languageIdentifier.language)
|
||||
);
|
||||
this._tokens = new ModelLinesTokens(this._languageIdentifier, tokenizationSupport);
|
||||
this._beginBackgroundTokenization();
|
||||
}
|
||||
|
||||
private _clearTimers(): void {
|
||||
if (this._revalidateTokensTimeout !== -1) {
|
||||
clearTimeout(this._revalidateTokensTimeout);
|
||||
this._revalidateTokensTimeout = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public onBeforeAttached(): void {
|
||||
this._attachedEditorCount++;
|
||||
// Warm up tokens for the editor
|
||||
this._warmUpTokens();
|
||||
if (this._attachedEditorCount === 1) {
|
||||
this._onDidChangeAttached.fire(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public onBeforeDetached(): void {
|
||||
this._attachedEditorCount--;
|
||||
}
|
||||
|
||||
private _shouldAutoTokenize(): boolean {
|
||||
return this.isAttachedToEditor();
|
||||
if (this._attachedEditorCount === 0) {
|
||||
this._onDidChangeAttached.fire(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
public isAttachedToEditor(): boolean {
|
||||
@@ -1292,36 +1260,6 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
}
|
||||
|
||||
private static _eolCount(text: string): [number, number] {
|
||||
let eolCount = 0;
|
||||
let firstLineLength = 0;
|
||||
for (let i = 0, len = text.length; i < len; i++) {
|
||||
const chr = text.charCodeAt(i);
|
||||
|
||||
if (chr === CharCode.CarriageReturn) {
|
||||
if (eolCount === 0) {
|
||||
firstLineLength = i;
|
||||
}
|
||||
eolCount++;
|
||||
if (i + 1 < len && text.charCodeAt(i + 1) === CharCode.LineFeed) {
|
||||
// \r\n... case
|
||||
i++; // skip \n
|
||||
} else {
|
||||
// \r... case
|
||||
}
|
||||
} else if (chr === CharCode.LineFeed) {
|
||||
if (eolCount === 0) {
|
||||
firstLineLength = i;
|
||||
}
|
||||
eolCount++;
|
||||
}
|
||||
}
|
||||
if (eolCount === 0) {
|
||||
firstLineLength = text.length;
|
||||
}
|
||||
return [eolCount, firstLineLength];
|
||||
}
|
||||
|
||||
private _applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[]): model.IIdentifiedSingleEditOperation[] {
|
||||
for (let i = 0, len = rawOperations.length; i < len; i++) {
|
||||
rawOperations[i].range = this.validateRange(rawOperations[i].range);
|
||||
@@ -1340,13 +1278,8 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
let lineCount = oldLineCount;
|
||||
for (let i = 0, len = contentChanges.length; i < len; i++) {
|
||||
const change = contentChanges[i];
|
||||
const [eolCount, firstLineLength] = TextModel._eolCount(change.text);
|
||||
try {
|
||||
this._tokens.applyEdits(change.range, eolCount, firstLineLength);
|
||||
} catch (err) {
|
||||
// emergency recovery => reset tokens
|
||||
this._tokens = new ModelLinesTokens(this._tokens.languageIdentifier, this._tokens.tokenizationSupport);
|
||||
}
|
||||
const [eolCount, firstLineLength] = countEOL(change.text);
|
||||
this._tokens.applyEdits(change.range, eolCount, firstLineLength);
|
||||
this._onDidChangeDecorations.fire();
|
||||
this._decorationsTree.acceptReplace(change.rangeOffset, change.rangeLength, change.text.length, change.forceMoveMarkers);
|
||||
|
||||
@@ -1407,10 +1340,6 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
);
|
||||
}
|
||||
|
||||
if (this._tokens.hasLinesToTokenize(this._buffer)) {
|
||||
this._beginBackgroundTokenization();
|
||||
}
|
||||
|
||||
return result.reverseEdits;
|
||||
}
|
||||
|
||||
@@ -1775,93 +1704,60 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
//#region Tokenization
|
||||
|
||||
public setLineTokens(lineNumber: number, tokens: Uint32Array): void {
|
||||
if (lineNumber < 1 || lineNumber > this.getLineCount()) {
|
||||
throw new Error('Illegal value for lineNumber');
|
||||
}
|
||||
|
||||
this._tokens.setTokens(this._languageIdentifier.id, lineNumber - 1, this._buffer.getLineLength(lineNumber), tokens);
|
||||
}
|
||||
|
||||
public setTokens(tokens: MultilineTokens[]): void {
|
||||
if (tokens.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
let ranges: { fromLineNumber: number; toLineNumber: number; }[] = [];
|
||||
|
||||
for (let i = 0, len = tokens.length; i < len; i++) {
|
||||
const element = tokens[i];
|
||||
ranges.push({ fromLineNumber: element.startLineNumber, toLineNumber: element.startLineNumber + element.tokens.length - 1 });
|
||||
for (let j = 0, lenJ = element.tokens.length; j < lenJ; j++) {
|
||||
this.setLineTokens(element.startLineNumber + j, element.tokens[j]);
|
||||
}
|
||||
}
|
||||
|
||||
this._emitModelTokensChangedEvent({
|
||||
tokenizationSupportChanged: false,
|
||||
ranges: ranges
|
||||
});
|
||||
}
|
||||
|
||||
public tokenizeViewport(startLineNumber: number, endLineNumber: number): void {
|
||||
if (!this._tokens.tokenizationSupport) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
startLineNumber = Math.max(1, startLineNumber);
|
||||
endLineNumber = Math.min(this.getLineCount(), endLineNumber);
|
||||
endLineNumber = Math.min(this._buffer.getLineCount(), endLineNumber);
|
||||
this._tokenization.tokenizeViewport(startLineNumber, endLineNumber);
|
||||
}
|
||||
|
||||
if (endLineNumber <= this._tokens.inValidLineStartIndex) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
public clearTokens(): void {
|
||||
this._tokens.flush();
|
||||
this._emitModelTokensChangedEvent({
|
||||
tokenizationSupportChanged: true,
|
||||
ranges: [{
|
||||
fromLineNumber: 1,
|
||||
toLineNumber: this._buffer.getLineCount()
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
if (startLineNumber <= this._tokens.inValidLineStartIndex) {
|
||||
// tokenization has reached the viewport start...
|
||||
this.forceTokenization(endLineNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
let nonWhitespaceColumn = this.getLineFirstNonWhitespaceColumn(startLineNumber);
|
||||
let fakeLines: string[] = [];
|
||||
let initialState: IState | null = null;
|
||||
for (let i = startLineNumber - 1; nonWhitespaceColumn > 0 && i >= 1; i--) {
|
||||
let newNonWhitespaceIndex = this.getLineFirstNonWhitespaceColumn(i);
|
||||
|
||||
if (newNonWhitespaceIndex === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (newNonWhitespaceIndex < nonWhitespaceColumn) {
|
||||
initialState = this._tokens._getState(i - 1);
|
||||
if (initialState) {
|
||||
break;
|
||||
}
|
||||
fakeLines.push(this.getLineContent(i));
|
||||
nonWhitespaceColumn = newNonWhitespaceIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (!initialState) {
|
||||
initialState = this._tokens.tokenizationSupport.getInitialState();
|
||||
}
|
||||
|
||||
let state = initialState.clone();
|
||||
for (let i = fakeLines.length - 1; i >= 0; i--) {
|
||||
let r = this._tokens._tokenizeText(this._buffer, fakeLines[i], state);
|
||||
if (r) {
|
||||
state = r.endState.clone();
|
||||
} else {
|
||||
state = initialState.clone();
|
||||
}
|
||||
}
|
||||
|
||||
const eventBuilder = new ModelTokensChangedEventBuilder();
|
||||
for (let i = startLineNumber; i <= endLineNumber; i++) {
|
||||
let text = this.getLineContent(i);
|
||||
let r = this._tokens._tokenizeText(this._buffer, text, state);
|
||||
if (r) {
|
||||
this._tokens._setTokens(this._tokens.languageIdentifier.id, i - 1, text.length, r.tokens);
|
||||
|
||||
// We cannot trust these states/tokens to be valid!
|
||||
// (see https://github.com/Microsoft/vscode/issues/67607)
|
||||
this._tokens._setIsInvalid(i - 1, true);
|
||||
this._tokens._setState(i - 1, state);
|
||||
state = r.endState.clone();
|
||||
eventBuilder.registerChangedTokens(i);
|
||||
} else {
|
||||
state = initialState.clone();
|
||||
}
|
||||
}
|
||||
|
||||
const e = eventBuilder.build();
|
||||
if (e) {
|
||||
private _emitModelTokensChangedEvent(e: IModelTokensChangedEvent): void {
|
||||
if (!this._isDisposing) {
|
||||
this._onDidChangeTokens.fire(e);
|
||||
}
|
||||
}
|
||||
|
||||
public flushTokens(): void {
|
||||
this._resetTokenizationState();
|
||||
this.emitModelTokensChangedEvent({
|
||||
tokenizationSupportChanged: false,
|
||||
ranges: [{
|
||||
fromLineNumber: 1,
|
||||
toLineNumber: this.getLineCount()
|
||||
}]
|
||||
});
|
||||
public resetTokenization(): void {
|
||||
this._tokenization.reset();
|
||||
}
|
||||
|
||||
public forceTokenization(lineNumber: number): void {
|
||||
@@ -1869,30 +1765,11 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
throw new Error('Illegal value for lineNumber');
|
||||
}
|
||||
|
||||
const eventBuilder = new ModelTokensChangedEventBuilder();
|
||||
|
||||
this._tokens._updateTokensUntilLine(this._buffer, eventBuilder, lineNumber);
|
||||
|
||||
const e = eventBuilder.build();
|
||||
if (e) {
|
||||
this._onDidChangeTokens.fire(e);
|
||||
}
|
||||
this._tokenization.forceTokenization(lineNumber);
|
||||
}
|
||||
|
||||
public isCheapToTokenize(lineNumber: number): boolean {
|
||||
if (!this._tokens.isCheapToTokenize(lineNumber)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lineNumber < this._tokens.inValidLineStartIndex + 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.getLineLength(lineNumber) < CHEAP_TOKENIZATION_LENGTH_LIMIT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return this._tokenization.isCheapToTokenize(lineNumber);
|
||||
}
|
||||
|
||||
public tokenizeIfCheap(lineNumber: number): void {
|
||||
@@ -1910,7 +1787,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
|
||||
private _getLineTokens(lineNumber: number): LineTokens {
|
||||
const lineText = this._buffer.getLineContent(lineNumber);
|
||||
const lineText = this.getLineContent(lineNumber);
|
||||
return this._tokens.getTokens(this._languageIdentifier.id, lineNumber - 1, lineText);
|
||||
}
|
||||
|
||||
@@ -1935,81 +1812,14 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
this._languageIdentifier = languageIdentifier;
|
||||
|
||||
// Cancel tokenization, clear all tokens and begin tokenizing
|
||||
this._resetTokenizationState();
|
||||
|
||||
this.emitModelTokensChangedEvent({
|
||||
tokenizationSupportChanged: true,
|
||||
ranges: [{
|
||||
fromLineNumber: 1,
|
||||
toLineNumber: this.getLineCount()
|
||||
}]
|
||||
});
|
||||
this._onDidChangeLanguage.fire(e);
|
||||
this._onDidChangeLanguageConfiguration.fire({});
|
||||
}
|
||||
|
||||
public getLanguageIdAtPosition(_lineNumber: number, _column: number): LanguageId {
|
||||
if (!this._tokens.tokenizationSupport) {
|
||||
return this._languageIdentifier.id;
|
||||
}
|
||||
let { lineNumber, column } = this.validatePosition({ lineNumber: _lineNumber, column: _column });
|
||||
|
||||
let lineTokens = this._getLineTokens(lineNumber);
|
||||
return lineTokens.getLanguageId(lineTokens.findTokenIndexAtOffset(column - 1));
|
||||
}
|
||||
|
||||
private _beginBackgroundTokenization(): void {
|
||||
if (this._shouldAutoTokenize() && this._revalidateTokensTimeout === -1) {
|
||||
this._revalidateTokensTimeout = setTimeout(() => {
|
||||
this._revalidateTokensTimeout = -1;
|
||||
this._revalidateTokensNow();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_warmUpTokens(): void {
|
||||
// Warm up first 100 lines (if it takes less than 50ms)
|
||||
const maxLineNumber = Math.min(100, this.getLineCount());
|
||||
this._revalidateTokensNow(maxLineNumber);
|
||||
|
||||
if (this._tokens.hasLinesToTokenize(this._buffer)) {
|
||||
this._beginBackgroundTokenization();
|
||||
}
|
||||
}
|
||||
|
||||
private _revalidateTokensNow(toLineNumber: number = this._buffer.getLineCount()): void {
|
||||
const MAX_ALLOWED_TIME = 20;
|
||||
const eventBuilder = new ModelTokensChangedEventBuilder();
|
||||
const sw = StopWatch.create(false);
|
||||
|
||||
while (this._tokens.hasLinesToTokenize(this._buffer)) {
|
||||
if (sw.elapsed() > MAX_ALLOWED_TIME) {
|
||||
// Stop if MAX_ALLOWED_TIME is reached
|
||||
break;
|
||||
}
|
||||
|
||||
const tokenizedLineNumber = this._tokens._tokenizeOneLine(this._buffer, eventBuilder);
|
||||
|
||||
if (tokenizedLineNumber >= toLineNumber) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (this._tokens.hasLinesToTokenize(this._buffer)) {
|
||||
this._beginBackgroundTokenization();
|
||||
}
|
||||
|
||||
const e = eventBuilder.build();
|
||||
if (e) {
|
||||
this._onDidChangeTokens.fire(e);
|
||||
}
|
||||
}
|
||||
|
||||
private emitModelTokensChangedEvent(e: IModelTokensChangedEvent): void {
|
||||
if (!this._isDisposing) {
|
||||
this._onDidChangeTokens.fire(e);
|
||||
}
|
||||
public getLanguageIdAtPosition(lineNumber: number, column: number): LanguageId {
|
||||
const position = this.validatePosition(new Position(lineNumber, column));
|
||||
const lineTokens = this.getLineTokens(position.lineNumber);
|
||||
return lineTokens.getLanguageId(lineTokens.findTokenIndexAtOffset(position.column - 1));
|
||||
}
|
||||
|
||||
// Having tokens allows implementing additional helper methods
|
||||
@@ -2823,17 +2633,25 @@ function cleanClassName(className: string): string {
|
||||
return className.replace(/[^a-z0-9\-_]/gi, ' ');
|
||||
}
|
||||
|
||||
export class ModelDecorationOverviewRulerOptions implements model.IModelDecorationOverviewRulerOptions {
|
||||
class DecorationOptions implements model.IDecorationOptions {
|
||||
readonly color: string | ThemeColor;
|
||||
readonly darkColor: string | ThemeColor;
|
||||
|
||||
constructor(options: model.IDecorationOptions) {
|
||||
this.color = options.color || strings.empty;
|
||||
this.darkColor = options.darkColor || strings.empty;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export class ModelDecorationOverviewRulerOptions extends DecorationOptions {
|
||||
readonly position: model.OverviewRulerLane;
|
||||
private _resolvedColor: string | null;
|
||||
|
||||
constructor(options: model.IModelDecorationOverviewRulerOptions) {
|
||||
this.color = options.color || strings.empty;
|
||||
this.darkColor = options.darkColor || strings.empty;
|
||||
this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center);
|
||||
super(options);
|
||||
this._resolvedColor = null;
|
||||
this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center);
|
||||
}
|
||||
|
||||
public getColor(theme: ITheme): string {
|
||||
@@ -2863,6 +2681,36 @@ export class ModelDecorationOverviewRulerOptions implements model.IModelDecorati
|
||||
}
|
||||
}
|
||||
|
||||
export class ModelDecorationMinimapOptions extends DecorationOptions {
|
||||
readonly position: model.MinimapPosition;
|
||||
private _resolvedColor: Color | undefined;
|
||||
|
||||
|
||||
constructor(options: model.IModelDecorationMinimapOptions) {
|
||||
super(options);
|
||||
this.position = options.position;
|
||||
}
|
||||
|
||||
public getColor(theme: ITheme): Color | undefined {
|
||||
if (!this._resolvedColor) {
|
||||
if (theme.type !== 'light' && this.darkColor) {
|
||||
this._resolvedColor = this._resolveColor(this.darkColor, theme);
|
||||
} else {
|
||||
this._resolvedColor = this._resolveColor(this.color, theme);
|
||||
}
|
||||
}
|
||||
|
||||
return this._resolvedColor;
|
||||
}
|
||||
|
||||
private _resolveColor(color: string | ThemeColor, theme: ITheme): Color | undefined {
|
||||
if (typeof color === 'string') {
|
||||
return Color.fromHex(color);
|
||||
}
|
||||
return theme.getColor(color.id);
|
||||
}
|
||||
}
|
||||
|
||||
export class ModelDecorationOptions implements model.IModelDecorationOptions {
|
||||
|
||||
public static EMPTY: ModelDecorationOptions;
|
||||
@@ -2884,6 +2732,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
|
||||
readonly showIfCollapsed: boolean;
|
||||
readonly collapseOnReplaceEdit: boolean;
|
||||
readonly overviewRuler: ModelDecorationOverviewRulerOptions | null;
|
||||
readonly minimap: ModelDecorationMinimapOptions | null;
|
||||
readonly glyphMarginClassName: string | null;
|
||||
readonly linesDecorationsClassName: string | null;
|
||||
readonly marginClassName: string | null;
|
||||
@@ -2902,6 +2751,7 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
|
||||
this.showIfCollapsed = options.showIfCollapsed || false;
|
||||
this.collapseOnReplaceEdit = options.collapseOnReplaceEdit || false;
|
||||
this.overviewRuler = options.overviewRuler ? new ModelDecorationOverviewRulerOptions(options.overviewRuler) : null;
|
||||
this.minimap = options.minimap ? new ModelDecorationMinimapOptions(options.minimap) : null;
|
||||
this.glyphMarginClassName = options.glyphMarginClassName ? cleanClassName(options.glyphMarginClassName) : null;
|
||||
this.linesDecorationsClassName = options.linesDecorationsClassName ? cleanClassName(options.linesDecorationsClassName) : null;
|
||||
this.marginClassName = options.marginClassName ? cleanClassName(options.marginClassName) : null;
|
||||
|
||||
@@ -7,501 +7,486 @@ import * as arrays from 'vs/base/common/arrays';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { TokenizationResult2 } from 'vs/editor/common/core/token';
|
||||
import { ITextBuffer } from 'vs/editor/common/model';
|
||||
import { IModelTokensChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
import { ColorId, FontStyle, IState, ITokenizationSupport, LanguageId, LanguageIdentifier, MetadataConsts, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes';
|
||||
import { RawContentChangedType } from 'vs/editor/common/model/textModelEvents';
|
||||
import { IState, ITokenizationSupport, LanguageIdentifier, TokenizationRegistry } from 'vs/editor/common/modes';
|
||||
import { nullTokenize2 } from 'vs/editor/common/modes/nullMode';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { MultilineTokensBuilder } from 'vs/editor/common/model/tokensStore';
|
||||
|
||||
function getDefaultMetadata(topLevelLanguageId: LanguageId): number {
|
||||
return (
|
||||
(topLevelLanguageId << MetadataConsts.LANGUAGEID_OFFSET)
|
||||
| (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET)
|
||||
| (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET)
|
||||
| (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET)
|
||||
| (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET)
|
||||
) >>> 0;
|
||||
export function countEOL(text: string): [number, number] {
|
||||
let eolCount = 0;
|
||||
let firstLineLength = 0;
|
||||
for (let i = 0, len = text.length; i < len; i++) {
|
||||
const chr = text.charCodeAt(i);
|
||||
|
||||
if (chr === CharCode.CarriageReturn) {
|
||||
if (eolCount === 0) {
|
||||
firstLineLength = i;
|
||||
}
|
||||
eolCount++;
|
||||
if (i + 1 < len && text.charCodeAt(i + 1) === CharCode.LineFeed) {
|
||||
// \r\n... case
|
||||
i++; // skip \n
|
||||
} else {
|
||||
// \r... case
|
||||
}
|
||||
} else if (chr === CharCode.LineFeed) {
|
||||
if (eolCount === 0) {
|
||||
firstLineLength = i;
|
||||
}
|
||||
eolCount++;
|
||||
}
|
||||
}
|
||||
if (eolCount === 0) {
|
||||
firstLineLength = text.length;
|
||||
}
|
||||
return [eolCount, firstLineLength];
|
||||
}
|
||||
|
||||
const EMPTY_LINE_TOKENS = (new Uint32Array(0)).buffer;
|
||||
|
||||
class ModelLineTokens {
|
||||
_state: IState | null;
|
||||
_lineTokens: ArrayBuffer | null;
|
||||
_invalid: boolean;
|
||||
|
||||
constructor(state: IState | null) {
|
||||
this._state = state;
|
||||
this._lineTokens = null;
|
||||
this._invalid = true;
|
||||
}
|
||||
|
||||
public deleteBeginning(toChIndex: number): void {
|
||||
if (this._lineTokens === null || this._lineTokens === EMPTY_LINE_TOKENS) {
|
||||
return;
|
||||
}
|
||||
this.delete(0, toChIndex);
|
||||
}
|
||||
|
||||
public deleteEnding(fromChIndex: number): void {
|
||||
if (this._lineTokens === null || this._lineTokens === EMPTY_LINE_TOKENS) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tokens = new Uint32Array(this._lineTokens);
|
||||
const lineTextLength = tokens[tokens.length - 2];
|
||||
this.delete(fromChIndex, lineTextLength);
|
||||
}
|
||||
|
||||
public delete(fromChIndex: number, toChIndex: number): void {
|
||||
if (this._lineTokens === null || this._lineTokens === EMPTY_LINE_TOKENS || fromChIndex === toChIndex) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tokens = new Uint32Array(this._lineTokens);
|
||||
const tokensCount = (tokens.length >>> 1);
|
||||
|
||||
// special case: deleting everything
|
||||
if (fromChIndex === 0 && tokens[tokens.length - 2] === toChIndex) {
|
||||
this._lineTokens = EMPTY_LINE_TOKENS;
|
||||
return;
|
||||
}
|
||||
|
||||
const fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, fromChIndex);
|
||||
const fromTokenStartOffset = (fromTokenIndex > 0 ? tokens[(fromTokenIndex - 1) << 1] : 0);
|
||||
const fromTokenEndOffset = tokens[fromTokenIndex << 1];
|
||||
|
||||
if (toChIndex < fromTokenEndOffset) {
|
||||
// the delete range is inside a single token
|
||||
const delta = (toChIndex - fromChIndex);
|
||||
for (let i = fromTokenIndex; i < tokensCount; i++) {
|
||||
tokens[i << 1] -= delta;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let dest: number;
|
||||
let lastEnd: number;
|
||||
if (fromTokenStartOffset !== fromChIndex) {
|
||||
tokens[fromTokenIndex << 1] = fromChIndex;
|
||||
dest = ((fromTokenIndex + 1) << 1);
|
||||
lastEnd = fromChIndex;
|
||||
} else {
|
||||
dest = (fromTokenIndex << 1);
|
||||
lastEnd = fromTokenStartOffset;
|
||||
}
|
||||
|
||||
const delta = (toChIndex - fromChIndex);
|
||||
for (let tokenIndex = fromTokenIndex + 1; tokenIndex < tokensCount; tokenIndex++) {
|
||||
const tokenEndOffset = tokens[tokenIndex << 1] - delta;
|
||||
if (tokenEndOffset > lastEnd) {
|
||||
tokens[dest++] = tokenEndOffset;
|
||||
tokens[dest++] = tokens[(tokenIndex << 1) + 1];
|
||||
lastEnd = tokenEndOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (dest === tokens.length) {
|
||||
// nothing to trim
|
||||
return;
|
||||
}
|
||||
|
||||
let tmp = new Uint32Array(dest);
|
||||
tmp.set(tokens.subarray(0, dest), 0);
|
||||
this._lineTokens = tmp.buffer;
|
||||
}
|
||||
|
||||
public append(_otherTokens: ArrayBuffer | null): void {
|
||||
if (_otherTokens === EMPTY_LINE_TOKENS) {
|
||||
return;
|
||||
}
|
||||
if (this._lineTokens === EMPTY_LINE_TOKENS) {
|
||||
this._lineTokens = _otherTokens;
|
||||
return;
|
||||
}
|
||||
if (this._lineTokens === null) {
|
||||
return;
|
||||
}
|
||||
if (_otherTokens === null) {
|
||||
// cannot determine combined line length...
|
||||
this._lineTokens = null;
|
||||
return;
|
||||
}
|
||||
const myTokens = new Uint32Array(this._lineTokens);
|
||||
const otherTokens = new Uint32Array(_otherTokens);
|
||||
const otherTokensCount = (otherTokens.length >>> 1);
|
||||
|
||||
let result = new Uint32Array(myTokens.length + otherTokens.length);
|
||||
result.set(myTokens, 0);
|
||||
let dest = myTokens.length;
|
||||
const delta = myTokens[myTokens.length - 2];
|
||||
for (let i = 0; i < otherTokensCount; i++) {
|
||||
result[dest++] = otherTokens[(i << 1)] + delta;
|
||||
result[dest++] = otherTokens[(i << 1) + 1];
|
||||
}
|
||||
this._lineTokens = result.buffer;
|
||||
}
|
||||
|
||||
public insert(chIndex: number, textLength: number): void {
|
||||
if (!this._lineTokens) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
const tokens = new Uint32Array(this._lineTokens);
|
||||
const tokensCount = (tokens.length >>> 1);
|
||||
|
||||
let fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, chIndex);
|
||||
if (fromTokenIndex > 0) {
|
||||
const fromTokenStartOffset = tokens[(fromTokenIndex - 1) << 1];
|
||||
if (fromTokenStartOffset === chIndex) {
|
||||
fromTokenIndex--;
|
||||
}
|
||||
}
|
||||
for (let tokenIndex = fromTokenIndex; tokenIndex < tokensCount; tokenIndex++) {
|
||||
tokens[tokenIndex << 1] += textLength;
|
||||
}
|
||||
}
|
||||
const enum Constants {
|
||||
CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048
|
||||
}
|
||||
|
||||
export class ModelLinesTokens {
|
||||
|
||||
public readonly languageIdentifier: LanguageIdentifier;
|
||||
public readonly tokenizationSupport: ITokenizationSupport | null;
|
||||
private _tokens: ModelLineTokens[];
|
||||
export class TokenizationStateStore {
|
||||
private _beginState: (IState | null)[];
|
||||
private _valid: boolean[];
|
||||
private _len: number;
|
||||
private _invalidLineStartIndex: number;
|
||||
private _lastState: IState | null;
|
||||
|
||||
constructor(languageIdentifier: LanguageIdentifier, tokenizationSupport: ITokenizationSupport | null) {
|
||||
this.languageIdentifier = languageIdentifier;
|
||||
this.tokenizationSupport = tokenizationSupport;
|
||||
this._tokens = [];
|
||||
if (this.tokenizationSupport) {
|
||||
let initialState: IState | null = null;
|
||||
try {
|
||||
initialState = this.tokenizationSupport.getInitialState();
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
this.tokenizationSupport = null;
|
||||
}
|
||||
|
||||
if (initialState) {
|
||||
this._tokens[0] = new ModelLineTokens(initialState);
|
||||
}
|
||||
}
|
||||
|
||||
this._invalidLineStartIndex = 0;
|
||||
this._lastState = null;
|
||||
constructor() {
|
||||
this._reset(null);
|
||||
}
|
||||
|
||||
public get inValidLineStartIndex() {
|
||||
private _reset(initialState: IState | null): void {
|
||||
this._beginState = [];
|
||||
this._valid = [];
|
||||
this._len = 0;
|
||||
this._invalidLineStartIndex = 0;
|
||||
|
||||
if (initialState) {
|
||||
this._setBeginState(0, initialState);
|
||||
}
|
||||
}
|
||||
|
||||
public flush(initialState: IState | null): void {
|
||||
this._reset(initialState);
|
||||
}
|
||||
|
||||
public get invalidLineStartIndex() {
|
||||
return this._invalidLineStartIndex;
|
||||
}
|
||||
|
||||
public getTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineText: string): LineTokens {
|
||||
let rawLineTokens: ArrayBuffer | null = null;
|
||||
if (lineIndex < this._tokens.length && this._tokens[lineIndex]) {
|
||||
rawLineTokens = this._tokens[lineIndex]._lineTokens;
|
||||
private _invalidateLine(lineIndex: number): void {
|
||||
if (lineIndex < this._len) {
|
||||
this._valid[lineIndex] = false;
|
||||
}
|
||||
|
||||
if (rawLineTokens !== null && rawLineTokens !== EMPTY_LINE_TOKENS) {
|
||||
return new LineTokens(new Uint32Array(rawLineTokens), lineText);
|
||||
}
|
||||
|
||||
let lineTokens = new Uint32Array(2);
|
||||
lineTokens[0] = lineText.length;
|
||||
lineTokens[1] = getDefaultMetadata(topLevelLanguageId);
|
||||
return new LineTokens(lineTokens, lineText);
|
||||
}
|
||||
|
||||
public isCheapToTokenize(lineNumber: number): boolean {
|
||||
const firstInvalidLineNumber = this._invalidLineStartIndex + 1;
|
||||
return (firstInvalidLineNumber >= lineNumber);
|
||||
}
|
||||
|
||||
public hasLinesToTokenize(buffer: ITextBuffer): boolean {
|
||||
return (this._invalidLineStartIndex < buffer.getLineCount());
|
||||
}
|
||||
|
||||
public invalidateLine(lineIndex: number): void {
|
||||
this._setIsInvalid(lineIndex, true);
|
||||
if (lineIndex < this._invalidLineStartIndex) {
|
||||
this._setIsInvalid(this._invalidLineStartIndex, true);
|
||||
this._invalidLineStartIndex = lineIndex;
|
||||
}
|
||||
}
|
||||
|
||||
_setIsInvalid(lineIndex: number, invalid: boolean): void {
|
||||
if (lineIndex < this._tokens.length && this._tokens[lineIndex]) {
|
||||
this._tokens[lineIndex]._invalid = invalid;
|
||||
private _isValid(lineIndex: number): boolean {
|
||||
if (lineIndex < this._len) {
|
||||
return this._valid[lineIndex];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_isInvalid(lineIndex: number): boolean {
|
||||
if (lineIndex < this._tokens.length && this._tokens[lineIndex]) {
|
||||
return this._tokens[lineIndex]._invalid;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_getState(lineIndex: number): IState | null {
|
||||
if (lineIndex < this._tokens.length && this._tokens[lineIndex]) {
|
||||
return this._tokens[lineIndex]._state;
|
||||
public getBeginState(lineIndex: number): IState | null {
|
||||
if (lineIndex < this._len) {
|
||||
return this._beginState[lineIndex];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
_setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, tokens: Uint32Array): void {
|
||||
let target: ModelLineTokens;
|
||||
if (lineIndex < this._tokens.length && this._tokens[lineIndex]) {
|
||||
target = this._tokens[lineIndex];
|
||||
} else {
|
||||
target = new ModelLineTokens(null);
|
||||
this._tokens[lineIndex] = target;
|
||||
private _ensureLine(lineIndex: number): void {
|
||||
while (lineIndex >= this._len) {
|
||||
this._beginState[this._len] = null;
|
||||
this._valid[this._len] = false;
|
||||
this._len++;
|
||||
}
|
||||
|
||||
if (lineTextLength === 0) {
|
||||
let hasDifferentLanguageId = false;
|
||||
if (tokens && tokens.length > 1) {
|
||||
hasDifferentLanguageId = (TokenMetadata.getLanguageId(tokens[1]) !== topLevelLanguageId);
|
||||
}
|
||||
|
||||
if (!hasDifferentLanguageId) {
|
||||
target._lineTokens = EMPTY_LINE_TOKENS;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tokens || tokens.length === 0) {
|
||||
tokens = new Uint32Array(2);
|
||||
tokens[0] = 0;
|
||||
tokens[1] = getDefaultMetadata(topLevelLanguageId);
|
||||
}
|
||||
|
||||
LineTokens.convertToEndOffset(tokens, lineTextLength);
|
||||
|
||||
target._lineTokens = tokens.buffer;
|
||||
}
|
||||
|
||||
_setState(lineIndex: number, state: IState): void {
|
||||
if (lineIndex < this._tokens.length && this._tokens[lineIndex]) {
|
||||
this._tokens[lineIndex]._state = state;
|
||||
} else {
|
||||
const tmp = new ModelLineTokens(state);
|
||||
this._tokens[lineIndex] = tmp;
|
||||
private _deleteLines(start: number, deleteCount: number): void {
|
||||
if (deleteCount === 0) {
|
||||
return;
|
||||
}
|
||||
this._beginState.splice(start, deleteCount);
|
||||
this._valid.splice(start, deleteCount);
|
||||
this._len -= deleteCount;
|
||||
}
|
||||
|
||||
private _insertLines(insertIndex: number, insertCount: number): void {
|
||||
if (insertCount === 0) {
|
||||
return;
|
||||
}
|
||||
let beginState: (IState | null)[] = [];
|
||||
let valid: boolean[] = [];
|
||||
for (let i = 0; i < insertCount; i++) {
|
||||
beginState[i] = null;
|
||||
valid[i] = false;
|
||||
}
|
||||
this._beginState = arrays.arrayInsert(this._beginState, insertIndex, beginState);
|
||||
this._valid = arrays.arrayInsert(this._valid, insertIndex, valid);
|
||||
this._len += insertCount;
|
||||
}
|
||||
|
||||
private _setValid(lineIndex: number, valid: boolean): void {
|
||||
this._ensureLine(lineIndex);
|
||||
this._valid[lineIndex] = valid;
|
||||
}
|
||||
|
||||
private _setBeginState(lineIndex: number, beginState: IState | null): void {
|
||||
this._ensureLine(lineIndex);
|
||||
this._beginState[lineIndex] = beginState;
|
||||
}
|
||||
|
||||
public setEndState(linesLength: number, lineIndex: number, endState: IState): void {
|
||||
this._setValid(lineIndex, true);
|
||||
this._invalidLineStartIndex = lineIndex + 1;
|
||||
|
||||
// Check if this was the last line
|
||||
if (lineIndex === linesLength - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the end state has changed
|
||||
const previousEndState = this.getBeginState(lineIndex + 1);
|
||||
if (previousEndState === null || !endState.equals(previousEndState)) {
|
||||
this._setBeginState(lineIndex + 1, endState);
|
||||
this._invalidateLine(lineIndex + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perhaps we can skip tokenizing some lines...
|
||||
let i = lineIndex + 1;
|
||||
while (i < linesLength) {
|
||||
if (!this._isValid(i)) {
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
this._invalidLineStartIndex = i;
|
||||
}
|
||||
|
||||
public setFakeTokens(lineIndex: number): void {
|
||||
this._setValid(lineIndex, false);
|
||||
}
|
||||
|
||||
//#region Editing
|
||||
|
||||
public applyEdits(range: Range, eolCount: number, firstLineLength: number): void {
|
||||
|
||||
public applyEdits(range: IRange, eolCount: number): void {
|
||||
const deletingLinesCnt = range.endLineNumber - range.startLineNumber;
|
||||
const insertingLinesCnt = eolCount;
|
||||
const editingLinesCnt = Math.min(deletingLinesCnt, insertingLinesCnt);
|
||||
|
||||
for (let j = editingLinesCnt; j >= 0; j--) {
|
||||
this.invalidateLine(range.startLineNumber + j - 1);
|
||||
this._invalidateLine(range.startLineNumber + j - 1);
|
||||
}
|
||||
|
||||
this._acceptDeleteRange(range);
|
||||
this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength);
|
||||
this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount);
|
||||
}
|
||||
|
||||
private _acceptDeleteRange(range: Range): void {
|
||||
private _acceptDeleteRange(range: IRange): void {
|
||||
|
||||
const firstLineIndex = range.startLineNumber - 1;
|
||||
if (firstLineIndex >= this._tokens.length) {
|
||||
if (firstLineIndex >= this._len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (range.startLineNumber === range.endLineNumber) {
|
||||
if (range.startColumn === range.endColumn) {
|
||||
// Nothing to delete
|
||||
return;
|
||||
}
|
||||
|
||||
this._tokens[firstLineIndex].delete(range.startColumn - 1, range.endColumn - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const firstLine = this._tokens[firstLineIndex];
|
||||
firstLine.deleteEnding(range.startColumn - 1);
|
||||
|
||||
const lastLineIndex = range.endLineNumber - 1;
|
||||
let lastLineTokens: ArrayBuffer | null = null;
|
||||
if (lastLineIndex < this._tokens.length) {
|
||||
const lastLine = this._tokens[lastLineIndex];
|
||||
lastLine.deleteBeginning(range.endColumn - 1);
|
||||
lastLineTokens = lastLine._lineTokens;
|
||||
}
|
||||
|
||||
// Take remaining text on last line and append it to remaining text on first line
|
||||
firstLine.append(lastLineTokens);
|
||||
|
||||
// Delete middle lines
|
||||
this._tokens.splice(range.startLineNumber, range.endLineNumber - range.startLineNumber);
|
||||
this._deleteLines(range.startLineNumber, range.endLineNumber - range.startLineNumber);
|
||||
}
|
||||
|
||||
private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number): void {
|
||||
|
||||
if (eolCount === 0 && firstLineLength === 0) {
|
||||
// Nothing to insert
|
||||
return;
|
||||
}
|
||||
private _acceptInsertText(position: Position, eolCount: number): void {
|
||||
|
||||
const lineIndex = position.lineNumber - 1;
|
||||
if (lineIndex >= this._tokens.length) {
|
||||
if (lineIndex >= this._len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (eolCount === 0) {
|
||||
// Inserting text on one line
|
||||
this._tokens[lineIndex].insert(position.column - 1, firstLineLength);
|
||||
return;
|
||||
}
|
||||
|
||||
const line = this._tokens[lineIndex];
|
||||
line.deleteEnding(position.column - 1);
|
||||
line.insert(position.column - 1, firstLineLength);
|
||||
|
||||
let insert: ModelLineTokens[] = new Array<ModelLineTokens>(eolCount);
|
||||
for (let i = eolCount - 1; i >= 0; i--) {
|
||||
insert[i] = new ModelLineTokens(null);
|
||||
}
|
||||
this._tokens = arrays.arrayInsert(this._tokens, position.lineNumber, insert);
|
||||
this._insertLines(position.lineNumber, eolCount);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
//#region Tokenization
|
||||
export class TextModelTokenization extends Disposable {
|
||||
|
||||
public _tokenizeOneLine(buffer: ITextBuffer, eventBuilder: ModelTokensChangedEventBuilder): number {
|
||||
if (!this.hasLinesToTokenize(buffer)) {
|
||||
return buffer.getLineCount() + 1;
|
||||
private readonly _textModel: TextModel;
|
||||
private readonly _tokenizationStateStore: TokenizationStateStore;
|
||||
private _revalidateTokensTimeout: any;
|
||||
private _tokenizationSupport: ITokenizationSupport | null;
|
||||
|
||||
constructor(textModel: TextModel) {
|
||||
super();
|
||||
this._textModel = textModel;
|
||||
this._tokenizationStateStore = new TokenizationStateStore();
|
||||
this._revalidateTokensTimeout = -1;
|
||||
this._tokenizationSupport = null;
|
||||
|
||||
this._register(TokenizationRegistry.onDidChange((e) => {
|
||||
const languageIdentifier = this._textModel.getLanguageIdentifier();
|
||||
if (e.changedLanguages.indexOf(languageIdentifier.language) === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._resetTokenizationState();
|
||||
this._textModel.clearTokens();
|
||||
}));
|
||||
|
||||
this._register(this._textModel.onDidChangeRawContentFast((e) => {
|
||||
if (e.containsEvent(RawContentChangedType.Flush)) {
|
||||
this._resetTokenizationState();
|
||||
return;
|
||||
}
|
||||
}));
|
||||
|
||||
this._register(this._textModel.onDidChangeContentFast((e) => {
|
||||
for (let i = 0, len = e.changes.length; i < len; i++) {
|
||||
const change = e.changes[i];
|
||||
const [eolCount] = countEOL(change.text);
|
||||
this._tokenizationStateStore.applyEdits(change.range, eolCount);
|
||||
}
|
||||
|
||||
this._beginBackgroundTokenization();
|
||||
}));
|
||||
|
||||
this._register(this._textModel.onDidChangeAttached(() => {
|
||||
this._beginBackgroundTokenization();
|
||||
}));
|
||||
|
||||
this._register(this._textModel.onDidChangeLanguage(() => {
|
||||
this._resetTokenizationState();
|
||||
this._textModel.clearTokens();
|
||||
}));
|
||||
|
||||
this._resetTokenizationState();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._clearTimers();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _clearTimers(): void {
|
||||
if (this._revalidateTokensTimeout !== -1) {
|
||||
clearTimeout(this._revalidateTokensTimeout);
|
||||
this._revalidateTokensTimeout = -1;
|
||||
}
|
||||
const lineNumber = this._invalidLineStartIndex + 1;
|
||||
this._updateTokensUntilLine(buffer, eventBuilder, lineNumber);
|
||||
}
|
||||
|
||||
private _resetTokenizationState(): void {
|
||||
this._clearTimers();
|
||||
const [tokenizationSupport, initialState] = initializeTokenization(this._textModel);
|
||||
this._tokenizationSupport = tokenizationSupport;
|
||||
this._tokenizationStateStore.flush(initialState);
|
||||
this._beginBackgroundTokenization();
|
||||
}
|
||||
|
||||
private _beginBackgroundTokenization(): void {
|
||||
if (this._textModel.isAttachedToEditor() && this._hasLinesToTokenize() && this._revalidateTokensTimeout === -1) {
|
||||
this._revalidateTokensTimeout = setTimeout(() => {
|
||||
this._revalidateTokensTimeout = -1;
|
||||
this._revalidateTokensNow();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private _revalidateTokensNow(toLineNumber: number = this._textModel.getLineCount()): void {
|
||||
const MAX_ALLOWED_TIME = 20;
|
||||
const builder = new MultilineTokensBuilder();
|
||||
const sw = StopWatch.create(false);
|
||||
|
||||
while (this._hasLinesToTokenize()) {
|
||||
if (sw.elapsed() > MAX_ALLOWED_TIME) {
|
||||
// Stop if MAX_ALLOWED_TIME is reached
|
||||
break;
|
||||
}
|
||||
|
||||
const tokenizedLineNumber = this._tokenizeOneInvalidLine(builder);
|
||||
|
||||
if (tokenizedLineNumber >= toLineNumber) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this._beginBackgroundTokenization();
|
||||
this._textModel.setTokens(builder.tokens);
|
||||
}
|
||||
|
||||
public tokenizeViewport(startLineNumber: number, endLineNumber: number): void {
|
||||
const builder = new MultilineTokensBuilder();
|
||||
this._tokenizeViewport(builder, startLineNumber, endLineNumber);
|
||||
this._textModel.setTokens(builder.tokens);
|
||||
}
|
||||
|
||||
public reset(): void {
|
||||
this._resetTokenizationState();
|
||||
this._textModel.clearTokens();
|
||||
}
|
||||
|
||||
public forceTokenization(lineNumber: number): void {
|
||||
const builder = new MultilineTokensBuilder();
|
||||
this._updateTokensUntilLine(builder, lineNumber);
|
||||
this._textModel.setTokens(builder.tokens);
|
||||
}
|
||||
|
||||
public isCheapToTokenize(lineNumber: number): boolean {
|
||||
if (!this._tokenizationSupport) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const firstInvalidLineNumber = this._tokenizationStateStore.invalidLineStartIndex + 1;
|
||||
if (lineNumber > firstInvalidLineNumber) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lineNumber < firstInvalidLineNumber) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this._textModel.getLineLength(lineNumber) < Constants.CHEAP_TOKENIZATION_LENGTH_LIMIT) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private _hasLinesToTokenize(): boolean {
|
||||
if (!this._tokenizationSupport) {
|
||||
return false;
|
||||
}
|
||||
return (this._tokenizationStateStore.invalidLineStartIndex < this._textModel.getLineCount());
|
||||
}
|
||||
|
||||
private _tokenizeOneInvalidLine(builder: MultilineTokensBuilder): number {
|
||||
if (!this._hasLinesToTokenize()) {
|
||||
return this._textModel.getLineCount() + 1;
|
||||
}
|
||||
const lineNumber = this._tokenizationStateStore.invalidLineStartIndex + 1;
|
||||
this._updateTokensUntilLine(builder, lineNumber);
|
||||
return lineNumber;
|
||||
}
|
||||
|
||||
public _tokenizeText(buffer: ITextBuffer, text: string, state: IState): TokenizationResult2 {
|
||||
let r: TokenizationResult2 | null = null;
|
||||
|
||||
if (this.tokenizationSupport) {
|
||||
try {
|
||||
r = this.tokenizationSupport.tokenize2(text, state, 0);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!r) {
|
||||
r = nullTokenize2(this.languageIdentifier.id, text, state, 0);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
public _updateTokensUntilLine(buffer: ITextBuffer, eventBuilder: ModelTokensChangedEventBuilder, lineNumber: number): void {
|
||||
if (!this.tokenizationSupport) {
|
||||
this._invalidLineStartIndex = buffer.getLineCount();
|
||||
private _updateTokensUntilLine(builder: MultilineTokensBuilder, lineNumber: number): void {
|
||||
if (!this._tokenizationSupport) {
|
||||
return;
|
||||
}
|
||||
|
||||
const linesLength = buffer.getLineCount();
|
||||
const languageIdentifier = this._textModel.getLanguageIdentifier();
|
||||
const linesLength = this._textModel.getLineCount();
|
||||
const endLineIndex = lineNumber - 1;
|
||||
|
||||
// Validate all states up to and including endLineIndex
|
||||
for (let lineIndex = this._invalidLineStartIndex; lineIndex <= endLineIndex; lineIndex++) {
|
||||
const endStateIndex = lineIndex + 1;
|
||||
const text = buffer.getLineContent(lineIndex + 1);
|
||||
const lineStartState = this._getState(lineIndex);
|
||||
for (let lineIndex = this._tokenizationStateStore.invalidLineStartIndex; lineIndex <= endLineIndex; lineIndex++) {
|
||||
const text = this._textModel.getLineContent(lineIndex + 1);
|
||||
const lineStartState = this._tokenizationStateStore.getBeginState(lineIndex);
|
||||
|
||||
let r: TokenizationResult2 | null = null;
|
||||
const r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, lineStartState!);
|
||||
builder.add(lineIndex + 1, r.tokens);
|
||||
this._tokenizationStateStore.setEndState(linesLength, lineIndex, r.endState);
|
||||
lineIndex = this._tokenizationStateStore.invalidLineStartIndex - 1; // -1 because the outer loop increments it
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Tokenize only the first X characters
|
||||
let freshState = lineStartState!.clone();
|
||||
r = this.tokenizationSupport.tokenize2(text, freshState, 0);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
private _tokenizeViewport(builder: MultilineTokensBuilder, startLineNumber: number, endLineNumber: number): void {
|
||||
if (!this._tokenizationSupport) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if (endLineNumber <= this._tokenizationStateStore.invalidLineStartIndex) {
|
||||
// nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
if (startLineNumber <= this._tokenizationStateStore.invalidLineStartIndex) {
|
||||
// tokenization has reached the viewport start...
|
||||
this._updateTokensUntilLine(builder, endLineNumber);
|
||||
return;
|
||||
}
|
||||
|
||||
let nonWhitespaceColumn = this._textModel.getLineFirstNonWhitespaceColumn(startLineNumber);
|
||||
let fakeLines: string[] = [];
|
||||
let initialState: IState | null = null;
|
||||
for (let i = startLineNumber - 1; nonWhitespaceColumn > 0 && i >= 1; i--) {
|
||||
let newNonWhitespaceIndex = this._textModel.getLineFirstNonWhitespaceColumn(i);
|
||||
|
||||
if (newNonWhitespaceIndex === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!r) {
|
||||
r = nullTokenize2(this.languageIdentifier.id, text, lineStartState, 0);
|
||||
}
|
||||
this._setTokens(this.languageIdentifier.id, lineIndex, text.length, r.tokens);
|
||||
eventBuilder.registerChangedTokens(lineIndex + 1);
|
||||
this._setIsInvalid(lineIndex, false);
|
||||
|
||||
if (endStateIndex < linesLength) {
|
||||
const previousEndState = this._getState(endStateIndex);
|
||||
if (previousEndState !== null && r.endState.equals(previousEndState)) {
|
||||
// The end state of this line remains the same
|
||||
let nextInvalidLineIndex = lineIndex + 1;
|
||||
while (nextInvalidLineIndex < linesLength) {
|
||||
if (this._isInvalid(nextInvalidLineIndex)) {
|
||||
break;
|
||||
}
|
||||
if (nextInvalidLineIndex + 1 < linesLength) {
|
||||
if (this._getState(nextInvalidLineIndex + 1) === null) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (this._lastState === null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
nextInvalidLineIndex++;
|
||||
}
|
||||
this._invalidLineStartIndex = Math.max(this._invalidLineStartIndex, nextInvalidLineIndex);
|
||||
lineIndex = nextInvalidLineIndex - 1; // -1 because the outer loop increments it
|
||||
} else {
|
||||
this._setState(endStateIndex, r.endState);
|
||||
if (newNonWhitespaceIndex < nonWhitespaceColumn) {
|
||||
initialState = this._tokenizationStateStore.getBeginState(i - 1);
|
||||
if (initialState) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this._lastState = r.endState;
|
||||
fakeLines.push(this._textModel.getLineContent(i));
|
||||
nonWhitespaceColumn = newNonWhitespaceIndex;
|
||||
}
|
||||
}
|
||||
this._invalidLineStartIndex = Math.max(this._invalidLineStartIndex, endLineIndex + 1);
|
||||
}
|
||||
|
||||
// #endregion
|
||||
if (!initialState) {
|
||||
initialState = this._tokenizationSupport.getInitialState();
|
||||
}
|
||||
|
||||
const languageIdentifier = this._textModel.getLanguageIdentifier();
|
||||
let state = initialState;
|
||||
for (let i = fakeLines.length - 1; i >= 0; i--) {
|
||||
let r = safeTokenize(languageIdentifier, this._tokenizationSupport, fakeLines[i], state);
|
||||
state = r.endState;
|
||||
}
|
||||
|
||||
for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
|
||||
let text = this._textModel.getLineContent(lineNumber);
|
||||
let r = safeTokenize(languageIdentifier, this._tokenizationSupport, text, state);
|
||||
builder.add(lineNumber, r.tokens);
|
||||
this._tokenizationStateStore.setFakeTokens(lineNumber - 1);
|
||||
state = r.endState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ModelTokensChangedEventBuilder {
|
||||
|
||||
private readonly _ranges: { fromLineNumber: number; toLineNumber: number; }[];
|
||||
|
||||
constructor() {
|
||||
this._ranges = [];
|
||||
function initializeTokenization(textModel: TextModel): [ITokenizationSupport | null, IState | null] {
|
||||
const languageIdentifier = textModel.getLanguageIdentifier();
|
||||
let tokenizationSupport = (
|
||||
textModel.isTooLargeForTokenization()
|
||||
? null
|
||||
: TokenizationRegistry.get(languageIdentifier.language)
|
||||
);
|
||||
let initialState: IState | null = null;
|
||||
if (tokenizationSupport) {
|
||||
try {
|
||||
initialState = tokenizationSupport.getInitialState();
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
tokenizationSupport = null;
|
||||
}
|
||||
}
|
||||
return [tokenizationSupport, initialState];
|
||||
}
|
||||
|
||||
public registerChangedTokens(lineNumber: number): void {
|
||||
const ranges = this._ranges;
|
||||
const rangesLength = ranges.length;
|
||||
const previousRange = rangesLength > 0 ? ranges[rangesLength - 1] : null;
|
||||
function safeTokenize(languageIdentifier: LanguageIdentifier, tokenizationSupport: ITokenizationSupport | null, text: string, state: IState): TokenizationResult2 {
|
||||
let r: TokenizationResult2 | null = null;
|
||||
|
||||
if (previousRange && previousRange.toLineNumber === lineNumber - 1) {
|
||||
// extend previous range
|
||||
previousRange.toLineNumber++;
|
||||
} else {
|
||||
// insert new range
|
||||
ranges[rangesLength] = {
|
||||
fromLineNumber: lineNumber,
|
||||
toLineNumber: lineNumber
|
||||
};
|
||||
if (tokenizationSupport) {
|
||||
try {
|
||||
r = tokenizationSupport.tokenize2(text, state.clone(), 0);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public build(): IModelTokensChangedEvent | null {
|
||||
if (this._ranges.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
tokenizationSupportChanged: false,
|
||||
ranges: this._ranges
|
||||
};
|
||||
if (!r) {
|
||||
r = nullTokenize2(languageIdentifier.id, text, state, 0);
|
||||
}
|
||||
|
||||
LineTokens.convertToEndOffset(r.tokens, text.length);
|
||||
return r;
|
||||
}
|
||||
|
||||
330
src/vs/editor/common/model/tokensStore.ts
Normal file
330
src/vs/editor/common/model/tokensStore.ts
Normal file
@@ -0,0 +1,330 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { ColorId, FontStyle, LanguageId, MetadataConsts, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes';
|
||||
|
||||
function getDefaultMetadata(topLevelLanguageId: LanguageId): number {
|
||||
return (
|
||||
(topLevelLanguageId << MetadataConsts.LANGUAGEID_OFFSET)
|
||||
| (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET)
|
||||
| (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET)
|
||||
| (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET)
|
||||
| (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET)
|
||||
) >>> 0;
|
||||
}
|
||||
|
||||
const EMPTY_LINE_TOKENS = (new Uint32Array(0)).buffer;
|
||||
|
||||
export class MultilineTokensBuilder {
|
||||
|
||||
public readonly tokens: MultilineTokens[];
|
||||
|
||||
constructor() {
|
||||
this.tokens = [];
|
||||
}
|
||||
|
||||
public add(lineNumber: number, lineTokens: Uint32Array): void {
|
||||
if (this.tokens.length > 0) {
|
||||
const last = this.tokens[this.tokens.length - 1];
|
||||
const lastLineNumber = last.startLineNumber + last.tokens.length - 1;
|
||||
if (lastLineNumber + 1 === lineNumber) {
|
||||
// append
|
||||
last.tokens.push(lineTokens);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.tokens.push(new MultilineTokens(lineNumber, lineTokens));
|
||||
}
|
||||
}
|
||||
|
||||
export class MultilineTokens {
|
||||
|
||||
public readonly startLineNumber: number;
|
||||
public readonly tokens: Uint32Array[];
|
||||
|
||||
constructor(lineNumber: number, tokens: Uint32Array) {
|
||||
this.startLineNumber = lineNumber;
|
||||
this.tokens = [tokens];
|
||||
}
|
||||
}
|
||||
|
||||
export class TokensStore {
|
||||
private _lineTokens: (ArrayBuffer | null)[];
|
||||
private _len: number;
|
||||
|
||||
constructor() {
|
||||
this._lineTokens = [];
|
||||
this._len = 0;
|
||||
}
|
||||
|
||||
public flush(): void {
|
||||
this._lineTokens = [];
|
||||
this._len = 0;
|
||||
}
|
||||
|
||||
public getTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineText: string): LineTokens {
|
||||
let rawLineTokens: ArrayBuffer | null = null;
|
||||
if (lineIndex < this._len) {
|
||||
rawLineTokens = this._lineTokens[lineIndex];
|
||||
}
|
||||
|
||||
if (rawLineTokens !== null && rawLineTokens !== EMPTY_LINE_TOKENS) {
|
||||
return new LineTokens(new Uint32Array(rawLineTokens), lineText);
|
||||
}
|
||||
|
||||
let lineTokens = new Uint32Array(2);
|
||||
lineTokens[0] = lineText.length;
|
||||
lineTokens[1] = getDefaultMetadata(topLevelLanguageId);
|
||||
return new LineTokens(lineTokens, lineText);
|
||||
}
|
||||
|
||||
private static _massageTokens(topLevelLanguageId: LanguageId, lineTextLength: number, tokens: Uint32Array): ArrayBuffer {
|
||||
if (lineTextLength === 0) {
|
||||
let hasDifferentLanguageId = false;
|
||||
if (tokens && tokens.length > 1) {
|
||||
hasDifferentLanguageId = (TokenMetadata.getLanguageId(tokens[1]) !== topLevelLanguageId);
|
||||
}
|
||||
|
||||
if (!hasDifferentLanguageId) {
|
||||
return EMPTY_LINE_TOKENS;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tokens || tokens.length === 0) {
|
||||
tokens = new Uint32Array(2);
|
||||
tokens[0] = lineTextLength;
|
||||
tokens[1] = getDefaultMetadata(topLevelLanguageId);
|
||||
}
|
||||
|
||||
return tokens.buffer;
|
||||
}
|
||||
|
||||
private _ensureLine(lineIndex: number): void {
|
||||
while (lineIndex >= this._len) {
|
||||
this._lineTokens[this._len] = null;
|
||||
this._len++;
|
||||
}
|
||||
}
|
||||
|
||||
private _deleteLines(start: number, deleteCount: number): void {
|
||||
if (deleteCount === 0) {
|
||||
return;
|
||||
}
|
||||
this._lineTokens.splice(start, deleteCount);
|
||||
this._len -= deleteCount;
|
||||
}
|
||||
|
||||
private _insertLines(insertIndex: number, insertCount: number): void {
|
||||
if (insertCount === 0) {
|
||||
return;
|
||||
}
|
||||
let lineTokens: (ArrayBuffer | null)[] = [];
|
||||
for (let i = 0; i < insertCount; i++) {
|
||||
lineTokens[i] = null;
|
||||
}
|
||||
this._lineTokens = arrays.arrayInsert(this._lineTokens, insertIndex, lineTokens);
|
||||
this._len += insertCount;
|
||||
}
|
||||
|
||||
public setTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineTextLength: number, _tokens: Uint32Array): void {
|
||||
const tokens = TokensStore._massageTokens(topLevelLanguageId, lineTextLength, _tokens);
|
||||
this._ensureLine(lineIndex);
|
||||
this._lineTokens[lineIndex] = tokens;
|
||||
}
|
||||
|
||||
//#region Editing
|
||||
|
||||
public applyEdits(range: IRange, eolCount: number, firstLineLength: number): void {
|
||||
this._acceptDeleteRange(range);
|
||||
this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength);
|
||||
}
|
||||
|
||||
private _acceptDeleteRange(range: IRange): void {
|
||||
|
||||
const firstLineIndex = range.startLineNumber - 1;
|
||||
if (firstLineIndex >= this._len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (range.startLineNumber === range.endLineNumber) {
|
||||
if (range.startColumn === range.endColumn) {
|
||||
// Nothing to delete
|
||||
return;
|
||||
}
|
||||
|
||||
this._lineTokens[firstLineIndex] = TokensStore._delete(this._lineTokens[firstLineIndex], range.startColumn - 1, range.endColumn - 1);
|
||||
return;
|
||||
}
|
||||
|
||||
this._lineTokens[firstLineIndex] = TokensStore._deleteEnding(this._lineTokens[firstLineIndex], range.startColumn - 1);
|
||||
|
||||
const lastLineIndex = range.endLineNumber - 1;
|
||||
let lastLineTokens: ArrayBuffer | null = null;
|
||||
if (lastLineIndex < this._len) {
|
||||
lastLineTokens = TokensStore._deleteBeginning(this._lineTokens[lastLineIndex], range.endColumn - 1);
|
||||
}
|
||||
|
||||
// Take remaining text on last line and append it to remaining text on first line
|
||||
this._lineTokens[firstLineIndex] = TokensStore._append(this._lineTokens[firstLineIndex], lastLineTokens);
|
||||
|
||||
// Delete middle lines
|
||||
this._deleteLines(range.startLineNumber, range.endLineNumber - range.startLineNumber);
|
||||
}
|
||||
|
||||
private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number): void {
|
||||
|
||||
if (eolCount === 0 && firstLineLength === 0) {
|
||||
// Nothing to insert
|
||||
return;
|
||||
}
|
||||
|
||||
const lineIndex = position.lineNumber - 1;
|
||||
if (lineIndex >= this._len) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (eolCount === 0) {
|
||||
// Inserting text on one line
|
||||
this._lineTokens[lineIndex] = TokensStore._insert(this._lineTokens[lineIndex], position.column - 1, firstLineLength);
|
||||
return;
|
||||
}
|
||||
|
||||
this._lineTokens[lineIndex] = TokensStore._deleteEnding(this._lineTokens[lineIndex], position.column - 1);
|
||||
this._lineTokens[lineIndex] = TokensStore._insert(this._lineTokens[lineIndex], position.column - 1, firstLineLength);
|
||||
|
||||
this._insertLines(position.lineNumber, eolCount);
|
||||
}
|
||||
|
||||
private static _deleteBeginning(lineTokens: ArrayBuffer | null, toChIndex: number): ArrayBuffer | null {
|
||||
if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) {
|
||||
return lineTokens;
|
||||
}
|
||||
return TokensStore._delete(lineTokens, 0, toChIndex);
|
||||
}
|
||||
|
||||
private static _deleteEnding(lineTokens: ArrayBuffer | null, fromChIndex: number): ArrayBuffer | null {
|
||||
if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) {
|
||||
return lineTokens;
|
||||
}
|
||||
|
||||
const tokens = new Uint32Array(lineTokens);
|
||||
const lineTextLength = tokens[tokens.length - 2];
|
||||
return TokensStore._delete(lineTokens, fromChIndex, lineTextLength);
|
||||
}
|
||||
|
||||
private static _delete(lineTokens: ArrayBuffer | null, fromChIndex: number, toChIndex: number): ArrayBuffer | null {
|
||||
if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS || fromChIndex === toChIndex) {
|
||||
return lineTokens;
|
||||
}
|
||||
|
||||
const tokens = new Uint32Array(lineTokens);
|
||||
const tokensCount = (tokens.length >>> 1);
|
||||
|
||||
// special case: deleting everything
|
||||
if (fromChIndex === 0 && tokens[tokens.length - 2] === toChIndex) {
|
||||
return EMPTY_LINE_TOKENS;
|
||||
}
|
||||
|
||||
const fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, fromChIndex);
|
||||
const fromTokenStartOffset = (fromTokenIndex > 0 ? tokens[(fromTokenIndex - 1) << 1] : 0);
|
||||
const fromTokenEndOffset = tokens[fromTokenIndex << 1];
|
||||
|
||||
if (toChIndex < fromTokenEndOffset) {
|
||||
// the delete range is inside a single token
|
||||
const delta = (toChIndex - fromChIndex);
|
||||
for (let i = fromTokenIndex; i < tokensCount; i++) {
|
||||
tokens[i << 1] -= delta;
|
||||
}
|
||||
return lineTokens;
|
||||
}
|
||||
|
||||
let dest: number;
|
||||
let lastEnd: number;
|
||||
if (fromTokenStartOffset !== fromChIndex) {
|
||||
tokens[fromTokenIndex << 1] = fromChIndex;
|
||||
dest = ((fromTokenIndex + 1) << 1);
|
||||
lastEnd = fromChIndex;
|
||||
} else {
|
||||
dest = (fromTokenIndex << 1);
|
||||
lastEnd = fromTokenStartOffset;
|
||||
}
|
||||
|
||||
const delta = (toChIndex - fromChIndex);
|
||||
for (let tokenIndex = fromTokenIndex + 1; tokenIndex < tokensCount; tokenIndex++) {
|
||||
const tokenEndOffset = tokens[tokenIndex << 1] - delta;
|
||||
if (tokenEndOffset > lastEnd) {
|
||||
tokens[dest++] = tokenEndOffset;
|
||||
tokens[dest++] = tokens[(tokenIndex << 1) + 1];
|
||||
lastEnd = tokenEndOffset;
|
||||
}
|
||||
}
|
||||
|
||||
if (dest === tokens.length) {
|
||||
// nothing to trim
|
||||
return lineTokens;
|
||||
}
|
||||
|
||||
let tmp = new Uint32Array(dest);
|
||||
tmp.set(tokens.subarray(0, dest), 0);
|
||||
return tmp.buffer;
|
||||
}
|
||||
|
||||
private static _append(lineTokens: ArrayBuffer | null, _otherTokens: ArrayBuffer | null): ArrayBuffer | null {
|
||||
if (_otherTokens === EMPTY_LINE_TOKENS) {
|
||||
return lineTokens;
|
||||
}
|
||||
if (lineTokens === EMPTY_LINE_TOKENS) {
|
||||
return _otherTokens;
|
||||
}
|
||||
if (lineTokens === null) {
|
||||
return lineTokens;
|
||||
}
|
||||
if (_otherTokens === null) {
|
||||
// cannot determine combined line length...
|
||||
return null;
|
||||
}
|
||||
const myTokens = new Uint32Array(lineTokens);
|
||||
const otherTokens = new Uint32Array(_otherTokens);
|
||||
const otherTokensCount = (otherTokens.length >>> 1);
|
||||
|
||||
let result = new Uint32Array(myTokens.length + otherTokens.length);
|
||||
result.set(myTokens, 0);
|
||||
let dest = myTokens.length;
|
||||
const delta = myTokens[myTokens.length - 2];
|
||||
for (let i = 0; i < otherTokensCount; i++) {
|
||||
result[dest++] = otherTokens[(i << 1)] + delta;
|
||||
result[dest++] = otherTokens[(i << 1) + 1];
|
||||
}
|
||||
return result.buffer;
|
||||
}
|
||||
|
||||
private static _insert(lineTokens: ArrayBuffer | null, chIndex: number, textLength: number): ArrayBuffer | null {
|
||||
if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) {
|
||||
// nothing to do
|
||||
return lineTokens;
|
||||
}
|
||||
|
||||
const tokens = new Uint32Array(lineTokens);
|
||||
const tokensCount = (tokens.length >>> 1);
|
||||
|
||||
let fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, chIndex);
|
||||
if (fromTokenIndex > 0) {
|
||||
const fromTokenStartOffset = tokens[(fromTokenIndex - 1) << 1];
|
||||
if (fromTokenStartOffset === chIndex) {
|
||||
fromTokenIndex--;
|
||||
}
|
||||
}
|
||||
for (let tokenIndex = fromTokenIndex; tokenIndex < tokensCount; tokenIndex++) {
|
||||
tokens[tokenIndex << 1] += textLength;
|
||||
}
|
||||
return lineTokens;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
@@ -17,8 +17,8 @@ import { TokenizationResult, TokenizationResult2 } from 'vs/editor/common/core/t
|
||||
import * as model from 'vs/editor/common/model';
|
||||
import { LanguageFeatureRegistry } from 'vs/editor/common/modes/languageFeatureRegistry';
|
||||
import { TokenizationRegistryImpl } from 'vs/editor/common/modes/tokenizationRegistry';
|
||||
import { IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IMarkerData } from 'vs/platform/markers/common/markers';
|
||||
|
||||
/**
|
||||
* Open ended enum at runtime
|
||||
@@ -510,6 +510,11 @@ export interface CompletionContext {
|
||||
*/
|
||||
export interface CompletionItemProvider {
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_debugDisplayName?: string;
|
||||
|
||||
triggerCharacters?: string[];
|
||||
/**
|
||||
* Provide completion items for the given position and document.
|
||||
@@ -550,6 +555,10 @@ export interface CodeActionContext {
|
||||
trigger: CodeActionTrigger;
|
||||
}
|
||||
|
||||
export interface CodeActionList extends IDisposable {
|
||||
readonly actions: ReadonlyArray<CodeAction>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The code action interface defines the contract between extensions and
|
||||
* the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature.
|
||||
@@ -559,7 +568,7 @@ export interface CodeActionProvider {
|
||||
/**
|
||||
* Provide commands for the given document and range.
|
||||
*/
|
||||
provideCodeActions(model: model.ITextModel, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<CodeAction[]>;
|
||||
provideCodeActions(model: model.ITextModel, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult<CodeActionList>;
|
||||
|
||||
/**
|
||||
* Optional list of CodeActionKinds that this provider returns.
|
||||
@@ -624,6 +633,10 @@ export interface SignatureHelp {
|
||||
activeParameter: number;
|
||||
}
|
||||
|
||||
export interface SignatureHelpResult extends IDisposable {
|
||||
value: SignatureHelp;
|
||||
}
|
||||
|
||||
export enum SignatureHelpTriggerKind {
|
||||
Invoke = 1,
|
||||
TriggerCharacter = 2,
|
||||
@@ -649,7 +662,7 @@ export interface SignatureHelpProvider {
|
||||
/**
|
||||
* Provide help for the signature at the given position and document.
|
||||
*/
|
||||
provideSignatureHelp(model: model.ITextModel, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult<SignatureHelp>;
|
||||
provideSignatureHelp(model: model.ITextModel, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult<SignatureHelpResult>;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1000,6 +1013,7 @@ export interface IInplaceReplaceSupportResult {
|
||||
export interface ILink {
|
||||
range: IRange;
|
||||
url?: URI | string;
|
||||
tooltip?: string;
|
||||
}
|
||||
|
||||
export interface ILinksList {
|
||||
@@ -1093,7 +1107,6 @@ export interface DocumentColorProvider {
|
||||
}
|
||||
|
||||
export interface SelectionRange {
|
||||
kind: string;
|
||||
range: IRange;
|
||||
}
|
||||
|
||||
@@ -1228,19 +1241,7 @@ export interface CommentThreadTemplate {
|
||||
export interface CommentInfo {
|
||||
extensionId?: string;
|
||||
threads: CommentThread[];
|
||||
commentingRanges?: (IRange[] | CommentingRanges);
|
||||
reply?: Command;
|
||||
draftMode?: DraftMode;
|
||||
template?: CommentThreadTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export enum DraftMode {
|
||||
NotSupported,
|
||||
InDraft,
|
||||
NotInDraft
|
||||
commentingRanges: CommentingRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1280,7 +1281,7 @@ export interface CommentInput {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface CommentThread2 {
|
||||
export interface CommentThread {
|
||||
commentThreadHandle: number;
|
||||
controllerHandle: number;
|
||||
extensionId?: string;
|
||||
@@ -1294,11 +1295,6 @@ export interface CommentThread2 {
|
||||
collapsibleState?: CommentThreadCollapsibleState;
|
||||
input?: CommentInput;
|
||||
onDidChangeInput: Event<CommentInput | undefined>;
|
||||
acceptInputCommand?: Command;
|
||||
additionalCommands?: Command[];
|
||||
deleteCommand?: Command;
|
||||
onDidChangeAcceptInputCommand: Event<Command | undefined>;
|
||||
onDidChangeAdditionalCommands: Event<Command[] | undefined>;
|
||||
onDidChangeRange: Event<IRange>;
|
||||
onDidChangeLabel: Event<string>;
|
||||
onDidChangeCollasibleState: Event<CommentThreadCollapsibleState | undefined>;
|
||||
@@ -1312,30 +1308,6 @@ export interface CommentThread2 {
|
||||
export interface CommentingRanges {
|
||||
readonly resource: URI;
|
||||
ranges: IRange[];
|
||||
newCommentThreadCallback?: (uri: UriComponents, range: IRange) => Promise<CommentThread | undefined>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface CommentThread {
|
||||
extensionId?: string;
|
||||
threadId: string | null;
|
||||
resource: string | null;
|
||||
range: IRange;
|
||||
comments: Comment[] | undefined;
|
||||
collapsibleState?: CommentThreadCollapsibleState;
|
||||
reply?: Command;
|
||||
isDisposed?: boolean;
|
||||
contextValue?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface NewCommentAction {
|
||||
ranges: IRange[];
|
||||
actions: Command[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1367,12 +1339,6 @@ export interface Comment {
|
||||
readonly userName: string;
|
||||
readonly userIconPath?: string;
|
||||
readonly contextValue?: string;
|
||||
readonly canEdit?: boolean;
|
||||
readonly canDelete?: boolean;
|
||||
readonly selectCommand?: Command;
|
||||
readonly editCommand?: Command;
|
||||
readonly deleteCommand?: Command;
|
||||
readonly isDraft?: boolean;
|
||||
readonly commentReactions?: CommentReaction[];
|
||||
readonly label?: string;
|
||||
readonly mode?: CommentMode;
|
||||
@@ -1385,54 +1351,17 @@ export interface CommentThreadChangedEvent {
|
||||
/**
|
||||
* Added comment threads.
|
||||
*/
|
||||
readonly added: (CommentThread | CommentThread2)[];
|
||||
readonly added: CommentThread[];
|
||||
|
||||
/**
|
||||
* Removed comment threads.
|
||||
*/
|
||||
readonly removed: (CommentThread | CommentThread2)[];
|
||||
readonly removed: CommentThread[];
|
||||
|
||||
/**
|
||||
* Changed comment threads.
|
||||
*/
|
||||
readonly changed: (CommentThread | CommentThread2)[];
|
||||
|
||||
/**
|
||||
* changed draft mode.
|
||||
*/
|
||||
readonly draftMode?: DraftMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface DocumentCommentProvider {
|
||||
provideDocumentComments(resource: URI, token: CancellationToken): Promise<CommentInfo | null>;
|
||||
createNewCommentThread(resource: URI, range: Range, text: string, token: CancellationToken): Promise<CommentThread | null>;
|
||||
replyToCommentThread(resource: URI, range: Range, thread: CommentThread, text: string, token: CancellationToken): Promise<CommentThread | null>;
|
||||
editComment(resource: URI, comment: Comment, text: string, token: CancellationToken): Promise<void>;
|
||||
deleteComment(resource: URI, comment: Comment, token: CancellationToken): Promise<void>;
|
||||
startDraft?(resource: URI, token: CancellationToken): Promise<void>;
|
||||
deleteDraft?(resource: URI, token: CancellationToken): Promise<void>;
|
||||
finishDraft?(resource: URI, token: CancellationToken): Promise<void>;
|
||||
|
||||
startDraftLabel?: string;
|
||||
deleteDraftLabel?: string;
|
||||
finishDraftLabel?: string;
|
||||
|
||||
addReaction?(resource: URI, comment: Comment, reaction: CommentReaction, token: CancellationToken): Promise<void>;
|
||||
deleteReaction?(resource: URI, comment: Comment, reaction: CommentReaction, token: CancellationToken): Promise<void>;
|
||||
reactionGroup?: CommentReaction[];
|
||||
|
||||
onDidChangeCommentThreads?(): Event<CommentThreadChangedEvent>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface WorkspaceCommentProvider {
|
||||
provideWorkspaceComments(token: CancellationToken): Promise<CommentThread[]>;
|
||||
onDidChangeCommentThreads(): Event<CommentThreadChangedEvent>;
|
||||
readonly changed: CommentThread[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1461,15 +1390,21 @@ export interface IWebviewPanelOptions {
|
||||
readonly retainContextWhenHidden?: boolean;
|
||||
}
|
||||
|
||||
export interface ICodeLensSymbol {
|
||||
export interface CodeLens {
|
||||
range: IRange;
|
||||
id?: string;
|
||||
command?: Command;
|
||||
}
|
||||
|
||||
export interface CodeLensList {
|
||||
lenses: CodeLens[];
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export interface CodeLensProvider {
|
||||
onDidChange?: Event<this>;
|
||||
provideCodeLenses(model: model.ITextModel, token: CancellationToken): ProviderResult<ICodeLensSymbol[]>;
|
||||
resolveCodeLens?(model: model.ITextModel, codeLens: ICodeLensSymbol, token: CancellationToken): ProviderResult<ICodeLensSymbol>;
|
||||
provideCodeLenses(model: model.ITextModel, token: CancellationToken): ProviderResult<CodeLensList>;
|
||||
resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult<CodeLens>;
|
||||
}
|
||||
|
||||
// --- feature registries ------
|
||||
|
||||
@@ -22,7 +22,8 @@ import { ILinkComputerTarget, computeLinks } from 'vs/editor/common/modes/linkCo
|
||||
import { BasicInplaceReplace } from 'vs/editor/common/modes/supports/inplaceReplaceSupport';
|
||||
import { IDiffComputationResult } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { createMonacoBaseAPI } from 'vs/editor/common/standalone/standaloneBase';
|
||||
import { getAllPropertyNames } from 'vs/base/common/types';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { EditorWorkerHost } from 'vs/editor/common/services/editorWorkerServiceImpl';
|
||||
|
||||
export interface IMirrorModel {
|
||||
readonly uri: URI;
|
||||
@@ -30,7 +31,11 @@ export interface IMirrorModel {
|
||||
getValue(): string;
|
||||
}
|
||||
|
||||
export interface IWorkerContext {
|
||||
export interface IWorkerContext<H = undefined> {
|
||||
/**
|
||||
* A proxy to the main thread host object.
|
||||
*/
|
||||
host: H;
|
||||
/**
|
||||
* Get all available mirror models in this worker.
|
||||
*/
|
||||
@@ -322,17 +327,53 @@ declare var require: any;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export abstract class BaseEditorSimpleWorker {
|
||||
export class EditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
_requestHandlerBrand: any;
|
||||
|
||||
private readonly _host: EditorWorkerHost;
|
||||
private _models: { [uri: string]: MirrorModel; };
|
||||
private readonly _foreignModuleFactory: IForeignModuleFactory | null;
|
||||
private _foreignModule: any;
|
||||
|
||||
constructor(foreignModuleFactory: IForeignModuleFactory | null) {
|
||||
constructor(host: EditorWorkerHost, foreignModuleFactory: IForeignModuleFactory | null) {
|
||||
this._host = host;
|
||||
this._models = Object.create(null);
|
||||
this._foreignModuleFactory = foreignModuleFactory;
|
||||
this._foreignModule = null;
|
||||
}
|
||||
|
||||
protected abstract _getModel(uri: string): ICommonModel;
|
||||
protected abstract _getModels(): ICommonModel[];
|
||||
public dispose(): void {
|
||||
this._models = Object.create(null);
|
||||
}
|
||||
|
||||
protected _getModel(uri: string): ICommonModel {
|
||||
return this._models[uri];
|
||||
}
|
||||
|
||||
private _getModels(): ICommonModel[] {
|
||||
let all: MirrorModel[] = [];
|
||||
Object.keys(this._models).forEach((key) => all.push(this._models[key]));
|
||||
return all;
|
||||
}
|
||||
|
||||
public acceptNewModel(data: IRawModelData): void {
|
||||
this._models[data.url] = new MirrorModel(URI.parse(data.url), data.lines, data.EOL, data.versionId);
|
||||
}
|
||||
|
||||
public acceptModelChanged(strURL: string, e: IModelChangedEvent): void {
|
||||
if (!this._models[strURL]) {
|
||||
return;
|
||||
}
|
||||
let model = this._models[strURL];
|
||||
model.onEvents(e);
|
||||
}
|
||||
|
||||
public acceptRemovedModel(strURL: string): void {
|
||||
if (!this._models[strURL]) {
|
||||
return;
|
||||
}
|
||||
delete this._models[strURL];
|
||||
}
|
||||
|
||||
// ---- BEGIN diff --------------------------------------------------------------------------
|
||||
|
||||
@@ -440,7 +481,7 @@ export abstract class BaseEditorSimpleWorker {
|
||||
}
|
||||
|
||||
// make sure diff won't take too long
|
||||
if (Math.max(text.length, original.length) > BaseEditorSimpleWorker._diffLimit) {
|
||||
if (Math.max(text.length, original.length) > EditorSimpleWorker._diffLimit) {
|
||||
result.push({ range, text });
|
||||
continue;
|
||||
}
|
||||
@@ -503,7 +544,7 @@ export abstract class BaseEditorSimpleWorker {
|
||||
|
||||
for (
|
||||
let iter = model.createWordIterator(wordDefRegExp), e = iter.next();
|
||||
!e.done && suggestions.length <= BaseEditorSimpleWorker._suggestionsLimit;
|
||||
!e.done && suggestions.length <= EditorSimpleWorker._suggestionsLimit;
|
||||
e = iter.next()
|
||||
) {
|
||||
const word = e.value;
|
||||
@@ -591,8 +632,15 @@ export abstract class BaseEditorSimpleWorker {
|
||||
|
||||
// ---- BEGIN foreign module support --------------------------------------------------------------------------
|
||||
|
||||
public loadForeignModule(moduleId: string, createData: any): Promise<string[]> {
|
||||
let ctx: IWorkerContext = {
|
||||
public loadForeignModule(moduleId: string, createData: any, foreignHostMethods: string[]): Promise<string[]> {
|
||||
const proxyMethodRequest = (method: string, args: any[]): Promise<any> => {
|
||||
return this._host.fhr(method, args);
|
||||
};
|
||||
|
||||
const foreignHost = types.createProxyObject(foreignHostMethods, proxyMethodRequest);
|
||||
|
||||
let ctx: IWorkerContext<any> = {
|
||||
host: foreignHost,
|
||||
getMirrorModels: (): IMirrorModel[] => {
|
||||
return this._getModels();
|
||||
}
|
||||
@@ -601,27 +649,14 @@ export abstract class BaseEditorSimpleWorker {
|
||||
if (this._foreignModuleFactory) {
|
||||
this._foreignModule = this._foreignModuleFactory(ctx, createData);
|
||||
// static foreing module
|
||||
let methods: string[] = [];
|
||||
for (const prop of getAllPropertyNames(this._foreignModule)) {
|
||||
if (typeof this._foreignModule[prop] === 'function') {
|
||||
methods.push(prop);
|
||||
}
|
||||
}
|
||||
return Promise.resolve(methods);
|
||||
return Promise.resolve(types.getAllMethodNames(this._foreignModule));
|
||||
}
|
||||
// ESM-comment-begin
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
require([moduleId], (foreignModule: { create: IForeignModuleFactory }) => {
|
||||
this._foreignModule = foreignModule.create(ctx, createData);
|
||||
|
||||
let methods: string[] = [];
|
||||
for (const prop of getAllPropertyNames(this._foreignModule)) {
|
||||
if (typeof this._foreignModule[prop] === 'function') {
|
||||
methods.push(prop);
|
||||
}
|
||||
}
|
||||
|
||||
resolve(methods);
|
||||
resolve(types.getAllMethodNames(this._foreignModule));
|
||||
|
||||
}, reject);
|
||||
});
|
||||
@@ -648,59 +683,12 @@ export abstract class BaseEditorSimpleWorker {
|
||||
// ---- END foreign module support --------------------------------------------------------------------------
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class EditorSimpleWorkerImpl extends BaseEditorSimpleWorker implements IRequestHandler, IDisposable {
|
||||
_requestHandlerBrand: any;
|
||||
|
||||
private _models: { [uri: string]: MirrorModel; };
|
||||
|
||||
constructor(foreignModuleFactory: IForeignModuleFactory | null) {
|
||||
super(foreignModuleFactory);
|
||||
this._models = Object.create(null);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._models = Object.create(null);
|
||||
}
|
||||
|
||||
protected _getModel(uri: string): ICommonModel {
|
||||
return this._models[uri];
|
||||
}
|
||||
|
||||
protected _getModels(): ICommonModel[] {
|
||||
let all: MirrorModel[] = [];
|
||||
Object.keys(this._models).forEach((key) => all.push(this._models[key]));
|
||||
return all;
|
||||
}
|
||||
|
||||
public acceptNewModel(data: IRawModelData): void {
|
||||
this._models[data.url] = new MirrorModel(URI.parse(data.url), data.lines, data.EOL, data.versionId);
|
||||
}
|
||||
|
||||
public acceptModelChanged(strURL: string, e: IModelChangedEvent): void {
|
||||
if (!this._models[strURL]) {
|
||||
return;
|
||||
}
|
||||
let model = this._models[strURL];
|
||||
model.onEvents(e);
|
||||
}
|
||||
|
||||
public acceptRemovedModel(strURL: string): void {
|
||||
if (!this._models[strURL]) {
|
||||
return;
|
||||
}
|
||||
delete this._models[strURL];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called on the worker side
|
||||
* @internal
|
||||
*/
|
||||
export function create(): IRequestHandler {
|
||||
return new EditorSimpleWorkerImpl(null);
|
||||
export function create(host: EditorWorkerHost): IRequestHandler {
|
||||
return new EditorSimpleWorker(host, null);
|
||||
}
|
||||
|
||||
// This is only available in a Web Worker
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IntervalTimer } from 'vs/base/common/async';
|
||||
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { SimpleWorkerClient, logOnceWebWorkerWarning } from 'vs/base/common/worker/simpleWorker';
|
||||
import { SimpleWorkerClient, logOnceWebWorkerWarning, IWorkerClient } from 'vs/base/common/worker/simpleWorker';
|
||||
import { DefaultWorkerFactory } from 'vs/base/worker/defaultWorkerFactory';
|
||||
import { IEditorOptions } from 'vs/editor/common/config/editorOptions';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
@@ -15,7 +15,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { EditorSimpleWorkerImpl } from 'vs/editor/common/services/editorSimpleWorker';
|
||||
import { EditorSimpleWorker } from 'vs/editor/common/services/editorSimpleWorker';
|
||||
import { IDiffComputationResult, IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
@@ -133,6 +133,8 @@ class WordBasedCompletionItemProvider implements modes.CompletionItemProvider {
|
||||
private readonly _configurationService: ITextResourceConfigurationService;
|
||||
private readonly _modelService: IModelService;
|
||||
|
||||
readonly _debugDisplayName = 'wordbasedCompletions';
|
||||
|
||||
constructor(
|
||||
workerManager: WorkerManager,
|
||||
configurationService: ITextResourceConfigurationService,
|
||||
@@ -222,12 +224,12 @@ class WorkerManager extends Disposable {
|
||||
|
||||
class EditorModelManager extends Disposable {
|
||||
|
||||
private readonly _proxy: EditorSimpleWorkerImpl;
|
||||
private readonly _proxy: EditorSimpleWorker;
|
||||
private readonly _modelService: IModelService;
|
||||
private _syncedModels: { [modelUrl: string]: IDisposable[]; } = Object.create(null);
|
||||
private _syncedModels: { [modelUrl: string]: IDisposable; } = Object.create(null);
|
||||
private _syncedModelsLastUsedTime: { [modelUrl: string]: number; } = Object.create(null);
|
||||
|
||||
constructor(proxy: EditorSimpleWorkerImpl, modelService: IModelService, keepIdleModels: boolean) {
|
||||
constructor(proxy: EditorSimpleWorker, modelService: IModelService, keepIdleModels: boolean) {
|
||||
super();
|
||||
this._proxy = proxy;
|
||||
this._modelService = modelService;
|
||||
@@ -295,14 +297,14 @@ class EditorModelManager extends Disposable {
|
||||
versionId: model.getVersionId()
|
||||
});
|
||||
|
||||
let toDispose: IDisposable[] = [];
|
||||
toDispose.push(model.onDidChangeContent((e) => {
|
||||
const toDispose = new DisposableStore();
|
||||
toDispose.add(model.onDidChangeContent((e) => {
|
||||
this._proxy.acceptModelChanged(modelUrl.toString(), e);
|
||||
}));
|
||||
toDispose.push(model.onWillDispose(() => {
|
||||
toDispose.add(model.onWillDispose(() => {
|
||||
this._stopModelSync(modelUrl);
|
||||
}));
|
||||
toDispose.push(toDisposable(() => {
|
||||
toDispose.add(toDisposable(() => {
|
||||
this._proxy.acceptRemovedModel(modelUrl);
|
||||
}));
|
||||
|
||||
@@ -317,11 +319,6 @@ class EditorModelManager extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
interface IWorkerClient<T> {
|
||||
getProxyObject(): Promise<T>;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
class SynchronousWorkerClient<T extends IDisposable> implements IWorkerClient<T> {
|
||||
private readonly _instance: T;
|
||||
private readonly _proxyObj: Promise<T>;
|
||||
@@ -340,10 +337,24 @@ class SynchronousWorkerClient<T extends IDisposable> implements IWorkerClient<T>
|
||||
}
|
||||
}
|
||||
|
||||
export class EditorWorkerHost {
|
||||
|
||||
private readonly _workerClient: EditorWorkerClient;
|
||||
|
||||
constructor(workerClient: EditorWorkerClient) {
|
||||
this._workerClient = workerClient;
|
||||
}
|
||||
|
||||
// foreign host request
|
||||
public fhr(method: string, args: any[]): Promise<any> {
|
||||
return this._workerClient.fhr(method, args);
|
||||
}
|
||||
}
|
||||
|
||||
export class EditorWorkerClient extends Disposable {
|
||||
|
||||
private readonly _modelService: IModelService;
|
||||
private _worker: IWorkerClient<EditorSimpleWorkerImpl> | null;
|
||||
private _worker: IWorkerClient<EditorSimpleWorker> | null;
|
||||
private readonly _workerFactory: DefaultWorkerFactory;
|
||||
private _modelManager: EditorModelManager | null;
|
||||
|
||||
@@ -355,37 +366,43 @@ export class EditorWorkerClient extends Disposable {
|
||||
this._modelManager = null;
|
||||
}
|
||||
|
||||
private _getOrCreateWorker(): IWorkerClient<EditorSimpleWorkerImpl> {
|
||||
// foreign host request
|
||||
public fhr(method: string, args: any[]): Promise<any> {
|
||||
throw new Error(`Not implemented!`);
|
||||
}
|
||||
|
||||
private _getOrCreateWorker(): IWorkerClient<EditorSimpleWorker> {
|
||||
if (!this._worker) {
|
||||
try {
|
||||
this._worker = this._register(new SimpleWorkerClient<EditorSimpleWorkerImpl>(
|
||||
this._worker = this._register(new SimpleWorkerClient<EditorSimpleWorker, EditorWorkerHost>(
|
||||
this._workerFactory,
|
||||
'vs/editor/common/services/editorSimpleWorker'
|
||||
'vs/editor/common/services/editorSimpleWorker',
|
||||
new EditorWorkerHost(this)
|
||||
));
|
||||
} catch (err) {
|
||||
logOnceWebWorkerWarning(err);
|
||||
this._worker = new SynchronousWorkerClient(new EditorSimpleWorkerImpl(null));
|
||||
this._worker = new SynchronousWorkerClient(new EditorSimpleWorker(new EditorWorkerHost(this), null));
|
||||
}
|
||||
}
|
||||
return this._worker;
|
||||
}
|
||||
|
||||
protected _getProxy(): Promise<EditorSimpleWorkerImpl> {
|
||||
protected _getProxy(): Promise<EditorSimpleWorker> {
|
||||
return this._getOrCreateWorker().getProxyObject().then(undefined, (err) => {
|
||||
logOnceWebWorkerWarning(err);
|
||||
this._worker = new SynchronousWorkerClient(new EditorSimpleWorkerImpl(null));
|
||||
this._worker = new SynchronousWorkerClient(new EditorSimpleWorker(new EditorWorkerHost(this), null));
|
||||
return this._getOrCreateWorker().getProxyObject();
|
||||
});
|
||||
}
|
||||
|
||||
private _getOrCreateModelManager(proxy: EditorSimpleWorkerImpl): EditorModelManager {
|
||||
private _getOrCreateModelManager(proxy: EditorSimpleWorker): EditorModelManager {
|
||||
if (!this._modelManager) {
|
||||
this._modelManager = this._register(new EditorModelManager(proxy, this._modelService, false));
|
||||
}
|
||||
return this._modelManager;
|
||||
}
|
||||
|
||||
protected _withSyncedResources(resources: URI[]): Promise<EditorSimpleWorkerImpl> {
|
||||
protected _withSyncedResources(resources: URI[]): Promise<EditorSimpleWorker> {
|
||||
return this._getProxy().then((proxy) => {
|
||||
this._getOrCreateModelManager(proxy).ensureSyncedResources(resources);
|
||||
return proxy;
|
||||
|
||||
@@ -85,19 +85,7 @@ export function detectModeId(modelService: IModelService, modeService: IModeServ
|
||||
}
|
||||
|
||||
// otherwise fallback to path based detection
|
||||
let path: string | undefined;
|
||||
if (resource.scheme === Schemas.data) {
|
||||
const metadata = DataUri.parseMetaData(resource);
|
||||
path = metadata.get(DataUri.META_DATA_LABEL);
|
||||
} else {
|
||||
path = resource.path.toLowerCase();
|
||||
}
|
||||
|
||||
if (path) {
|
||||
return modeService.getModeIdByFilepathOrFirstLine(path);
|
||||
}
|
||||
|
||||
return null; // finally - we do not know the mode id
|
||||
return modeService.getModeIdByFilepathOrFirstLine(resource);
|
||||
}
|
||||
|
||||
export function cssEscape(val: string): string {
|
||||
|
||||
@@ -323,11 +323,11 @@ export class LanguagesRegistry extends Disposable {
|
||||
return [];
|
||||
}
|
||||
|
||||
public getModeIdsFromFilepathOrFirstLine(filepath: string | null, firstLine?: string): string[] {
|
||||
if (!filepath && !firstLine) {
|
||||
public getModeIdsFromFilepathOrFirstLine(resource: URI | null, firstLine?: string): string[] {
|
||||
if (!resource && !firstLine) {
|
||||
return [];
|
||||
}
|
||||
let mimeTypes = mime.guessMimeTypes(filepath, firstLine);
|
||||
let mimeTypes = mime.guessMimeTypes(resource, firstLine);
|
||||
return this.extractModeIds(mimeTypes.join(','));
|
||||
}
|
||||
|
||||
|
||||
@@ -63,10 +63,10 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private readonly _onDidChangeMarker = new Emitter<ITextModel>();
|
||||
private readonly _onDidChangeMarker = this._register(new Emitter<ITextModel>());
|
||||
readonly onDidChangeMarker: Event<ITextModel> = this._onDidChangeMarker.event;
|
||||
|
||||
private readonly _markerDecorations: Map<string, MarkerDecorations> = new Map<string, MarkerDecorations>();
|
||||
private readonly _markerDecorations = new Map<string, MarkerDecorations>();
|
||||
|
||||
constructor(
|
||||
@IModelService modelService: IModelService,
|
||||
@@ -79,6 +79,12 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor
|
||||
this._register(this._markerService.onMarkerChanged(this._handleMarkerChange, this));
|
||||
}
|
||||
|
||||
dispose() {
|
||||
super.dispose();
|
||||
this._markerDecorations.forEach(value => value.dispose());
|
||||
this._markerDecorations.clear();
|
||||
}
|
||||
|
||||
getMarker(model: ITextModel, decoration: IModelDecoration): IMarker | null {
|
||||
const markerDecorations = this._markerDecorations.get(MODEL_ID(model.uri));
|
||||
return markerDecorations ? withUndefinedAsNull(markerDecorations.getMarker(decoration)) : null;
|
||||
|
||||
@@ -41,7 +41,7 @@ export interface IModeService {
|
||||
getMimeForMode(modeId: string): string | null;
|
||||
getLanguageName(modeId: string): string | null;
|
||||
getModeIdForLanguageName(alias: string): string | null;
|
||||
getModeIdByFilepathOrFirstLine(filepath: string, firstLine?: string): string | null;
|
||||
getModeIdByFilepathOrFirstLine(resource: URI, firstLine?: string): string | null;
|
||||
getModeId(commaSeparatedMimetypesOrCommaSeparatedIds: string): string | null;
|
||||
getLanguageIdentifier(modeId: string | LanguageId): LanguageIdentifier | null;
|
||||
getConfigurationFiles(modeId: string): URI[];
|
||||
@@ -49,7 +49,7 @@ export interface IModeService {
|
||||
// --- instantiation
|
||||
create(commaSeparatedMimetypesOrCommaSeparatedIds: string | undefined): ILanguageSelection;
|
||||
createByLanguageName(languageName: string): ILanguageSelection;
|
||||
createByFilepathOrFirstLine(filepath: string | null, firstLine?: string): ILanguageSelection;
|
||||
createByFilepathOrFirstLine(rsource: URI | null, firstLine?: string): ILanguageSelection;
|
||||
|
||||
triggerMode(commaSeparatedMimetypesOrCommaSeparatedIds: string): void;
|
||||
}
|
||||
|
||||
@@ -94,8 +94,8 @@ export class ModeServiceImpl implements IModeService {
|
||||
return this._registry.getModeIdForLanguageNameLowercase(alias);
|
||||
}
|
||||
|
||||
public getModeIdByFilepathOrFirstLine(filepath: string | null, firstLine?: string): string | null {
|
||||
const modeIds = this._registry.getModeIdsFromFilepathOrFirstLine(filepath, firstLine);
|
||||
public getModeIdByFilepathOrFirstLine(resource: URI | null, firstLine?: string): string | null {
|
||||
const modeIds = this._registry.getModeIdsFromFilepathOrFirstLine(resource, firstLine);
|
||||
|
||||
if (modeIds.length > 0) {
|
||||
return modeIds[0];
|
||||
@@ -138,9 +138,9 @@ export class ModeServiceImpl implements IModeService {
|
||||
});
|
||||
}
|
||||
|
||||
public createByFilepathOrFirstLine(filepath: string | null, firstLine?: string): ILanguageSelection {
|
||||
public createByFilepathOrFirstLine(resource: URI | null, firstLine?: string): ILanguageSelection {
|
||||
return new LanguageSelection(this.onLanguagesMaybeChanged, () => {
|
||||
const modeId = this.getModeIdByFilepathOrFirstLine(filepath, firstLine);
|
||||
const modeId = this.getModeIdByFilepathOrFirstLine(resource, firstLine);
|
||||
return this._createModeAndGetLanguageIdentifier(modeId);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||
@@ -30,7 +30,7 @@ class ModelData implements IDisposable {
|
||||
private _languageSelection: ILanguageSelection | null;
|
||||
private _languageSelectionListener: IDisposable | null;
|
||||
|
||||
private _modelEventListeners: IDisposable[];
|
||||
private readonly _modelEventListeners = new DisposableStore();
|
||||
|
||||
constructor(
|
||||
model: ITextModel,
|
||||
@@ -42,9 +42,8 @@ class ModelData implements IDisposable {
|
||||
this._languageSelection = null;
|
||||
this._languageSelectionListener = null;
|
||||
|
||||
this._modelEventListeners = [];
|
||||
this._modelEventListeners.push(model.onWillDispose(() => onWillDispose(model)));
|
||||
this._modelEventListeners.push(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e)));
|
||||
this._modelEventListeners.add(model.onWillDispose(() => onWillDispose(model)));
|
||||
this._modelEventListeners.add(model.onDidChangeLanguage((e) => onDidChangeLanguage(model, e)));
|
||||
}
|
||||
|
||||
private _disposeLanguageSelection(): void {
|
||||
@@ -59,7 +58,7 @@ class ModelData implements IDisposable {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._modelEventListeners = dispose(this._modelEventListeners);
|
||||
this._modelEventListeners.dispose();
|
||||
this._disposeLanguageSelection();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export interface ITextResourceConfigurationService {
|
||||
* Value can be of native type or an object keyed off the section name.
|
||||
*
|
||||
* @param resource - Resource for which the configuration has to be fetched.
|
||||
* @param postion - Position in the resource for which configuration has to be fetched.
|
||||
* @param position - Position in the resource for which configuration has to be fetched.
|
||||
* @param section - Section of the configuraion.
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -50,7 +50,7 @@ export class TextResourceConfigurationService extends Disposable implements ITex
|
||||
if (model) {
|
||||
return position ? this.modeService.getLanguageIdentifier(model.getLanguageIdAtPosition(position.lineNumber, position.column))!.language : model.getLanguageIdentifier().language;
|
||||
}
|
||||
return this.modeService.getModeIdByFilepathOrFirstLine(resource.path);
|
||||
return this.modeService.getModeIdByFilepathOrFirstLine(resource);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { EditorWorkerClient } from 'vs/editor/common/services/editorWorkerServiceImpl';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import * as types from 'vs/base/common/types';
|
||||
|
||||
/**
|
||||
* Create a new web worker that has model syncing capabilities built in.
|
||||
@@ -48,11 +49,16 @@ export interface IWebWorkerOptions {
|
||||
* A label to be used to identify the web worker for debugging purposes.
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* An object that can be used by the web worker to make calls back to the main thread.
|
||||
*/
|
||||
host?: any;
|
||||
}
|
||||
|
||||
class MonacoWebWorkerImpl<T> extends EditorWorkerClient implements MonacoWebWorker<T> {
|
||||
|
||||
private readonly _foreignModuleId: string;
|
||||
private readonly _foreignModuleHost: { [method: string]: Function } | null;
|
||||
private _foreignModuleCreateData: any | null;
|
||||
private _foreignProxy: Promise<T> | null;
|
||||
|
||||
@@ -60,13 +66,28 @@ class MonacoWebWorkerImpl<T> extends EditorWorkerClient implements MonacoWebWork
|
||||
super(modelService, opts.label);
|
||||
this._foreignModuleId = opts.moduleId;
|
||||
this._foreignModuleCreateData = opts.createData || null;
|
||||
this._foreignModuleHost = opts.host || null;
|
||||
this._foreignProxy = null;
|
||||
}
|
||||
|
||||
// foreign host request
|
||||
public fhr(method: string, args: any[]): Promise<any> {
|
||||
if (!this._foreignModuleHost || typeof this._foreignModuleHost[method] !== 'function') {
|
||||
return Promise.reject(new Error('Missing method ' + method + ' or missing main thread foreign host.'));
|
||||
}
|
||||
|
||||
try {
|
||||
return Promise.resolve(this._foreignModuleHost[method].apply(this._foreignModuleHost, args));
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
private _getForeignProxy(): Promise<T> {
|
||||
if (!this._foreignProxy) {
|
||||
this._foreignProxy = this._getProxy().then((proxy) => {
|
||||
return proxy.loadForeignModule(this._foreignModuleId, this._foreignModuleCreateData).then((foreignMethods) => {
|
||||
const foreignHostMethods = this._foreignModuleHost ? types.getAllMethodNames(this._foreignModuleHost) : [];
|
||||
return proxy.loadForeignModule(this._foreignModuleId, this._foreignModuleCreateData, foreignHostMethods).then((foreignMethods) => {
|
||||
this._foreignModuleCreateData = null;
|
||||
|
||||
const proxyMethodRequest = (method: string, args: any[]): Promise<any> => {
|
||||
|
||||
@@ -228,6 +228,13 @@ export enum OverviewRulerLane {
|
||||
Full = 7
|
||||
}
|
||||
|
||||
/**
|
||||
* Position in the minimap to render the decoration.
|
||||
*/
|
||||
export enum MinimapPosition {
|
||||
Inline = 1
|
||||
}
|
||||
|
||||
/**
|
||||
* End of line character preference.
|
||||
*/
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Color, RGBA } from 'vs/base/common/color';
|
||||
import { activeContrastBorder, editorBackground, editorForeground, registerColor } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { activeContrastBorder, editorBackground, editorForeground, registerColor, editorWarningForeground, editorInfoForeground, editorWarningBorder, editorInfoBorder } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
/**
|
||||
@@ -37,26 +37,14 @@ export const editorOverviewRulerBorder = registerColor('editorOverviewRuler.bord
|
||||
|
||||
export const editorGutter = registerColor('editorGutter.background', { dark: editorBackground, light: editorBackground, hc: editorBackground }, nls.localize('editorGutter', 'Background color of the editor gutter. The gutter contains the glyph margins and the line numbers.'));
|
||||
|
||||
export const editorErrorForeground = registerColor('editorError.foreground', { dark: '#ea4646', light: '#d60a0a', hc: null }, nls.localize('errorForeground', 'Foreground color of error squigglies in the editor.'));
|
||||
export const editorErrorBorder = registerColor('editorError.border', { dark: null, light: null, hc: Color.fromHex('#E47777').transparent(0.8) }, nls.localize('errorBorder', 'Border color of error squigglies in the editor.'));
|
||||
|
||||
export const editorWarningForeground = registerColor('editorWarning.foreground', { dark: '#4d9e4d', light: '#117711', hc: null }, nls.localize('warningForeground', 'Foreground color of warning squigglies in the editor.'));
|
||||
export const editorWarningBorder = registerColor('editorWarning.border', { dark: null, light: null, hc: Color.fromHex('#71B771').transparent(0.8) }, nls.localize('warningBorder', 'Border color of warning squigglies in the editor.'));
|
||||
|
||||
export const editorInfoForeground = registerColor('editorInfo.foreground', { dark: '#008000', light: '#008000', hc: null }, nls.localize('infoForeground', 'Foreground color of info squigglies in the editor.'));
|
||||
export const editorInfoBorder = registerColor('editorInfo.border', { dark: null, light: null, hc: Color.fromHex('#71B771').transparent(0.8) }, nls.localize('infoBorder', 'Border color of info squigglies in the editor.'));
|
||||
|
||||
export const editorHintForeground = registerColor('editorHint.foreground', { dark: Color.fromHex('#eeeeee').transparent(0.7), light: '#6c6c6c', hc: null }, nls.localize('hintForeground', 'Foreground color of hint squigglies in the editor.'));
|
||||
export const editorHintBorder = registerColor('editorHint.border', { dark: null, light: null, hc: Color.fromHex('#eeeeee').transparent(0.8) }, nls.localize('hintBorder', 'Border color of hint squigglies in the editor.'));
|
||||
|
||||
export const editorUnnecessaryCodeBorder = registerColor('editorUnnecessaryCode.border', { dark: null, light: null, hc: Color.fromHex('#fff').transparent(0.8) }, nls.localize('unnecessaryCodeBorder', 'Border color of unnecessary (unused) source code in the editor.'));
|
||||
export const editorUnnecessaryCodeOpacity = registerColor('editorUnnecessaryCode.opacity', { dark: Color.fromHex('#000a'), light: Color.fromHex('#0007'), hc: null }, nls.localize('unnecessaryCodeOpacity', 'Opacity of unnecessary (unused) source code in the editor. For example, "#000000c0" will render the code with 75% opacity. For high contrast themes, use the \'editorUnnecessaryCode.border\' theme color to underline unnecessary code instead of fading it out.'));
|
||||
|
||||
const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6));
|
||||
export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hc: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque so as not to hide underlying decorations.'), true);
|
||||
export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hc: new Color(new RGBA(255, 50, 50, 1)) }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.'));
|
||||
export const overviewRulerWarning = registerColor('editorOverviewRuler.warningForeground', { dark: new Color(new RGBA(18, 136, 18, 0.7)), light: new Color(new RGBA(18, 136, 18, 0.7)), hc: new Color(new RGBA(50, 255, 50, 1)) }, nls.localize('overviewRuleWarning', 'Overview ruler marker color for warnings.'));
|
||||
export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForeground', { dark: new Color(new RGBA(18, 18, 136, 0.7)), light: new Color(new RGBA(18, 18, 136, 0.7)), hc: new Color(new RGBA(50, 50, 255, 1)) }, nls.localize('overviewRuleInfo', 'Overview ruler marker color for infos.'));
|
||||
export const overviewRulerWarning = registerColor('editorOverviewRuler.warningForeground', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningBorder }, nls.localize('overviewRuleWarning', 'Overview ruler marker color for warnings.'));
|
||||
export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForeground', { dark: editorInfoForeground, light: editorInfoForeground, hc: editorInfoBorder }, nls.localize('overviewRuleInfo', 'Overview ruler marker color for infos.'));
|
||||
|
||||
// contains all color rules that used to defined in editor/browser/widget/editor.css
|
||||
registerThemingParticipant((theme, collector) => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import * as viewEvents from 'vs/editor/common/view/viewEvents';
|
||||
import { PrefixSumComputerWithCache } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
import { ICoordinatesConverter, IOverviewRulerDecorations, ViewLineData } from 'vs/editor/common/viewModel/viewModel';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
export class OutputPosition {
|
||||
_outputPositionBrand: void;
|
||||
@@ -62,11 +63,9 @@ export interface ISplitLine {
|
||||
getViewLineNumberOfModelPosition(deltaLineNumber: number, inputColumn: number): number;
|
||||
}
|
||||
|
||||
export interface IViewModelLinesCollection {
|
||||
export interface IViewModelLinesCollection extends IDisposable {
|
||||
createCoordinatesConverter(): ICoordinatesConverter;
|
||||
|
||||
dispose(): void;
|
||||
|
||||
setWrappingSettings(wrappingIndent: WrappingIndent, wrappingColumn: number, columnsForFullWidthChar: number): boolean;
|
||||
setTabSize(newTabSize: number): boolean;
|
||||
getHiddenAreas(): Range[];
|
||||
|
||||
Reference in New Issue
Block a user