mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from master
This commit is contained in:
@@ -2,11 +2,10 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { ICursorStateComputer, IIdentifiedSingleEditOperation, EndOfLineSequence } from 'vs/editor/common/model';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { EndOfLineSequence, ICursorStateComputer, IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
|
||||
interface IEditOperation {
|
||||
@@ -15,8 +14,8 @@ interface IEditOperation {
|
||||
|
||||
interface IStackElement {
|
||||
readonly beforeVersionId: number;
|
||||
readonly beforeCursorState: Selection[];
|
||||
readonly afterCursorState: Selection[];
|
||||
readonly beforeCursorState: Selection[] | null;
|
||||
readonly afterCursorState: Selection[] | null;
|
||||
readonly afterVersionId: number;
|
||||
|
||||
undo(model: TextModel): void;
|
||||
@@ -26,7 +25,7 @@ interface IStackElement {
|
||||
class EditStackElement implements IStackElement {
|
||||
public readonly beforeVersionId: number;
|
||||
public readonly beforeCursorState: Selection[];
|
||||
public afterCursorState: Selection[];
|
||||
public afterCursorState: Selection[] | null;
|
||||
public afterVersionId: number;
|
||||
|
||||
public editOperations: IEditOperation[];
|
||||
@@ -69,8 +68,8 @@ function getModelEOL(model: TextModel): EndOfLineSequence {
|
||||
|
||||
class EOLStackElement implements IStackElement {
|
||||
public readonly beforeVersionId: number;
|
||||
public readonly beforeCursorState: Selection[];
|
||||
public readonly afterCursorState: Selection[];
|
||||
public readonly beforeCursorState: Selection[] | null;
|
||||
public readonly afterCursorState: Selection[] | null;
|
||||
public afterVersionId: number;
|
||||
|
||||
public eol: EndOfLineSequence;
|
||||
@@ -97,14 +96,14 @@ class EOLStackElement implements IStackElement {
|
||||
}
|
||||
|
||||
export interface IUndoRedoResult {
|
||||
selections: Selection[];
|
||||
selections: Selection[] | null;
|
||||
recordedVersionId: number;
|
||||
}
|
||||
|
||||
export class EditStack {
|
||||
|
||||
private model: TextModel;
|
||||
private currentOpenStackElement: IStackElement;
|
||||
private currentOpenStackElement: IStackElement | null;
|
||||
private past: IStackElement[];
|
||||
private future: IStackElement[];
|
||||
|
||||
@@ -146,11 +145,11 @@ export class EditStack {
|
||||
this.pushStackElement();
|
||||
}
|
||||
|
||||
public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] {
|
||||
public pushEditOperation(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null {
|
||||
// No support for parallel universes :(
|
||||
this.future = [];
|
||||
|
||||
let stackElement: EditStackElement = null;
|
||||
let stackElement: EditStackElement | null = null;
|
||||
|
||||
if (this.currentOpenStackElement) {
|
||||
if (this.currentOpenStackElement instanceof EditStackElement) {
|
||||
@@ -169,13 +168,13 @@ export class EditStack {
|
||||
operations: this.model.applyEdits(editOperations)
|
||||
};
|
||||
|
||||
stackElement.editOperations.push(inverseEditOperation);
|
||||
stackElement.afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperation.operations);
|
||||
stackElement.afterVersionId = this.model.getVersionId();
|
||||
return stackElement.afterCursorState;
|
||||
stackElement!.editOperations.push(inverseEditOperation);
|
||||
stackElement!.afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperation.operations);
|
||||
stackElement!.afterVersionId = this.model.getVersionId();
|
||||
return stackElement!.afterCursorState;
|
||||
}
|
||||
|
||||
private static _computeCursorState(cursorStateComputer: ICursorStateComputer, inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] {
|
||||
private static _computeCursorState(cursorStateComputer: ICursorStateComputer, inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] | null {
|
||||
try {
|
||||
return cursorStateComputer ? cursorStateComputer(inverseEditOperations) : null;
|
||||
} catch (e) {
|
||||
@@ -184,12 +183,12 @@ export class EditStack {
|
||||
}
|
||||
}
|
||||
|
||||
public undo(): IUndoRedoResult {
|
||||
public undo(): IUndoRedoResult | null {
|
||||
|
||||
this.pushStackElement();
|
||||
|
||||
if (this.past.length > 0) {
|
||||
const pastStackElement = this.past.pop();
|
||||
const pastStackElement = this.past.pop()!;
|
||||
|
||||
try {
|
||||
pastStackElement.undo(this.model);
|
||||
@@ -214,10 +213,10 @@ export class EditStack {
|
||||
return (this.past.length > 0);
|
||||
}
|
||||
|
||||
public redo(): IUndoRedoResult {
|
||||
public redo(): IUndoRedoResult | null {
|
||||
|
||||
if (this.future.length > 0) {
|
||||
const futureStackElement = this.future.pop();
|
||||
const futureStackElement = this.future.pop()!;
|
||||
|
||||
try {
|
||||
futureStackElement.redo(this.model);
|
||||
|
||||
@@ -2,15 +2,22 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { ITextBuffer } from 'vs/editor/common/model';
|
||||
|
||||
class SpacesDiffResult {
|
||||
public spacesDiff: number;
|
||||
public looksLikeAlignment: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute the diff in spaces between two line's indentation.
|
||||
*/
|
||||
function spacesDiff(a: string, aLength: number, b: string, bLength: number): number {
|
||||
function spacesDiff(a: string, aLength: number, b: string, bLength: number, result: SpacesDiffResult): void {
|
||||
|
||||
result.spacesDiff = 0;
|
||||
result.looksLikeAlignment = false;
|
||||
|
||||
// This can go both ways (e.g.):
|
||||
// - a: "\t"
|
||||
@@ -49,22 +56,35 @@ function spacesDiff(a: string, aLength: number, b: string, bLength: number): num
|
||||
}
|
||||
|
||||
if (aSpacesCnt > 0 && aTabsCount > 0) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
if (bSpacesCnt > 0 && bTabsCount > 0) {
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
let tabsDiff = Math.abs(aTabsCount - bTabsCount);
|
||||
let spacesDiff = Math.abs(aSpacesCnt - bSpacesCnt);
|
||||
|
||||
if (tabsDiff === 0) {
|
||||
return spacesDiff;
|
||||
// check if the indentation difference might be caused by alignment reasons
|
||||
// sometime folks like to align their code, but this should not be used as a hint
|
||||
result.spacesDiff = spacesDiff;
|
||||
|
||||
if (spacesDiff > 0 && 0 <= bSpacesCnt - 1 && bSpacesCnt - 1 < a.length && bSpacesCnt < b.length) {
|
||||
if (b.charCodeAt(bSpacesCnt) !== CharCode.Space && a.charCodeAt(bSpacesCnt - 1) === CharCode.Space) {
|
||||
// This looks like an alignment desire: e.g.
|
||||
// const a = b + c,
|
||||
// d = b - c;
|
||||
|
||||
result.looksLikeAlignment = true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (spacesDiff % tabsDiff === 0) {
|
||||
return spacesDiff / tabsDiff;
|
||||
result.spacesDiff = spacesDiff / tabsDiff;
|
||||
return;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,10 +111,11 @@ export function guessIndentation(source: ITextBuffer, defaultTabSize: number, de
|
||||
let previousLineText = ''; // content of latest line that contained non-whitespace chars
|
||||
let previousLineIndentation = 0; // index at which latest line contained the first non-whitespace char
|
||||
|
||||
const ALLOWED_TAB_SIZE_GUESSES = [2, 4, 6, 8]; // limit guesses for `tabSize` to 2, 4, 6 or 8.
|
||||
const MAX_ALLOWED_TAB_SIZE_GUESS = 8; // max(2,4,6,8) = 8
|
||||
const ALLOWED_TAB_SIZE_GUESSES = [2, 4, 6, 8, 3, 5, 7]; // prefer even guesses for `tabSize`, limit to [2, 8].
|
||||
const MAX_ALLOWED_TAB_SIZE_GUESS = 8; // max(ALLOWED_TAB_SIZE_GUESSES) = 8
|
||||
|
||||
let spacesDiffCount = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // `tabSize` scores
|
||||
let tmp = new SpacesDiffResult();
|
||||
|
||||
for (let lineNumber = 1; lineNumber <= linesCount; lineNumber++) {
|
||||
let currentLineLength = source.getLineLength(lineNumber);
|
||||
@@ -134,7 +155,14 @@ export function guessIndentation(source: ITextBuffer, defaultTabSize: number, de
|
||||
linesIndentedWithSpacesCount++;
|
||||
}
|
||||
|
||||
let currentSpacesDiff = spacesDiff(previousLineText, previousLineIndentation, currentLineText, currentLineIndentation);
|
||||
spacesDiff(previousLineText, previousLineIndentation, currentLineText, currentLineIndentation, tmp);
|
||||
|
||||
if (tmp.looksLikeAlignment) {
|
||||
// skip this line entirely
|
||||
continue;
|
||||
}
|
||||
|
||||
let currentSpacesDiff = tmp.spacesDiff;
|
||||
if (currentSpacesDiff <= MAX_ALLOWED_TAB_SIZE_GUESS) {
|
||||
spacesDiffCount[currentSpacesDiff]++;
|
||||
}
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IModelDecoration, TrackedRangeStickiness as ActualTrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { IModelDecoration, TrackedRangeStickiness, TrackedRangeStickiness as ActualTrackedRangeStickiness } from 'vs/editor/common/model';
|
||||
import { ModelDecorationOptions } from 'vs/editor/common/model/textModel';
|
||||
|
||||
//
|
||||
// The red-black tree is based on the "Introduction to Algorithms" by Cormen, Leiserson and Rivest.
|
||||
@@ -21,17 +20,6 @@ export const enum ClassName {
|
||||
EditorUnnecessaryInlineDecoration = 'squiggly-inline-unnecessary'
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the behavior of decorations when typing/editing near their edges.
|
||||
* Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior`
|
||||
*/
|
||||
const enum TrackedRangeStickiness {
|
||||
AlwaysGrowsWhenTypingAtEdges = 0,
|
||||
NeverGrowsWhenTypingAtEdges = 1,
|
||||
GrowsOnlyWhenTypingBefore = 2,
|
||||
GrowsOnlyWhenTypingAfter = 3,
|
||||
}
|
||||
|
||||
export const enum NodeColor {
|
||||
Black = 0,
|
||||
Red = 1,
|
||||
@@ -58,6 +46,10 @@ const enum Constants {
|
||||
StickinessMaskInverse = 0b11001111,
|
||||
StickinessOffset = 4,
|
||||
|
||||
CollapseOnReplaceEditMask = 0b01000000,
|
||||
CollapseOnReplaceEditMaskInverse = 0b10111111,
|
||||
CollapseOnReplaceEditOffset = 6,
|
||||
|
||||
/**
|
||||
* Due to how deletion works (in order to avoid always walking the right subtree of the deleted node),
|
||||
* the deltas for nodes can grow and shrink dramatically. It has been observed, in practice, that unless
|
||||
@@ -121,6 +113,14 @@ function _setNodeStickiness(node: IntervalNode, stickiness: TrackedRangeStickine
|
||||
(node.metadata & Constants.StickinessMaskInverse) | (stickiness << Constants.StickinessOffset)
|
||||
);
|
||||
}
|
||||
function getCollapseOnReplaceEdit(node: IntervalNode): boolean {
|
||||
return ((node.metadata & Constants.CollapseOnReplaceEditMask) >>> Constants.CollapseOnReplaceEditOffset) === 1;
|
||||
}
|
||||
function setCollapseOnReplaceEdit(node: IntervalNode, value: boolean): void {
|
||||
node.metadata = (
|
||||
(node.metadata & Constants.CollapseOnReplaceEditMaskInverse) | ((value ? 1 : 0) << Constants.CollapseOnReplaceEditOffset)
|
||||
);
|
||||
}
|
||||
export function setNodeStickiness(node: IntervalNode, stickiness: ActualTrackedRangeStickiness): void {
|
||||
_setNodeStickiness(node, <number>stickiness);
|
||||
}
|
||||
@@ -153,9 +153,9 @@ export class IntervalNode implements IModelDecoration {
|
||||
constructor(id: string, start: number, end: number) {
|
||||
this.metadata = 0;
|
||||
|
||||
this.parent = null;
|
||||
this.left = null;
|
||||
this.right = null;
|
||||
this.parent = this;
|
||||
this.left = this;
|
||||
this.right = this;
|
||||
setNodeColor(this, NodeColor.Red);
|
||||
|
||||
this.start = start;
|
||||
@@ -166,15 +166,16 @@ export class IntervalNode implements IModelDecoration {
|
||||
|
||||
this.id = id;
|
||||
this.ownerId = 0;
|
||||
this.options = null;
|
||||
this.options = null!;
|
||||
setNodeIsForValidation(this, false);
|
||||
_setNodeStickiness(this, TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges);
|
||||
setNodeIsInOverviewRuler(this, false);
|
||||
setCollapseOnReplaceEdit(this, false);
|
||||
|
||||
this.cachedVersionId = 0;
|
||||
this.cachedAbsoluteStart = start;
|
||||
this.cachedAbsoluteEnd = end;
|
||||
this.range = null;
|
||||
this.range = null!;
|
||||
|
||||
setNodeIsVisited(this, false);
|
||||
}
|
||||
@@ -198,12 +199,13 @@ export class IntervalNode implements IModelDecoration {
|
||||
|| className === ClassName.EditorInfoDecoration
|
||||
));
|
||||
_setNodeStickiness(this, <number>this.options.stickiness);
|
||||
setNodeIsInOverviewRuler(this, this.options.overviewRuler.color ? true : false);
|
||||
setNodeIsInOverviewRuler(this, (this.options.overviewRuler && this.options.overviewRuler.color) ? true : false);
|
||||
setCollapseOnReplaceEdit(this, this.options.collapseOnReplaceEdit);
|
||||
}
|
||||
|
||||
public setCachedOffsets(absoluteStart: number, absoluteEnd: number, cachedVersionId: number): void {
|
||||
if (this.cachedVersionId !== cachedVersionId) {
|
||||
this.range = null;
|
||||
this.range = null!;
|
||||
}
|
||||
this.cachedVersionId = cachedVersionId;
|
||||
this.cachedAbsoluteStart = absoluteStart;
|
||||
@@ -211,13 +213,13 @@ export class IntervalNode implements IModelDecoration {
|
||||
}
|
||||
|
||||
public detach(): void {
|
||||
this.parent = null;
|
||||
this.left = null;
|
||||
this.right = null;
|
||||
this.parent = null!;
|
||||
this.left = null!;
|
||||
this.right = null!;
|
||||
}
|
||||
}
|
||||
|
||||
export const SENTINEL: IntervalNode = new IntervalNode(null, 0, 0);
|
||||
export const SENTINEL: IntervalNode = new IntervalNode(null!, 0, 0);
|
||||
SENTINEL.parent = SENTINEL;
|
||||
SENTINEL.left = SENTINEL;
|
||||
SENTINEL.right = SENTINEL;
|
||||
@@ -417,6 +419,15 @@ export function nodeAcceptEdit(node: IntervalNode, start: number, end: number, t
|
||||
const nodeEnd = node.end;
|
||||
let endDone = false;
|
||||
|
||||
if (start <= nodeStart && nodeEnd <= end && getCollapseOnReplaceEdit(node)) {
|
||||
// This edit encompasses the entire decoration range
|
||||
// and the decoration has asked to become collapsed
|
||||
node.start = start;
|
||||
startDone = true;
|
||||
node.end = start;
|
||||
endDone = true;
|
||||
}
|
||||
|
||||
{
|
||||
const moveSemantics = forceMoveMarkers ? MarkerMoveSemantics.ForceMove : (deletingCnt > 0 ? MarkerMoveSemantics.ForceStay : MarkerMoveSemantics.MarkerDefined);
|
||||
if (!startDone && adjustMarkerBeforeColumn(nodeStart, startStickToPreviousCharacter, start, moveSemantics)) {
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
import { IModelContentChange } from 'vs/editor/common/model/textModelEvents';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { IModelContentChange } from 'vs/editor/common/model/textModelEvents';
|
||||
import { PrefixSumComputer } from 'vs/editor/common/viewModel/prefixSumComputer';
|
||||
|
||||
export interface IModelChangedEvent {
|
||||
/**
|
||||
@@ -31,13 +30,14 @@ export class MirrorTextModel {
|
||||
protected _lines: string[];
|
||||
protected _eol: string;
|
||||
protected _versionId: number;
|
||||
protected _lineStarts: PrefixSumComputer;
|
||||
protected _lineStarts: PrefixSumComputer | null;
|
||||
|
||||
constructor(uri: URI, lines: string[], eol: string, versionId: number) {
|
||||
this._uri = uri;
|
||||
this._lines = lines;
|
||||
this._eol = eol;
|
||||
this._versionId = versionId;
|
||||
this._lineStarts = null;
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ITextSnapshot } from 'vs/platform/files/common/files';
|
||||
import { leftest, righttest, updateTreeMetadata, rbDelete, fixInsert, NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase';
|
||||
import { SearchData, isValidMatch, Searcher, createFindMatch } from 'vs/editor/common/model/textModelSearch';
|
||||
import { FindMatch } from 'vs/editor/common/model';
|
||||
import { NodeColor, SENTINEL, TreeNode, fixInsert, leftest, rbDelete, righttest, updateTreeMetadata } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase';
|
||||
import { SearchData, Searcher, createFindMatch, isValidMatch } from 'vs/editor/common/model/textModelSearch';
|
||||
import { ITextSnapshot } from 'vs/platform/files/common/files';
|
||||
|
||||
// const lfRegex = new RegExp(/\r\n|\r|\n/g);
|
||||
export const AverageBufferSize = 65535;
|
||||
@@ -178,7 +177,7 @@ class PieceTreeSnapshot implements ITextSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
read(): string {
|
||||
read(): string | null {
|
||||
if (this._pieces.length === 0) {
|
||||
if (this._index === 0) {
|
||||
this._index++;
|
||||
@@ -199,16 +198,22 @@ class PieceTreeSnapshot implements ITextSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
interface CacheEntry {
|
||||
node: TreeNode;
|
||||
nodeStartOffset: number;
|
||||
nodeStartLineNumber?: number;
|
||||
}
|
||||
|
||||
class PieceTreeSearchCache {
|
||||
private _limit: number;
|
||||
private _cache: { node: TreeNode, nodeStartOffset: number, nodeStartLineNumber?: number }[];
|
||||
private _cache: CacheEntry[];
|
||||
|
||||
constructor(limit: number) {
|
||||
this._limit = limit;
|
||||
this._cache = [];
|
||||
}
|
||||
|
||||
public get(offset: number): { node: TreeNode, nodeStartOffset: number, nodeStartLineNumber?: number } {
|
||||
public get(offset: number): CacheEntry | null {
|
||||
for (let i = this._cache.length - 1; i >= 0; i--) {
|
||||
let nodePos = this._cache[i];
|
||||
if (nodePos.nodeStartOffset <= offset && nodePos.nodeStartOffset + nodePos.node.piece.length >= offset) {
|
||||
@@ -218,17 +223,17 @@ class PieceTreeSearchCache {
|
||||
return null;
|
||||
}
|
||||
|
||||
public get2(lineNumber: number): { node: TreeNode, nodeStartOffset: number, nodeStartLineNumber?: number } {
|
||||
public get2(lineNumber: number): { node: TreeNode, nodeStartOffset: number, nodeStartLineNumber: number } | null {
|
||||
for (let i = this._cache.length - 1; i >= 0; i--) {
|
||||
let nodePos = this._cache[i];
|
||||
if (nodePos.nodeStartLineNumber && nodePos.nodeStartLineNumber < lineNumber && nodePos.nodeStartLineNumber + nodePos.node.piece.lineFeedCnt >= lineNumber) {
|
||||
return nodePos;
|
||||
return <{ node: TreeNode, nodeStartOffset: number, nodeStartLineNumber: number }>nodePos;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public set(nodePosition: { node: TreeNode, nodeStartOffset: number, nodeStartLineNumber?: number }) {
|
||||
public set(nodePosition: CacheEntry) {
|
||||
if (this._cache.length >= this._limit) {
|
||||
this._cache.shift();
|
||||
}
|
||||
@@ -237,20 +242,22 @@ class PieceTreeSearchCache {
|
||||
|
||||
public valdiate(offset: number) {
|
||||
let hasInvalidVal = false;
|
||||
for (let i = 0; i < this._cache.length; i++) {
|
||||
let nodePos = this._cache[i];
|
||||
let tmp: (CacheEntry | null)[] = this._cache;
|
||||
for (let i = 0; i < tmp.length; i++) {
|
||||
let nodePos = tmp[i]!;
|
||||
if (nodePos.node.parent === null || nodePos.nodeStartOffset >= offset) {
|
||||
this._cache[i] = null;
|
||||
tmp[i] = null;
|
||||
hasInvalidVal = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasInvalidVal) {
|
||||
let newArr = [];
|
||||
for (let i = 0; i < this._cache.length; i++) {
|
||||
if (this._cache[i] !== null) {
|
||||
newArr.push(this._cache[i]);
|
||||
let newArr: CacheEntry[] = [];
|
||||
for (let i = 0; i < tmp.length; i++) {
|
||||
const entry = tmp[i];
|
||||
if (entry !== null) {
|
||||
newArr.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +276,7 @@ export class PieceTreeBase {
|
||||
protected _EOLNormalized: boolean;
|
||||
private _lastChangeBufferPos: BufferCursor;
|
||||
private _searchCache: PieceTreeSearchCache;
|
||||
private _lastVisitedLine: { lineNumber: number, value: string };
|
||||
private _lastVisitedLine: { lineNumber: number; value: string; };
|
||||
|
||||
constructor(chunks: StringBuffer[], eol: '\r\n' | '\n', eolNormalized: boolean) {
|
||||
this.create(chunks, eol, eolNormalized);
|
||||
@@ -287,7 +294,7 @@ export class PieceTreeBase {
|
||||
this._EOLLength = eol.length;
|
||||
this._EOLNormalized = eolNormalized;
|
||||
|
||||
let lastNode: TreeNode = null;
|
||||
let lastNode: TreeNode | null = null;
|
||||
for (let i = 0, len = chunks.length; i < len; i++) {
|
||||
if (chunks[i].buffer.length > 0) {
|
||||
if (!chunks[i].lineStarts) {
|
||||
@@ -307,7 +314,7 @@ export class PieceTreeBase {
|
||||
}
|
||||
|
||||
this._searchCache = new PieceTreeSearchCache(1);
|
||||
this._lastVisitedLine = { lineNumber: 0, value: null };
|
||||
this._lastVisitedLine = { lineNumber: 0, value: '' };
|
||||
this.computeBufferMetadata();
|
||||
}
|
||||
|
||||
@@ -570,7 +577,7 @@ export class PieceTreeBase {
|
||||
let start = this.offsetInBuffer(node.piece.bufferIndex, startCursor);
|
||||
let end = this.offsetInBuffer(node.piece.bufferIndex, endCursor);
|
||||
|
||||
let m: RegExpExecArray;
|
||||
let m: RegExpExecArray | null;
|
||||
// Reset regex to search from the beginning
|
||||
searcher.reset(start);
|
||||
let ret: BufferCursor = { line: 0, column: 0 };
|
||||
@@ -694,7 +701,7 @@ export class PieceTreeBase {
|
||||
return resultLen;
|
||||
}
|
||||
|
||||
let m: RegExpExecArray;
|
||||
let m: RegExpExecArray | null;
|
||||
// Reset regex to search from the beginning
|
||||
searcher.reset(0);
|
||||
do {
|
||||
@@ -715,7 +722,7 @@ export class PieceTreeBase {
|
||||
insert(offset: number, value: string, eolNormalized: boolean = false): void {
|
||||
this._EOLNormalized = this._EOLNormalized && eolNormalized;
|
||||
this._lastVisitedLine.lineNumber = 0;
|
||||
this._lastVisitedLine.value = null;
|
||||
this._lastVisitedLine.value = '';
|
||||
|
||||
if (this.root !== SENTINEL) {
|
||||
let { node, remainder, nodeStartOffset } = this.nodeAt(offset);
|
||||
@@ -739,7 +746,7 @@ export class PieceTreeBase {
|
||||
this._searchCache.valdiate(offset);
|
||||
} else if (nodeStartOffset + node.piece.length > offset) {
|
||||
// we are inserting into the middle of a node.
|
||||
let nodesToDel = [];
|
||||
let nodesToDel: TreeNode[] = [];
|
||||
let newRightPiece = new Piece(
|
||||
piece.bufferIndex,
|
||||
insertPosInBuffer,
|
||||
@@ -812,7 +819,7 @@ export class PieceTreeBase {
|
||||
|
||||
delete(offset: number, cnt: number): void {
|
||||
this._lastVisitedLine.lineNumber = 0;
|
||||
this._lastVisitedLine.value = null;
|
||||
this._lastVisitedLine.value = '';
|
||||
|
||||
if (cnt <= 0 || this.root === SENTINEL) {
|
||||
return;
|
||||
@@ -855,7 +862,7 @@ export class PieceTreeBase {
|
||||
return;
|
||||
}
|
||||
|
||||
let nodesToDel = [];
|
||||
let nodesToDel: TreeNode[] = [];
|
||||
|
||||
let startSplitPosInBuffer = this.positionInBuffer(startNode, startPosition.remainder);
|
||||
this.deleteNodeTail(startNode, startSplitPosInBuffer);
|
||||
@@ -885,7 +892,7 @@ export class PieceTreeBase {
|
||||
|
||||
insertContentToNodeLeft(value: string, node: TreeNode) {
|
||||
// we are inserting content to the beginning of node
|
||||
let nodesToDel = [];
|
||||
let nodesToDel: TreeNode[] = [];
|
||||
if (this.shouldCheckCRLF() && this.endWithCR(value) && this.startWithLF(node)) {
|
||||
// move `\n` to new node.
|
||||
|
||||
@@ -936,7 +943,9 @@ export class PieceTreeBase {
|
||||
this.validateCRLFWithPrevNode(newNode);
|
||||
}
|
||||
|
||||
positionInBuffer(node: TreeNode, remainder: number, ret?: BufferCursor): BufferCursor {
|
||||
positionInBuffer(node: TreeNode, remainder: number): BufferCursor;
|
||||
positionInBuffer(node: TreeNode, remainder: number, ret: BufferCursor): null;
|
||||
positionInBuffer(node: TreeNode, remainder: number, ret?: BufferCursor): BufferCursor | null {
|
||||
let piece = node.piece;
|
||||
let bufferIndex = node.piece.bufferIndex;
|
||||
let lineStarts = this._buffers[bufferIndex].lineStarts;
|
||||
@@ -949,9 +958,9 @@ export class PieceTreeBase {
|
||||
let low = piece.start.line;
|
||||
let high = piece.end.line;
|
||||
|
||||
let mid: number;
|
||||
let midStop: number;
|
||||
let midStart: number;
|
||||
let mid: number = 0;
|
||||
let midStop: number = 0;
|
||||
let midStart: number = 0;
|
||||
|
||||
while (low <= high) {
|
||||
mid = low + ((high - low) / 2) | 0;
|
||||
@@ -1029,7 +1038,7 @@ export class PieceTreeBase {
|
||||
if (text.length > AverageBufferSize) {
|
||||
// the content is large, operations like substring, charCode becomes slow
|
||||
// so here we split it into smaller chunks, just like what we did for CR/LF normalization
|
||||
let newPieces = [];
|
||||
let newPieces: Piece[] = [];
|
||||
while (text.length > AverageBufferSize) {
|
||||
const lastChar = text.charCodeAt(AverageBufferSize - 1);
|
||||
let splitText;
|
||||
@@ -1100,7 +1109,7 @@ export class PieceTreeBase {
|
||||
let endColumn = endOffset - this._buffers[0].lineStarts[endIndex];
|
||||
let endPos = { line: endIndex, column: endColumn };
|
||||
let newPiece = new Piece(
|
||||
0, /** todo */
|
||||
0, /** todo@peng */
|
||||
start,
|
||||
endPos,
|
||||
this.getLineFeedCnt(0, start, endPos),
|
||||
@@ -1388,7 +1397,7 @@ export class PieceTreeBase {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return null!;
|
||||
}
|
||||
|
||||
nodeAt2(lineNumber: number, column: number): NodePosition {
|
||||
@@ -1455,7 +1464,7 @@ export class PieceTreeBase {
|
||||
x = x.next();
|
||||
}
|
||||
|
||||
return null;
|
||||
return null!;
|
||||
}
|
||||
|
||||
nodeCharCodeAt(node: TreeNode, offset: number): number {
|
||||
@@ -1545,7 +1554,7 @@ export class PieceTreeBase {
|
||||
}
|
||||
|
||||
fixCRLF(prev: TreeNode, next: TreeNode) {
|
||||
let nodesToDel = [];
|
||||
let nodesToDel: TreeNode[] = [];
|
||||
// update node
|
||||
let lineStarts = this._buffers[prev.piece.bufferIndex].lineStarts;
|
||||
let newEnd: BufferCursor;
|
||||
@@ -1677,7 +1686,7 @@ export class PieceTreeBase {
|
||||
* /
|
||||
* z
|
||||
*/
|
||||
rbInsertRight(node: TreeNode, p: Piece): TreeNode {
|
||||
rbInsertRight(node: TreeNode | null, p: Piece): TreeNode {
|
||||
let z = new TreeNode(p, NodeColor.Red);
|
||||
z.left = SENTINEL;
|
||||
z.right = SENTINEL;
|
||||
@@ -1689,11 +1698,11 @@ export class PieceTreeBase {
|
||||
if (x === SENTINEL) {
|
||||
this.root = z;
|
||||
z.color = NodeColor.Black;
|
||||
} else if (node.right === SENTINEL) {
|
||||
node.right = z;
|
||||
z.parent = node;
|
||||
} else if (node!.right === SENTINEL) {
|
||||
node!.right = z;
|
||||
z.parent = node!;
|
||||
} else {
|
||||
let nextNode = leftest(node.right);
|
||||
let nextNode = leftest(node!.right);
|
||||
nextNode.left = z;
|
||||
z.parent = nextNode;
|
||||
}
|
||||
@@ -1709,7 +1718,7 @@ export class PieceTreeBase {
|
||||
* \
|
||||
* z
|
||||
*/
|
||||
rbInsertLeft(node: TreeNode, p: Piece): TreeNode {
|
||||
rbInsertLeft(node: TreeNode | null, p: Piece): TreeNode {
|
||||
let z = new TreeNode(p, NodeColor.Red);
|
||||
z.left = SENTINEL;
|
||||
z.right = SENTINEL;
|
||||
@@ -1717,15 +1726,14 @@ export class PieceTreeBase {
|
||||
z.size_left = 0;
|
||||
z.lf_left = 0;
|
||||
|
||||
let x = this.root;
|
||||
if (x === SENTINEL) {
|
||||
if (this.root === SENTINEL) {
|
||||
this.root = z;
|
||||
z.color = NodeColor.Black;
|
||||
} else if (node.left === SENTINEL) {
|
||||
node.left = z;
|
||||
z.parent = node;
|
||||
} else if (node!.left === SENTINEL) {
|
||||
node!.left = z;
|
||||
z.parent = node!;
|
||||
} else {
|
||||
let prevNode = righttest(node.left); // a
|
||||
let prevNode = righttest(node!.left); // a
|
||||
prevNode.right = z;
|
||||
z.parent = prevNode;
|
||||
}
|
||||
|
||||
@@ -2,23 +2,22 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { ApplyEditsResult, EndOfLinePreference, FindMatch, IIdentifiedSingleEditOperation, IInternalModelContentChange, ISingleEditOperationIdentifier, ITextBuffer } from 'vs/editor/common/model';
|
||||
import { PieceTreeBase, StringBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase';
|
||||
import { IIdentifiedSingleEditOperation, EndOfLinePreference, ITextBuffer, ApplyEditsResult, IInternalModelContentChange, FindMatch, ISingleEditOperationIdentifier } from 'vs/editor/common/model';
|
||||
import { ITextSnapshot } from 'vs/platform/files/common/files';
|
||||
import { SearchData } from 'vs/editor/common/model/textModelSearch';
|
||||
import { ITextSnapshot } from 'vs/platform/files/common/files';
|
||||
|
||||
export interface IValidatedEditOperation {
|
||||
sortIndex: number;
|
||||
identifier: ISingleEditOperationIdentifier;
|
||||
identifier: ISingleEditOperationIdentifier | null;
|
||||
range: Range;
|
||||
rangeOffset: number;
|
||||
rangeLength: number;
|
||||
lines: string[];
|
||||
lines: string[] | null;
|
||||
forceMoveMarkers: boolean;
|
||||
isAutoWhitespaceEdit: boolean;
|
||||
}
|
||||
@@ -193,12 +192,12 @@ export class PieceTreeTextBuffer implements ITextBuffer {
|
||||
}
|
||||
operations[i] = {
|
||||
sortIndex: i,
|
||||
identifier: op.identifier,
|
||||
identifier: op.identifier || null,
|
||||
range: validatedRange,
|
||||
rangeOffset: this.getOffsetAt(validatedRange.startLineNumber, validatedRange.startColumn),
|
||||
rangeLength: this.getValueLengthInRange(validatedRange),
|
||||
lines: op.text ? op.text.split(/\r\n|\r|\n/) : null,
|
||||
forceMoveMarkers: op.forceMoveMarkers,
|
||||
forceMoveMarkers: Boolean(op.forceMoveMarkers),
|
||||
isAutoWhitespaceEdit: op.isAutoWhitespaceEdit || false
|
||||
};
|
||||
}
|
||||
@@ -271,7 +270,7 @@ export class PieceTreeTextBuffer implements ITextBuffer {
|
||||
|
||||
const contentChanges = this._doApplyEdits(operations);
|
||||
|
||||
let trimAutoWhitespaceLineNumbers: number[] = null;
|
||||
let trimAutoWhitespaceLineNumbers: number[] | null = null;
|
||||
if (recordTrimAutoWhitespace && newTrimAutoWhitespaceCandidates.length > 0) {
|
||||
// sort line numbers auto whitespace removal candidates for next edit descending
|
||||
newTrimAutoWhitespaceCandidates.sort((a, b) => b.lineNumber - a.lineNumber);
|
||||
@@ -313,7 +312,7 @@ export class PieceTreeTextBuffer implements ITextBuffer {
|
||||
}
|
||||
|
||||
// At one point, due to how events are emitted and how each operation is handled,
|
||||
// some operations can trigger a high ammount of temporary string allocations,
|
||||
// some operations can trigger a high amount of temporary string allocations,
|
||||
// that will immediately get edited again.
|
||||
// e.g. a formatter inserting ridiculous ammounts of \n on a model with a single line
|
||||
// Therefore, the strategy is to collapse all the operations into a huge single edit operation
|
||||
@@ -416,7 +415,7 @@ export class PieceTreeTextBuffer implements ITextBuffer {
|
||||
if (editingLinesCnt < insertingLinesCnt) {
|
||||
let newLinesContent: string[] = [];
|
||||
for (let j = editingLinesCnt + 1; j <= insertingLinesCnt; j++) {
|
||||
newLinesContent.push(op.lines[j]);
|
||||
newLinesContent.push(op.lines![j]);
|
||||
}
|
||||
|
||||
newLinesContent[newLinesContent.length - 1] = this.getLineContent(startLineNumber + insertingLinesCnt - 1);
|
||||
@@ -451,9 +450,9 @@ export class PieceTreeTextBuffer implements ITextBuffer {
|
||||
public static _getInverseEditRanges(operations: IValidatedEditOperation[]): Range[] {
|
||||
let result: Range[] = [];
|
||||
|
||||
let prevOpEndLineNumber: number;
|
||||
let prevOpEndColumn: number;
|
||||
let prevOp: IValidatedEditOperation = null;
|
||||
let prevOpEndLineNumber: number = 0;
|
||||
let prevOpEndColumn: number = 0;
|
||||
let prevOp: IValidatedEditOperation | null = null;
|
||||
for (let i = 0, len = operations.length; i < len; i++) {
|
||||
let op = operations[i];
|
||||
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { ITextBufferBuilder, DefaultEndOfLine, ITextBufferFactory, ITextBuffer } from 'vs/editor/common/model';
|
||||
import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer';
|
||||
import { StringBuffer, createLineStarts, createLineStartsFast } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { DefaultEndOfLine, ITextBuffer, ITextBufferBuilder, ITextBufferFactory } from 'vs/editor/common/model';
|
||||
import { StringBuffer, createLineStarts, createLineStartsFast } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase';
|
||||
import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer';
|
||||
|
||||
export class PieceTreeTextBufferFactory implements ITextBufferFactory {
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { Piece, PieceTreeBase } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase';
|
||||
|
||||
@@ -22,9 +21,9 @@ export class TreeNode {
|
||||
this.color = color;
|
||||
this.size_left = 0;
|
||||
this.lf_left = 0;
|
||||
this.parent = null;
|
||||
this.left = null;
|
||||
this.right = null;
|
||||
this.parent = this;
|
||||
this.left = this;
|
||||
this.right = this;
|
||||
}
|
||||
|
||||
public next(): TreeNode {
|
||||
@@ -72,9 +71,9 @@ export class TreeNode {
|
||||
}
|
||||
|
||||
public detach(): void {
|
||||
this.parent = null;
|
||||
this.left = null;
|
||||
this.right = null;
|
||||
this.parent = null!;
|
||||
this.left = null!;
|
||||
this.right = null!;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +82,7 @@ export const enum NodeColor {
|
||||
Red = 1,
|
||||
}
|
||||
|
||||
export const SENTINEL: TreeNode = new TreeNode(null, NodeColor.Black);
|
||||
export const SENTINEL: TreeNode = new TreeNode(null!, NodeColor.Black);
|
||||
SENTINEL.parent = SENTINEL;
|
||||
SENTINEL.left = SENTINEL;
|
||||
SENTINEL.right = SENTINEL;
|
||||
|
||||
@@ -2,38 +2,38 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import * as model from 'vs/editor/common/model';
|
||||
import { LanguageIdentifier, TokenizationRegistry, LanguageId } from 'vs/editor/common/modes';
|
||||
import { EditStack } from 'vs/editor/common/model/editStack';
|
||||
import { Range, IRange } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { ModelRawContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelTokensChangedEvent, IModelOptionsChangedEvent, IModelContentChangedEvent, InternalModelContentChangeEvent, ModelRawFlush, ModelRawEOLChanged, ModelRawChange, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from 'vs/editor/common/model/textModelEvents';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { IntervalNode, IntervalTree, recomputeMaxEnd, getNodeIsInOverviewRuler } from 'vs/editor/common/model/intervalTree';
|
||||
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';
|
||||
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import * as model from 'vs/editor/common/model';
|
||||
import { EditStack } from 'vs/editor/common/model/editStack';
|
||||
import { guessIndentation } from 'vs/editor/common/model/indentationGuesser';
|
||||
import { IntervalNode, IntervalTree, getNodeIsInOverviewRuler, recomputeMaxEnd } from 'vs/editor/common/model/intervalTree';
|
||||
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 { getWordAtText } from 'vs/editor/common/model/wordHelper';
|
||||
import { IState, LanguageId, LanguageIdentifier, TokenizationRegistry } 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';
|
||||
import { BracketsUtils, RichEditBrackets, RichEditBracket } from 'vs/editor/common/modes/supports/richEditBrackets';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
|
||||
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||
import { getWordAtText } from 'vs/editor/common/model/wordHelper';
|
||||
import { ModelLinesTokens, ModelTokensChangedEventBuilder } from 'vs/editor/common/model/textModelTokens';
|
||||
import { guessIndentation } from 'vs/editor/common/model/indentationGuesser';
|
||||
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||
import { TextModelSearch, SearchParams, SearchData } from 'vs/editor/common/model/textModelSearch';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets';
|
||||
import { IStringStream, ITextSnapshot } from 'vs/platform/files/common/files';
|
||||
import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
|
||||
import { ITheme, ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
const CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048;
|
||||
|
||||
function createTextBufferBuilder() {
|
||||
return new PieceTreeTextBufferBuilder();
|
||||
@@ -45,8 +45,8 @@ export function createTextBufferFactory(text: string): model.ITextBufferFactory
|
||||
return builder.finish();
|
||||
}
|
||||
|
||||
export function createTextBufferFactoryFromStream(stream: IStringStream, filter?: (chunk: string) => string): TPromise<model.ITextBufferFactory> {
|
||||
return new TPromise<model.ITextBufferFactory>((c, e) => {
|
||||
export function createTextBufferFactoryFromStream(stream: IStringStream, filter?: (chunk: string) => string): Promise<model.ITextBufferFactory> {
|
||||
return new Promise<model.ITextBufferFactory>((c, e) => {
|
||||
let done = false;
|
||||
let builder = createTextBufferBuilder();
|
||||
|
||||
@@ -77,7 +77,7 @@ export function createTextBufferFactoryFromStream(stream: IStringStream, filter?
|
||||
export function createTextBufferFactoryFromSnapshot(snapshot: ITextSnapshot): model.ITextBufferFactory {
|
||||
let builder = createTextBufferBuilder();
|
||||
|
||||
let chunk: string;
|
||||
let chunk: string | null;
|
||||
while (typeof (chunk = snapshot.read()) === 'string') {
|
||||
builder.acceptChunk(chunk);
|
||||
}
|
||||
@@ -120,7 +120,7 @@ class TextModelSnapshot implements ITextSnapshot {
|
||||
this._eos = false;
|
||||
}
|
||||
|
||||
public read(): string {
|
||||
public read(): string | null {
|
||||
if (this._eos) {
|
||||
return null;
|
||||
}
|
||||
@@ -152,6 +152,8 @@ class TextModelSnapshot implements ITextSnapshot {
|
||||
}
|
||||
}
|
||||
|
||||
const invalidFunc = () => { throw new Error(`Invalid change accessor`); };
|
||||
|
||||
export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
private static readonly MODEL_SYNC_LIMIT = 50 * 1024 * 1024; // 50 MB
|
||||
@@ -168,7 +170,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
largeFileOptimizations: EDITOR_MODEL_DEFAULTS.largeFileOptimizations,
|
||||
};
|
||||
|
||||
public static createFromString(text: string, options: model.ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier = null, uri: URI = null): TextModel {
|
||||
public static createFromString(text: string, options: model.ITextModelCreationOptions = TextModel.DEFAULT_CREATION_OPTIONS, languageIdentifier: LanguageIdentifier | null = null, uri: URI | null = null): TextModel {
|
||||
return new TextModel(text, options, languageIdentifier, uri);
|
||||
}
|
||||
|
||||
@@ -244,7 +246,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
private _commandManager: EditStack;
|
||||
private _isUndoing: boolean;
|
||||
private _isRedoing: boolean;
|
||||
private _trimAutoWhitespaceLines: number[];
|
||||
private _trimAutoWhitespaceLines: number[] | null;
|
||||
//#endregion
|
||||
|
||||
//#region Decorations
|
||||
@@ -262,11 +264,11 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
private _languageIdentifier: LanguageIdentifier;
|
||||
private _tokenizationListener: IDisposable;
|
||||
private _languageRegistryListener: IDisposable;
|
||||
private _revalidateTokensTimeout: number;
|
||||
private _revalidateTokensTimeout: any;
|
||||
/*private*/_tokens: ModelLinesTokens;
|
||||
//#endregion
|
||||
|
||||
constructor(source: string | model.ITextBufferFactory, creationOptions: model.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier, associatedResource: URI = null) {
|
||||
constructor(source: string | model.ITextBufferFactory, creationOptions: model.ITextModelCreationOptions, languageIdentifier: LanguageIdentifier | null, associatedResource: URI | null = null) {
|
||||
super();
|
||||
|
||||
// Generate a new unique model id
|
||||
@@ -345,16 +347,11 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
public dispose(): void {
|
||||
this._isDisposing = true;
|
||||
this._onWillDispose.fire();
|
||||
this._commandManager = null;
|
||||
this._decorations = null;
|
||||
this._decorationsTree = null;
|
||||
this._tokenizationListener.dispose();
|
||||
this._languageRegistryListener.dispose();
|
||||
this._clearTimers();
|
||||
this._tokens = null;
|
||||
this._isDisposed = true;
|
||||
// Null out members, such that any use of a disposed model will throw exceptions sooner rather than later
|
||||
this._buffer = null;
|
||||
super.dispose();
|
||||
this._isDisposing = false;
|
||||
}
|
||||
@@ -1108,13 +1105,17 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return TextModelSearch.findMatches(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchRange, captureMatches, limitResultCount);
|
||||
}
|
||||
|
||||
public findNextMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch {
|
||||
public findNextMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch | null {
|
||||
this._assertNotDisposed();
|
||||
const searchStart = this.validatePosition(rawSearchStart);
|
||||
|
||||
if (!isRegex && searchString.indexOf('\n') < 0) {
|
||||
const searchParams = new SearchParams(searchString, isRegex, matchCase, wordSeparators);
|
||||
const searchData = searchParams.parseSearchRequest();
|
||||
if (!searchData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const lineCount = this.getLineCount();
|
||||
let searchRange = new Range(searchStart.lineNumber, searchStart.column, lineCount, this.getLineMaxColumn(lineCount));
|
||||
let ret = this.findMatchesLineByLine(searchRange, searchData, captureMatches, 1);
|
||||
@@ -1136,7 +1137,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return TextModelSearch.findNextMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
|
||||
}
|
||||
|
||||
public findPreviousMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch {
|
||||
public findPreviousMatch(searchString: string, rawSearchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string, captureMatches: boolean): model.FindMatch | null {
|
||||
this._assertNotDisposed();
|
||||
const searchStart = this.validatePosition(rawSearchStart);
|
||||
return TextModelSearch.findPreviousMatch(this, new SearchParams(searchString, isRegex, matchCase, wordSeparators), searchStart, captureMatches);
|
||||
@@ -1165,7 +1166,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
}
|
||||
|
||||
public pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer): Selection[] {
|
||||
public pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer): Selection[] | null {
|
||||
try {
|
||||
this._onDidChangeDecorations.beginDeferredEmit();
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
@@ -1176,7 +1177,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
}
|
||||
|
||||
private _pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer): Selection[] {
|
||||
private _pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer): Selection[] | null {
|
||||
if (this._options.trimAutoWhitespace && this._trimAutoWhitespaceLines) {
|
||||
// Go through each saved line number and insert a trim whitespace edit
|
||||
// if it is safe to do so (no conflicts with other edits).
|
||||
@@ -1323,7 +1324,12 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
for (let i = 0, len = contentChanges.length; i < len; i++) {
|
||||
const change = contentChanges[i];
|
||||
const [eolCount, firstLineLength] = TextModel._eolCount(change.text);
|
||||
this._tokens.applyEdits(change.range, eolCount, firstLineLength);
|
||||
try {
|
||||
this._tokens.applyEdits(change.range, eolCount, firstLineLength);
|
||||
} catch (err) {
|
||||
// emergency recovery => reset tokens
|
||||
this._tokens = new ModelLinesTokens(this._tokens.languageIdentifier, this._tokens.tokenizationSupport);
|
||||
}
|
||||
this._onDidChangeDecorations.fire();
|
||||
this._decorationsTree.acceptReplace(change.rangeOffset, change.rangeLength, change.text.length, change.forceMoveMarkers);
|
||||
|
||||
@@ -1391,7 +1397,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return result.reverseEdits;
|
||||
}
|
||||
|
||||
private _undo(): Selection[] {
|
||||
private _undo(): Selection[] | null {
|
||||
this._isUndoing = true;
|
||||
let r = this._commandManager.undo();
|
||||
this._isUndoing = false;
|
||||
@@ -1405,7 +1411,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return r.selections;
|
||||
}
|
||||
|
||||
public undo(): Selection[] {
|
||||
public undo(): Selection[] | null {
|
||||
try {
|
||||
this._onDidChangeDecorations.beginDeferredEmit();
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
@@ -1420,7 +1426,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return this._commandManager.canUndo();
|
||||
}
|
||||
|
||||
private _redo(): Selection[] {
|
||||
private _redo(): Selection[] | null {
|
||||
this._isRedoing = true;
|
||||
let r = this._commandManager.redo();
|
||||
this._isRedoing = false;
|
||||
@@ -1434,7 +1440,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return r.selections;
|
||||
}
|
||||
|
||||
public redo(): Selection[] {
|
||||
public redo(): Selection[] | null {
|
||||
try {
|
||||
this._onDidChangeDecorations.beginDeferredEmit();
|
||||
this._eventEmitter.beginDeferredEmit();
|
||||
@@ -1453,7 +1459,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
//#region Decorations
|
||||
|
||||
public changeDecorations<T>(callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T, ownerId: number = 0): T {
|
||||
public changeDecorations<T>(callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T, ownerId: number = 0): T | null {
|
||||
this._assertNotDisposed();
|
||||
|
||||
try {
|
||||
@@ -1464,7 +1470,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
}
|
||||
|
||||
private _changeDecorations<T>(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T {
|
||||
private _changeDecorations<T>(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null {
|
||||
let changeAccessor: model.IModelDecorationsChangeAccessor = {
|
||||
addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => {
|
||||
this._onDidChangeDecorations.fire();
|
||||
@@ -1491,17 +1497,18 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return this._deltaDecorationsImpl(ownerId, oldDecorations, newDecorations);
|
||||
}
|
||||
};
|
||||
let result: T = null;
|
||||
let result: T | null = null;
|
||||
try {
|
||||
result = callback(changeAccessor);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
// Invalidate change accessor
|
||||
changeAccessor.addDecoration = null;
|
||||
changeAccessor.changeDecoration = null;
|
||||
changeAccessor.removeDecoration = null;
|
||||
changeAccessor.deltaDecorations = null;
|
||||
changeAccessor.addDecoration = invalidFunc;
|
||||
changeAccessor.changeDecoration = invalidFunc;
|
||||
changeAccessor.changeDecorationOptions = invalidFunc;
|
||||
changeAccessor.removeDecoration = invalidFunc;
|
||||
changeAccessor.deltaDecorations = invalidFunc;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1524,11 +1531,13 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
}
|
||||
|
||||
_getTrackedRange(id: string): Range {
|
||||
_getTrackedRange(id: string): Range | null {
|
||||
return this.getDecorationRange(id);
|
||||
}
|
||||
|
||||
_setTrackedRange(id: string, newRange: Range, newStickiness: model.TrackedRangeStickiness): string {
|
||||
_setTrackedRange(id: string | null, newRange: null, newStickiness: model.TrackedRangeStickiness): null;
|
||||
_setTrackedRange(id: string | null, newRange: Range, newStickiness: model.TrackedRangeStickiness): string;
|
||||
_setTrackedRange(id: string | null, newRange: Range | null, newStickiness: model.TrackedRangeStickiness): string | null {
|
||||
const node = (id ? this._decorations[id] : null);
|
||||
|
||||
if (!node) {
|
||||
@@ -1571,7 +1580,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
}
|
||||
|
||||
public getDecorationOptions(decorationId: string): model.IModelDecorationOptions {
|
||||
public getDecorationOptions(decorationId: string): model.IModelDecorationOptions | null {
|
||||
const node = this._decorations[decorationId];
|
||||
if (!node) {
|
||||
return null;
|
||||
@@ -1579,7 +1588,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return node.options;
|
||||
}
|
||||
|
||||
public getDecorationRange(decorationId: string): Range {
|
||||
public getDecorationRange(decorationId: string): Range | null {
|
||||
const node = this._decorations[decorationId];
|
||||
if (!node) {
|
||||
return null;
|
||||
@@ -1671,8 +1680,8 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return;
|
||||
}
|
||||
|
||||
const nodeWasInOverviewRuler = (node.options.overviewRuler.color ? true : false);
|
||||
const nodeIsInOverviewRuler = (options.overviewRuler.color ? true : false);
|
||||
const nodeWasInOverviewRuler = (node.options.overviewRuler && node.options.overviewRuler.color ? true : false);
|
||||
const nodeIsInOverviewRuler = (options.overviewRuler && options.overviewRuler.color ? true : false);
|
||||
|
||||
if (nodeWasInOverviewRuler !== nodeIsInOverviewRuler) {
|
||||
// Delete + Insert due to an overview ruler status change
|
||||
@@ -1696,7 +1705,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
let result = new Array<string>(newDecorationsLen);
|
||||
while (oldDecorationIndex < oldDecorationsLen || newDecorationIndex < newDecorationsLen) {
|
||||
|
||||
let node: IntervalNode = null;
|
||||
let node: IntervalNode | null = null;
|
||||
|
||||
if (oldDecorationIndex < oldDecorationsLen) {
|
||||
// (1) get ourselves an old node
|
||||
@@ -1765,9 +1774,9 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
const eventBuilder = new ModelTokensChangedEventBuilder();
|
||||
let nonWhitespaceColumn = this.getLineFirstNonWhitespaceColumn(startLineNumber);
|
||||
let fakeLines = [];
|
||||
let fakeLines: string[] = [];
|
||||
let i = startLineNumber - 1;
|
||||
let initialState = null;
|
||||
let initialState: IState | null = null;
|
||||
if (nonWhitespaceColumn > 0) {
|
||||
while (nonWhitespaceColumn > 0 && i >= 1) {
|
||||
let newNonWhitespaceIndex = this.getLineFirstNonWhitespaceColumn(i);
|
||||
@@ -1847,7 +1856,19 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
|
||||
public isCheapToTokenize(lineNumber: number): boolean {
|
||||
return this._tokens.isCheapToTokenize(lineNumber);
|
||||
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;
|
||||
}
|
||||
|
||||
public tokenizeIfCheap(lineNumber: number): void {
|
||||
@@ -1968,7 +1989,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
// Having tokens allows implementing additional helper methods
|
||||
|
||||
public getWordAtPosition(_position: IPosition): model.IWordAtPosition {
|
||||
public getWordAtPosition(_position: IPosition): model.IWordAtPosition | null {
|
||||
this._assertNotDisposed();
|
||||
const position = this.validatePosition(_position);
|
||||
const lineContent = this.getLineContent(position.lineNumber);
|
||||
@@ -1983,7 +2004,8 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
lineContent.substring(rbStartOffset, rbEndOffset),
|
||||
rbStartOffset
|
||||
);
|
||||
if (rightBiasedWord) {
|
||||
// Make sure the result touches the original passed in position
|
||||
if (rightBiasedWord && rightBiasedWord.startColumn <= _position.column && _position.column <= rightBiasedWord.endColumn) {
|
||||
return rightBiasedWord;
|
||||
}
|
||||
|
||||
@@ -1997,7 +2019,8 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
lineContent.substring(lbStartOffset, lbEndOffset),
|
||||
lbStartOffset
|
||||
);
|
||||
if (leftBiasedWord) {
|
||||
// Make sure the result touches the original passed in position
|
||||
if (leftBiasedWord && leftBiasedWord.startColumn <= _position.column && _position.column <= leftBiasedWord.endColumn) {
|
||||
return leftBiasedWord;
|
||||
}
|
||||
}
|
||||
@@ -2009,13 +2032,13 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
const languageId = lineTokens.getLanguageId(tokenIndex);
|
||||
|
||||
// go left until a different language is hit
|
||||
let startOffset: number;
|
||||
let startOffset = 0;
|
||||
for (let i = tokenIndex; i >= 0 && lineTokens.getLanguageId(i) === languageId; i--) {
|
||||
startOffset = lineTokens.getStartOffset(i);
|
||||
}
|
||||
|
||||
// go right until a different language is hit
|
||||
let endOffset: number;
|
||||
let endOffset = lineTokens.getLineContent().length;
|
||||
for (let i = tokenIndex, tokenCount = lineTokens.getCount(); i < tokenCount && lineTokens.getLanguageId(i) === languageId; i++) {
|
||||
endOffset = lineTokens.getEndOffset(i);
|
||||
}
|
||||
@@ -2039,7 +2062,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
};
|
||||
}
|
||||
|
||||
public findMatchingBracketUp(_bracket: string, _position: IPosition): Range {
|
||||
public findMatchingBracketUp(_bracket: string, _position: IPosition): Range | null {
|
||||
let bracket = _bracket.toLowerCase();
|
||||
let position = this.validatePosition(_position);
|
||||
|
||||
@@ -2060,11 +2083,11 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return this._findMatchingBracketUp(data, position);
|
||||
}
|
||||
|
||||
public matchBracket(position: IPosition): [Range, Range] {
|
||||
public matchBracket(position: IPosition): [Range, Range] | null {
|
||||
return this._matchBracket(this.validatePosition(position));
|
||||
}
|
||||
|
||||
private _matchBracket(position: Position): [Range, Range] {
|
||||
private _matchBracket(position: Position): [Range, Range] | null {
|
||||
const lineNumber = position.lineNumber;
|
||||
const lineTokens = this._getLineTokens(lineNumber);
|
||||
const lineText = this._buffer.getLineContent(lineNumber);
|
||||
@@ -2084,7 +2107,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
|
||||
// it might be the case that [currentTokenStart -> currentTokenEnd] contains multiple brackets
|
||||
// `bestResult` will contain the most right-side result
|
||||
let bestResult: [Range, Range] = null;
|
||||
let bestResult: [Range, Range] | null = null;
|
||||
while (true) {
|
||||
let foundBracket = BracketsUtils.findNextBracketInToken(currentModeBrackets.forwardRegex, lineNumber, lineText, searchStartOffset, searchEndOffset);
|
||||
if (!foundBracket) {
|
||||
@@ -2143,7 +2166,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return null;
|
||||
}
|
||||
|
||||
private _matchFoundBracket(foundBracket: Range, data: RichEditBracket, isOpen: boolean): [Range, Range] {
|
||||
private _matchFoundBracket(foundBracket: Range, data: RichEditBracket, isOpen: boolean): [Range, Range] | null {
|
||||
if (!data) {
|
||||
return null;
|
||||
}
|
||||
@@ -2163,7 +2186,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return null;
|
||||
}
|
||||
|
||||
private _findMatchingBracketUp(bracket: RichEditBracket, position: Position): Range {
|
||||
private _findMatchingBracketUp(bracket: RichEditBracket, position: Position): Range | null {
|
||||
// console.log('_findMatchingBracketUp: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position));
|
||||
|
||||
const languageId = bracket.languageIdentifier.id;
|
||||
@@ -2224,7 +2247,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return null;
|
||||
}
|
||||
|
||||
private _findMatchingBracketDown(bracket: RichEditBracket, position: Position): Range {
|
||||
private _findMatchingBracketDown(bracket: RichEditBracket, position: Position): Range | null {
|
||||
// console.log('_findMatchingBracketDown: ', 'bracket: ', JSON.stringify(bracket), 'startPosition: ', String(position));
|
||||
|
||||
const languageId = bracket.languageIdentifier.id;
|
||||
@@ -2284,11 +2307,11 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return null;
|
||||
}
|
||||
|
||||
public findPrevBracket(_position: IPosition): model.IFoundBracket {
|
||||
public findPrevBracket(_position: IPosition): model.IFoundBracket | null {
|
||||
const position = this.validatePosition(_position);
|
||||
|
||||
let languageId: LanguageId = -1;
|
||||
let modeBrackets: RichEditBrackets = null;
|
||||
let modeBrackets: RichEditBrackets | null = null;
|
||||
for (let lineNumber = position.lineNumber; lineNumber >= 1; lineNumber--) {
|
||||
const lineTokens = this._getLineTokens(lineNumber);
|
||||
const tokenCount = lineTokens.getCount();
|
||||
@@ -2328,11 +2351,11 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return null;
|
||||
}
|
||||
|
||||
public findNextBracket(_position: IPosition): model.IFoundBracket {
|
||||
public findNextBracket(_position: IPosition): model.IFoundBracket | null {
|
||||
const position = this.validatePosition(_position);
|
||||
|
||||
let languageId: LanguageId = -1;
|
||||
let modeBrackets: RichEditBrackets = null;
|
||||
let modeBrackets: RichEditBrackets | null = null;
|
||||
for (let lineNumber = position.lineNumber, lineCount = this.getLineCount(); lineNumber <= lineCount; lineNumber++) {
|
||||
const lineTokens = this._getLineTokens(lineNumber);
|
||||
const tokenCount = lineTokens.getCount();
|
||||
@@ -2373,7 +2396,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
return null;
|
||||
}
|
||||
|
||||
private _toFoundBracket(modeBrackets: RichEditBrackets, r: Range): model.IFoundBracket {
|
||||
private _toFoundBracket(modeBrackets: RichEditBrackets, r: Range): model.IFoundBracket | null {
|
||||
if (!r) {
|
||||
return null;
|
||||
}
|
||||
@@ -2436,7 +2459,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
|
||||
const foldingRules = LanguageConfigurationRegistry.getFoldingRules(this._languageIdentifier.id);
|
||||
const offSide = foldingRules && foldingRules.offSide;
|
||||
const offSide = Boolean(foldingRules && foldingRules.offSide);
|
||||
|
||||
let up_aboveContentLineIndex = -2; /* -2 is a marker for not having computed it */
|
||||
let up_aboveContentLineIndent = -1;
|
||||
@@ -2606,7 +2629,7 @@ export class TextModel extends Disposable implements model.ITextModel {
|
||||
}
|
||||
|
||||
const foldingRules = LanguageConfigurationRegistry.getFoldingRules(this._languageIdentifier.id);
|
||||
const offSide = foldingRules && foldingRules.offSide;
|
||||
const offSide = Boolean(foldingRules && foldingRules.offSide);
|
||||
|
||||
let result: number[] = new Array<number>(endLineNumber - startLineNumber + 1);
|
||||
|
||||
@@ -2778,30 +2801,40 @@ function cleanClassName(className: string): string {
|
||||
export class ModelDecorationOverviewRulerOptions implements model.IModelDecorationOverviewRulerOptions {
|
||||
readonly color: string | ThemeColor;
|
||||
readonly darkColor: string | ThemeColor;
|
||||
readonly hcColor: string | ThemeColor;
|
||||
readonly position: model.OverviewRulerLane;
|
||||
_resolvedColor: string;
|
||||
private _resolvedColor: string | null;
|
||||
|
||||
constructor(options: model.IModelDecorationOverviewRulerOptions) {
|
||||
this.color = strings.empty;
|
||||
this.darkColor = strings.empty;
|
||||
this.hcColor = strings.empty;
|
||||
this.position = model.OverviewRulerLane.Center;
|
||||
this.color = options.color || strings.empty;
|
||||
this.darkColor = options.darkColor || strings.empty;
|
||||
this.position = (typeof options.position === 'number' ? options.position : model.OverviewRulerLane.Center);
|
||||
this._resolvedColor = null;
|
||||
}
|
||||
|
||||
if (options && options.color) {
|
||||
this.color = options.color;
|
||||
public getColor(theme: ITheme): string {
|
||||
if (!this._resolvedColor) {
|
||||
if (theme.type !== 'light' && this.darkColor) {
|
||||
this._resolvedColor = this._resolveColor(this.darkColor, theme);
|
||||
} else {
|
||||
this._resolvedColor = this._resolveColor(this.color, theme);
|
||||
}
|
||||
}
|
||||
if (options && options.darkColor) {
|
||||
this.darkColor = options.darkColor;
|
||||
this.hcColor = options.darkColor;
|
||||
return this._resolvedColor;
|
||||
}
|
||||
|
||||
public invalidateCachedColor(): void {
|
||||
this._resolvedColor = null;
|
||||
}
|
||||
|
||||
private _resolveColor(color: string | ThemeColor, theme: ITheme): string {
|
||||
if (typeof color === 'string') {
|
||||
return color;
|
||||
}
|
||||
if (options && options.hcColor) {
|
||||
this.hcColor = options.hcColor;
|
||||
}
|
||||
if (options && options.hasOwnProperty('position')) {
|
||||
this.position = options.position;
|
||||
let c = color ? theme.getColor(color.id) : null;
|
||||
if (!c) {
|
||||
return strings.empty;
|
||||
}
|
||||
return c.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2819,36 +2852,38 @@ export class ModelDecorationOptions implements model.IModelDecorationOptions {
|
||||
|
||||
readonly stickiness: model.TrackedRangeStickiness;
|
||||
readonly zIndex: number;
|
||||
readonly className: string;
|
||||
readonly hoverMessage: IMarkdownString | IMarkdownString[];
|
||||
readonly glyphMarginHoverMessage: IMarkdownString | IMarkdownString[];
|
||||
readonly className: string | null;
|
||||
readonly hoverMessage: IMarkdownString | IMarkdownString[] | null;
|
||||
readonly glyphMarginHoverMessage: IMarkdownString | IMarkdownString[] | null;
|
||||
readonly isWholeLine: boolean;
|
||||
readonly showIfCollapsed: boolean;
|
||||
readonly overviewRuler: ModelDecorationOverviewRulerOptions;
|
||||
readonly glyphMarginClassName: string;
|
||||
readonly linesDecorationsClassName: string;
|
||||
readonly marginClassName: string;
|
||||
readonly inlineClassName: string;
|
||||
readonly collapseOnReplaceEdit: boolean;
|
||||
readonly overviewRuler: ModelDecorationOverviewRulerOptions | null;
|
||||
readonly glyphMarginClassName: string | null;
|
||||
readonly linesDecorationsClassName: string | null;
|
||||
readonly marginClassName: string | null;
|
||||
readonly inlineClassName: string | null;
|
||||
readonly inlineClassNameAffectsLetterSpacing: boolean;
|
||||
readonly beforeContentClassName: string;
|
||||
readonly afterContentClassName: string;
|
||||
readonly beforeContentClassName: string | null;
|
||||
readonly afterContentClassName: string | null;
|
||||
|
||||
private constructor(options: model.IModelDecorationOptions) {
|
||||
this.stickiness = options.stickiness || model.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges;
|
||||
this.zIndex = options.zIndex || 0;
|
||||
this.className = options.className ? cleanClassName(options.className) : strings.empty;
|
||||
this.hoverMessage = options.hoverMessage || [];
|
||||
this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || [];
|
||||
this.className = options.className ? cleanClassName(options.className) : null;
|
||||
this.hoverMessage = options.hoverMessage || null;
|
||||
this.glyphMarginHoverMessage = options.glyphMarginHoverMessage || null;
|
||||
this.isWholeLine = options.isWholeLine || false;
|
||||
this.showIfCollapsed = options.showIfCollapsed || false;
|
||||
this.overviewRuler = new ModelDecorationOverviewRulerOptions(options.overviewRuler);
|
||||
this.glyphMarginClassName = options.glyphMarginClassName ? cleanClassName(options.glyphMarginClassName) : strings.empty;
|
||||
this.linesDecorationsClassName = options.linesDecorationsClassName ? cleanClassName(options.linesDecorationsClassName) : strings.empty;
|
||||
this.marginClassName = options.marginClassName ? cleanClassName(options.marginClassName) : strings.empty;
|
||||
this.inlineClassName = options.inlineClassName ? cleanClassName(options.inlineClassName) : strings.empty;
|
||||
this.collapseOnReplaceEdit = options.collapseOnReplaceEdit || false;
|
||||
this.overviewRuler = options.overviewRuler ? new ModelDecorationOverviewRulerOptions(options.overviewRuler) : 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;
|
||||
this.inlineClassName = options.inlineClassName ? cleanClassName(options.inlineClassName) : null;
|
||||
this.inlineClassNameAffectsLetterSpacing = options.inlineClassNameAffectsLetterSpacing || false;
|
||||
this.beforeContentClassName = options.beforeContentClassName ? cleanClassName(options.beforeContentClassName) : strings.empty;
|
||||
this.afterContentClassName = options.afterContentClassName ? cleanClassName(options.afterContentClassName) : strings.empty;
|
||||
this.beforeContentClassName = options.beforeContentClassName ? cleanClassName(options.beforeContentClassName) : null;
|
||||
this.afterContentClassName = options.afterContentClassName ? cleanClassName(options.afterContentClassName) : null;
|
||||
}
|
||||
}
|
||||
ModelDecorationOptions.EMPTY = ModelDecorationOptions.register({});
|
||||
@@ -2916,7 +2951,7 @@ export class DidChangeContentEmitter extends Disposable {
|
||||
public readonly slowEvent: Event<InternalModelContentChangeEvent> = this._slowEmitter.event;
|
||||
|
||||
private _deferredCnt: number;
|
||||
private _deferredEvent: InternalModelContentChangeEvent;
|
||||
private _deferredEvent: InternalModelContentChangeEvent | null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
|
||||
/**
|
||||
@@ -240,7 +238,7 @@ export class ModelRawContentChangedEvent {
|
||||
}
|
||||
|
||||
public static merge(a: ModelRawContentChangedEvent, b: ModelRawContentChangedEvent): ModelRawContentChangedEvent {
|
||||
const changes = [].concat(a.changes).concat(b.changes);
|
||||
const changes = ([] as ModelRawChange[]).concat(a.changes).concat(b.changes);
|
||||
const versionId = b.versionId;
|
||||
const isUndoing = (a.isUndoing || b.isUndoing);
|
||||
const isRedoing = (a.isRedoing || b.isRedoing);
|
||||
@@ -264,7 +262,7 @@ export class InternalModelContentChangeEvent {
|
||||
}
|
||||
|
||||
private static _mergeChangeEvents(a: IModelContentChangedEvent, b: IModelContentChangedEvent): IModelContentChangedEvent {
|
||||
const changes = [].concat(a.changes).concat(b.changes);
|
||||
const changes = ([] as IModelContentChange[]).concat(a.changes).concat(b.changes);
|
||||
const eol = b.eol;
|
||||
const versionId = b.versionId;
|
||||
const isUndoing = (a.isUndoing || b.isUndoing);
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { WordCharacterClass, WordCharacterClassifier, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier';
|
||||
import { Position } from 'vs/editor/common/core/position';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { FindMatch, EndOfLinePreference } from 'vs/editor/common/model';
|
||||
import { CharCode } from 'vs/base/common/charCode';
|
||||
import { EndOfLinePreference, FindMatch } from 'vs/editor/common/model';
|
||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||
import { getMapForWordSeparators, WordCharacterClassifier, WordCharacterClass } from 'vs/editor/common/controller/wordCharacterClassifier';
|
||||
|
||||
const LIMIT_FIND_COUNT = 999;
|
||||
|
||||
@@ -18,44 +17,16 @@ export class SearchParams {
|
||||
public readonly searchString: string;
|
||||
public readonly isRegex: boolean;
|
||||
public readonly matchCase: boolean;
|
||||
public readonly wordSeparators: string;
|
||||
public readonly wordSeparators: string | null;
|
||||
|
||||
constructor(searchString: string, isRegex: boolean, matchCase: boolean, wordSeparators: string) {
|
||||
constructor(searchString: string, isRegex: boolean, matchCase: boolean, wordSeparators: string | null) {
|
||||
this.searchString = searchString;
|
||||
this.isRegex = isRegex;
|
||||
this.matchCase = matchCase;
|
||||
this.wordSeparators = wordSeparators;
|
||||
}
|
||||
|
||||
private static _isMultilineRegexSource(searchString: string): boolean {
|
||||
if (!searchString || searchString.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0, len = searchString.length; i < len; i++) {
|
||||
const chCode = searchString.charCodeAt(i);
|
||||
|
||||
if (chCode === CharCode.Backslash) {
|
||||
|
||||
// move to next char
|
||||
i++;
|
||||
|
||||
if (i >= len) {
|
||||
// string ends with a \
|
||||
break;
|
||||
}
|
||||
|
||||
const nextChCode = searchString.charCodeAt(i);
|
||||
if (nextChCode === CharCode.n || nextChCode === CharCode.r) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public parseSearchRequest(): SearchData {
|
||||
public parseSearchRequest(): SearchData | null {
|
||||
if (this.searchString === '') {
|
||||
return null;
|
||||
}
|
||||
@@ -63,12 +34,12 @@ export class SearchParams {
|
||||
// Try to create a RegExp out of the params
|
||||
let multiline: boolean;
|
||||
if (this.isRegex) {
|
||||
multiline = SearchParams._isMultilineRegexSource(this.searchString);
|
||||
multiline = isMultilineRegexSource(this.searchString);
|
||||
} else {
|
||||
multiline = (this.searchString.indexOf('\n') >= 0);
|
||||
}
|
||||
|
||||
let regex: RegExp = null;
|
||||
let regex: RegExp | null = null;
|
||||
try {
|
||||
regex = strings.createRegExp(this.searchString, this.isRegex, {
|
||||
matchCase: this.matchCase,
|
||||
@@ -94,6 +65,34 @@ export class SearchParams {
|
||||
}
|
||||
}
|
||||
|
||||
export function isMultilineRegexSource(searchString: string): boolean {
|
||||
if (!searchString || searchString.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0, len = searchString.length; i < len; i++) {
|
||||
const chCode = searchString.charCodeAt(i);
|
||||
|
||||
if (chCode === CharCode.Backslash) {
|
||||
|
||||
// move to next char
|
||||
i++;
|
||||
|
||||
if (i >= len) {
|
||||
// string ends with a \
|
||||
break;
|
||||
}
|
||||
|
||||
const nextChCode = searchString.charCodeAt(i);
|
||||
if (nextChCode === CharCode.n || nextChCode === CharCode.r || nextChCode === CharCode.W) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export class SearchData {
|
||||
|
||||
/**
|
||||
@@ -103,13 +102,13 @@ export class SearchData {
|
||||
/**
|
||||
* The word separator classifier.
|
||||
*/
|
||||
public readonly wordSeparators: WordCharacterClassifier;
|
||||
public readonly wordSeparators: WordCharacterClassifier | null;
|
||||
/**
|
||||
* The simple string to search for (if possible).
|
||||
*/
|
||||
public readonly simpleSearch: string;
|
||||
public readonly simpleSearch: string | null;
|
||||
|
||||
constructor(regex: RegExp, wordSeparators: WordCharacterClassifier, simpleSearch: string) {
|
||||
constructor(regex: RegExp, wordSeparators: WordCharacterClassifier | null, simpleSearch: string | null) {
|
||||
this.regex = regex;
|
||||
this.wordSeparators = wordSeparators;
|
||||
this.simpleSearch = simpleSearch;
|
||||
@@ -194,10 +193,10 @@ export class TextModelSearch {
|
||||
* Multiline search always executes on the lines concatenated with \n.
|
||||
* We must therefore compensate for the count of \n in case the model is CRLF
|
||||
*/
|
||||
private static _getMultilineMatchRange(model: TextModel, deltaOffset: number, text: string, lfCounter: LineFeedCounter, matchIndex: number, match0: string): Range {
|
||||
private static _getMultilineMatchRange(model: TextModel, deltaOffset: number, text: string, lfCounter: LineFeedCounter | null, matchIndex: number, match0: string): Range {
|
||||
let startOffset: number;
|
||||
let lineFeedCountBeforeMatch = 0;
|
||||
if (model.getEOL() === '\r\n') {
|
||||
if (lfCounter) {
|
||||
lineFeedCountBeforeMatch = lfCounter.findLineFeedCountBeforeOffset(matchIndex);
|
||||
startOffset = deltaOffset + matchIndex + lineFeedCountBeforeMatch /* add as many \r as there were \n */;
|
||||
} else {
|
||||
@@ -205,7 +204,7 @@ export class TextModelSearch {
|
||||
}
|
||||
|
||||
let endOffset: number;
|
||||
if (model.getEOL() === '\r\n') {
|
||||
if (lfCounter) {
|
||||
let lineFeedCountBeforeEndOfMatch = lfCounter.findLineFeedCountBeforeOffset(matchIndex + match0.length);
|
||||
let lineFeedCountInMatch = lineFeedCountBeforeEndOfMatch - lineFeedCountBeforeMatch;
|
||||
endOffset = startOffset + match0.length + lineFeedCountInMatch /* add as many \r as there were \n */;
|
||||
@@ -229,7 +228,7 @@ export class TextModelSearch {
|
||||
const result: FindMatch[] = [];
|
||||
let counter = 0;
|
||||
|
||||
let m: RegExpExecArray;
|
||||
let m: RegExpExecArray | null;
|
||||
searcher.reset(0);
|
||||
while ((m = searcher.next(text))) {
|
||||
result[counter++] = createFindMatch(this._getMultilineMatchRange(model, deltaOffset, text, lfCounter, m.index, m[0]), m, captureMatches);
|
||||
@@ -290,7 +289,7 @@ export class TextModelSearch {
|
||||
}
|
||||
|
||||
const searcher = new Searcher(searchData.wordSeparators, searchData.regex);
|
||||
let m: RegExpExecArray;
|
||||
let m: RegExpExecArray | null;
|
||||
// Reset regex to search from the beginning
|
||||
searcher.reset(0);
|
||||
do {
|
||||
@@ -305,7 +304,7 @@ export class TextModelSearch {
|
||||
return resultLen;
|
||||
}
|
||||
|
||||
public static findNextMatch(model: TextModel, searchParams: SearchParams, searchStart: Position, captureMatches: boolean): FindMatch {
|
||||
public static findNextMatch(model: TextModel, searchParams: SearchParams, searchStart: Position, captureMatches: boolean): FindMatch | null {
|
||||
const searchData = searchParams.parseSearchRequest();
|
||||
if (!searchData) {
|
||||
return null;
|
||||
@@ -319,7 +318,7 @@ export class TextModelSearch {
|
||||
return this._doFindNextMatchLineByLine(model, searchStart, searcher, captureMatches);
|
||||
}
|
||||
|
||||
private static _doFindNextMatchMultiline(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch {
|
||||
private static _doFindNextMatchMultiline(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch | null {
|
||||
const searchTextStart = new Position(searchStart.lineNumber, 1);
|
||||
const deltaOffset = model.getOffsetAt(searchTextStart);
|
||||
const lineCount = model.getLineCount();
|
||||
@@ -346,7 +345,7 @@ export class TextModelSearch {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _doFindNextMatchLineByLine(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch {
|
||||
private static _doFindNextMatchLineByLine(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch | null {
|
||||
const lineCount = model.getLineCount();
|
||||
const startLineNumber = searchStart.lineNumber;
|
||||
|
||||
@@ -369,10 +368,10 @@ export class TextModelSearch {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _findFirstMatchInLine(searcher: Searcher, text: string, lineNumber: number, fromColumn: number, captureMatches: boolean): FindMatch {
|
||||
private static _findFirstMatchInLine(searcher: Searcher, text: string, lineNumber: number, fromColumn: number, captureMatches: boolean): FindMatch | null {
|
||||
// Set regex to search from column
|
||||
searcher.reset(fromColumn - 1);
|
||||
const m: RegExpExecArray = searcher.next(text);
|
||||
const m: RegExpExecArray | null = searcher.next(text);
|
||||
if (m) {
|
||||
return createFindMatch(
|
||||
new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length),
|
||||
@@ -383,7 +382,7 @@ export class TextModelSearch {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static findPreviousMatch(model: TextModel, searchParams: SearchParams, searchStart: Position, captureMatches: boolean): FindMatch {
|
||||
public static findPreviousMatch(model: TextModel, searchParams: SearchParams, searchStart: Position, captureMatches: boolean): FindMatch | null {
|
||||
const searchData = searchParams.parseSearchRequest();
|
||||
if (!searchData) {
|
||||
return null;
|
||||
@@ -397,7 +396,7 @@ export class TextModelSearch {
|
||||
return this._doFindPreviousMatchLineByLine(model, searchStart, searcher, captureMatches);
|
||||
}
|
||||
|
||||
private static _doFindPreviousMatchMultiline(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch {
|
||||
private static _doFindPreviousMatchMultiline(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch | null {
|
||||
const matches = this._doFindMatchesMultiline(model, new Range(1, 1, searchStart.lineNumber, searchStart.column), searcher, captureMatches, 10 * LIMIT_FIND_COUNT);
|
||||
if (matches.length > 0) {
|
||||
return matches[matches.length - 1];
|
||||
@@ -412,7 +411,7 @@ export class TextModelSearch {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _doFindPreviousMatchLineByLine(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch {
|
||||
private static _doFindPreviousMatchLineByLine(model: TextModel, searchStart: Position, searcher: Searcher, captureMatches: boolean): FindMatch | null {
|
||||
const lineCount = model.getLineCount();
|
||||
const startLineNumber = searchStart.lineNumber;
|
||||
|
||||
@@ -435,9 +434,9 @@ export class TextModelSearch {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static _findLastMatchInLine(searcher: Searcher, text: string, lineNumber: number, captureMatches: boolean): FindMatch {
|
||||
let bestResult: FindMatch = null;
|
||||
let m: RegExpExecArray;
|
||||
private static _findLastMatchInLine(searcher: Searcher, text: string, lineNumber: number, captureMatches: boolean): FindMatch | null {
|
||||
let bestResult: FindMatch | null = null;
|
||||
let m: RegExpExecArray | null;
|
||||
searcher.reset(0);
|
||||
while ((m = searcher.next(text))) {
|
||||
bestResult = createFindMatch(new Range(lineNumber, m.index + 1, lineNumber, m.index + 1 + m[0].length), m, captureMatches);
|
||||
@@ -510,12 +509,12 @@ export function isValidMatch(wordSeparators: WordCharacterClassifier, text: stri
|
||||
}
|
||||
|
||||
export class Searcher {
|
||||
private _wordSeparators: WordCharacterClassifier;
|
||||
private _wordSeparators: WordCharacterClassifier | null;
|
||||
private _searchRegex: RegExp;
|
||||
private _prevMatchStartIndex: number;
|
||||
private _prevMatchLength: number;
|
||||
|
||||
constructor(wordSeparators: WordCharacterClassifier, searchRegex: RegExp, ) {
|
||||
constructor(wordSeparators: WordCharacterClassifier | null, searchRegex: RegExp, ) {
|
||||
this._wordSeparators = wordSeparators;
|
||||
this._searchRegex = searchRegex;
|
||||
this._prevMatchStartIndex = -1;
|
||||
@@ -528,10 +527,10 @@ export class Searcher {
|
||||
this._prevMatchLength = 0;
|
||||
}
|
||||
|
||||
public next(text: string): RegExpExecArray {
|
||||
public next(text: string): RegExpExecArray | null {
|
||||
const textLength = text.length;
|
||||
|
||||
let m: RegExpExecArray;
|
||||
let m: RegExpExecArray | null;
|
||||
do {
|
||||
if (this._prevMatchStartIndex + this._prevMatchLength === textLength) {
|
||||
// Reached the end of the line
|
||||
|
||||
@@ -2,18 +2,17 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { IState, FontStyle, StandardTokenType, MetadataConsts, ColorId, LanguageId, ITokenizationSupport, LanguageIdentifier } from 'vs/editor/common/modes';
|
||||
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||
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 { IModelTokensChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { TokenizationResult2 } from 'vs/editor/common/core/token';
|
||||
import { nullTokenize2 } from 'vs/editor/common/modes/nullMode';
|
||||
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 { nullTokenize2 } from 'vs/editor/common/modes/nullMode';
|
||||
|
||||
function getDefaultMetadata(topLevelLanguageId: LanguageId): number {
|
||||
return (
|
||||
@@ -28,11 +27,11 @@ function getDefaultMetadata(topLevelLanguageId: LanguageId): number {
|
||||
const EMPTY_LINE_TOKENS = (new Uint32Array(0)).buffer;
|
||||
|
||||
class ModelLineTokens {
|
||||
_state: IState;
|
||||
_lineTokens: ArrayBuffer;
|
||||
_state: IState | null;
|
||||
_lineTokens: ArrayBuffer | null;
|
||||
_invalid: boolean;
|
||||
|
||||
constructor(state: IState) {
|
||||
constructor(state: IState | null) {
|
||||
this._state = state;
|
||||
this._lineTokens = null;
|
||||
this._invalid = true;
|
||||
@@ -113,7 +112,7 @@ class ModelLineTokens {
|
||||
this._lineTokens = tmp.buffer;
|
||||
}
|
||||
|
||||
public append(_otherTokens: ArrayBuffer): void {
|
||||
public append(_otherTokens: ArrayBuffer | null): void {
|
||||
if (_otherTokens === EMPTY_LINE_TOKENS) {
|
||||
return;
|
||||
}
|
||||
@@ -169,17 +168,17 @@ class ModelLineTokens {
|
||||
export class ModelLinesTokens {
|
||||
|
||||
public readonly languageIdentifier: LanguageIdentifier;
|
||||
public readonly tokenizationSupport: ITokenizationSupport;
|
||||
public readonly tokenizationSupport: ITokenizationSupport | null;
|
||||
private _tokens: ModelLineTokens[];
|
||||
private _invalidLineStartIndex: number;
|
||||
private _lastState: IState;
|
||||
private _lastState: IState | null;
|
||||
|
||||
constructor(languageIdentifier: LanguageIdentifier, tokenizationSupport: ITokenizationSupport) {
|
||||
constructor(languageIdentifier: LanguageIdentifier, tokenizationSupport: ITokenizationSupport | null) {
|
||||
this.languageIdentifier = languageIdentifier;
|
||||
this.tokenizationSupport = tokenizationSupport;
|
||||
this._tokens = [];
|
||||
if (this.tokenizationSupport) {
|
||||
let initialState: IState = null;
|
||||
let initialState: IState | null = null;
|
||||
try {
|
||||
initialState = this.tokenizationSupport.getInitialState();
|
||||
} catch (e) {
|
||||
@@ -201,7 +200,7 @@ export class ModelLinesTokens {
|
||||
}
|
||||
|
||||
public getTokens(topLevelLanguageId: LanguageId, lineIndex: number, lineText: string): LineTokens {
|
||||
let rawLineTokens: ArrayBuffer = null;
|
||||
let rawLineTokens: ArrayBuffer | null = null;
|
||||
if (lineIndex < this._tokens.length && this._tokens[lineIndex]) {
|
||||
rawLineTokens = this._tokens[lineIndex]._lineTokens;
|
||||
}
|
||||
@@ -246,7 +245,7 @@ export class ModelLinesTokens {
|
||||
return true;
|
||||
}
|
||||
|
||||
_getState(lineIndex: number): IState {
|
||||
_getState(lineIndex: number): IState | null {
|
||||
if (lineIndex < this._tokens.length && this._tokens[lineIndex]) {
|
||||
return this._tokens[lineIndex]._state;
|
||||
}
|
||||
@@ -263,8 +262,15 @@ export class ModelLinesTokens {
|
||||
}
|
||||
|
||||
if (lineTextLength === 0) {
|
||||
target._lineTokens = EMPTY_LINE_TOKENS;
|
||||
return;
|
||||
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) {
|
||||
@@ -324,7 +330,7 @@ export class ModelLinesTokens {
|
||||
firstLine.deleteEnding(range.startColumn - 1);
|
||||
|
||||
const lastLineIndex = range.endLineNumber - 1;
|
||||
let lastLineTokens: ArrayBuffer = null;
|
||||
let lastLineTokens: ArrayBuffer | null = null;
|
||||
if (lastLineIndex < this._tokens.length) {
|
||||
const lastLine = this._tokens[lastLineIndex];
|
||||
lastLine.deleteBeginning(range.endColumn - 1);
|
||||
@@ -381,12 +387,14 @@ export class ModelLinesTokens {
|
||||
}
|
||||
|
||||
public _tokenizeText(buffer: ITextBuffer, text: string, state: IState): TokenizationResult2 {
|
||||
let r: TokenizationResult2 = null;
|
||||
let r: TokenizationResult2 | null = null;
|
||||
|
||||
try {
|
||||
r = this.tokenizationSupport.tokenize2(text, state, 0);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
if (this.tokenizationSupport) {
|
||||
try {
|
||||
r = this.tokenizationSupport.tokenize2(text, state, 0);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!r) {
|
||||
@@ -407,26 +415,29 @@ export class ModelLinesTokens {
|
||||
// Validate all states up to and including endLineIndex
|
||||
for (let lineIndex = this._invalidLineStartIndex; lineIndex <= endLineIndex; lineIndex++) {
|
||||
const endStateIndex = lineIndex + 1;
|
||||
let r: TokenizationResult2 = null;
|
||||
const text = buffer.getLineContent(lineIndex + 1);
|
||||
const lineStartState = this._getState(lineIndex);
|
||||
|
||||
let r: TokenizationResult2 | null = null;
|
||||
|
||||
try {
|
||||
// Tokenize only the first X characters
|
||||
let freshState = this._getState(lineIndex).clone();
|
||||
let freshState = lineStartState!.clone();
|
||||
r = this.tokenizationSupport.tokenize2(text, freshState, 0);
|
||||
} catch (e) {
|
||||
onUnexpectedError(e);
|
||||
}
|
||||
|
||||
if (!r) {
|
||||
r = nullTokenize2(this.languageIdentifier.id, text, this._getState(lineIndex), 0);
|
||||
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) {
|
||||
if (this._getState(endStateIndex) !== null && r.endState.equals(this._getState(endStateIndex))) {
|
||||
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) {
|
||||
@@ -484,7 +495,7 @@ export class ModelTokensChangedEventBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
public build(): IModelTokensChangedEvent {
|
||||
public build(): IModelTokensChangedEvent | null {
|
||||
if (this._ranges.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { IWordAtPosition } from 'vs/editor/common/model';
|
||||
|
||||
@@ -31,7 +30,7 @@ function createWordRegExp(allowInWords: string = ''): RegExp {
|
||||
// catches numbers (including floating numbers) in the first group, and alphanum in the second
|
||||
export const DEFAULT_WORD_REGEXP = createWordRegExp();
|
||||
|
||||
export function ensureValidWordDefinition(wordDefinition?: RegExp): RegExp {
|
||||
export function ensureValidWordDefinition(wordDefinition?: RegExp | null): RegExp {
|
||||
let result: RegExp = DEFAULT_WORD_REGEXP;
|
||||
|
||||
if (wordDefinition && (wordDefinition instanceof RegExp)) {
|
||||
@@ -54,7 +53,7 @@ export function ensureValidWordDefinition(wordDefinition?: RegExp): RegExp {
|
||||
return result;
|
||||
}
|
||||
|
||||
function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
|
||||
function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition | null {
|
||||
// find whitespace enclosed text around column and match from there
|
||||
|
||||
let pos = column - 1 - textOffset;
|
||||
@@ -65,12 +64,13 @@ function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string,
|
||||
}
|
||||
|
||||
wordDefinition.lastIndex = start;
|
||||
let match: RegExpMatchArray;
|
||||
let match: RegExpMatchArray | null;
|
||||
while (match = wordDefinition.exec(text)) {
|
||||
if (match.index <= pos && wordDefinition.lastIndex >= pos) {
|
||||
const matchIndex = match.index || 0;
|
||||
if (matchIndex <= pos && wordDefinition.lastIndex >= pos) {
|
||||
return {
|
||||
word: match[0],
|
||||
startColumn: textOffset + 1 + match.index,
|
||||
startColumn: textOffset + 1 + matchIndex,
|
||||
endColumn: textOffset + 1 + wordDefinition.lastIndex
|
||||
};
|
||||
}
|
||||
@@ -80,7 +80,7 @@ function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string,
|
||||
}
|
||||
|
||||
|
||||
function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
|
||||
function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition | null {
|
||||
// matches all words starting at the beginning
|
||||
// of the input until it finds a match that encloses
|
||||
// the desired column. slow but correct
|
||||
@@ -88,10 +88,10 @@ function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string,
|
||||
let pos = column - 1 - textOffset;
|
||||
wordDefinition.lastIndex = 0;
|
||||
|
||||
let match: RegExpMatchArray;
|
||||
let match: RegExpMatchArray | null;
|
||||
while (match = wordDefinition.exec(text)) {
|
||||
|
||||
if (match.index > pos) {
|
||||
const matchIndex = match.index || 0;
|
||||
if (matchIndex > pos) {
|
||||
// |nW -> matched only after the pos
|
||||
return null;
|
||||
|
||||
@@ -99,7 +99,7 @@ function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string,
|
||||
// W|W -> match encloses pos
|
||||
return {
|
||||
word: match[0],
|
||||
startColumn: textOffset + 1 + match.index,
|
||||
startColumn: textOffset + 1 + matchIndex,
|
||||
endColumn: textOffset + 1 + wordDefinition.lastIndex
|
||||
};
|
||||
}
|
||||
@@ -108,7 +108,7 @@ function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string,
|
||||
return null;
|
||||
}
|
||||
|
||||
export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition {
|
||||
export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number): IWordAtPosition | null {
|
||||
|
||||
// if `words` can contain whitespace character we have to use the slow variant
|
||||
// otherwise we use the fast variant of finding a word
|
||||
|
||||
Reference in New Issue
Block a user