From 0c403f82c3d18b600eeddcf0d3d9fe98d3683350 Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Thu, 11 Oct 2018 21:59:39 +0200 Subject: [PATCH] More strict null (#60565) --- src/tsconfig.strictNullChecks.json | 20 +++ src/vs/base/common/map.ts | 8 +- src/vs/editor/common/config/editorOptions.ts | 32 ++-- src/vs/editor/common/config/fontInfo.ts | 10 +- src/vs/editor/common/model.ts | 62 ++++---- src/vs/editor/common/model/editStack.ts | 36 ++--- src/vs/editor/common/model/intervalTree.ts | 20 +-- .../pieceTreeTextBuffer/pieceTreeBase.ts | 95 ++++++------ .../pieceTreeTextBuffer.ts | 18 +-- .../model/pieceTreeTextBuffer/rbTreeBase.ts | 14 +- src/vs/editor/common/model/textModel.ts | 142 ++++++++++-------- src/vs/editor/common/model/textModelSearch.ts | 50 +++--- src/vs/editor/common/model/textModelTokens.ts | 47 +++--- src/vs/editor/common/model/wordHelper.ts | 23 +-- src/vs/editor/common/modes.ts | 6 +- .../modes/languageConfigurationRegistry.ts | 64 ++++---- .../common/modes/languageFeatureRegistry.ts | 6 +- src/vs/editor/common/modes/nullMode.ts | 4 +- .../modes/supports/electricCharacter.ts | 10 +- .../editor/common/modes/supports/onEnter.ts | 27 ++-- .../common/modes/supports/richEditBrackets.ts | 18 +-- .../common/modes/tokenizationRegistry.ts | 13 +- src/vs/monaco.d.ts | 38 ++--- 23 files changed, 417 insertions(+), 346 deletions(-) diff --git a/src/tsconfig.strictNullChecks.json b/src/tsconfig.strictNullChecks.json index f9ab6aa8986..dc9c93f5555 100644 --- a/src/tsconfig.strictNullChecks.json +++ b/src/tsconfig.strictNullChecks.json @@ -90,7 +90,9 @@ "./vs/code/electron-main/keyboard.ts", "./vs/code/electron-main/theme.ts", "./vs/code/node/shellEnv.ts", + "./vs/editor/common/config/editorOptions.ts", "./vs/editor/common/config/editorZoom.ts", + "./vs/editor/common/config/fontInfo.ts", "./vs/editor/common/controller/cursorEvents.ts", "./vs/editor/common/controller/wordCharacterClassifier.ts", "./vs/editor/common/core/characterClassifier.ts", @@ -100,9 +102,27 @@ "./vs/editor/common/core/selection.ts", "./vs/editor/common/core/stringBuilder.ts", "./vs/editor/common/core/uint.ts", + "./vs/editor/common/model.ts", + "./vs/editor/common/model/editStack.ts", + "./vs/editor/common/model/intervalTree.ts", "./vs/editor/common/model/mirrorTextModel.ts", + "./vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts", + "./vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts", + "./vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts", + "./vs/editor/common/model/textModel.ts", "./vs/editor/common/model/textModelEvents.ts", "./vs/editor/common/modes/languageSelector.ts", + "./vs/editor/common/model/textModelSearch.ts", + "./vs/editor/common/model/textModelTokens.ts", + "./vs/editor/common/model/wordHelper.ts", + "./vs/editor/common/modes.ts", + "./vs/editor/common/modes/languageConfigurationRegistry.ts", + "./vs/editor/common/modes/languageFeatureRegistry.ts", + "./vs/editor/common/modes/nullMode.ts", + "./vs/editor/common/modes/supports/electricCharacter.ts", + "./vs/editor/common/modes/supports/onEnter.ts", + "./vs/editor/common/modes/supports/richEditBrackets.ts", + "./vs/editor/common/modes/tokenizationRegistry.ts", "./vs/editor/common/view/overviewZoneManager.ts", "./vs/editor/common/viewLayout/whitespaceComputer.ts", "./vs/editor/common/viewModel/prefixSumComputer.ts", diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index 99b858b68c6..f5c25133c5b 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -639,16 +639,18 @@ export class LinkedMap { this.clear(); return; } - let current = this._head!; + let current = this._head; let currentSize = this.size; while (current && currentSize > newSize) { this._map.delete(current.key); - current = current.next!; + current = current.next; currentSize--; } this._head = current; this._size = currentSize; - current.previous = void 0; + if (current) { + current.previous = void 0; + } } private addItemFirst(item: Item): void { diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index f7d9fa0c404..bda346b4448 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -834,7 +834,7 @@ export function cursorStyleToString(cursorStyle: TextEditorCursorStyle): string } } -function _cursorStyleFromString(cursorStyle: string, defaultValue: TextEditorCursorStyle): TextEditorCursorStyle { +function _cursorStyleFromString(cursorStyle: string | undefined, defaultValue: TextEditorCursorStyle): TextEditorCursorStyle { if (typeof cursorStyle !== 'string') { return defaultValue; } @@ -930,7 +930,7 @@ export interface InternalEditorViewOptions { readonly rulers: number[]; readonly ariaLabel: string; readonly renderLineNumbers: RenderLineNumbersType; - readonly renderCustomLineNumbers: (lineNumber: number) => string; + readonly renderCustomLineNumbers: ((lineNumber: number) => string) | null; readonly selectOnLineNumbers: boolean; readonly glyphMargin: boolean; readonly revealHorizontalRightPadding: number; @@ -1597,7 +1597,7 @@ function _boolean(value: any, defaultValue: T): boolean | T { return Boolean(value); } -function _booleanMap(value: { [key: string]: boolean }, defaultValue: { [key: string]: boolean }): { [key: string]: boolean } { +function _booleanMap(value: { [key: string]: boolean } | undefined, defaultValue: { [key: string]: boolean }): { [key: string]: boolean } { if (!value) { return defaultValue; } @@ -1619,7 +1619,7 @@ function _string(value: any, defaultValue: string): string { return value; } -function _stringSet(value: T, defaultValue: T, allowedValues: T[]): T { +function _stringSet(value: T | undefined, defaultValue: T, allowedValues: T[]): T { if (typeof value !== 'string') { return defaultValue; } @@ -1652,7 +1652,7 @@ function _float(value: any, defaultValue: number): number { return r; } -function _wrappingIndentFromString(wrappingIndent: string, defaultValue: WrappingIndent): WrappingIndent { +function _wrappingIndentFromString(wrappingIndent: string | undefined, defaultValue: WrappingIndent): WrappingIndent { if (typeof wrappingIndent !== 'string') { return defaultValue; } @@ -1667,7 +1667,7 @@ function _wrappingIndentFromString(wrappingIndent: string, defaultValue: Wrappin } } -function _cursorBlinkingStyleFromString(cursorBlinkingStyle: string, defaultValue: TextEditorCursorBlinkingStyle): TextEditorCursorBlinkingStyle { +function _cursorBlinkingStyleFromString(cursorBlinkingStyle: string | undefined, defaultValue: TextEditorCursorBlinkingStyle): TextEditorCursorBlinkingStyle { if (typeof cursorBlinkingStyle !== 'string') { return defaultValue; } @@ -1687,7 +1687,7 @@ function _cursorBlinkingStyleFromString(cursorBlinkingStyle: string, defaultValu return TextEditorCursorBlinkingStyle.Blink; } -function _scrollbarVisibilityFromString(visibility: string, defaultValue: ScrollbarVisibility): ScrollbarVisibility { +function _scrollbarVisibilityFromString(visibility: string | undefined, defaultValue: ScrollbarVisibility): ScrollbarVisibility { if (typeof visibility !== 'string') { return defaultValue; } @@ -1726,7 +1726,7 @@ export class EditorOptionsValidator { const viewInfo = this._sanitizeViewInfo(opts, defaults.viewInfo); const contribInfo = this._sanitizeContribInfo(opts, defaults.contribInfo); - let configuredMulticursorModifier: 'altKey' | 'metaKey' | 'ctrlKey'; + let configuredMulticursorModifier: 'altKey' | 'metaKey' | 'ctrlKey' | undefined = undefined; if (typeof opts.multiCursorModifier === 'string') { if (opts.multiCursorModifier === 'ctrlCmd') { configuredMulticursorModifier = platform.isMacintosh ? 'metaKey' : 'ctrlKey'; @@ -1783,7 +1783,7 @@ export class EditorOptionsValidator { }; } - private static _sanitizeScrollbarOpts(opts: IEditorScrollbarOptions, defaults: InternalEditorScrollbarOptions, mouseWheelScrollSensitivity: number): InternalEditorScrollbarOptions { + private static _sanitizeScrollbarOpts(opts: IEditorScrollbarOptions | undefined, defaults: InternalEditorScrollbarOptions, mouseWheelScrollSensitivity: number): InternalEditorScrollbarOptions { if (typeof opts !== 'object') { return defaults; } @@ -1810,7 +1810,7 @@ export class EditorOptionsValidator { }; } - private static _sanitizeMinimapOpts(opts: IEditorMinimapOptions, defaults: InternalEditorMinimapOptions): InternalEditorMinimapOptions { + private static _sanitizeMinimapOpts(opts: IEditorMinimapOptions | undefined, defaults: InternalEditorMinimapOptions): InternalEditorMinimapOptions { if (typeof opts !== 'object') { return defaults; } @@ -1823,7 +1823,7 @@ export class EditorOptionsValidator { }; } - private static _santizeFindOpts(opts: IEditorFindOptions, defaults: InternalEditorFindOptions): InternalEditorFindOptions { + private static _santizeFindOpts(opts: IEditorFindOptions | undefined, defaults: InternalEditorFindOptions): InternalEditorFindOptions { if (typeof opts !== 'object') { return defaults; } @@ -1835,7 +1835,7 @@ export class EditorOptionsValidator { }; } - private static _sanitizeParameterHintOpts(opts: IEditorParameterHintOptions, defaults: InternalParameterHintOptions): InternalParameterHintOptions { + private static _sanitizeParameterHintOpts(opts: IEditorParameterHintOptions | undefined, defaults: InternalParameterHintOptions): InternalParameterHintOptions { if (typeof opts !== 'object') { return defaults; } @@ -1846,7 +1846,7 @@ export class EditorOptionsValidator { }; } - private static _santizeHoverOpts(_opts: boolean | IEditorHoverOptions, defaults: InternalEditorHoverOptions): InternalEditorHoverOptions { + private static _santizeHoverOpts(_opts: boolean | IEditorHoverOptions | undefined, defaults: InternalEditorHoverOptions): InternalEditorHoverOptions { let opts: IEditorHoverOptions; if (typeof _opts === 'boolean') { opts = { @@ -1875,7 +1875,7 @@ export class EditorOptionsValidator { }; } - private static _sanitizeTabCompletionOpts(opts: boolean | 'on' | 'off' | 'onlySnippets', defaults: 'on' | 'off' | 'onlySnippets'): 'on' | 'off' | 'onlySnippets' { + private static _sanitizeTabCompletionOpts(opts: boolean | 'on' | 'off' | 'onlySnippets' | undefined, defaults: 'on' | 'off' | 'onlySnippets'): 'on' | 'off' | 'onlySnippets' { if (opts === false) { return 'off'; } else if (opts === true) { @@ -1896,7 +1896,7 @@ export class EditorOptionsValidator { } let renderLineNumbers: RenderLineNumbersType = defaults.renderLineNumbers; - let renderCustomLineNumbers: (lineNumber: number) => string = defaults.renderCustomLineNumbers; + let renderCustomLineNumbers: ((lineNumber: number) => string) | null = defaults.renderCustomLineNumbers; if (typeof opts.lineNumbers !== 'undefined') { let lineNumbers = opts.lineNumbers; @@ -2198,7 +2198,7 @@ export class InternalEditorOptionsFactory { pixelRatio: env.pixelRatio }); - let bareWrappingInfo: { isWordWrapMinified: boolean; isViewportWrapping: boolean; wrappingColumn: number; } = null; + let bareWrappingInfo: { isWordWrapMinified: boolean; isViewportWrapping: boolean; wrappingColumn: number; } | null = null; { const wordWrap = opts.wordWrap; const wordWrapColumn = opts.wordWrapColumn; diff --git a/src/vs/editor/common/config/fontInfo.ts b/src/vs/editor/common/config/fontInfo.ts index b0b5b84369d..652ddb81350 100644 --- a/src/vs/editor/common/config/fontInfo.ts +++ b/src/vs/editor/common/config/fontInfo.ts @@ -23,10 +23,13 @@ const MAXIMUM_LINE_HEIGHT = 150; const MINIMUM_LETTER_SPACING = -5; const MAXIMUM_LETTER_SPACING = 20; -function safeParseFloat(n: number | string, defaultValue: number): number { +function safeParseFloat(n: number | string | undefined, defaultValue: number): number { if (typeof n === 'number') { return n; } + if (typeof n === 'undefined') { + return defaultValue; + } let r = parseFloat(n); if (isNaN(r)) { return defaultValue; @@ -34,10 +37,13 @@ function safeParseFloat(n: number | string, defaultValue: number): number { return r; } -function safeParseInt(n: number | string, defaultValue: number): number { +function safeParseInt(n: number | string | undefined, defaultValue: number): number { if (typeof n === 'number') { return Math.round(n); } + if (typeof n === 'undefined') { + return defaultValue; + } let r = parseInt(n); if (isNaN(r)) { return defaultValue; diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 382372cb583..440bd75453c 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -58,15 +58,15 @@ export interface IModelDecorationOptions { /** * CSS class name describing the decoration. */ - className?: string; + className?: string | null; /** * Message to be rendered when hovering over the glyph margin decoration. */ - glyphMarginHoverMessage?: IMarkdownString | IMarkdownString[]; + glyphMarginHoverMessage?: IMarkdownString | IMarkdownString[] | null; /** * Array of MarkdownString to render as the decoration message. */ - hoverMessage?: IMarkdownString | IMarkdownString[]; + hoverMessage?: IMarkdownString | IMarkdownString[] | null; /** * Should the decoration expand to encompass a whole line. */ @@ -89,25 +89,25 @@ export interface IModelDecorationOptions { /** * If set, render this decoration in the overview ruler. */ - overviewRuler?: IModelDecorationOverviewRulerOptions; + overviewRuler?: IModelDecorationOverviewRulerOptions | null; /** * If set, the decoration will be rendered in the glyph margin with this CSS class name. */ - glyphMarginClassName?: string; + glyphMarginClassName?: string | null; /** * If set, the decoration will be rendered in the lines decorations with this CSS class name. */ - linesDecorationsClassName?: string; + linesDecorationsClassName?: string | null; /** * If set, the decoration will be rendered in the margin (covering its full width) with this CSS class name. */ - marginClassName?: string; + marginClassName?: string | null; /** * If set, the decoration will be rendered inline with the text with this CSS class name. * Please use this only for CSS rules that must impact the text. For example, use `className` * to have a background color decoration. */ - inlineClassName?: string; + inlineClassName?: string | null; /** * If there is an `inlineClassName` which affects letter spacing. */ @@ -115,11 +115,11 @@ export interface IModelDecorationOptions { /** * If set, the decoration will be rendered before the text with this CSS class name. */ - beforeContentClassName?: string; + beforeContentClassName?: string | null; /** * If set, the decoration will be rendered after the text with this CSS class name. */ - afterContentClassName?: string; + afterContentClassName?: string | null; } /** @@ -305,7 +305,7 @@ export interface IIdentifiedSingleEditOperation { * An identifier associated with this single edit operation. * @internal */ - identifier?: ISingleEditOperationIdentifier; + identifier?: ISingleEditOperationIdentifier | null; /** * The range to replace. This can be empty to emulate a simple insert. */ @@ -313,7 +313,7 @@ export interface IIdentifiedSingleEditOperation { /** * The text to replace with. This can be null to emulate a simple delete. */ - text: string; + text: string | null; /** * This indicates that this operation has "insert" semantics. * i.e. forceMoveMarkers = true => if `range` is collapsed, all markers at the position will be moved. @@ -412,12 +412,12 @@ export class FindMatch { _findMatchBrand: void; public readonly range: Range; - public readonly matches: string[]; + public readonly matches: string[] | null; /** * @internal */ - constructor(range: Range, matches: string[]) { + constructor(range: Range, matches: string[] | null) { this.range = range; this.matches = matches; } @@ -719,7 +719,7 @@ export interface ITextModel { * @param captureMatches The result will contain the captured groups. * @return The range where the next match is. It is null if no next match has been found. */ - findNextMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch; + findNextMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch | null; /** * Search the model for the previous match. Loops to the end of the model if needed. * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. @@ -730,7 +730,7 @@ export interface ITextModel { * @param captureMatches The result will contain the captured groups. * @return The range where the previous match is. It is null if no previous match has been found. */ - findPreviousMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch; + findPreviousMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch | null; /** * Force tokenization information for `lineNumber` to be accurate. @@ -788,7 +788,7 @@ export interface ITextModel { * @param position The position to look for a word. * @return The word under or besides `position`. Might be null. */ - getWordAtPosition(position: IPosition): IWordAtPosition; + getWordAtPosition(position: IPosition): IWordAtPosition | null; /** * Get the word under or besides `position` trimmed to `position`.column @@ -804,7 +804,7 @@ export interface ITextModel { * @return The range of the matching bracket, or null if the bracket match was not found. * @internal */ - findMatchingBracketUp(bracket: string, position: IPosition): Range; + findMatchingBracketUp(bracket: string, position: IPosition): Range | null; /** * Find the first bracket in the model before `position`. @@ -812,7 +812,7 @@ export interface ITextModel { * @return The info for the first bracket before `position`, or null if there are no more brackets before `positions`. * @internal */ - findPrevBracket(position: IPosition): IFoundBracket; + findPrevBracket(position: IPosition): IFoundBracket | null; /** * Find the first bracket in the model after `position`. @@ -820,7 +820,7 @@ export interface ITextModel { * @return The info for the first bracket after `position`, or null if there are no more brackets after `positions`. * @internal */ - findNextBracket(position: IPosition): IFoundBracket; + findNextBracket(position: IPosition): IFoundBracket | null; /** * Given a `position`, if the position is on top or near a bracket, @@ -828,7 +828,7 @@ export interface ITextModel { * @param position The position at which to look for a bracket. * @internal */ - matchBracket(position: IPosition): [Range, Range]; + matchBracket(position: IPosition): [Range, Range] | null; /** * @internal @@ -848,7 +848,7 @@ export interface ITextModel { * @param ownerId Identifies the editor id in which these decorations should appear. If no `ownerId` is provided, the decorations will appear in all editors that attach this model. * @internal */ - changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T, ownerId?: number): T; + changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T, ownerId?: number): T | null; /** * Perform a minimum ammount of operations, in order to transform the decorations @@ -874,14 +874,14 @@ export interface ITextModel { * @param id The decoration id. * @return The decoration options or null if the decoration was not found. */ - getDecorationOptions(id: string): IModelDecorationOptions; + getDecorationOptions(id: string): IModelDecorationOptions | null; /** * Get the range associated with a decoration. * @param id The decoration id. * @return The decoration range or null if the decoration was not found. */ - getDecorationRange(id: string): Range; + getDecorationRange(id: string): Range | null; /** * Gets all the decorations for the line `lineNumber` as an array. @@ -929,12 +929,12 @@ export interface ITextModel { /** * @internal */ - _getTrackedRange(id: string): Range; + _getTrackedRange(id: string): Range | null; /** * @internal */ - _setTrackedRange(id: string, newRange: Range, newStickiness: TrackedRangeStickiness): string; + _setTrackedRange(id: string, newRange: Range, newStickiness: TrackedRangeStickiness): string | null; /** * Normalize a string containing whitespace according to indentation rules (converts to spaces or to tabs). @@ -971,7 +971,7 @@ export interface ITextModel { * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. * @return The cursor state returned by the `cursorStateComputer`. */ - pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[]; + pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null; /** * Change the end of line sequence. This is the preferred way of @@ -998,7 +998,7 @@ export interface ITextModel { * The inverse edit operations will be pushed on the redo stack. * @internal */ - undo(): Selection[]; + undo(): Selection[] | null; /** * Is there anything in the undo stack? @@ -1011,7 +1011,7 @@ export interface ITextModel { * The inverse edit operations will be pushed on the undo stack. * @internal */ - redo(): Selection[]; + redo(): Selection[] | null; /** * Is there anything in the redo stack? @@ -1143,7 +1143,7 @@ export interface ITextBuffer { setEOL(newEOL: '\r\n' | '\n'): void; applyEdits(rawOperations: IIdentifiedSingleEditOperation[], recordTrimAutoWhitespace: boolean): ApplyEditsResult; - findMatchesLineByLine?(searchRange: Range, searchData: SearchData, captureMatches: boolean, limitResultCount: number): FindMatch[]; + findMatchesLineByLine(searchRange: Range, searchData: SearchData, captureMatches: boolean, limitResultCount: number): FindMatch[]; } /** @@ -1154,7 +1154,7 @@ export class ApplyEditsResult { constructor( public readonly reverseEdits: IIdentifiedSingleEditOperation[], public readonly changes: IInternalModelContentChange[], - public readonly trimAutoWhitespaceLineNumbers: number[] + public readonly trimAutoWhitespaceLineNumbers: number[] | null ) { } } diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index 73c61e9d6d0..eda58f8d7b6 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -14,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; @@ -25,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[]; @@ -68,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; @@ -96,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[]; @@ -145,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) { @@ -168,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) { @@ -183,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); @@ -213,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); diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/model/intervalTree.ts index ec867864618..9a2723697c0 100644 --- a/src/vs/editor/common/model/intervalTree.ts +++ b/src/vs/editor/common/model/intervalTree.ts @@ -164,9 +164,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; @@ -177,7 +177,7 @@ 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); @@ -186,7 +186,7 @@ export class IntervalNode implements IModelDecoration { this.cachedVersionId = 0; this.cachedAbsoluteStart = start; this.cachedAbsoluteEnd = end; - this.range = null; + this.range = null!; setNodeIsVisited(this, false); } @@ -216,7 +216,7 @@ export class IntervalNode implements IModelDecoration { 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; @@ -224,13 +224,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; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts index 12455b6e408..99ada260453 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -177,7 +177,7 @@ class PieceTreeSnapshot implements ITextSnapshot { } } - read(): string { + read(): string | null { if (this._pieces.length === 0) { if (this._index === 0) { this._index++; @@ -198,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) { @@ -217,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(); } @@ -236,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); } } @@ -268,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); @@ -286,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) { @@ -306,7 +314,7 @@ export class PieceTreeBase { } this._searchCache = new PieceTreeSearchCache(1); - this._lastVisitedLine = { lineNumber: 0, value: null }; + this._lastVisitedLine = { lineNumber: 0, value: '' }; this.computeBufferMetadata(); } @@ -569,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 }; @@ -693,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 { @@ -714,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); @@ -738,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, @@ -811,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; @@ -854,7 +862,7 @@ export class PieceTreeBase { return; } - let nodesToDel = []; + let nodesToDel: TreeNode[] = []; let startSplitPosInBuffer = this.positionInBuffer(startNode, startPosition.remainder); this.deleteNodeTail(startNode, startSplitPosInBuffer); @@ -884,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. @@ -935,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; @@ -948,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; @@ -1028,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; @@ -1387,7 +1397,7 @@ export class PieceTreeBase { } } - return null; + return null!; } nodeAt2(lineNumber: number, column: number): NodePosition { @@ -1454,7 +1464,7 @@ export class PieceTreeBase { x = x.next(); } - return null; + return null!; } nodeCharCodeAt(node: TreeNode, offset: number): number { @@ -1544,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; @@ -1676,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; @@ -1688,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; } @@ -1708,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; @@ -1716,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; } diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index b60f1d90e3a..ee21a3311c9 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -13,11 +13,11 @@ import { SearchData } from 'vs/editor/common/model/textModelSearch'; 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; } @@ -192,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 }; } @@ -270,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); @@ -415,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); @@ -450,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]; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts index 69efbdb8802..ea9a7d043a5 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts @@ -21,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 { @@ -71,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!; } } @@ -82,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; diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 8533d5951c6..fb1973a199e 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -6,7 +6,7 @@ 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 { LanguageIdentifier, TokenizationRegistry, LanguageId, IState } 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'; @@ -78,7 +78,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); } @@ -121,7 +121,7 @@ class TextModelSnapshot implements ITextSnapshot { this._eos = false; } - public read(): string { + public read(): string | null { if (this._eos) { return null; } @@ -153,6 +153,24 @@ class TextModelSnapshot implements ITextSnapshot { } } +const invalidChangeAccessor: model.IModelDecorationsChangeAccessor = { + addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => { + throw new Error(`Invalid change accessor`); + }, + changeDecoration: (id: string, newRange: IRange): void => { + throw new Error(`Invalid change accessor`); + }, + changeDecorationOptions: (id: string, options: model.IModelDecorationOptions) => { + throw new Error(`Invalid change accessor`); + }, + removeDecoration: (id: string): void => { + throw new Error(`Invalid change accessor`); + }, + deltaDecorations: (oldDecorations: string[], newDecorations: model.IModelDeltaDecoration[]): string[] => { + 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 @@ -169,7 +187,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); } @@ -245,7 +263,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 @@ -267,7 +285,7 @@ export class TextModel extends Disposable implements model.ITextModel { /*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 @@ -346,16 +364,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; } @@ -1109,13 +1122,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); @@ -1137,7 +1154,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); @@ -1166,7 +1183,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(); @@ -1177,7 +1194,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). @@ -1397,7 +1414,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; @@ -1411,7 +1428,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(); @@ -1426,7 +1443,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; @@ -1440,7 +1457,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(); @@ -1459,7 +1476,7 @@ export class TextModel extends Disposable implements model.ITextModel { //#region Decorations - public changeDecorations(callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T, ownerId: number = 0): T { + public changeDecorations(callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T, ownerId: number = 0): T | null { this._assertNotDisposed(); try { @@ -1470,7 +1487,7 @@ export class TextModel extends Disposable implements model.ITextModel { } } - private _changeDecorations(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T { + private _changeDecorations(ownerId: number, callback: (changeAccessor: model.IModelDecorationsChangeAccessor) => T): T | null { let changeAccessor: model.IModelDecorationsChangeAccessor = { addDecoration: (range: IRange, options: model.IModelDecorationOptions): string => { this._onDidChangeDecorations.fire(); @@ -1497,17 +1514,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 = invalidChangeAccessor.addDecoration; + changeAccessor.changeDecoration = invalidChangeAccessor.changeDecoration; + changeAccessor.changeDecorationOptions = invalidChangeAccessor.changeDecorationOptions; + changeAccessor.removeDecoration = invalidChangeAccessor.removeDecoration; + changeAccessor.deltaDecorations = invalidChangeAccessor.deltaDecorations; return result; } @@ -1530,11 +1548,11 @@ 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, newRange: Range, newStickiness: model.TrackedRangeStickiness): string | null { const node = (id ? this._decorations[id] : null); if (!node) { @@ -1577,7 +1595,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; @@ -1585,7 +1603,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; @@ -1702,7 +1720,7 @@ export class TextModel extends Disposable implements model.ITextModel { let result = new Array(newDecorationsLen); while (oldDecorationIndex < oldDecorationsLen || newDecorationIndex < newDecorationsLen) { - let node: IntervalNode = null; + let node: IntervalNode | null = null; if (oldDecorationIndex < oldDecorationsLen) { // (1) get ourselves an old node @@ -1771,9 +1789,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); @@ -1986,7 +2004,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); @@ -2027,13 +2045,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); } @@ -2057,7 +2075,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); @@ -2078,11 +2096,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); @@ -2102,7 +2120,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) { @@ -2161,7 +2179,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; } @@ -2181,7 +2199,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; @@ -2242,7 +2260,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; @@ -2302,11 +2320,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(); @@ -2346,11 +2364,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(); @@ -2391,7 +2409,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; } @@ -2454,7 +2472,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; @@ -2624,7 +2642,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(endLineNumber - startLineNumber + 1); @@ -2797,7 +2815,7 @@ export class ModelDecorationOverviewRulerOptions implements model.IModelDecorati readonly color: string | ThemeColor; readonly darkColor: string | ThemeColor; readonly position: model.OverviewRulerLane; - private _resolvedColor: string; + private _resolvedColor: string | null; constructor(options: model.IModelDecorationOverviewRulerOptions) { this.color = options.color || strings.empty; @@ -2847,20 +2865,20 @@ 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 collapseOnReplaceEdit: boolean; - readonly overviewRuler: ModelDecorationOverviewRulerOptions; - readonly glyphMarginClassName: string; - readonly linesDecorationsClassName: string; - readonly marginClassName: string; - readonly inlineClassName: string; + 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; @@ -2946,7 +2964,7 @@ export class DidChangeContentEmitter extends Disposable { public readonly slowEvent: Event = this._slowEmitter.event; private _deferredCnt: number; - private _deferredEvent: InternalModelContentChangeEvent; + private _deferredEvent: InternalModelContentChangeEvent | null; constructor() { super(); diff --git a/src/vs/editor/common/model/textModelSearch.ts b/src/vs/editor/common/model/textModelSearch.ts index c75b008b120..d523c8de262 100644 --- a/src/vs/editor/common/model/textModelSearch.ts +++ b/src/vs/editor/common/model/textModelSearch.ts @@ -26,7 +26,7 @@ export class SearchParams { this.wordSeparators = wordSeparators; } - public parseSearchRequest(): SearchData { + public parseSearchRequest(): SearchData | null { if (this.searchString === '') { return null; } @@ -39,7 +39,7 @@ export class SearchParams { 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, @@ -102,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; @@ -193,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 { @@ -204,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 */; @@ -228,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); @@ -289,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 { @@ -304,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; @@ -318,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(); @@ -345,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; @@ -368,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), @@ -382,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; @@ -396,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]; @@ -411,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; @@ -434,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); @@ -509,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; @@ -527,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 diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/model/textModelTokens.ts index 45f9c85fd2c..8144fead98c 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/model/textModelTokens.ts @@ -27,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; @@ -112,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; } @@ -168,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) { @@ -200,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; } @@ -245,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; } @@ -323,7 +323,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); @@ -380,12 +380,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) { @@ -406,26 +408,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) { @@ -483,7 +488,7 @@ export class ModelTokensChangedEventBuilder { } } - public build(): IModelTokensChangedEvent { + public build(): IModelTokensChangedEvent | null { if (this._ranges.length === 0) { return null; } diff --git a/src/vs/editor/common/model/wordHelper.ts b/src/vs/editor/common/model/wordHelper.ts index f238d3d1c73..46d2a719310 100644 --- a/src/vs/editor/common/model/wordHelper.ts +++ b/src/vs/editor/common/model/wordHelper.ts @@ -29,7 +29,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)) { @@ -52,7 +52,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; @@ -63,12 +63,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 }; } @@ -78,7 +79,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 @@ -86,10 +87,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; @@ -97,7 +98,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 }; } @@ -106,7 +107,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 diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 4a26b10086a..2e1428422c8 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -1366,16 +1366,16 @@ export interface ITokenizationRegistry { * Get the promise of a tokenization support for a language. * `null` is returned if no support is available and no promise for the support has been registered yet. */ - getPromise(language: string): Thenable; + getPromise(language: string): Thenable | null; /** * Set the new color map that all tokens will use in their ColorId binary encoded bits for foreground and background. */ setColorMap(colorMap: Color[]): void; - getColorMap(): Color[]; + getColorMap(): Color[] | null; - getDefaultBackground(): Color; + getDefaultBackground(): Color | null; } /** diff --git a/src/vs/editor/common/modes/languageConfigurationRegistry.ts b/src/vs/editor/common/modes/languageConfigurationRegistry.ts index 08693dddfe1..eafbd9290e6 100644 --- a/src/vs/editor/common/modes/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/modes/languageConfigurationRegistry.ts @@ -37,8 +37,8 @@ export interface IVirtualModel { } export interface IIndentConverter { - shiftIndent?(indentation: string): string; - unshiftIndent?(indentation: string): string; + shiftIndent(indentation: string): string; + unshiftIndent(indentation: string): string; normalizeIndentation?(indentation: string): string; } @@ -46,15 +46,15 @@ export class RichEditSupport { private readonly _conf: LanguageConfiguration; private readonly _languageIdentifier: LanguageIdentifier; - private _brackets: RichEditBrackets; - private _electricCharacter: BracketElectricCharacterSupport; + private _brackets: RichEditBrackets | null; + private _electricCharacter: BracketElectricCharacterSupport | null; - public readonly comments: ICommentsConfiguration; + public readonly comments: ICommentsConfiguration | null; public readonly characterPair: CharacterPairSupport; public readonly wordDefinition: RegExp; - public readonly onEnter: OnEnterSupport; + public readonly onEnter: OnEnterSupport | null; public readonly indentRulesSupport: IndentRulesSupport; - public readonly indentationRules: IndentationRule; + public readonly indentationRules: IndentationRule | undefined; public readonly foldingRules: FoldingRules; constructor(languageIdentifier: LanguageIdentifier, previous: RichEditSupport, rawConf: LanguageConfiguration) { @@ -63,7 +63,7 @@ export class RichEditSupport { this._brackets = null; this._electricCharacter = null; - let prev: LanguageConfiguration = null; + let prev: LanguageConfiguration | null = null; if (previous) { prev = previous._conf; } @@ -86,14 +86,14 @@ export class RichEditSupport { this.foldingRules = this._conf.folding || {}; } - public get brackets(): RichEditBrackets { + public get brackets(): RichEditBrackets | null { if (!this._brackets && this._conf.brackets) { this._brackets = new RichEditBrackets(this._languageIdentifier, this._conf.brackets); } return this._brackets; } - public get electricCharacter(): BracketElectricCharacterSupport { + public get electricCharacter(): BracketElectricCharacterSupport | null { if (!this._electricCharacter) { let autoClosingPairs: IAutoClosingPairConditional[] = []; if (this._conf.autoClosingPairs) { @@ -109,7 +109,7 @@ export class RichEditSupport { return this._electricCharacter; } - private static _mergeConf(prev: LanguageConfiguration, current: LanguageConfiguration): LanguageConfiguration { + private static _mergeConf(prev: LanguageConfiguration | null, current: LanguageConfiguration): LanguageConfiguration { return { comments: (prev ? current.comments || prev.comments : current.comments), brackets: (prev ? current.brackets || prev.brackets : current.brackets), @@ -124,7 +124,7 @@ export class RichEditSupport { }; } - private static _handleOnEnter(conf: LanguageConfiguration): OnEnterSupport { + private static _handleOnEnter(conf: LanguageConfiguration): OnEnterSupport | null { // on enter let onEnter: IOnEnterSupportOptions = {}; let empty = true; @@ -147,7 +147,7 @@ export class RichEditSupport { return null; } - private static _handleComments(conf: LanguageConfiguration): ICommentsConfiguration { + private static _handleComments(conf: LanguageConfiguration): ICommentsConfiguration | null { let commentRule = conf.comments; if (!commentRule) { return null; @@ -213,7 +213,7 @@ export class LanguageConfigurationRegistryImpl { // begin electricCharacter - private _getElectricCharacterSupport(languageId: LanguageId): BracketElectricCharacterSupport { + private _getElectricCharacterSupport(languageId: LanguageId): BracketElectricCharacterSupport | null { let value = this._getRichEditSupport(languageId); if (!value) { return null; @@ -232,7 +232,7 @@ export class LanguageConfigurationRegistryImpl { /** * Should return opening bracket type to match indentation with */ - public onElectricCharacter(character: string, context: LineTokens, column: number): IElectricAction { + public onElectricCharacter(character: string, context: LineTokens, column: number): IElectricAction | null { let scopedLineTokens = createScopedLineTokens(context, column - 1); let electricCharacterSupport = this._getElectricCharacterSupport(scopedLineTokens.languageId); if (!electricCharacterSupport) { @@ -243,7 +243,7 @@ export class LanguageConfigurationRegistryImpl { // end electricCharacter - public getComments(languageId: LanguageId): ICommentsConfiguration { + public getComments(languageId: LanguageId): ICommentsConfiguration | null { let value = this._getRichEditSupport(languageId); if (!value) { return null; @@ -253,7 +253,7 @@ export class LanguageConfigurationRegistryImpl { // begin characterPair - private _getCharacterPairSupport(languageId: LanguageId): CharacterPairSupport { + private _getCharacterPairSupport(languageId: LanguageId): CharacterPairSupport | null { let value = this._getRichEditSupport(languageId); if (!value) { return null; @@ -314,7 +314,7 @@ export class LanguageConfigurationRegistryImpl { // begin Indent Rules - public getIndentRulesSupport(languageId: LanguageId): IndentRulesSupport { + public getIndentRulesSupport(languageId: LanguageId): IndentRulesSupport | null { let value = this._getRichEditSupport(languageId); if (!value) { return null; @@ -364,7 +364,7 @@ export class LanguageConfigurationRegistryImpl { * * This function only return the inherited indent based on above lines, it doesn't check whether current line should decrease or not. */ - public getInheritIndentForLine(model: IVirtualModel, lineNumber: number, honorIntentialIndent: boolean = true): { indentation: string, action: IndentAction, line?: number } { + public getInheritIndentForLine(model: IVirtualModel, lineNumber: number, honorIntentialIndent: boolean = true): { indentation: string; action: IndentAction | null; line?: number; } | null { let indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id); if (!indentRulesSupport) { return null; @@ -485,7 +485,7 @@ export class LanguageConfigurationRegistryImpl { } } - public getGoodIndentForLine(virtualModel: IVirtualModel, languageId: LanguageId, lineNumber: number, indentConverter: IIndentConverter): string { + public getGoodIndentForLine(virtualModel: IVirtualModel, languageId: LanguageId, lineNumber: number, indentConverter: IIndentConverter): string | null { let indentRulesSupport = this.getIndentRulesSupport(languageId); if (!indentRulesSupport) { return null; @@ -498,9 +498,11 @@ export class LanguageConfigurationRegistryImpl { let inheritLine = indent.line; if (inheritLine !== undefined) { let onEnterSupport = this._getOnEnterSupport(languageId); - let enterResult: EnterAction = null; + let enterResult: EnterAction | null = null; try { - enterResult = onEnterSupport.onEnter('', virtualModel.getLineContent(inheritLine), ''); + if (onEnterSupport) { + enterResult = onEnterSupport.onEnter('', virtualModel.getLineContent(inheritLine), ''); + } } catch (e) { onUnexpectedError(e); } @@ -550,7 +552,7 @@ export class LanguageConfigurationRegistryImpl { return null; } - public getIndentForEnter(model: ITextModel, range: Range, indentConverter: IIndentConverter, autoIndent: boolean): { beforeEnter: string, afterEnter: string } { + public getIndentForEnter(model: ITextModel, range: Range, indentConverter: IIndentConverter, autoIndent: boolean): { beforeEnter: string, afterEnter: string } | null { model.forceTokenization(range.startLineNumber); let lineTokens = model.getLineTokens(range.startLineNumber); @@ -648,7 +650,7 @@ export class LanguageConfigurationRegistryImpl { * We should always allow intentional indentation. It means, if users change the indentation of `lineNumber` and the content of * this line doesn't match decreaseIndentPattern, we should not adjust the indentation. */ - public getIndentActionForType(model: ITextModel, range: Range, ch: string, indentConverter: IIndentConverter): string { + public getIndentActionForType(model: ITextModel, range: Range, ch: string, indentConverter: IIndentConverter): string | null { let scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn); let indentRulesSupport = this.getIndentRulesSupport(scopedLineTokens.languageId); if (!indentRulesSupport) { @@ -689,7 +691,7 @@ export class LanguageConfigurationRegistryImpl { return null; } - public getIndentMetadata(model: ITextModel, lineNumber: number): number { + public getIndentMetadata(model: ITextModel, lineNumber: number): number | null { let indentRulesSupport = this.getIndentRulesSupport(model.getLanguageIdentifier().id); if (!indentRulesSupport) { return null; @@ -706,7 +708,7 @@ export class LanguageConfigurationRegistryImpl { // begin onEnter - private _getOnEnterSupport(languageId: LanguageId): OnEnterSupport { + private _getOnEnterSupport(languageId: LanguageId): OnEnterSupport | null { let value = this._getRichEditSupport(languageId); if (!value) { return null; @@ -714,13 +716,13 @@ export class LanguageConfigurationRegistryImpl { return value.onEnter || null; } - public getRawEnterActionAtPosition(model: ITextModel, lineNumber: number, column: number): EnterAction { + public getRawEnterActionAtPosition(model: ITextModel, lineNumber: number, column: number): EnterAction | null { let r = this.getEnterAction(model, new Range(lineNumber, column, lineNumber, column)); return r ? r.enterAction : null; } - public getEnterAction(model: ITextModel, range: Range): { enterAction: EnterAction; indentation: string; } { + public getEnterAction(model: ITextModel, range: Range): { enterAction: EnterAction; indentation: string; } | null { let indentation = this.getIndentationAtPosition(model, range.startLineNumber, range.startColumn); let scopedLineTokens = this.getScopedLineTokens(model, range.startLineNumber, range.startColumn); @@ -753,7 +755,7 @@ export class LanguageConfigurationRegistryImpl { } } - let enterResult: EnterAction = null; + let enterResult: EnterAction | null = null; try { enterResult = onEnterSupport.onEnter(oneLineAboveText, beforeEnterText, afterEnterText); } catch (e) { @@ -799,14 +801,14 @@ export class LanguageConfigurationRegistryImpl { private getScopedLineTokens(model: ITextModel, lineNumber: number, columnNumber?: number) { model.forceTokenization(lineNumber); let lineTokens = model.getLineTokens(lineNumber); - let column = isNaN(columnNumber) ? model.getLineMaxColumn(lineNumber) - 1 : columnNumber - 1; + let column = (typeof columnNumber === 'undefined' ? model.getLineMaxColumn(lineNumber) - 1 : columnNumber - 1); let scopedLineTokens = createScopedLineTokens(lineTokens, column); return scopedLineTokens; } // end onEnter - public getBracketsSupport(languageId: LanguageId): RichEditBrackets { + public getBracketsSupport(languageId: LanguageId): RichEditBrackets | null { let value = this._getRichEditSupport(languageId); if (!value) { return null; diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index af2d6e40754..eef1757486e 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -22,7 +22,7 @@ function isExclusive(selector: LanguageSelector): boolean { } else if (Array.isArray(selector)) { return selector.every(isExclusive); } else { - return selector.exclusive; + return !!selector.exclusive; } } @@ -41,7 +41,7 @@ export default class LanguageFeatureRegistry { register(selector: LanguageSelector, provider: T): IDisposable { - let entry: Entry = { + let entry: Entry | undefined = { selector, provider, _score: -1, @@ -127,7 +127,7 @@ export default class LanguageFeatureRegistry { } } - private _lastCandidate: { uri: string; language: string; }; + private _lastCandidate: { uri: string; language: string; } | undefined; private _updateScores(model: ITextModel): void { diff --git a/src/vs/editor/common/modes/nullMode.ts b/src/vs/editor/common/modes/nullMode.ts index 1558e3b2a35..b0d8fe393b2 100644 --- a/src/vs/editor/common/modes/nullMode.ts +++ b/src/vs/editor/common/modes/nullMode.ts @@ -27,7 +27,7 @@ export function nullTokenize(modeId: string, buffer: string, state: IState, delt return new TokenizationResult([new Token(deltaOffset, '', modeId)], state); } -export function nullTokenize2(languageId: LanguageId, buffer: string, state: IState, deltaOffset: number): TokenizationResult2 { +export function nullTokenize2(languageId: LanguageId, buffer: string, state: IState | null, deltaOffset: number): TokenizationResult2 { let tokens = new Uint32Array(2); tokens[0] = deltaOffset; tokens[1] = ( @@ -38,5 +38,5 @@ export function nullTokenize2(languageId: LanguageId, buffer: string, state: ISt | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) ) >>> 0; - return new TokenizationResult2(tokens, state); + return new TokenizationResult2(tokens, state === null ? NULL_STATE : state); } diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts index def16eb8aa8..33273ab64be 100644 --- a/src/vs/editor/common/modes/supports/electricCharacter.ts +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -24,10 +24,10 @@ export interface IElectricAction { export class BracketElectricCharacterSupport { - private readonly _richEditBrackets: RichEditBrackets; + private readonly _richEditBrackets: RichEditBrackets | null; private readonly _complexAutoClosePairs: StandardAutoClosingPairConditional[]; - constructor(richEditBrackets: RichEditBrackets, autoClosePairs: IAutoClosingPairConditional[], contribution: IBracketElectricCharacterContribution) { + constructor(richEditBrackets: RichEditBrackets | null, autoClosePairs: IAutoClosingPairConditional[], contribution: IBracketElectricCharacterContribution | undefined) { contribution = contribution || {}; this._richEditBrackets = richEditBrackets; this._complexAutoClosePairs = autoClosePairs.filter(pair => pair.open.length > 1 && !!pair.close).map(el => new StandardAutoClosingPairConditional(el)); @@ -61,12 +61,12 @@ export class BracketElectricCharacterSupport { return result; } - public onElectricCharacter(character: string, context: ScopedLineTokens, column: number): IElectricAction { + public onElectricCharacter(character: string, context: ScopedLineTokens, column: number): IElectricAction | null { return (this._onElectricAutoClose(character, context, column) || this._onElectricAutoIndent(character, context, column)); } - private _onElectricAutoIndent(character: string, context: ScopedLineTokens, column: number): IElectricAction { + private _onElectricAutoIndent(character: string, context: ScopedLineTokens, column: number): IElectricAction | null { if (!this._richEditBrackets || this._richEditBrackets.brackets.length === 0) { return null; @@ -104,7 +104,7 @@ export class BracketElectricCharacterSupport { }; } - private _onElectricAutoClose(character: string, context: ScopedLineTokens, column: number): IElectricAction { + private _onElectricAutoClose(character: string, context: ScopedLineTokens, column: number): IElectricAction | null { if (!this._complexAutoClosePairs.length) { return null; } diff --git a/src/vs/editor/common/modes/supports/onEnter.ts b/src/vs/editor/common/modes/supports/onEnter.ts index 4615dc4903f..6822b79541d 100644 --- a/src/vs/editor/common/modes/supports/onEnter.ts +++ b/src/vs/editor/common/modes/supports/onEnter.ts @@ -32,18 +32,23 @@ export class OnEnterSupport { ['[', ']'] ]; - this._brackets = opts.brackets.map((bracket) => { - return { - open: bracket[0], - openRegExp: OnEnterSupport._createOpenBracketRegExp(bracket[0]), - close: bracket[1], - closeRegExp: OnEnterSupport._createCloseBracketRegExp(bracket[1]), - }; + this._brackets = []; + opts.brackets.forEach((bracket) => { + const openRegExp = OnEnterSupport._createOpenBracketRegExp(bracket[0]); + const closeRegExp = OnEnterSupport._createCloseBracketRegExp(bracket[1]); + if (openRegExp && closeRegExp) { + this._brackets.push({ + open: bracket[0], + openRegExp: openRegExp, + close: bracket[1], + closeRegExp: closeRegExp, + }); + } }); this._regExpRules = opts.regExpRules || []; } - public onEnter(oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction { + public onEnter(oneLineAboveText: string, beforeEnterText: string, afterEnterText: string): EnterAction | null { // (1): `regExpRules` for (let i = 0, len = this._regExpRules.length; i < len; i++) { let rule = this._regExpRules[i]; @@ -90,7 +95,7 @@ export class OnEnterSupport { return null; } - private static _createOpenBracketRegExp(bracket: string): RegExp { + private static _createOpenBracketRegExp(bracket: string): RegExp | null { let str = strings.escapeRegExpCharacters(bracket); if (!/\B/.test(str.charAt(0))) { str = '\\b' + str; @@ -99,7 +104,7 @@ export class OnEnterSupport { return OnEnterSupport._safeRegExp(str); } - private static _createCloseBracketRegExp(bracket: string): RegExp { + private static _createCloseBracketRegExp(bracket: string): RegExp | null { let str = strings.escapeRegExpCharacters(bracket); if (!/\B/.test(str.charAt(str.length - 1))) { str = str + '\\b'; @@ -108,7 +113,7 @@ export class OnEnterSupport { return OnEnterSupport._safeRegExp(str); } - private static _safeRegExp(def: string): RegExp { + private static _safeRegExp(def: string): RegExp | null { try { return new RegExp(def); } catch (err) { diff --git a/src/vs/editor/common/modes/supports/richEditBrackets.ts b/src/vs/editor/common/modes/supports/richEditBrackets.ts index d11badc422c..5a389e8a1b0 100644 --- a/src/vs/editor/common/modes/supports/richEditBrackets.ts +++ b/src/vs/editor/common/modes/supports/richEditBrackets.ts @@ -141,34 +141,34 @@ let toReversedString = (function () { return reversedStr; } - let lastInput: string = null; - let lastOutput: string = null; + let lastInput: string | null = null; + let lastOutput: string | null = null; return function toReversedString(str: string): string { if (lastInput !== str) { lastInput = str; lastOutput = reverse(lastInput); } - return lastOutput; + return lastOutput!; }; })(); export class BracketsUtils { - private static _findPrevBracketInText(reversedBracketRegex: RegExp, lineNumber: number, reversedText: string, offset: number): Range { + private static _findPrevBracketInText(reversedBracketRegex: RegExp, lineNumber: number, reversedText: string, offset: number): Range | null { let m = reversedText.match(reversedBracketRegex); if (!m) { return null; } - let matchOffset = reversedText.length - m.index; + let matchOffset = reversedText.length - (m.index || 0); let matchLength = m[0].length; let absoluteMatchOffset = offset + matchOffset; return new Range(lineNumber, absoluteMatchOffset - matchLength + 1, lineNumber, absoluteMatchOffset + 1); } - public static findPrevBracketInToken(reversedBracketRegex: RegExp, lineNumber: number, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range { + public static findPrevBracketInToken(reversedBracketRegex: RegExp, lineNumber: number, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range | null { // Because JS does not support backwards regex search, we search forwards in a reversed string with a reversed regex ;) let reversedLineText = toReversedString(lineText); let reversedTokenText = reversedLineText.substring(lineText.length - currentTokenEnd, lineText.length - currentTokenStart); @@ -176,14 +176,14 @@ export class BracketsUtils { return this._findPrevBracketInText(reversedBracketRegex, lineNumber, reversedTokenText, currentTokenStart); } - public static findNextBracketInText(bracketRegex: RegExp, lineNumber: number, text: string, offset: number): Range { + public static findNextBracketInText(bracketRegex: RegExp, lineNumber: number, text: string, offset: number): Range | null { let m = text.match(bracketRegex); if (!m) { return null; } - let matchOffset = m.index; + let matchOffset = m.index || 0; let matchLength = m[0].length; if (matchLength === 0) { return null; @@ -193,7 +193,7 @@ export class BracketsUtils { return new Range(lineNumber, absoluteMatchOffset + 1, lineNumber, absoluteMatchOffset + 1 + matchLength); } - public static findNextBracketInToken(bracketRegex: RegExp, lineNumber: number, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range { + public static findNextBracketInToken(bracketRegex: RegExp, lineNumber: number, lineText: string, currentTokenStart: number, currentTokenEnd: number): Range | null { let currentTokenText = lineText.substring(currentTokenStart, currentTokenEnd); return this.findNextBracketInText(bracketRegex, lineNumber, currentTokenText, currentTokenStart); diff --git a/src/vs/editor/common/modes/tokenizationRegistry.ts b/src/vs/editor/common/modes/tokenizationRegistry.ts index a3f815fdd24..502d76a349c 100644 --- a/src/vs/editor/common/modes/tokenizationRegistry.ts +++ b/src/vs/editor/common/modes/tokenizationRegistry.ts @@ -16,7 +16,7 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry { private readonly _onDidChange: Emitter = new Emitter(); public readonly onDidChange: Event = this._onDidChange.event; - private _colorMap: Color[]; + private _colorMap: Color[] | null; constructor() { this._map = Object.create(null); @@ -51,7 +51,7 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry { return promise; } - public getPromise(language: string): Thenable { + public getPromise(language: string): Thenable | null { const support = this.get(language); if (support) { return Promise.resolve(support); @@ -75,11 +75,14 @@ export class TokenizationRegistryImpl implements ITokenizationRegistry { }); } - public getColorMap(): Color[] { + public getColorMap(): Color[] | null { return this._colorMap; } - public getDefaultBackground(): Color { - return this._colorMap[ColorId.DefaultBackground]; + public getDefaultBackground(): Color | null { + if (this._colorMap && this._colorMap.length > ColorId.DefaultBackground) { + return this._colorMap[ColorId.DefaultBackground]; + } + return null; } } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 293a553d05a..38c82ebded3 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -1222,15 +1222,15 @@ declare namespace monaco.editor { /** * CSS class name describing the decoration. */ - className?: string; + className?: string | null; /** * Message to be rendered when hovering over the glyph margin decoration. */ - glyphMarginHoverMessage?: IMarkdownString | IMarkdownString[]; + glyphMarginHoverMessage?: IMarkdownString | IMarkdownString[] | null; /** * Array of MarkdownString to render as the decoration message. */ - hoverMessage?: IMarkdownString | IMarkdownString[]; + hoverMessage?: IMarkdownString | IMarkdownString[] | null; /** * Should the decoration expand to encompass a whole line. */ @@ -1243,25 +1243,25 @@ declare namespace monaco.editor { /** * If set, render this decoration in the overview ruler. */ - overviewRuler?: IModelDecorationOverviewRulerOptions; + overviewRuler?: IModelDecorationOverviewRulerOptions | null; /** * If set, the decoration will be rendered in the glyph margin with this CSS class name. */ - glyphMarginClassName?: string; + glyphMarginClassName?: string | null; /** * If set, the decoration will be rendered in the lines decorations with this CSS class name. */ - linesDecorationsClassName?: string; + linesDecorationsClassName?: string | null; /** * If set, the decoration will be rendered in the margin (covering its full width) with this CSS class name. */ - marginClassName?: string; + marginClassName?: string | null; /** * If set, the decoration will be rendered inline with the text with this CSS class name. * Please use this only for CSS rules that must impact the text. For example, use `className` * to have a background color decoration. */ - inlineClassName?: string; + inlineClassName?: string | null; /** * If there is an `inlineClassName` which affects letter spacing. */ @@ -1269,11 +1269,11 @@ declare namespace monaco.editor { /** * If set, the decoration will be rendered before the text with this CSS class name. */ - beforeContentClassName?: string; + beforeContentClassName?: string | null; /** * If set, the decoration will be rendered after the text with this CSS class name. */ - afterContentClassName?: string; + afterContentClassName?: string | null; } /** @@ -1421,7 +1421,7 @@ declare namespace monaco.editor { /** * The text to replace with. This can be null to emulate a simple delete. */ - text: string; + text: string | null; /** * This indicates that this operation has "insert" semantics. * i.e. forceMoveMarkers = true => if `range` is collapsed, all markers at the position will be moved. @@ -1456,7 +1456,7 @@ declare namespace monaco.editor { export class FindMatch { _findMatchBrand: void; readonly range: Range; - readonly matches: string[]; + readonly matches: string[] | null; } /** @@ -1642,7 +1642,7 @@ declare namespace monaco.editor { * @param captureMatches The result will contain the captured groups. * @return The range where the next match is. It is null if no next match has been found. */ - findNextMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch; + findNextMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch | null; /** * Search the model for the previous match. Loops to the end of the model if needed. * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. @@ -1653,7 +1653,7 @@ declare namespace monaco.editor { * @param captureMatches The result will contain the captured groups. * @return The range where the previous match is. It is null if no previous match has been found. */ - findPreviousMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch; + findPreviousMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch | null; /** * Get the language associated with this model. */ @@ -1663,7 +1663,7 @@ declare namespace monaco.editor { * @param position The position to look for a word. * @return The word under or besides `position`. Might be null. */ - getWordAtPosition(position: IPosition): IWordAtPosition; + getWordAtPosition(position: IPosition): IWordAtPosition | null; /** * Get the word under or besides `position` trimmed to `position`.column * @param position The position to look for a word. @@ -1686,13 +1686,13 @@ declare namespace monaco.editor { * @param id The decoration id. * @return The decoration options or null if the decoration was not found. */ - getDecorationOptions(id: string): IModelDecorationOptions; + getDecorationOptions(id: string): IModelDecorationOptions | null; /** * Get the range associated with a decoration. * @param id The decoration id. * @return The decoration range or null if the decoration was not found. */ - getDecorationRange(id: string): Range; + getDecorationRange(id: string): Range | null; /** * Gets all the decorations for the line `lineNumber` as an array. * @param lineNumber The line number @@ -1761,7 +1761,7 @@ declare namespace monaco.editor { * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. * @return The cursor state returned by the `cursorStateComputer`. */ - pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[]; + pushEditOperations(beforeCursorState: Selection[], editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null; /** * Change the end of line sequence. This is the preferred way of * changing the eol sequence. This will land on the undo stack. @@ -3222,7 +3222,7 @@ declare namespace monaco.editor { readonly rulers: number[]; readonly ariaLabel: string; readonly renderLineNumbers: RenderLineNumbersType; - readonly renderCustomLineNumbers: (lineNumber: number) => string; + readonly renderCustomLineNumbers: ((lineNumber: number) => string) | null; readonly selectOnLineNumbers: boolean; readonly glyphMargin: boolean; readonly revealHorizontalRightPadding: number;