From 5efebbd4c4cd4c74800dadb037cf64ed8629caee Mon Sep 17 00:00:00 2001 From: Alexandru Dima Date: Mon, 22 Mar 2021 15:52:56 +0100 Subject: [PATCH] Fixes #118270: Add new option to control deleting character pairs and delete by default only auto-inserted closing characters --- .../editor/browser/controller/coreCommands.ts | 2 +- src/vs/editor/common/config/editorOptions.ts | 22 +- src/vs/editor/common/controller/cursor.ts | 10 +- .../editor/common/controller/cursorCommon.ts | 7 +- .../controller/cursorDeleteOperations.ts | 28 +- .../common/controller/cursorWordOperations.ts | 6 +- .../common/standalone/standaloneEnums.ts | 241 ++++++++--------- src/vs/editor/common/viewModel/viewModel.ts | 1 + .../editor/common/viewModel/viewModelImpl.ts | 3 + .../contrib/wordOperations/wordOperations.ts | 3 + .../test/browser/controller/cursor.test.ts | 35 +++ src/vs/monaco.d.ts | 250 +++++++++--------- 12 files changed, 351 insertions(+), 257 deletions(-) diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index ae4acf17e56..78326608824 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -1822,7 +1822,7 @@ export namespace CoreEditingCommands { } public runCoreEditingCommand(editor: ICodeEditor, viewModel: IViewModel, args: any): void { - const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(viewModel.getPrevEditOperationType(), viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection)); + const [shouldPushStackElementBefore, commands] = DeleteOperations.deleteLeft(viewModel.getPrevEditOperationType(), viewModel.cursorConfig, viewModel.model, viewModel.getCursorStates().map(s => s.modelState.selection), viewModel.getCursorAutoClosedCharacters()); if (shouldPushStackElementBefore) { editor.pushUndoStop(); } diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 8f86989a8bc..e25c49f4c6c 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -28,7 +28,7 @@ export type EditorAutoSurroundStrategy = 'languageDefined' | 'quotes' | 'bracket /** * Configuration options for typing over closing quotes or brackets */ -export type EditorAutoClosingOvertypeStrategy = 'always' | 'auto' | 'never'; +export type EditorAutoClosingEditStrategy = 'always' | 'auto' | 'never'; /** * Configuration options for auto indentation in the editor @@ -413,10 +413,14 @@ export interface IEditorOptions { * Defaults to language defined behavior. */ autoClosingQuotes?: EditorAutoClosingStrategy; + /** + * Options for pressing backspace near quotes or bracket pairs. + */ + autoClosingDelete?: EditorAutoClosingEditStrategy; /** * Options for typing over closing quotes or brackets. */ - autoClosingOvertype?: EditorAutoClosingOvertypeStrategy; + autoClosingOvertype?: EditorAutoClosingEditStrategy; /** * Options for auto surrounding. * Defaults to always allowing auto surrounding. @@ -3725,6 +3729,7 @@ export const enum EditorOption { accessibilityPageSize, ariaLabel, autoClosingBrackets, + autoClosingDelete, autoClosingOvertype, autoClosingQuotes, autoIndent, @@ -3904,6 +3909,19 @@ export const EditorOptions = { description: nls.localize('autoClosingBrackets', "Controls whether the editor should automatically close brackets after the user adds an opening bracket.") } )), + autoClosingDelete: register(new EditorStringEnumOption( + EditorOption.autoClosingDelete, 'autoClosingDelete', + 'auto' as 'always' | 'auto' | 'never', + ['always', 'auto', 'never'] as const, + { + enumDescriptions: [ + '', + nls.localize('editor.autoClosingDelete.auto', "Remove adjacent closing quotes or brackets only if they were automatically inserted."), + '', + ], + description: nls.localize('autoClosingDelete', "Controls whether the editor should remove adjacent closing quotes or brackets when deleting.") + } + )), autoClosingOvertype: register(new EditorStringEnumOption( EditorOption.autoClosingOvertype, 'autoClosingOvertype', 'auto' as 'always' | 'auto' | 'never', diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 7a753dbc2e6..36c9691b295 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -620,6 +620,10 @@ export class Cursor extends Disposable { this._isDoingComposition = isDoingComposition; } + public getAutoClosedCharacters(): Range[] { + return AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); + } + public startComposition(eventsCollector: ViewModelEventsCollector): void { this._selectionsWhenCompositionStarted = this.getSelections().slice(0); } @@ -628,8 +632,7 @@ export class Cursor extends Disposable { this._executeEdit(() => { if (source === 'keyboard') { // composition finishes, let's check if we need to auto complete if necessary. - const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); - this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this._selectionsWhenCompositionStarted, this.getSelections(), autoClosedCharacters)); + this._executeEditOperation(TypeOperations.compositionEndWithInterceptors(this._prevEditOperationType, this.context.cursorConfig, this._model, this._selectionsWhenCompositionStarted, this.getSelections(), this.getAutoClosedCharacters())); this._selectionsWhenCompositionStarted = null; } }, eventsCollector, source); @@ -647,8 +650,7 @@ export class Cursor extends Disposable { const chr = text.substr(offset, charLength); // Here we must interpret each typed character individually - const autoClosedCharacters = AutoClosedAction.getAllAutoClosedCharacters(this._autoClosedActions); - this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), autoClosedCharacters, chr)); + this._executeEditOperation(TypeOperations.typeWithInterceptors(this._isDoingComposition, this._prevEditOperationType, this.context.cursorConfig, this._model, this.getSelections(), this.getAutoClosedCharacters(), chr)); offset += charLength; } diff --git a/src/vs/editor/common/controller/cursorCommon.ts b/src/vs/editor/common/controller/cursorCommon.ts index e35e89e0642..ce63ac7b368 100644 --- a/src/vs/editor/common/controller/cursorCommon.ts +++ b/src/vs/editor/common/controller/cursorCommon.ts @@ -6,7 +6,7 @@ import { CharCode } from 'vs/base/common/charCode'; import { onUnexpectedError } from 'vs/base/common/errors'; import * as strings from 'vs/base/common/strings'; -import { EditorAutoClosingStrategy, EditorAutoSurroundStrategy, ConfigurationChangedEvent, EditorAutoClosingOvertypeStrategy, EditorOption, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; +import { EditorAutoClosingStrategy, EditorAutoSurroundStrategy, ConfigurationChangedEvent, EditorAutoClosingEditStrategy, EditorOption, EditorAutoIndentStrategy } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; @@ -73,7 +73,8 @@ export class CursorConfiguration { public readonly multiCursorPaste: 'spread' | 'full'; public readonly autoClosingBrackets: EditorAutoClosingStrategy; public readonly autoClosingQuotes: EditorAutoClosingStrategy; - public readonly autoClosingOvertype: EditorAutoClosingOvertypeStrategy; + public readonly autoClosingDelete: EditorAutoClosingEditStrategy; + public readonly autoClosingOvertype: EditorAutoClosingEditStrategy; public readonly autoSurround: EditorAutoSurroundStrategy; public readonly autoIndent: EditorAutoIndentStrategy; public readonly autoClosingPairs: AutoClosingPairs; @@ -92,6 +93,7 @@ export class CursorConfiguration { || e.hasChanged(EditorOption.multiCursorPaste) || e.hasChanged(EditorOption.autoClosingBrackets) || e.hasChanged(EditorOption.autoClosingQuotes) + || e.hasChanged(EditorOption.autoClosingDelete) || e.hasChanged(EditorOption.autoClosingOvertype) || e.hasChanged(EditorOption.autoSurround) || e.hasChanged(EditorOption.useTabStops) @@ -125,6 +127,7 @@ export class CursorConfiguration { this.multiCursorPaste = options.get(EditorOption.multiCursorPaste); this.autoClosingBrackets = options.get(EditorOption.autoClosingBrackets); this.autoClosingQuotes = options.get(EditorOption.autoClosingQuotes); + this.autoClosingDelete = options.get(EditorOption.autoClosingDelete); this.autoClosingOvertype = options.get(EditorOption.autoClosingOvertype); this.autoSurround = options.get(EditorOption.autoSurround); this.autoIndent = options.get(EditorOption.autoIndent); diff --git a/src/vs/editor/common/controller/cursorDeleteOperations.ts b/src/vs/editor/common/controller/cursorDeleteOperations.ts index 1a4a86d8795..1d0fdd26576 100644 --- a/src/vs/editor/common/controller/cursorDeleteOperations.ts +++ b/src/vs/editor/common/controller/cursorDeleteOperations.ts @@ -5,7 +5,7 @@ import * as strings from 'vs/base/common/strings'; import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand'; -import { EditorAutoClosingStrategy } from 'vs/editor/common/config/editorOptions'; +import { EditorAutoClosingEditStrategy, EditorAutoClosingStrategy } from 'vs/editor/common/config/editorOptions'; import { CursorColumns, CursorConfiguration, EditOperationResult, EditOperationType, ICursorSimpleModel, isQuote } from 'vs/editor/common/controller/cursorCommon'; import { MoveOperations } from 'vs/editor/common/controller/cursorMoveOperations'; import { Range } from 'vs/editor/common/core/range'; @@ -50,15 +50,20 @@ export class DeleteOperations { } public static isAutoClosingPairDelete( + autoClosingDelete: EditorAutoClosingEditStrategy, autoClosingBrackets: EditorAutoClosingStrategy, autoClosingQuotes: EditorAutoClosingStrategy, autoClosingPairsOpen: Map, model: ICursorSimpleModel, - selections: Selection[] + selections: Selection[], + autoClosedCharacters: Range[] ): boolean { if (autoClosingBrackets === 'never' && autoClosingQuotes === 'never') { return false; } + if (autoClosingDelete === 'never') { + return false; + } for (let i = 0, len = selections.length; i < len; i++) { const selection = selections[i]; @@ -100,6 +105,21 @@ export class DeleteOperations { if (!foundAutoClosingPair) { return false; } + + // Must delete the pair only if it was automatically inserted by the editor + if (autoClosingDelete === 'auto') { + let found = false; + for (let j = 0, lenJ = autoClosedCharacters.length; j < lenJ; j++) { + const autoClosedCharacter = autoClosedCharacters[j]; + if (position.lineNumber === autoClosedCharacter.startLineNumber && position.column === autoClosedCharacter.startColumn) { + found = true; + break; + } + } + if (!found) { + return false; + } + } } return true; @@ -120,9 +140,9 @@ export class DeleteOperations { return [true, commands]; } - public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, Array] { + public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[], autoClosedCharacters: Range[]): [boolean, Array] { - if (this.isAutoClosingPairDelete(config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairs.autoClosingPairsOpenByEnd, model, selections)) { + if (this.isAutoClosingPairDelete(config.autoClosingDelete, config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairs.autoClosingPairsOpenByEnd, model, selections, autoClosedCharacters)) { return this._runAutoClosingPairDelete(config, model, selections); } diff --git a/src/vs/editor/common/controller/cursorWordOperations.ts b/src/vs/editor/common/controller/cursorWordOperations.ts index 73820f9af30..37c53efd5fa 100644 --- a/src/vs/editor/common/controller/cursorWordOperations.ts +++ b/src/vs/editor/common/controller/cursorWordOperations.ts @@ -5,7 +5,7 @@ import { CharCode } from 'vs/base/common/charCode'; import * as strings from 'vs/base/common/strings'; -import { EditorAutoClosingStrategy } from 'vs/editor/common/config/editorOptions'; +import { EditorAutoClosingEditStrategy, EditorAutoClosingStrategy } from 'vs/editor/common/config/editorOptions'; import { CursorConfiguration, ICursorSimpleModel, SingleCursorState } from 'vs/editor/common/controller/cursorCommon'; import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; import { WordCharacterClass, WordCharacterClassifier, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; @@ -52,9 +52,11 @@ export interface DeleteWordContext { model: ITextModel; selection: Selection; whitespaceHeuristics: boolean; + autoClosingDelete: EditorAutoClosingEditStrategy; autoClosingBrackets: EditorAutoClosingStrategy; autoClosingQuotes: EditorAutoClosingStrategy; autoClosingPairs: AutoClosingPairs; + autoClosedCharacters: Range[]; } export class WordOperations { @@ -384,7 +386,7 @@ export class WordOperations { return selection; } - if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpenByEnd, ctx.model, [ctx.selection])) { + if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingDelete, ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpenByEnd, ctx.model, [ctx.selection], ctx.autoClosedCharacters)) { const position = ctx.selection.getPosition(); return new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column + 1); } diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index ce53a324ea9..9f46a2a572e 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -173,126 +173,127 @@ export enum EditorOption { accessibilityPageSize = 3, ariaLabel = 4, autoClosingBrackets = 5, - autoClosingOvertype = 6, - autoClosingQuotes = 7, - autoIndent = 8, - automaticLayout = 9, - autoSurround = 10, - codeLens = 11, - codeLensFontFamily = 12, - codeLensFontSize = 13, - colorDecorators = 14, - columnSelection = 15, - comments = 16, - contextmenu = 17, - copyWithSyntaxHighlighting = 18, - cursorBlinking = 19, - cursorSmoothCaretAnimation = 20, - cursorStyle = 21, - cursorSurroundingLines = 22, - cursorSurroundingLinesStyle = 23, - cursorWidth = 24, - disableLayerHinting = 25, - disableMonospaceOptimizations = 26, - dragAndDrop = 27, - emptySelectionClipboard = 28, - extraEditorClassName = 29, - fastScrollSensitivity = 30, - find = 31, - fixedOverflowWidgets = 32, - folding = 33, - foldingStrategy = 34, - foldingHighlight = 35, - unfoldOnClickAfterEndOfLine = 36, - fontFamily = 37, - fontInfo = 38, - fontLigatures = 39, - fontSize = 40, - fontWeight = 41, - formatOnPaste = 42, - formatOnType = 43, - glyphMargin = 44, - gotoLocation = 45, - hideCursorInOverviewRuler = 46, - highlightActiveIndentGuide = 47, - hover = 48, - inDiffEditor = 49, - letterSpacing = 50, - lightbulb = 51, - lineDecorationsWidth = 52, - lineHeight = 53, - lineNumbers = 54, - lineNumbersMinChars = 55, - linkedEditing = 56, - links = 57, - matchBrackets = 58, - minimap = 59, - mouseStyle = 60, - mouseWheelScrollSensitivity = 61, - mouseWheelZoom = 62, - multiCursorMergeOverlapping = 63, - multiCursorModifier = 64, - multiCursorPaste = 65, - occurrencesHighlight = 66, - overviewRulerBorder = 67, - overviewRulerLanes = 68, - padding = 69, - parameterHints = 70, - peekWidgetDefaultFocus = 71, - definitionLinkOpensInPeek = 72, - quickSuggestions = 73, - quickSuggestionsDelay = 74, - readOnly = 75, - renameOnType = 76, - renderControlCharacters = 77, - renderIndentGuides = 78, - renderFinalNewline = 79, - renderLineHighlight = 80, - renderLineHighlightOnlyWhenFocus = 81, - renderValidationDecorations = 82, - renderWhitespace = 83, - revealHorizontalRightPadding = 84, - roundedSelection = 85, - rulers = 86, - scrollbar = 87, - scrollBeyondLastColumn = 88, - scrollBeyondLastLine = 89, - scrollPredominantAxis = 90, - selectionClipboard = 91, - selectionHighlight = 92, - selectOnLineNumbers = 93, - showFoldingControls = 94, - showUnused = 95, - snippetSuggestions = 96, - smartSelect = 97, - smoothScrolling = 98, - stickyTabStops = 99, - stopRenderingLineAfter = 100, - suggest = 101, - suggestFontSize = 102, - suggestLineHeight = 103, - suggestOnTriggerCharacters = 104, - suggestSelection = 105, - tabCompletion = 106, - tabIndex = 107, - unusualLineTerminators = 108, - useTabStops = 109, - wordSeparators = 110, - wordWrap = 111, - wordWrapBreakAfterCharacters = 112, - wordWrapBreakBeforeCharacters = 113, - wordWrapColumn = 114, - wordWrapOverride1 = 115, - wordWrapOverride2 = 116, - wrappingIndent = 117, - wrappingStrategy = 118, - showDeprecated = 119, - inlineHints = 120, - editorClassName = 121, - pixelRatio = 122, - tabFocusMode = 123, - layoutInfo = 124, - wrappingInfo = 125 + autoClosingDelete = 6, + autoClosingOvertype = 7, + autoClosingQuotes = 8, + autoIndent = 9, + automaticLayout = 10, + autoSurround = 11, + codeLens = 12, + codeLensFontFamily = 13, + codeLensFontSize = 14, + colorDecorators = 15, + columnSelection = 16, + comments = 17, + contextmenu = 18, + copyWithSyntaxHighlighting = 19, + cursorBlinking = 20, + cursorSmoothCaretAnimation = 21, + cursorStyle = 22, + cursorSurroundingLines = 23, + cursorSurroundingLinesStyle = 24, + cursorWidth = 25, + disableLayerHinting = 26, + disableMonospaceOptimizations = 27, + dragAndDrop = 28, + emptySelectionClipboard = 29, + extraEditorClassName = 30, + fastScrollSensitivity = 31, + find = 32, + fixedOverflowWidgets = 33, + folding = 34, + foldingStrategy = 35, + foldingHighlight = 36, + unfoldOnClickAfterEndOfLine = 37, + fontFamily = 38, + fontInfo = 39, + fontLigatures = 40, + fontSize = 41, + fontWeight = 42, + formatOnPaste = 43, + formatOnType = 44, + glyphMargin = 45, + gotoLocation = 46, + hideCursorInOverviewRuler = 47, + highlightActiveIndentGuide = 48, + hover = 49, + inDiffEditor = 50, + letterSpacing = 51, + lightbulb = 52, + lineDecorationsWidth = 53, + lineHeight = 54, + lineNumbers = 55, + lineNumbersMinChars = 56, + linkedEditing = 57, + links = 58, + matchBrackets = 59, + minimap = 60, + mouseStyle = 61, + mouseWheelScrollSensitivity = 62, + mouseWheelZoom = 63, + multiCursorMergeOverlapping = 64, + multiCursorModifier = 65, + multiCursorPaste = 66, + occurrencesHighlight = 67, + overviewRulerBorder = 68, + overviewRulerLanes = 69, + padding = 70, + parameterHints = 71, + peekWidgetDefaultFocus = 72, + definitionLinkOpensInPeek = 73, + quickSuggestions = 74, + quickSuggestionsDelay = 75, + readOnly = 76, + renameOnType = 77, + renderControlCharacters = 78, + renderIndentGuides = 79, + renderFinalNewline = 80, + renderLineHighlight = 81, + renderLineHighlightOnlyWhenFocus = 82, + renderValidationDecorations = 83, + renderWhitespace = 84, + revealHorizontalRightPadding = 85, + roundedSelection = 86, + rulers = 87, + scrollbar = 88, + scrollBeyondLastColumn = 89, + scrollBeyondLastLine = 90, + scrollPredominantAxis = 91, + selectionClipboard = 92, + selectionHighlight = 93, + selectOnLineNumbers = 94, + showFoldingControls = 95, + showUnused = 96, + snippetSuggestions = 97, + smartSelect = 98, + smoothScrolling = 99, + stickyTabStops = 100, + stopRenderingLineAfter = 101, + suggest = 102, + suggestFontSize = 103, + suggestLineHeight = 104, + suggestOnTriggerCharacters = 105, + suggestSelection = 106, + tabCompletion = 107, + tabIndex = 108, + unusualLineTerminators = 109, + useTabStops = 110, + wordSeparators = 111, + wordWrap = 112, + wordWrapBreakAfterCharacters = 113, + wordWrapBreakBeforeCharacters = 114, + wordWrapColumn = 115, + wordWrapOverride1 = 116, + wordWrapOverride2 = 117, + wrappingIndent = 118, + wrappingStrategy = 119, + showDeprecated = 120, + inlineHints = 121, + editorClassName = 122, + pixelRatio = 123, + tabFocusMode = 124, + layoutInfo = 125, + wrappingInfo = 126 } /** diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 125cb5880ae..92966555364 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -213,6 +213,7 @@ export interface IViewModel extends ICursorSimpleModel { getCursorStates(): CursorState[]; setCursorStates(source: string | null | undefined, reason: CursorChangeReason, states: PartialCursorState[] | null): void; getCursorColumnSelectData(): IColumnSelectData; + getCursorAutoClosedCharacters(): Range[]; setCursorColumnSelectData(columnSelectData: IColumnSelectData): void; getPrevEditOperationType(): EditOperationType; setPrevEditOperationType(type: EditOperationType): void; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index d382802a8bf..82dc9853efb 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -898,6 +898,9 @@ export class ViewModel extends Disposable implements IViewModel { public getCursorColumnSelectData(): IColumnSelectData { return this._cursor.getCursorColumnSelectData(); } + public getCursorAutoClosedCharacters(): Range[] { + return this._cursor.getAutoClosedCharacters(); + } public setCursorColumnSelectData(columnSelectData: IColumnSelectData): void { this._cursor.setCursorColumnSelectData(columnSelectData); } diff --git a/src/vs/editor/contrib/wordOperations/wordOperations.ts b/src/vs/editor/contrib/wordOperations/wordOperations.ts index dfc3e124329..d617c47bc68 100644 --- a/src/vs/editor/contrib/wordOperations/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/wordOperations.ts @@ -340,6 +340,7 @@ export abstract class DeleteWordCommand extends EditorCommand { const autoClosingBrackets = editor.getOption(EditorOption.autoClosingBrackets); const autoClosingQuotes = editor.getOption(EditorOption.autoClosingQuotes); const autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(model.getLanguageIdentifier().id); + const viewModel = editor._getViewModel(); const commands = selections.map((sel) => { const deleteRange = this._delete({ @@ -347,9 +348,11 @@ export abstract class DeleteWordCommand extends EditorCommand { model, selection: sel, whitespaceHeuristics: this._whitespaceHeuristics, + autoClosingDelete: editor.getOption(EditorOption.autoClosingDelete), autoClosingBrackets, autoClosingQuotes, autoClosingPairs, + autoClosedCharacters: viewModel.getCursorAutoClosedCharacters() }, this._wordNavigationType); return new ReplaceCommand(deleteRange, ''); }); diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index 94582121dd9..c9f56987ddb 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -5326,6 +5326,41 @@ suite('autoClosingPairs', () => { mode.dispose(); }); + test('issue #118270 - auto closing deletes only those characters that it inserted', () => { + let mode = new AutoClosingMode(); + usingCursor({ + text: [ + '', + 'y=();' + ], + languageIdentifier: mode.getLanguageIdentifier() + }, (editor, model, viewModel) => { + assertCursor(viewModel, new Position(1, 1)); + + viewModel.type('x=(', 'keyboard'); + assert.strictEqual(model.getLineContent(1), 'x=()'); + + viewModel.type('asd', 'keyboard'); + assert.strictEqual(model.getLineContent(1), 'x=(asd)'); + + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.strictEqual(model.getLineContent(1), 'x=()'); + + // delete closing char! + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.strictEqual(model.getLineContent(1), 'x='); + + // do not delete closing char! + viewModel.setSelections('test', [new Selection(2, 4, 2, 4)]); + CoreEditingCommands.DeleteLeft.runEditorCommand(null, editor, null); + assert.strictEqual(model.getLineContent(2), 'y=);'); + + }); + mode.dispose(); + }); + test('issue #78527 - does not close quote on odd count', () => { let mode = new AutoClosingMode(); usingCursor({ diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 5c6b0e1dfda..69cb1c58c51 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -2609,7 +2609,7 @@ declare namespace monaco.editor { /** * Configuration options for typing over closing quotes or brackets */ - export type EditorAutoClosingOvertypeStrategy = 'always' | 'auto' | 'never'; + export type EditorAutoClosingEditStrategy = 'always' | 'auto' | 'never'; /** * Configuration options for auto indentation in the editor @@ -2994,10 +2994,14 @@ declare namespace monaco.editor { * Defaults to language defined behavior. */ autoClosingQuotes?: EditorAutoClosingStrategy; + /** + * Options for pressing backspace near quotes or bracket pairs. + */ + autoClosingDelete?: EditorAutoClosingEditStrategy; /** * Options for typing over closing quotes or brackets. */ - autoClosingOvertype?: EditorAutoClosingOvertypeStrategy; + autoClosingOvertype?: EditorAutoClosingEditStrategy; /** * Options for auto surrounding. * Defaults to always allowing auto surrounding. @@ -3979,126 +3983,127 @@ declare namespace monaco.editor { accessibilityPageSize = 3, ariaLabel = 4, autoClosingBrackets = 5, - autoClosingOvertype = 6, - autoClosingQuotes = 7, - autoIndent = 8, - automaticLayout = 9, - autoSurround = 10, - codeLens = 11, - codeLensFontFamily = 12, - codeLensFontSize = 13, - colorDecorators = 14, - columnSelection = 15, - comments = 16, - contextmenu = 17, - copyWithSyntaxHighlighting = 18, - cursorBlinking = 19, - cursorSmoothCaretAnimation = 20, - cursorStyle = 21, - cursorSurroundingLines = 22, - cursorSurroundingLinesStyle = 23, - cursorWidth = 24, - disableLayerHinting = 25, - disableMonospaceOptimizations = 26, - dragAndDrop = 27, - emptySelectionClipboard = 28, - extraEditorClassName = 29, - fastScrollSensitivity = 30, - find = 31, - fixedOverflowWidgets = 32, - folding = 33, - foldingStrategy = 34, - foldingHighlight = 35, - unfoldOnClickAfterEndOfLine = 36, - fontFamily = 37, - fontInfo = 38, - fontLigatures = 39, - fontSize = 40, - fontWeight = 41, - formatOnPaste = 42, - formatOnType = 43, - glyphMargin = 44, - gotoLocation = 45, - hideCursorInOverviewRuler = 46, - highlightActiveIndentGuide = 47, - hover = 48, - inDiffEditor = 49, - letterSpacing = 50, - lightbulb = 51, - lineDecorationsWidth = 52, - lineHeight = 53, - lineNumbers = 54, - lineNumbersMinChars = 55, - linkedEditing = 56, - links = 57, - matchBrackets = 58, - minimap = 59, - mouseStyle = 60, - mouseWheelScrollSensitivity = 61, - mouseWheelZoom = 62, - multiCursorMergeOverlapping = 63, - multiCursorModifier = 64, - multiCursorPaste = 65, - occurrencesHighlight = 66, - overviewRulerBorder = 67, - overviewRulerLanes = 68, - padding = 69, - parameterHints = 70, - peekWidgetDefaultFocus = 71, - definitionLinkOpensInPeek = 72, - quickSuggestions = 73, - quickSuggestionsDelay = 74, - readOnly = 75, - renameOnType = 76, - renderControlCharacters = 77, - renderIndentGuides = 78, - renderFinalNewline = 79, - renderLineHighlight = 80, - renderLineHighlightOnlyWhenFocus = 81, - renderValidationDecorations = 82, - renderWhitespace = 83, - revealHorizontalRightPadding = 84, - roundedSelection = 85, - rulers = 86, - scrollbar = 87, - scrollBeyondLastColumn = 88, - scrollBeyondLastLine = 89, - scrollPredominantAxis = 90, - selectionClipboard = 91, - selectionHighlight = 92, - selectOnLineNumbers = 93, - showFoldingControls = 94, - showUnused = 95, - snippetSuggestions = 96, - smartSelect = 97, - smoothScrolling = 98, - stickyTabStops = 99, - stopRenderingLineAfter = 100, - suggest = 101, - suggestFontSize = 102, - suggestLineHeight = 103, - suggestOnTriggerCharacters = 104, - suggestSelection = 105, - tabCompletion = 106, - tabIndex = 107, - unusualLineTerminators = 108, - useTabStops = 109, - wordSeparators = 110, - wordWrap = 111, - wordWrapBreakAfterCharacters = 112, - wordWrapBreakBeforeCharacters = 113, - wordWrapColumn = 114, - wordWrapOverride1 = 115, - wordWrapOverride2 = 116, - wrappingIndent = 117, - wrappingStrategy = 118, - showDeprecated = 119, - inlineHints = 120, - editorClassName = 121, - pixelRatio = 122, - tabFocusMode = 123, - layoutInfo = 124, - wrappingInfo = 125 + autoClosingDelete = 6, + autoClosingOvertype = 7, + autoClosingQuotes = 8, + autoIndent = 9, + automaticLayout = 10, + autoSurround = 11, + codeLens = 12, + codeLensFontFamily = 13, + codeLensFontSize = 14, + colorDecorators = 15, + columnSelection = 16, + comments = 17, + contextmenu = 18, + copyWithSyntaxHighlighting = 19, + cursorBlinking = 20, + cursorSmoothCaretAnimation = 21, + cursorStyle = 22, + cursorSurroundingLines = 23, + cursorSurroundingLinesStyle = 24, + cursorWidth = 25, + disableLayerHinting = 26, + disableMonospaceOptimizations = 27, + dragAndDrop = 28, + emptySelectionClipboard = 29, + extraEditorClassName = 30, + fastScrollSensitivity = 31, + find = 32, + fixedOverflowWidgets = 33, + folding = 34, + foldingStrategy = 35, + foldingHighlight = 36, + unfoldOnClickAfterEndOfLine = 37, + fontFamily = 38, + fontInfo = 39, + fontLigatures = 40, + fontSize = 41, + fontWeight = 42, + formatOnPaste = 43, + formatOnType = 44, + glyphMargin = 45, + gotoLocation = 46, + hideCursorInOverviewRuler = 47, + highlightActiveIndentGuide = 48, + hover = 49, + inDiffEditor = 50, + letterSpacing = 51, + lightbulb = 52, + lineDecorationsWidth = 53, + lineHeight = 54, + lineNumbers = 55, + lineNumbersMinChars = 56, + linkedEditing = 57, + links = 58, + matchBrackets = 59, + minimap = 60, + mouseStyle = 61, + mouseWheelScrollSensitivity = 62, + mouseWheelZoom = 63, + multiCursorMergeOverlapping = 64, + multiCursorModifier = 65, + multiCursorPaste = 66, + occurrencesHighlight = 67, + overviewRulerBorder = 68, + overviewRulerLanes = 69, + padding = 70, + parameterHints = 71, + peekWidgetDefaultFocus = 72, + definitionLinkOpensInPeek = 73, + quickSuggestions = 74, + quickSuggestionsDelay = 75, + readOnly = 76, + renameOnType = 77, + renderControlCharacters = 78, + renderIndentGuides = 79, + renderFinalNewline = 80, + renderLineHighlight = 81, + renderLineHighlightOnlyWhenFocus = 82, + renderValidationDecorations = 83, + renderWhitespace = 84, + revealHorizontalRightPadding = 85, + roundedSelection = 86, + rulers = 87, + scrollbar = 88, + scrollBeyondLastColumn = 89, + scrollBeyondLastLine = 90, + scrollPredominantAxis = 91, + selectionClipboard = 92, + selectionHighlight = 93, + selectOnLineNumbers = 94, + showFoldingControls = 95, + showUnused = 96, + snippetSuggestions = 97, + smartSelect = 98, + smoothScrolling = 99, + stickyTabStops = 100, + stopRenderingLineAfter = 101, + suggest = 102, + suggestFontSize = 103, + suggestLineHeight = 104, + suggestOnTriggerCharacters = 105, + suggestSelection = 106, + tabCompletion = 107, + tabIndex = 108, + unusualLineTerminators = 109, + useTabStops = 110, + wordSeparators = 111, + wordWrap = 112, + wordWrapBreakAfterCharacters = 113, + wordWrapBreakBeforeCharacters = 114, + wordWrapColumn = 115, + wordWrapOverride1 = 116, + wordWrapOverride2 = 117, + wrappingIndent = 118, + wrappingStrategy = 119, + showDeprecated = 120, + inlineHints = 121, + editorClassName = 122, + pixelRatio = 123, + tabFocusMode = 124, + layoutInfo = 125, + wrappingInfo = 126 } export const EditorOptions: { acceptSuggestionOnCommitCharacter: IEditorOption; @@ -4107,6 +4112,7 @@ declare namespace monaco.editor { accessibilityPageSize: IEditorOption; ariaLabel: IEditorOption; autoClosingBrackets: IEditorOption; + autoClosingDelete: IEditorOption; autoClosingOvertype: IEditorOption; autoClosingQuotes: IEditorOption; autoIndent: IEditorOption;