未验证 提交 4a9caecf 编写于 作者: A Alex Dima

Fixes #91855

上级 615de737
......@@ -55,14 +55,6 @@ const autoCloseAlways = () => true;
const autoCloseNever = () => false;
const autoCloseBeforeWhitespace = (chr: string) => (chr === ' ' || chr === '\t');
function appendEntry<K, V>(target: Map<K, V[]>, key: K, value: V): void {
if (target.has(key)) {
target.get(key)!.push(value);
} else {
target.set(key, [value]);
}
}
export class CursorConfiguration {
_cursorMoveConfigurationBrand: void;
......@@ -136,8 +128,6 @@ export class CursorConfiguration {
this.autoSurround = options.get(EditorOption.autoSurround);
this.autoIndent = options.get(EditorOption.autoIndent);
this.autoClosingPairsOpen2 = new Map<string, StandardAutoClosingPairConditional[]>();
this.autoClosingPairsClose2 = new Map<string, StandardAutoClosingPairConditional[]>();
this.surroundingPairs = {};
this._electricChars = null;
......@@ -146,15 +136,9 @@ export class CursorConfiguration {
bracket: CursorConfiguration._getShouldAutoClose(languageIdentifier, this.autoClosingBrackets)
};
let autoClosingPairs = CursorConfiguration._getAutoClosingPairs(languageIdentifier);
if (autoClosingPairs) {
for (const pair of autoClosingPairs) {
appendEntry(this.autoClosingPairsOpen2, pair.open.charAt(pair.open.length - 1), pair);
if (pair.close.length === 1) {
appendEntry(this.autoClosingPairsClose2, pair.close, pair);
}
}
}
const autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id);
this.autoClosingPairsOpen2 = autoClosingPairs.autoClosingPairsOpen;
this.autoClosingPairsClose2 = autoClosingPairs.autoClosingPairsClose;
let surroundingPairs = CursorConfiguration._getSurroundingPairs(languageIdentifier);
if (surroundingPairs) {
......@@ -190,15 +174,6 @@ export class CursorConfiguration {
}
}
private static _getAutoClosingPairs(languageIdentifier: LanguageIdentifier): StandardAutoClosingPairConditional[] | null {
try {
return LanguageConfigurationRegistry.getAutoClosingPairs(languageIdentifier.id);
} catch (e) {
onUnexpectedError(e);
return null;
}
}
private static _getShouldAutoClose(languageIdentifier: LanguageIdentifier, autoCloseConfig: EditorAutoClosingStrategy): (ch: string) => boolean {
switch (autoCloseConfig) {
case 'beforeWhitespace':
......
......@@ -5,11 +5,13 @@
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 { 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';
import { Selection } from 'vs/editor/common/core/selection';
import { ICommand } from 'vs/editor/common/editorCommon';
import { StandardAutoClosingPairConditional } from 'vs/editor/common/modes/languageConfiguration';
export class DeleteOperations {
......@@ -47,8 +49,14 @@ export class DeleteOperations {
return [shouldPushStackElementBefore, commands];
}
private static _isAutoClosingPairDelete(config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): boolean {
if (config.autoClosingBrackets === 'never' && config.autoClosingQuotes === 'never') {
public static isAutoClosingPairDelete(
autoClosingBrackets: EditorAutoClosingStrategy,
autoClosingQuotes: EditorAutoClosingStrategy,
autoClosingPairsOpen: Map<string, StandardAutoClosingPairConditional[]>,
model: ICursorSimpleModel,
selections: Selection[]
): boolean {
if (autoClosingBrackets === 'never' && autoClosingQuotes === 'never') {
return false;
}
......@@ -61,24 +69,27 @@ export class DeleteOperations {
}
const lineText = model.getLineContent(position.lineNumber);
const character = lineText[position.column - 2];
if (position.column < 2 || position.column >= lineText.length + 1) {
return false;
}
const character = lineText.charAt(position.column - 2);
const autoClosingPairCandidates = config.autoClosingPairsOpen2.get(character);
const autoClosingPairCandidates = autoClosingPairsOpen.get(character);
if (!autoClosingPairCandidates) {
return false;
}
if (isQuote(character)) {
if (config.autoClosingQuotes === 'never') {
if (autoClosingQuotes === 'never') {
return false;
}
} else {
if (config.autoClosingBrackets === 'never') {
if (autoClosingBrackets === 'never') {
return false;
}
}
const afterCharacter = lineText[position.column - 1];
const afterCharacter = lineText.charAt(position.column - 1);
let foundAutoClosingPair = false;
for (const autoClosingPairCandidate of autoClosingPairCandidates) {
......@@ -111,7 +122,7 @@ export class DeleteOperations {
public static deleteLeft(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ICursorSimpleModel, selections: Selection[]): [boolean, Array<ICommand | null>] {
if (this._isAutoClosingPairDelete(config, model, selections)) {
if (this.isAutoClosingPairDelete(config.autoClosingBrackets, config.autoClosingQuotes, config.autoClosingPairsOpen2, model, selections)) {
return this._runAutoClosingPairDelete(config, model, selections);
}
......
......@@ -5,12 +5,15 @@
import { CharCode } from 'vs/base/common/charCode';
import * as strings from 'vs/base/common/strings';
import { 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';
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 { ITextModel, IWordAtPosition } from 'vs/editor/common/model';
import { AutoClosingPairs } from 'vs/editor/common/modes/languageConfiguration';
interface IFindWordResult {
/**
......@@ -44,6 +47,16 @@ export const enum WordNavigationType {
WordAccessibility = 3 // Respect chrome defintion of a word
}
export interface DeleteWordContext {
wordSeparators: WordCharacterClassifier;
model: ITextModel;
selection: Selection;
whitespaceHeuristics: boolean;
autoClosingBrackets: EditorAutoClosingStrategy;
autoClosingQuotes: EditorAutoClosingStrategy;
autoClosingPairs: AutoClosingPairs;
}
export class WordOperations {
private static _createWord(lineContent: string, wordType: WordType, nextCharClass: WordCharacterClass, start: number, end: number): IFindWordResult {
......@@ -361,11 +374,21 @@ export class WordOperations {
return null;
}
public static deleteWordLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range | null {
public static deleteWordLeft(ctx: DeleteWordContext, wordNavigationType: WordNavigationType): Range | null {
const wordSeparators = ctx.wordSeparators;
const model = ctx.model;
const selection = ctx.selection;
const whitespaceHeuristics = ctx.whitespaceHeuristics;
if (!selection.isEmpty()) {
return selection;
}
if (DeleteOperations.isAutoClosingPairDelete(ctx.autoClosingBrackets, ctx.autoClosingQuotes, ctx.autoClosingPairs.autoClosingPairsOpen, ctx.model, [ctx.selection])) {
const position = ctx.selection.getPosition();
return new Range(position.lineNumber, position.column - 1, position.lineNumber, position.column + 1);
}
const position = new Position(selection.positionLineNumber, selection.positionColumn);
let lineNumber = position.lineNumber;
......@@ -447,7 +470,12 @@ export class WordOperations {
return null;
}
public static deleteWordRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range | null {
public static deleteWordRight(ctx: DeleteWordContext, wordNavigationType: WordNavigationType): Range | null {
const wordSeparators = ctx.wordSeparators;
const model = ctx.model;
const selection = ctx.selection;
const whitespaceHeuristics = ctx.whitespaceHeuristics;
if (!selection.isEmpty()) {
return selection;
}
......@@ -621,21 +649,21 @@ export class WordOperations {
}
export class WordPartOperations extends WordOperations {
public static deleteWordPartLeft(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean): Range {
public static deleteWordPartLeft(ctx: DeleteWordContext): Range {
const candidates = enforceDefined([
WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, WordNavigationType.WordStart),
WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, WordNavigationType.WordEnd),
WordOperations._deleteWordPartLeft(model, selection)
WordOperations.deleteWordLeft(ctx, WordNavigationType.WordStart),
WordOperations.deleteWordLeft(ctx, WordNavigationType.WordEnd),
WordOperations._deleteWordPartLeft(ctx.model, ctx.selection)
]);
candidates.sort(Range.compareRangesUsingEnds);
return candidates[2];
}
public static deleteWordPartRight(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, selection: Selection, whitespaceHeuristics: boolean): Range {
public static deleteWordPartRight(ctx: DeleteWordContext): Range {
const candidates = enforceDefined([
WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, WordNavigationType.WordStart),
WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, WordNavigationType.WordEnd),
WordOperations._deleteWordPartRight(model, selection)
WordOperations.deleteWordRight(ctx, WordNavigationType.WordStart),
WordOperations.deleteWordRight(ctx, WordNavigationType.WordEnd),
WordOperations._deleteWordPartRight(ctx.model, ctx.selection)
]);
candidates.sort(Range.compareRangesUsingStarts);
return candidates[0];
......
......@@ -289,3 +289,31 @@ export class StandardAutoClosingPairConditional {
return (this._standardTokenMask & <number>standardToken) === 0;
}
}
/**
* @internal
*/
export class AutoClosingPairs {
public readonly autoClosingPairsOpen: Map<string, StandardAutoClosingPairConditional[]>;
public readonly autoClosingPairsClose: Map<string, StandardAutoClosingPairConditional[]>;
constructor(autoClosingPairs: StandardAutoClosingPairConditional[]) {
this.autoClosingPairsOpen = new Map<string, StandardAutoClosingPairConditional[]>();
this.autoClosingPairsClose = new Map<string, StandardAutoClosingPairConditional[]>();
for (const pair of autoClosingPairs) {
appendEntry(this.autoClosingPairsOpen, pair.open.charAt(pair.open.length - 1), pair);
if (pair.close.length === 1) {
appendEntry(this.autoClosingPairsClose, pair.close, pair);
}
}
}
}
function appendEntry<K, V>(target: Map<K, V[]>, key: K, value: V): void {
if (target.has(key)) {
target.get(key)!.push(value);
} else {
target.set(key, [value]);
}
}
......@@ -11,7 +11,7 @@ import { Range } from 'vs/editor/common/core/range';
import { ITextModel } from 'vs/editor/common/model';
import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from 'vs/editor/common/model/wordHelper';
import { LanguageId, LanguageIdentifier } from 'vs/editor/common/modes';
import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, StandardAutoClosingPairConditional, CompleteEnterAction } from 'vs/editor/common/modes/languageConfiguration';
import { EnterAction, FoldingRules, IAutoClosingPair, IndentAction, IndentationRule, LanguageConfiguration, StandardAutoClosingPairConditional, CompleteEnterAction, AutoClosingPairs } from 'vs/editor/common/modes/languageConfiguration';
import { createScopedLineTokens, ScopedLineTokens } from 'vs/editor/common/modes/supports';
import { CharacterPairSupport } from 'vs/editor/common/modes/supports/characterPair';
import { BracketElectricCharacterSupport, IElectricAction } from 'vs/editor/common/modes/supports/electricCharacter';
......@@ -235,12 +235,9 @@ export class LanguageConfigurationRegistryImpl {
return value.characterPair || null;
}
public getAutoClosingPairs(languageId: LanguageId): StandardAutoClosingPairConditional[] {
let characterPairSupport = this._getCharacterPairSupport(languageId);
if (!characterPairSupport) {
return [];
}
return characterPairSupport.getAutoClosingPairs();
public getAutoClosingPairs(languageId: LanguageId): AutoClosingPairs {
const characterPairSupport = this._getCharacterPairSupport(languageId);
return new AutoClosingPairs(characterPairSupport ? characterPairSupport.getAutoClosingPairs() : []);
}
public getAutoCloseBeforeSet(languageId: LanguageId): string {
......
......@@ -13,6 +13,10 @@ import { CursorWordEndLeft, CursorWordEndLeftSelect, CursorWordEndRight, CursorW
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
suite('WordOperations', () => {
......@@ -721,4 +725,29 @@ suite('WordOperations', () => {
deleteWordLeft(editor); assert.equal(model.getLineContent(1), 'A line with text. And another one', '001');
});
});
test('deleteWordLeft - issue #91855: Matching (quote, bracket, paren) doesn\'t get deleted when hitting Ctrl+Backspace', () => {
const languageId = new LanguageIdentifier('myTestMode', 5);
class TestMode extends MockMode {
constructor() {
super(languageId);
this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
autoClosingPairs: [
{ open: '\"', close: '\"' }
]
}));
}
}
const mode = new TestMode();
const model = createTextModel('a ""', undefined, languageId);
withTestCodeEditor(null, { model }, (editor, _) => {
editor.setPosition(new Position(1, 4));
deleteWordLeft(editor); assert.equal(model.getLineContent(1), 'a ');
});
model.dispose();
mode.dispose();
});
});
......@@ -9,7 +9,7 @@ import { EditorCommand, ICommandOptions, ServicesAccessor, registerEditorCommand
import { ReplaceCommand } from 'vs/editor/common/commands/replaceCommand';
import { CursorState } from 'vs/editor/common/controller/cursorCommon';
import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents';
import { WordNavigationType, WordOperations } from 'vs/editor/common/controller/cursorWordOperations';
import { DeleteWordContext, WordNavigationType, WordOperations } from 'vs/editor/common/controller/cursorWordOperations';
import { WordCharacterClassifier, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier';
import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
......@@ -21,6 +21,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis
import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { EditorOption, EditorOptions } from 'vs/editor/common/config/editorOptions';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
export interface MoveWordOptions extends ICommandOptions {
inSelectionMode: boolean;
......@@ -354,9 +355,20 @@ export abstract class DeleteWordCommand extends EditorCommand {
const wordSeparators = getMapForWordSeparators(editor.getOption(EditorOption.wordSeparators));
const model = editor.getModel();
const selections = editor.getSelections();
const autoClosingBrackets = editor.getOption(EditorOption.autoClosingBrackets);
const autoClosingQuotes = editor.getOption(EditorOption.autoClosingQuotes);
const autoClosingPairs = LanguageConfigurationRegistry.getAutoClosingPairs(model.getLanguageIdentifier().id);
const commands = selections.map((sel) => {
const deleteRange = this._delete(wordSeparators, model, sel, this._whitespaceHeuristics, this._wordNavigationType);
const deleteRange = this._delete({
wordSeparators,
model,
selection: sel,
whitespaceHeuristics: this._whitespaceHeuristics,
autoClosingBrackets,
autoClosingQuotes,
autoClosingPairs,
}, this._wordNavigationType);
return new ReplaceCommand(deleteRange, '');
});
......@@ -365,12 +377,12 @@ export abstract class DeleteWordCommand extends EditorCommand {
editor.pushUndoStop();
}
protected abstract _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range;
protected abstract _delete(ctx: DeleteWordContext, wordNavigationType: WordNavigationType): Range;
}
export class DeleteWordLeftCommand extends DeleteWordCommand {
protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range {
let r = WordOperations.deleteWordLeft(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType);
protected _delete(ctx: DeleteWordContext, wordNavigationType: WordNavigationType): Range {
let r = WordOperations.deleteWordLeft(ctx, wordNavigationType);
if (r) {
return r;
}
......@@ -379,13 +391,13 @@ export class DeleteWordLeftCommand extends DeleteWordCommand {
}
export class DeleteWordRightCommand extends DeleteWordCommand {
protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range {
let r = WordOperations.deleteWordRight(wordSeparators, model, selection, whitespaceHeuristics, wordNavigationType);
protected _delete(ctx: DeleteWordContext, wordNavigationType: WordNavigationType): Range {
let r = WordOperations.deleteWordRight(ctx, wordNavigationType);
if (r) {
return r;
}
const lineCount = model.getLineCount();
const maxColumn = model.getLineMaxColumn(lineCount);
const lineCount = ctx.model.getLineCount();
const maxColumn = ctx.model.getLineMaxColumn(lineCount);
return new Range(lineCount, maxColumn, lineCount, maxColumn);
}
}
......
......@@ -5,11 +5,10 @@
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { registerEditorCommand } from 'vs/editor/browser/editorExtensions';
import { WordNavigationType, WordPartOperations } from 'vs/editor/common/controller/cursorWordOperations';
import { DeleteWordContext, WordNavigationType, WordPartOperations } from 'vs/editor/common/controller/cursorWordOperations';
import { WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier';
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 { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ITextModel } from 'vs/editor/common/model';
import { DeleteWordCommand, MoveWordCommand } from 'vs/editor/contrib/wordOperations/wordOperations';
......@@ -32,8 +31,8 @@ export class DeleteWordPartLeft extends DeleteWordCommand {
});
}
protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range {
let r = WordPartOperations.deleteWordPartLeft(wordSeparators, model, selection, whitespaceHeuristics);
protected _delete(ctx: DeleteWordContext, wordNavigationType: WordNavigationType): Range {
let r = WordPartOperations.deleteWordPartLeft(ctx);
if (r) {
return r;
}
......@@ -57,13 +56,13 @@ export class DeleteWordPartRight extends DeleteWordCommand {
});
}
protected _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection, whitespaceHeuristics: boolean, wordNavigationType: WordNavigationType): Range {
let r = WordPartOperations.deleteWordPartRight(wordSeparators, model, selection, whitespaceHeuristics);
protected _delete(ctx: DeleteWordContext, wordNavigationType: WordNavigationType): Range {
let r = WordPartOperations.deleteWordPartRight(ctx);
if (r) {
return r;
}
const lineCount = model.getLineCount();
const maxColumn = model.getLineMaxColumn(lineCount);
const lineCount = ctx.model.getLineCount();
const maxColumn = ctx.model.getLineMaxColumn(lineCount);
return new Range(lineCount, maxColumn, lineCount, maxColumn);
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册