diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 2b4be5b293c..82204018f2c 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -12,10 +12,10 @@ import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperat import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; import { TypeOperations, TypeWithAutoClosingCommand } from 'vs/editor/common/controller/cursorTypeOperations'; import { Position } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import { Range, IRange } from 'vs/editor/common/core/range'; import { ISelection, Selection, SelectionDirection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer } from 'vs/editor/common/model'; +import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation } from 'vs/editor/common/model'; import { RawContentChangedType } from 'vs/editor/common/model/textModelEvents'; import * as viewEvents from 'vs/editor/common/view/viewEvents'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; @@ -903,8 +903,8 @@ class CommandExecutor { if (commandsData.hadTrackedEditOperation && filteredOperations.length > 0) { filteredOperations[0]._isTracked = true; } - let selectionsAfter = ctx.model.pushEditOperations(ctx.selectionsBefore, filteredOperations, (inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] => { - let groupedInverseEditOperations: IIdentifiedSingleEditOperation[][] = []; + let selectionsAfter = ctx.model.pushEditOperations(ctx.selectionsBefore, filteredOperations, (inverseEditOperations: IValidEditOperation[]): Selection[] => { + let groupedInverseEditOperations: IValidEditOperation[][] = []; for (let i = 0; i < ctx.selectionsBefore.length; i++) { groupedInverseEditOperations[i] = []; } @@ -915,7 +915,7 @@ class CommandExecutor { } groupedInverseEditOperations[op.identifier.major].push(op); } - const minorBasedSorter = (a: IIdentifiedSingleEditOperation, b: IIdentifiedSingleEditOperation) => { + const minorBasedSorter = (a: IValidEditOperation, b: IValidEditOperation) => { return a.identifier!.minor - b.identifier!.minor; }; let cursorSelections: Selection[] = []; @@ -1000,8 +1000,8 @@ class CommandExecutor { let operations: IIdentifiedSingleEditOperation[] = []; let operationMinor = 0; - const addEditOperation = (selection: Range, text: string | null, forceMoveMarkers: boolean = false) => { - if (selection.isEmpty() && text === '') { + const addEditOperation = (range: IRange, text: string | null, forceMoveMarkers: boolean = false) => { + if (Range.isEmpty(range) && text === '') { // This command wants to add a no-op => no thank you return; } @@ -1010,7 +1010,7 @@ class CommandExecutor { major: majorIdentifier, minor: operationMinor++ }, - range: selection, + range: range, text: text, forceMoveMarkers: forceMoveMarkers, isAutoWhitespaceEdit: command.insertsAutoWhitespace @@ -1018,12 +1018,13 @@ class CommandExecutor { }; let hadTrackedEditOperation = false; - const addTrackedEditOperation = (selection: Range, text: string | null, forceMoveMarkers?: boolean) => { + const addTrackedEditOperation = (selection: IRange, text: string | null, forceMoveMarkers?: boolean) => { hadTrackedEditOperation = true; addEditOperation(selection, text, forceMoveMarkers); }; - const trackSelection = (selection: Selection, trackPreviousOnEmpty?: boolean) => { + const trackSelection = (_selection: ISelection, trackPreviousOnEmpty?: boolean) => { + const selection = Selection.liftSelection(_selection); let stickiness: TrackedRangeStickiness; if (selection.isEmpty()) { if (typeof trackPreviousOnEmpty === 'boolean') { @@ -1093,7 +1094,7 @@ class CommandExecutor { const previousOp = operations[i - 1]; const currentOp = operations[i]; - if (previousOp.range.getStartPosition().isBefore(currentOp.range.getEndPosition())) { + if (Range.getStartPosition(previousOp.range).isBefore(Range.getEndPosition(currentOp.range))) { let loserMajor: number; diff --git a/src/vs/editor/common/core/range.ts b/src/vs/editor/common/core/range.ts index e212e757dce..d684e3b9520 100644 --- a/src/vs/editor/common/core/range.ts +++ b/src/vs/editor/common/core/range.ts @@ -264,14 +264,28 @@ export class Range { * Return the end position (which will be after or equal to the start position) */ public getEndPosition(): Position { - return new Position(this.endLineNumber, this.endColumn); + return Range.getEndPosition(this); + } + + /** + * Return the end position (which will be after or equal to the start position) + */ + public static getEndPosition(range: IRange): Position { + return new Position(range.endLineNumber, range.endColumn); } /** * Return the start position (which will be before or equal to the end position) */ public getStartPosition(): Position { - return new Position(this.startLineNumber, this.startColumn); + return Range.getStartPosition(this); + } + + /** + * Return the start position (which will be before or equal to the end position) + */ + public static getStartPosition(range: IRange): Position { + return new Position(range.startLineNumber, range.startColumn); } /** diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index 426b028ed3d..fe7e0f9eab8 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -10,7 +10,7 @@ import { ConfigurationChangedEvent, IComputedEditorOptions, IEditorOptions } fro import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import { IIdentifiedSingleEditOperation, IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness, IValidEditOperation } from 'vs/editor/common/model'; import { ThemeColor } from 'vs/platform/theme/common/themeService'; /** @@ -22,7 +22,7 @@ export interface IEditOperationBuilder { * @param range The range to replace (delete). May be empty to represent a simple insert. * @param text The text to replace with. May be null to represent a simple delete. */ - addEditOperation(range: Range, text: string | null, forceMoveMarkers?: boolean): void; + addEditOperation(range: IRange, text: string | null, forceMoveMarkers?: boolean): void; /** * Add a new edit operation (a replace operation). @@ -30,7 +30,7 @@ export interface IEditOperationBuilder { * @param range The range to replace (delete). May be empty to represent a simple insert. * @param text The text to replace with. May be null to represent a simple delete. */ - addTrackedEditOperation(range: Range, text: string | null, forceMoveMarkers?: boolean): void; + addTrackedEditOperation(range: IRange, text: string | null, forceMoveMarkers?: boolean): void; /** * Track `selection` when applying edit operations. @@ -51,7 +51,7 @@ export interface ICursorStateComputerData { /** * Get the inverse edit operations of the added edit operations. */ - getInverseEditOperations(): IIdentifiedSingleEditOperation[]; + getInverseEditOperations(): IValidEditOperation[]; /** * Get a previously tracked selection. * @param id The unique identifier returned by `trackSelection`. diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 2a632baf1c8..566aca3d7a1 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -358,6 +358,27 @@ export interface IIdentifiedSingleEditOperation { _isTracked?: boolean; } +export interface IValidEditOperation { + /** + * An identifier associated with this single edit operation. + * @internal + */ + identifier: ISingleEditOperationIdentifier | null; + /** + * The range to replace. This can be empty to emulate a simple insert. + */ + range: Range; + /** + * The text to replace with. This can be null to emulate a simple delete. + */ + 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. + */ + forceMoveMarkers: boolean; +} + /** * A callback that can compute the cursor state after applying a series of edit operations. */ @@ -365,7 +386,7 @@ export interface ICursorStateComputer { /** * A callback that can compute the resulting cursors state after some edit operations have been executed. */ - (inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] | null; + (inverseEditOperations: IValidEditOperation[]): Selection[] | null; } export class TextModelResolvedOptions { @@ -1063,7 +1084,7 @@ export interface ITextModel { * @param operations The edit operations. * @return The inverse edit operations, that, when applied, will bring the model back to the previous state. */ - applyEdits(operations: IIdentifiedSingleEditOperation[]): IIdentifiedSingleEditOperation[]; + applyEdits(operations: IIdentifiedSingleEditOperation[]): IValidEditOperation[]; /** * Change the end of line sequence without recording in the undo stack. @@ -1206,6 +1227,20 @@ export const enum ModelConstants { FIRST_LINE_DETECTION_LENGTH_LIMIT = 1000 } +/** + * @internal + */ +export class ValidAnnotatedEditOperation implements IIdentifiedSingleEditOperation { + constructor( + public readonly identifier: ISingleEditOperationIdentifier | null, + public readonly range: Range, + public readonly text: string | null, + public readonly forceMoveMarkers: boolean, + public readonly isAutoWhitespaceEdit: boolean, + public readonly _isTracked: boolean, + ) { } +} + /** * @internal */ @@ -1234,7 +1269,7 @@ export interface ITextBuffer { getLineLastNonWhitespaceColumn(lineNumber: number): number; setEOL(newEOL: '\r\n' | '\n'): void; - applyEdits(rawOperations: IIdentifiedSingleEditOperation[], recordTrimAutoWhitespace: boolean): ApplyEditsResult; + applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean): ApplyEditsResult; findMatchesLineByLine(searchRange: Range, searchData: SearchData, captureMatches: boolean, limitResultCount: number): FindMatch[]; } @@ -1244,7 +1279,7 @@ export interface ITextBuffer { export class ApplyEditsResult { constructor( - public readonly reverseEdits: IIdentifiedSingleEditOperation[], + public readonly reverseEdits: IValidEditOperation[], public readonly changes: IInternalModelContentChange[], public readonly trimAutoWhitespaceLineNumbers: number[] | null ) { } diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/model/editStack.ts index add6b310ea5..a870a0822d1 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/model/editStack.ts @@ -5,11 +5,11 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Selection } from 'vs/editor/common/core/selection'; -import { EndOfLineSequence, ICursorStateComputer, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; +import { EndOfLineSequence, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; interface IEditOperation { - operations: IIdentifiedSingleEditOperation[]; + operations: IValidEditOperation[]; } interface IStackElement { @@ -174,7 +174,7 @@ export class EditStack { return stackElement!.afterCursorState; } - private static _computeCursorState(cursorStateComputer: ICursorStateComputer | null, inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] | null { + private static _computeCursorState(cursorStateComputer: ICursorStateComputer | null, inverseEditOperations: IValidEditOperation[]): Selection[] | null { try { return cursorStateComputer ? cursorStateComputer(inverseEditOperations) : null; } catch (e) { diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index a987231c3ab..0d9c539ff8a 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -6,7 +6,7 @@ import * as strings from 'vs/base/common/strings'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; -import { ApplyEditsResult, EndOfLinePreference, FindMatch, IIdentifiedSingleEditOperation, IInternalModelContentChange, ISingleEditOperationIdentifier, ITextBuffer, ITextSnapshot } from 'vs/editor/common/model'; +import { ApplyEditsResult, EndOfLinePreference, FindMatch, IInternalModelContentChange, ISingleEditOperationIdentifier, ITextBuffer, ITextSnapshot, ValidAnnotatedEditOperation, IValidEditOperation } from 'vs/editor/common/model'; import { PieceTreeBase, StringBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; import { SearchData } from 'vs/editor/common/model/textModelSearch'; @@ -21,7 +21,7 @@ export interface IValidatedEditOperation { isAutoWhitespaceEdit: boolean; } -export interface IReverseSingleEditOperation extends IIdentifiedSingleEditOperation { +export interface IReverseSingleEditOperation extends IValidEditOperation { sortIndex: number; } @@ -201,7 +201,7 @@ export class PieceTreeTextBuffer implements ITextBuffer { this._pieceTree.setEOL(newEOL); } - public applyEdits(rawOperations: IIdentifiedSingleEditOperation[], recordTrimAutoWhitespace: boolean): ApplyEditsResult { + public applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean): ApplyEditsResult { let mightContainRTL = this._mightContainRTL; let mightContainNonBasicASCII = this._mightContainNonBasicASCII; let canReduceOperations = true; diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index 8aa9b5a53d4..68660134bf7 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1154,18 +1154,40 @@ export class TextModel extends Disposable implements model.ITextModel { } } + private _validateEditOperation(rawOperation: model.IIdentifiedSingleEditOperation): model.ValidAnnotatedEditOperation { + if (rawOperation instanceof model.ValidAnnotatedEditOperation) { + return rawOperation; + } + return new model.ValidAnnotatedEditOperation( + rawOperation.identifier || null, + this.validateRange(rawOperation.range), + rawOperation.text, + rawOperation.forceMoveMarkers || false, + rawOperation.isAutoWhitespaceEdit || false, + rawOperation._isTracked || false + ); + } + + private _validateEditOperations(rawOperations: model.IIdentifiedSingleEditOperation[]): model.ValidAnnotatedEditOperation[] { + const result: model.ValidAnnotatedEditOperation[] = []; + for (let i = 0, len = rawOperations.length; i < len; i++) { + result[i] = this._validateEditOperation(rawOperations[i]); + } + return result; + } + public pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null { try { this._onDidChangeDecorations.beginDeferredEmit(); this._eventEmitter.beginDeferredEmit(); - return this._pushEditOperations(beforeCursorState, editOperations, cursorStateComputer); + return this._pushEditOperations(beforeCursorState, this._validateEditOperations(editOperations), cursorStateComputer); } finally { this._eventEmitter.endDeferredEmit(); this._onDidChangeDecorations.endDeferredEmit(); } } - private _pushEditOperations(beforeCursorState: Selection[], editOperations: model.IIdentifiedSingleEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): Selection[] | null { + private _pushEditOperations(beforeCursorState: Selection[], editOperations: model.ValidAnnotatedEditOperation[], cursorStateComputer: model.ICursorStateComputer | null): 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). @@ -1238,10 +1260,8 @@ export class TextModel extends Disposable implements model.ITextModel { } if (allowTrimLine) { - editOperations.push({ - range: new Range(trimLineNumber, 1, trimLineNumber, maxLineColumn), - text: null - }); + const trimRange = new Range(trimLineNumber, 1, trimLineNumber, maxLineColumn); + editOperations.push(new model.ValidAnnotatedEditOperation(null, trimRange, null, false, false, false)); } } @@ -1252,21 +1272,18 @@ export class TextModel extends Disposable implements model.ITextModel { return this._commandManager.pushEditOperation(beforeCursorState, editOperations, cursorStateComputer); } - public applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[]): model.IIdentifiedSingleEditOperation[] { + public applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[]): model.IValidEditOperation[] { try { this._onDidChangeDecorations.beginDeferredEmit(); this._eventEmitter.beginDeferredEmit(); - return this._applyEdits(rawOperations); + return this._applyEdits(this._validateEditOperations(rawOperations)); } finally { this._eventEmitter.endDeferredEmit(); this._onDidChangeDecorations.endDeferredEmit(); } } - private _applyEdits(rawOperations: model.IIdentifiedSingleEditOperation[]): model.IIdentifiedSingleEditOperation[] { - for (let i = 0, len = rawOperations.length; i < len; i++) { - rawOperations[i].range = this.validateRange(rawOperations[i].range); - } + private _applyEdits(rawOperations: model.ValidAnnotatedEditOperation[]): model.IValidEditOperation[] { const oldLineCount = this._buffer.getLineCount(); const result = this._buffer.applyEdits(rawOperations, this._options.trimAutoWhitespace); diff --git a/src/vs/editor/common/services/modelServiceImpl.ts b/src/vs/editor/common/services/modelServiceImpl.ts index bc8824d7f89..a46a64c4db5 100644 --- a/src/vs/editor/common/services/modelServiceImpl.ts +++ b/src/vs/editor/common/services/modelServiceImpl.ts @@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Range } from 'vs/editor/common/core/range'; -import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model'; +import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions, IValidEditOperation } from 'vs/editor/common/model'; import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel'; import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { LanguageIdentifier, DocumentSemanticTokensProviderRegistry, DocumentSemanticTokensProvider, SemanticTokensLegend, SemanticTokens, SemanticTokensEdits, TokenMetadata, FontStyle, MetadataConsts } from 'vs/editor/common/modes'; @@ -305,7 +305,7 @@ export class ModelServiceImpl extends Disposable implements IModelService { model.pushEditOperations( [], ModelServiceImpl._computeEdits(model, textBuffer), - (inverseEditOperations: IIdentifiedSingleEditOperation[]) => [] + (inverseEditOperations: IValidEditOperation[]) => [] ); model.pushStackElement(); } diff --git a/src/vs/editor/contrib/comment/blockCommentCommand.ts b/src/vs/editor/contrib/comment/blockCommentCommand.ts index efa593a811d..0a6d6a59ea5 100644 --- a/src/vs/editor/contrib/comment/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/blockCommentCommand.ts @@ -9,7 +9,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ICommand, IEditOperationBuilder, ICursorStateComputerData } from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; +import { ITextModel, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; export class BlockCommentCommand implements ICommand { diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index f266b70380a..bf088f0d67c 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -205,7 +205,7 @@ export class LineCommentCommand implements ICommand { for (let i = 0, len = ops.length; i < len; i++) { builder.addEditOperation(ops[i].range, ops[i].text); - if (ops[i].range.isEmpty() && ops[i].range.getStartPosition().equals(cursorPosition)) { + if (Range.isEmpty(ops[i].range) && Range.getStartPosition(ops[i].range).equals(cursorPosition)) { const lineContent = model.getLineContent(cursorPosition.lineNumber); if (lineContent.length + 1 === cursorPosition.column) { this._deltaColumn = (ops[i].text || '').length; diff --git a/src/vs/editor/test/browser/testCommand.ts b/src/vs/editor/test/browser/testCommand.ts index 8126f24735a..f5670377c6e 100644 --- a/src/vs/editor/test/browser/testCommand.ts +++ b/src/vs/editor/test/browser/testCommand.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Range } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; +import { IRange } from 'vs/editor/common/core/range'; +import { Selection, ISelection } from 'vs/editor/common/core/selection'; import { ICommand, Handler, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; @@ -50,7 +50,7 @@ export function testCommand( export function getEditOperation(model: ITextModel, command: ICommand): IIdentifiedSingleEditOperation[] { let operations: IIdentifiedSingleEditOperation[] = []; let editOperationBuilder: IEditOperationBuilder = { - addEditOperation: (range: Range, text: string, forceMoveMarkers: boolean = false) => { + addEditOperation: (range: IRange, text: string, forceMoveMarkers: boolean = false) => { operations.push({ range: range, text: text, @@ -58,7 +58,7 @@ export function getEditOperation(model: ITextModel, command: ICommand): IIdentif }); }, - addTrackedEditOperation: (range: Range, text: string, forceMoveMarkers: boolean = false) => { + addTrackedEditOperation: (range: IRange, text: string, forceMoveMarkers: boolean = false) => { operations.push({ range: range, text: text, @@ -67,7 +67,7 @@ export function getEditOperation(model: ITextModel, command: ICommand): IIdentif }, - trackSelection: (selection: Selection) => { + trackSelection: (selection: ISelection) => { return ''; } }; diff --git a/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts b/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts index c483bb64f81..49841dabc05 100644 --- a/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts +++ b/src/vs/editor/test/common/model/linesTextBuffer/textBufferAutoTestUtils.ts @@ -5,7 +5,7 @@ import { CharCode } from 'vs/base/common/charCode'; import { Range } from 'vs/editor/common/core/range'; -import { DefaultEndOfLine, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferBuilder } from 'vs/editor/common/model'; +import { DefaultEndOfLine, ITextBuffer, ITextBufferBuilder, ValidAnnotatedEditOperation } from 'vs/editor/common/model'; export function getRandomInt(min: number, max: number): number { return Math.floor(Math.random() * (max - min + 1)) + min; @@ -31,7 +31,7 @@ export function getRandomString(minLength: number, maxLength: number): string { return r; } -export function generateRandomEdits(chunks: string[], editCnt: number): IIdentifiedSingleEditOperation[] { +export function generateRandomEdits(chunks: string[], editCnt: number): ValidAnnotatedEditOperation[] { let lines: string[] = []; for (const chunk of chunks) { let newLines = chunk.split(/\r\n|\r|\n/); @@ -43,7 +43,7 @@ export function generateRandomEdits(chunks: string[], editCnt: number): IIdentif } } - let ops: IIdentifiedSingleEditOperation[] = []; + let ops: ValidAnnotatedEditOperation[] = []; for (let i = 0; i < editCnt; i++) { let line = getRandomInt(1, lines.length); @@ -54,17 +54,14 @@ export function generateRandomEdits(chunks: string[], editCnt: number): IIdentif text = getRandomString(5, 10); } - ops.push({ - text: text, - range: new Range(line, startColumn, line, endColumn) - }); + ops.push(new ValidAnnotatedEditOperation(null, new Range(line, startColumn, line, endColumn), text, false, false, false)); lines[line - 1] = lines[line - 1].substring(0, startColumn - 1) + text + lines[line - 1].substring(endColumn - 1); } return ops; } -export function generateSequentialInserts(chunks: string[], editCnt: number): IIdentifiedSingleEditOperation[] { +export function generateSequentialInserts(chunks: string[], editCnt: number): ValidAnnotatedEditOperation[] { let lines: string[] = []; for (const chunk of chunks) { let newLines = chunk.split(/\r\n|\r|\n/); @@ -76,7 +73,7 @@ export function generateSequentialInserts(chunks: string[], editCnt: number): II } } - let ops: IIdentifiedSingleEditOperation[] = []; + let ops: ValidAnnotatedEditOperation[] = []; for (let i = 0; i < editCnt; i++) { let line = lines.length; @@ -90,16 +87,13 @@ export function generateSequentialInserts(chunks: string[], editCnt: number): II lines[line - 1] += text; } - ops.push({ - text: text, - range: new Range(line, column, line, column) - }); + ops.push(new ValidAnnotatedEditOperation(null, new Range(line, column, line, column), text, false, false, false)); } return ops; } -export function generateRandomReplaces(chunks: string[], editCnt: number, searchStringLen: number, replaceStringLen: number): IIdentifiedSingleEditOperation[] { +export function generateRandomReplaces(chunks: string[], editCnt: number, searchStringLen: number, replaceStringLen: number): ValidAnnotatedEditOperation[] { let lines: string[] = []; for (const chunk of chunks) { let newLines = chunk.split(/\r\n|\r|\n/); @@ -111,7 +105,7 @@ export function generateRandomReplaces(chunks: string[], editCnt: number, search } } - let ops: IIdentifiedSingleEditOperation[] = []; + let ops: ValidAnnotatedEditOperation[] = []; let chunkSize = Math.max(1, Math.floor(lines.length / editCnt)); let chunkCnt = Math.floor(lines.length / chunkSize); let replaceString = getRandomString(replaceStringLen, replaceStringLen); @@ -125,10 +119,7 @@ export function generateRandomReplaces(chunks: string[], editCnt: number, search let startColumn = getRandomInt(1, maxColumn); let endColumn = Math.min(maxColumn, startColumn + searchStringLen); - ops.push({ - text: replaceString, - range: new Range(line, startColumn, line, endColumn) - }); + ops.push(new ValidAnnotatedEditOperation(null, new Range(line, startColumn, line, endColumn), replaceString, false, false, false)); previousChunksLength = endLine; } @@ -166,4 +157,4 @@ export function generateRandomChunkWithLF(minLength: number, maxLength: number): } } return r; -} \ No newline at end of file +} diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index ccd369acc0d..fac22d2960d 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -638,10 +638,18 @@ declare namespace monaco { * Return the end position (which will be after or equal to the start position) */ getEndPosition(): Position; + /** + * Return the end position (which will be after or equal to the start position) + */ + static getEndPosition(range: IRange): Position; /** * Return the start position (which will be before or equal to the end position) */ getStartPosition(): Position; + /** + * Return the start position (which will be before or equal to the end position) + */ + static getStartPosition(range: IRange): Position; /** * Transform to a user presentable string representation. */ @@ -1508,7 +1516,7 @@ declare namespace monaco.editor { /** * The range to replace. This can be empty to emulate a simple insert. */ - range: Range; + range: IRange; /** * The text to replace with. This can be null to emulate a simple delete. */ @@ -1520,6 +1528,22 @@ declare namespace monaco.editor { forceMoveMarkers?: boolean; } + export interface IValidEditOperation { + /** + * The range to replace. This can be empty to emulate a simple insert. + */ + range: Range; + /** + * The text to replace with. This can be null to emulate a simple delete. + */ + 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. + */ + forceMoveMarkers: boolean; + } + /** * A callback that can compute the cursor state after applying a series of edit operations. */ @@ -1527,7 +1551,7 @@ declare namespace monaco.editor { /** * A callback that can compute the resulting cursors state after some edit operations have been executed. */ - (inverseEditOperations: IIdentifiedSingleEditOperation[]): Selection[] | null; + (inverseEditOperations: IValidEditOperation[]): Selection[] | null; } export class TextModelResolvedOptions { @@ -1867,7 +1891,7 @@ declare namespace monaco.editor { * @param operations The edit operations. * @return The inverse edit operations, that, when applied, will bring the model back to the previous state. */ - applyEdits(operations: IIdentifiedSingleEditOperation[]): IIdentifiedSingleEditOperation[]; + applyEdits(operations: IIdentifiedSingleEditOperation[]): IValidEditOperation[]; /** * Change the end of line sequence without recording in the undo stack. * This can have dire consequences on the undo stack! See @pushEOL for the preferred way. @@ -1919,14 +1943,14 @@ declare namespace monaco.editor { * @param range The range to replace (delete). May be empty to represent a simple insert. * @param text The text to replace with. May be null to represent a simple delete. */ - addEditOperation(range: Range, text: string | null, forceMoveMarkers?: boolean): void; + addEditOperation(range: IRange, text: string | null, forceMoveMarkers?: boolean): void; /** * Add a new edit operation (a replace operation). * The inverse edits will be accessible in `ICursorStateComputerData.getInverseEditOperations()` * @param range The range to replace (delete). May be empty to represent a simple insert. * @param text The text to replace with. May be null to represent a simple delete. */ - addTrackedEditOperation(range: Range, text: string | null, forceMoveMarkers?: boolean): void; + addTrackedEditOperation(range: IRange, text: string | null, forceMoveMarkers?: boolean): void; /** * Track `selection` when applying edit operations. * A best effort will be made to not grow/expand the selection. @@ -1946,7 +1970,7 @@ declare namespace monaco.editor { /** * Get the inverse edit operations of the added edit operations. */ - getInverseEditOperations(): IIdentifiedSingleEditOperation[]; + getInverseEditOperations(): IValidEditOperation[]; /** * Get a previously tracked selection. * @param id The unique identifier returned by `trackSelection`. diff --git a/src/vs/workbench/api/browser/mainThreadEditor.ts b/src/vs/workbench/api/browser/mainThreadEditor.ts index 4de14d85d61..e7d981fe98e 100644 --- a/src/vs/workbench/api/browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/browser/mainThreadEditor.ts @@ -10,7 +10,7 @@ import { RenderLineNumbersType, TextEditorCursorStyle, cursorStyleToString, Edit import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; import { IDecorationOptions, ScrollType } from 'vs/editor/common/editorCommon'; -import { IIdentifiedSingleEditOperation, ISingleEditOperation, ITextModel, ITextModelUpdateOptions } from 'vs/editor/common/model'; +import { ISingleEditOperation, ITextModel, ITextModelUpdateOptions, IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { IModelService } from 'vs/editor/common/services/modelService'; import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2'; import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, IUndoStopOptions, TextEditorRevealType } from 'vs/workbench/api/common/extHost.protocol';