diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index aeebeceae370b4d0904864972946eae32ca47966..6f04941875ac8c05add621fc684e4915d8133527 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -1402,7 +1402,7 @@ export class Cursor extends EventEmitter { } } else { - this._invokeForAll(ctx, (cursorIndex: number, oneCursor: OneCursor, oneCtx: IOneCursorOperationContext) => OneCursorOp.actualType(oneCursor, text, false, oneCtx)); + this._invokeForAll(ctx, (cursorIndex: number, oneCursor: OneCursor, oneCtx: IOneCursorOperationContext) => OneCursorOp.actualType(oneCursor, text, oneCtx)); } return true; diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index 8344e81c35243b55b4465629b9c1a1b27d28c850..05c4e5b9a4bcda2c0acea35b7c46aacddc36938b 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -8,7 +8,7 @@ import { Position } from 'vs/editor/common/core/position'; import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; import { IModeConfiguration } from 'vs/editor/common/controller/oneCursor'; -import { IConfigurationChangedEvent, TextModelResolvedOptions, IConfiguration } from 'vs/editor/common/editorCommon'; +import { ICommand, CursorChangeReason, IConfigurationChangedEvent, TextModelResolvedOptions, IConfiguration } from 'vs/editor/common/editorCommon'; import { TextModel } from 'vs/editor/common/model/textModel'; export interface CharacterMap { @@ -74,6 +74,53 @@ export interface ICursorSimpleModel { getLineLastNonWhitespaceColumn(lineNumber: number): number; } +export class EditOperationResult { + _editOperationBrand: void; + + readonly command: ICommand; + readonly shouldPushStackElementBefore: boolean; + readonly shouldPushStackElementAfter: boolean; + readonly isAutoWhitespaceCommand: boolean; + readonly shouldRevealHorizontal: boolean; + readonly cursorPositionChangeReason: CursorChangeReason; + + constructor( + command: ICommand, + opts?: { + shouldPushStackElementBefore: boolean; + shouldPushStackElementAfter: boolean; + isAutoWhitespaceCommand?: boolean; + shouldRevealHorizontal?: boolean; + cursorPositionChangeReason?: CursorChangeReason; + } + ) { + this.command = command; + this.shouldPushStackElementBefore = false; + this.shouldPushStackElementAfter = false; + this.isAutoWhitespaceCommand = false; + this.shouldRevealHorizontal = true; + this.cursorPositionChangeReason = CursorChangeReason.NotSet; + + if (typeof opts !== 'undefined') { + if (typeof opts.shouldPushStackElementBefore !== 'undefined') { + this.shouldPushStackElementBefore = opts.shouldPushStackElementBefore; + } + if (typeof opts.shouldPushStackElementAfter !== 'undefined') { + this.shouldPushStackElementAfter = opts.shouldPushStackElementAfter; + } + if (typeof opts.isAutoWhitespaceCommand !== 'undefined') { + this.isAutoWhitespaceCommand = opts.isAutoWhitespaceCommand; + } + if (typeof opts.shouldRevealHorizontal !== 'undefined') { + this.shouldRevealHorizontal = opts.shouldRevealHorizontal; + } + if (typeof opts.cursorPositionChangeReason !== 'undefined') { + this.cursorPositionChangeReason = opts.cursorPositionChangeReason; + } + } + } +} + /** * Common operations that work and make sense both on the model and on the view model. */ diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index 61d83fa5e5f74b8f0f0431b91e0184b859e8ceb0..cd9e0c0b73964dffbbbd6700a333e2936d00f63b 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -5,60 +5,12 @@ 'use strict'; import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; -import { CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; +import { EditOperationResult, CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; import { Range } from 'vs/editor/common/core/range'; -import { ICommand, CursorChangeReason } from 'vs/editor/common/editorCommon'; import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; import { CursorModelState } from 'vs/editor/common/controller/oneCursor'; import * as strings from 'vs/base/common/strings'; -export class EditOperationResult { - _editOperationBrand: void; - - readonly command: ICommand; - readonly shouldPushStackElementBefore: boolean; - readonly shouldPushStackElementAfter: boolean; - readonly isAutoWhitespaceCommand: boolean; - readonly shouldRevealHorizontal: boolean; - readonly cursorPositionChangeReason: CursorChangeReason; - - constructor( - command: ICommand, - opts?: { - shouldPushStackElementBefore: boolean; - shouldPushStackElementAfter: boolean; - isAutoWhitespaceCommand?: boolean; - shouldRevealHorizontal?: boolean; - cursorPositionChangeReason?: CursorChangeReason; - } - ) { - this.command = command; - this.shouldPushStackElementBefore = false; - this.shouldPushStackElementAfter = false; - this.isAutoWhitespaceCommand = false; - this.shouldRevealHorizontal = true; - this.cursorPositionChangeReason = CursorChangeReason.NotSet; - - if (typeof opts !== 'undefined') { - if (typeof opts.shouldPushStackElementBefore !== 'undefined') { - this.shouldPushStackElementBefore = opts.shouldPushStackElementBefore; - } - if (typeof opts.shouldPushStackElementAfter !== 'undefined') { - this.shouldPushStackElementAfter = opts.shouldPushStackElementAfter; - } - if (typeof opts.isAutoWhitespaceCommand !== 'undefined') { - this.isAutoWhitespaceCommand = opts.isAutoWhitespaceCommand; - } - if (typeof opts.shouldRevealHorizontal !== 'undefined') { - this.shouldRevealHorizontal = opts.shouldRevealHorizontal; - } - if (typeof opts.cursorPositionChangeReason !== 'undefined') { - this.cursorPositionChangeReason = opts.cursorPositionChangeReason; - } - } - } -} - export class DeleteOperations { public static deleteRight(config: CursorConfiguration, model: ICursorSimpleModel, cursor: CursorModelState): EditOperationResult { diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index b63b75c565fbf70d62b04797abcb6af024382833..8ff7fde325366945a50b6ee81f98c430daec53db 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -4,18 +4,21 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { ReplaceCommand, ReplaceCommandWithoutChangingPosition, ReplaceCommandWithOffsetCursorState } from 'vs/editor/common/commands/replaceCommand'; -import { CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; +import { EditOperationResult, CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; import { Range } from 'vs/editor/common/core/range'; import { CursorChangeReason, ICommand } from 'vs/editor/common/editorCommon'; import { CursorModelState } from 'vs/editor/common/controller/oneCursor'; import * as strings from 'vs/base/common/strings'; -import { EditOperationResult } from 'vs/editor/common/controller/cursorDeleteOperations'; import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand'; import { Selection } from 'vs/editor/common/core/selection'; import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { ITokenizedModel } from 'vs/editor/common/editorCommon'; import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { CharCode } from 'vs/base/common/charCode'; +import { SurroundSelectionCommand } from 'vs/editor/common/commands/surroundSelectionCommand'; +import { IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; export class TypeOperations { @@ -187,7 +190,7 @@ export class TypeOperations { } } - static _enter(config: CursorConfiguration, model: ITokenizedModel, keepPosition: boolean, range: Range): EditOperationResult { + private static _enter(config: CursorConfiguration, model: ITokenizedModel, keepPosition: boolean, range: Range): EditOperationResult { let r = LanguageConfigurationRegistry.getEnterActionAtPosition(model, range.startLineNumber, range.startColumn); let enterAction = r.enterAction; @@ -229,4 +232,223 @@ export class TypeOperations { isAutoWhitespaceCommand: true }); } + + private static _typeInterceptorEnter(config: CursorConfiguration, model: ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { + if (ch !== '\n') { + return null; + } + + return TypeOperations._enter(config, model, false, cursor.selection); + } + + private static _typeInterceptorAutoClosingCloseChar(config: CursorConfiguration, model: ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { + if (!config.autoClosingBrackets) { + return null; + } + + let selection = cursor.selection; + + if (!selection.isEmpty() || !config.autoClosingPairsClose.hasOwnProperty(ch)) { + return null; + } + + let position = cursor.position; + + let lineText = model.getLineContent(position.lineNumber); + let beforeCharacter = lineText.charAt(position.column - 1); + + if (beforeCharacter !== ch) { + return null; + } + + let typeSelection = new Range(position.lineNumber, position.column, position.lineNumber, position.column + 1); + return new EditOperationResult(new ReplaceCommand(typeSelection, ch)); + } + + private static _typeInterceptorAutoClosingOpenChar(config: CursorConfiguration, model: ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { + if (!config.autoClosingBrackets) { + return null; + } + + let selection = cursor.selection; + + if (!selection.isEmpty() || !config.autoClosingPairsOpen.hasOwnProperty(ch)) { + return null; + } + + let position = cursor.position; + let lineText = model.getLineContent(position.lineNumber); + let beforeCharacter = lineText.charAt(position.column - 1); + + // Only consider auto closing the pair if a space follows or if another autoclosed pair follows + if (beforeCharacter) { + let isBeforeCloseBrace = false; + for (let closeBrace in config.autoClosingPairsClose) { + if (beforeCharacter === closeBrace) { + isBeforeCloseBrace = true; + break; + } + } + if (!isBeforeCloseBrace && !/\s/.test(beforeCharacter)) { + return null; + } + } + + let lineTokens = model.getLineTokens(position.lineNumber, false); + + let shouldAutoClosePair = false; + try { + shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column); + } catch (e) { + onUnexpectedError(e); + } + + if (!shouldAutoClosePair) { + return null; + } + + let closeCharacter = config.autoClosingPairsOpen[ch]; + return new EditOperationResult(new ReplaceCommandWithOffsetCursorState(selection, ch + closeCharacter, 0, -closeCharacter.length), { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: false + }); + } + + private static _typeInterceptorSurroundSelection(config: CursorConfiguration, model: ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { + if (!config.autoClosingBrackets) { + return null; + } + + let selection = cursor.selection; + + if (selection.isEmpty() || !config.surroundingPairs.hasOwnProperty(ch)) { + return null; + } + + let selectionContainsOnlyWhitespace = true; + + for (let lineNumber = selection.startLineNumber; lineNumber <= selection.endLineNumber; lineNumber++) { + let lineText = model.getLineContent(lineNumber); + let startIndex = (lineNumber === selection.startLineNumber ? selection.startColumn - 1 : 0); + let endIndex = (lineNumber === selection.endLineNumber ? selection.endColumn - 1 : lineText.length); + for (let charIndex = startIndex; charIndex < endIndex; charIndex++) { + let charCode = lineText.charCodeAt(charIndex); + if (charCode !== CharCode.Tab && charCode !== CharCode.Space) { + selectionContainsOnlyWhitespace = false; + + // Break outer loop + lineNumber = selection.endLineNumber + 1; + + // Break inner loop + charIndex = endIndex; + } + } + } + + if (selectionContainsOnlyWhitespace) { + return null; + } + + let closeCharacter = config.surroundingPairs[ch]; + + return new EditOperationResult(new SurroundSelectionCommand(selection, ch, closeCharacter), { + shouldPushStackElementBefore: true, + shouldPushStackElementAfter: true + }); + } + + private static _typeInterceptorElectricChar(config: CursorConfiguration, model: ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { + if (!config.electricChars.hasOwnProperty(ch)) { + return null; + } + + let position = cursor.position; + let lineTokens = model.getLineTokens(position.lineNumber, false); + + let electricAction: IElectricAction; + try { + electricAction = LanguageConfigurationRegistry.onElectricCharacter(ch, lineTokens, position.column); + } catch (e) { + onUnexpectedError(e); + } + + if (!electricAction) { + return null; + } + + if (electricAction.appendText) { + return new EditOperationResult(new ReplaceCommandWithOffsetCursorState(cursor.selection, ch + electricAction.appendText, 0, -electricAction.appendText.length), { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: true + }); + } + + if (electricAction.matchOpenBracket) { + let match = model.findMatchingBracketUp(electricAction.matchOpenBracket, { + lineNumber: position.lineNumber, + column: position.column + }); + + if (match) { + let matchLine = model.getLineContent(match.startLineNumber); + let matchLineIndentation = strings.getLeadingWhitespace(matchLine); + let newIndentation = config.normalizeIndentation(matchLineIndentation); + + let lineText = model.getLineContent(position.lineNumber); + let lineFirstNonBlankColumn = model.getLineFirstNonWhitespaceColumn(position.lineNumber) || position.column; + + let prefix = lineText.substring(lineFirstNonBlankColumn - 1, position.column - 1); + let typeText = newIndentation + prefix + ch; + + let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, position.column); + + return new EditOperationResult(new ReplaceCommand(typeSelection, typeText), { + shouldPushStackElementBefore: false, + shouldPushStackElementAfter: true + }); + } + } + + return null; + } + + public static typeWithInterceptors(config: CursorConfiguration, model: ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { + let r: EditOperationResult = null; + + r = r || this._typeInterceptorEnter(config, model, cursor, ch); + r = r || this._typeInterceptorAutoClosingCloseChar(config, model, cursor, ch); + r = r || this._typeInterceptorAutoClosingOpenChar(config, model, cursor, ch); + r = r || this._typeInterceptorSurroundSelection(config, model, cursor, ch); + r = r || this._typeInterceptorElectricChar(config, model, cursor, ch); + r = r || this.typeWithoutInterceptors(config, model, cursor, ch); + + return r; + } + + public static typeWithoutInterceptors(config: CursorConfiguration, model: ITokenizedModel, cursor: CursorModelState, str: string): EditOperationResult { + return new EditOperationResult(TypeOperations.typeCommand(cursor.selection, str, false)); + } + + public static lineInsertBefore(config: CursorConfiguration, model: ITokenizedModel, cursor: CursorModelState): EditOperationResult { + let lineNumber = cursor.position.lineNumber; + + if (lineNumber === 1) { + return new EditOperationResult(new ReplaceCommandWithoutChangingPosition(new Range(1, 1, 1, 1), '\n')); + } + + lineNumber--; + let column = model.getLineMaxColumn(lineNumber); + + return this._enter(config, model, false, new Range(lineNumber, column, lineNumber, column)); + } + + public static lineInsertAfter(config: CursorConfiguration, model: ITokenizedModel, cursor: CursorModelState): EditOperationResult { + let position = cursor.position; + let column = model.getLineMaxColumn(position.lineNumber); + return this._enter(config, model, false, new Range(position.lineNumber, column, position.lineNumber, column)); + } + + public static lineBreakInsert(config: CursorConfiguration, model: ITokenizedModel, cursor: CursorModelState): EditOperationResult { + return this._enter(config, model, true, cursor.selection); + } } diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index e9e809023a55f6b1f826ca22acb92d9329d0fcf6..cfb3f78bee48ee343099e5efffa2baf4edd615f8 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; +import { EditOperationResult, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; import { Position } from 'vs/editor/common/core/position'; import { CharCode } from 'vs/base/common/charCode'; import { CharacterClassifier } from 'vs/editor/common/core/characterClassifier'; import { MoveOperationResult } from 'vs/editor/common/controller/cursorMoveOperations'; import { CursorChangeReason } from 'vs/editor/common/editorCommon'; import { CursorModelState } from 'vs/editor/common/controller/oneCursor'; -import { DeleteOperations, EditOperationResult } from 'vs/editor/common/controller/cursorDeleteOperations'; +import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; import * as strings from 'vs/base/common/strings'; import { Range } from 'vs/editor/common/core/range'; import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; diff --git a/src/vs/editor/common/controller/oneCursor.ts b/src/vs/editor/common/controller/oneCursor.ts index 6c8a95ea4dc768e28d2fcb10715d989275a981db..b97c94114929611088feff045cb8f9f9e3efce4d 100644 --- a/src/vs/editor/common/controller/oneCursor.ts +++ b/src/vs/editor/common/controller/oneCursor.ts @@ -4,24 +4,18 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import { onUnexpectedError, illegalArgument } from 'vs/base/common/errors'; -import * as strings from 'vs/base/common/strings'; -import { ReplaceCommand, ReplaceCommandWithOffsetCursorState, ReplaceCommandWithoutChangingPosition } from 'vs/editor/common/commands/replaceCommand'; -import { SurroundSelectionCommand } from 'vs/editor/common/commands/surroundSelectionCommand'; +import { illegalArgument } from 'vs/base/common/errors'; import { CursorMoveHelper, IColumnSelectResult } from 'vs/editor/common/controller/cursorMoveHelper'; -import { CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; +import { EditOperationResult, CursorColumns, CursorConfiguration, ICursorSimpleModel } from 'vs/editor/common/controller/cursorCommon'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection, SelectionDirection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; -import { CharCode } from 'vs/base/common/charCode'; -import { IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter'; import { IDisposable } from 'vs/base/common/lifecycle'; import { MoveOperations, MoveOperationResult } from 'vs/editor/common/controller/cursorMoveOperations'; import { WordType, WordOperations, WordNavigationType } from 'vs/editor/common/controller/cursorWordOperations'; import { ColumnSelection } from 'vs/editor/common/controller/cursorColumnSelection'; -import { DeleteOperations, EditOperationResult } from 'vs/editor/common/controller/cursorDeleteOperations'; +import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; export interface IOneCursorOperationContext { @@ -1218,235 +1212,39 @@ export class OneCursorOp { public static lineInsertBefore(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let lineNumber = cursor.modelState.position.lineNumber; - - if (lineNumber === 1) { - ctx.executeCommand = new ReplaceCommandWithoutChangingPosition(new Range(1, 1, 1, 1), '\n'); - return true; - } - - lineNumber--; - let column = cursor.model.getLineMaxColumn(lineNumber); + return this._applyEditOperation( + ctx, + TypeOperations.lineInsertBefore(cursor.config, cursor.model, cursor.modelState) + ); - return this._enter(cursor, false, ctx, new Range(lineNumber, column, lineNumber, column)); } public static lineInsertAfter(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - let position = cursor.modelState.position; - let column = cursor.model.getLineMaxColumn(position.lineNumber); - return this._enter(cursor, false, ctx, new Range(position.lineNumber, column, position.lineNumber, column)); + return this._applyEditOperation( + ctx, + TypeOperations.lineInsertAfter(cursor.config, cursor.model, cursor.modelState) + ); } public static lineBreakInsert(cursor: OneCursor, ctx: IOneCursorOperationContext): boolean { - return this._enter(cursor, true, ctx, cursor.modelState.selection); - } - - private static _enter(cursor: OneCursor, keepPosition: boolean, ctx: IOneCursorOperationContext, range: Range): boolean { return this._applyEditOperation( ctx, - TypeOperations._enter(cursor.config, cursor.model, keepPosition, range) + TypeOperations.lineBreakInsert(cursor.config, cursor.model, cursor.modelState) ); } - private static _typeInterceptorEnter(config: CursorConfiguration, model: editorCommon.ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { - if (ch !== '\n') { - return null; - } - - return TypeOperations._enter(config, model, false, cursor.selection); - } - - private static _typeInterceptorAutoClosingCloseChar(config: CursorConfiguration, model: editorCommon.ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { - if (!config.autoClosingBrackets) { - return null; - } - - let selection = cursor.selection; - - if (!selection.isEmpty() || !config.autoClosingPairsClose.hasOwnProperty(ch)) { - return null; - } - - let position = cursor.position; - - let lineText = model.getLineContent(position.lineNumber); - let beforeCharacter = lineText.charAt(position.column - 1); - - if (beforeCharacter !== ch) { - return null; - } - - let typeSelection = new Range(position.lineNumber, position.column, position.lineNumber, position.column + 1); - return new EditOperationResult(new ReplaceCommand(typeSelection, ch)); - } - - private static _typeInterceptorAutoClosingOpenChar(config: CursorConfiguration, model: editorCommon.ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { - if (!config.autoClosingBrackets) { - return null; - } - - let selection = cursor.selection; - - if (!selection.isEmpty() || !config.autoClosingPairsOpen.hasOwnProperty(ch)) { - return null; - } - - let position = cursor.position; - let lineText = model.getLineContent(position.lineNumber); - let beforeCharacter = lineText.charAt(position.column - 1); - - // Only consider auto closing the pair if a space follows or if another autoclosed pair follows - if (beforeCharacter) { - let isBeforeCloseBrace = false; - for (let closeBrace in config.autoClosingPairsClose) { - if (beforeCharacter === closeBrace) { - isBeforeCloseBrace = true; - break; - } - } - if (!isBeforeCloseBrace && !/\s/.test(beforeCharacter)) { - return null; - } - } - - let lineTokens = model.getLineTokens(position.lineNumber, false); - - let shouldAutoClosePair = false; - try { - shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(ch, lineTokens, position.column); - } catch (e) { - onUnexpectedError(e); - } - - if (!shouldAutoClosePair) { - return null; - } - - let closeCharacter = config.autoClosingPairsOpen[ch]; - return new EditOperationResult(new ReplaceCommandWithOffsetCursorState(selection, ch + closeCharacter, 0, -closeCharacter.length), { - shouldPushStackElementBefore: true, - shouldPushStackElementAfter: false - }); - } - - private static _typeInterceptorSurroundSelection(config: CursorConfiguration, model: editorCommon.ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { - if (!config.autoClosingBrackets) { - return null; - } - - let selection = cursor.selection; - - if (selection.isEmpty() || !config.surroundingPairs.hasOwnProperty(ch)) { - return null; - } - - let selectionContainsOnlyWhitespace = true; - - for (let lineNumber = selection.startLineNumber; lineNumber <= selection.endLineNumber; lineNumber++) { - let lineText = model.getLineContent(lineNumber); - let startIndex = (lineNumber === selection.startLineNumber ? selection.startColumn - 1 : 0); - let endIndex = (lineNumber === selection.endLineNumber ? selection.endColumn - 1 : lineText.length); - for (let charIndex = startIndex; charIndex < endIndex; charIndex++) { - let charCode = lineText.charCodeAt(charIndex); - if (charCode !== CharCode.Tab && charCode !== CharCode.Space) { - selectionContainsOnlyWhitespace = false; - - // Break outer loop - lineNumber = selection.endLineNumber + 1; - - // Break inner loop - charIndex = endIndex; - } - } - } - - if (selectionContainsOnlyWhitespace) { - return null; - } - - let closeCharacter = config.surroundingPairs[ch]; - - return new EditOperationResult(new SurroundSelectionCommand(selection, ch, closeCharacter), { - shouldPushStackElementBefore: true, - shouldPushStackElementAfter: true - }); - } - - private static _typeInterceptorElectricChar(config: CursorConfiguration, model: editorCommon.ITokenizedModel, cursor: CursorModelState, ch: string): EditOperationResult { - if (!config.electricChars.hasOwnProperty(ch)) { - return null; - } - - let position = cursor.position; - let lineTokens = model.getLineTokens(position.lineNumber, false); - - let electricAction: IElectricAction; - try { - electricAction = LanguageConfigurationRegistry.onElectricCharacter(ch, lineTokens, position.column); - } catch (e) { - onUnexpectedError(e); - } - - if (!electricAction) { - return null; - } - - if (electricAction.appendText) { - return new EditOperationResult(new ReplaceCommandWithOffsetCursorState(cursor.selection, ch + electricAction.appendText, 0, -electricAction.appendText.length), { - shouldPushStackElementBefore: false, - shouldPushStackElementAfter: true - }); - } - - if (electricAction.matchOpenBracket) { - let match = model.findMatchingBracketUp(electricAction.matchOpenBracket, { - lineNumber: position.lineNumber, - column: position.column - }); - - if (match) { - let matchLine = model.getLineContent(match.startLineNumber); - let matchLineIndentation = strings.getLeadingWhitespace(matchLine); - let newIndentation = config.normalizeIndentation(matchLineIndentation); - - let lineText = model.getLineContent(position.lineNumber); - let lineFirstNonBlankColumn = model.getLineFirstNonWhitespaceColumn(position.lineNumber) || position.column; - - let prefix = lineText.substring(lineFirstNonBlankColumn - 1, position.column - 1); - let typeText = newIndentation + prefix + ch; - - let typeSelection = new Range(position.lineNumber, 1, position.lineNumber, position.column); - - return new EditOperationResult(new ReplaceCommand(typeSelection, typeText), { - shouldPushStackElementBefore: false, - shouldPushStackElementAfter: true - }); - } - } - - return null; - } - - public static actualType(cursor: OneCursor, text: string, keepPosition: boolean, ctx: IOneCursorOperationContext): boolean { - ctx.executeCommand = TypeOperations.typeCommand(cursor.modelState.selection, text, keepPosition); - return true; + public static actualType(cursor: OneCursor, text: string, ctx: IOneCursorOperationContext): boolean { + return this._applyEditOperation( + ctx, + TypeOperations.typeWithoutInterceptors(cursor.config, cursor.model, cursor.modelState, text) + ); } public static type(cursor: OneCursor, ch: string, ctx: IOneCursorOperationContext): boolean { - let r: EditOperationResult = null; - - r = r || this._typeInterceptorEnter(cursor.config, cursor.model, cursor.modelState, ch); - r = r || this._typeInterceptorAutoClosingCloseChar(cursor.config, cursor.model, cursor.modelState, ch); - r = r || this._typeInterceptorAutoClosingOpenChar(cursor.config, cursor.model, cursor.modelState, ch); - r = r || this._typeInterceptorSurroundSelection(cursor.config, cursor.model, cursor.modelState, ch); - r = r || this._typeInterceptorElectricChar(cursor.config, cursor.model, cursor.modelState, ch); - - if (r) { - return this._applyEditOperation(ctx, r); - } - - ctx.executeCommand = TypeOperations.typeCommand(cursor.modelState.selection, ch, false); - return true; + return this._applyEditOperation( + ctx, + TypeOperations.typeWithInterceptors(cursor.config, cursor.model, cursor.modelState, ch) + ); } public static replacePreviousChar(cursor: OneCursor, txt: string, replaceCharCnt: number, ctx: IOneCursorOperationContext): boolean {