Simplify logic dealing with the case when the character is already typed (the composition end case)

This commit is contained in:
Alex Dima 2021-11-20 22:24:05 +01:00
parent 94d07f18ea
commit 97aa083b93
No known key found for this signature in database
GPG key ID: 39563C1504FDD0C9

View file

@ -561,16 +561,39 @@ export class TypeOperations {
return result;
}
private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean): string | null {
private static _getAutoClosingPairClose(config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, chIsAlreadyTyped: boolean): string | null {
const chIsQuote = isQuote(ch);
const autoCloseConfig = chIsQuote ? config.autoClosingQuotes : config.autoClosingBrackets;
if (autoCloseConfig === 'never') {
return null;
}
for (const selection of selections) {
if (!selection.isEmpty()) {
return null;
}
}
// This method is called both when typing (regularly) and when composition ends
// This means that we need to work with a text buffer where sometimes `ch` is not
// there (it is being typed right now) or with a text buffer where `ch` has already been typed
//
// In order to avoid adding checks for `chIsAlreadyTyped` in all places, we will work
// with two conceptual positions, the position before `ch` and the position after `ch`
//
const positions: { lineNumber: number; beforeColumn: number; afterColumn: number; }[] = selections.map((s) => {
const position = s.getPosition();
if (chIsAlreadyTyped) {
return { lineNumber: position.lineNumber, beforeColumn: position.column - ch.length, afterColumn: position.column };
} else {
return { lineNumber: position.lineNumber, beforeColumn: position.column, afterColumn: position.column };
}
});
// Find the longest auto-closing open pair in case of multiple ending in `ch`
// e.g. when having [f","] and [","], it picks [f","] if the character before is f
const pair = this._findAutoClosingPairOpen(config, model, selections.map(s => s.getPosition()), ch);
const pair = this._findAutoClosingPairOpen(config, model, positions.map(p => new Position(p.lineNumber, p.beforeColumn)), ch);
if (!pair) {
return null;
}
@ -585,14 +608,11 @@ export class TypeOperations {
const shouldAutoCloseBefore = chIsQuote ? config.shouldAutoCloseBefore.quote : config.shouldAutoCloseBefore.bracket;
for (const selection of selections) {
if (!selection.isEmpty()) {
return null;
}
const position = selection.getPosition();
const lineText = model.getLineContent(position.lineNumber);
const lineAfter = lineText.substring(position.column - 1);
for (const position of positions) {
const { lineNumber, beforeColumn, afterColumn } = position;
const lineText = model.getLineContent(lineNumber);
const lineBefore = lineText.substring(0, beforeColumn - 1);
const lineAfter = lineText.substring(afterColumn - 1);
if (!lineAfter.startsWith(containedPairClose)) {
isContainedPairPresent = false;
@ -608,7 +628,7 @@ export class TypeOperations {
}
}
if (!model.isCheapToTokenize(position.lineNumber)) {
if (!model.isCheapToTokenize(lineNumber)) {
// Do not force tokenization
return null;
}
@ -616,20 +636,20 @@ export class TypeOperations {
// Do not auto-close ' or " after a word character
if (pair.open.length === 1 && (ch === '\'' || ch === '"') && autoCloseConfig !== 'always') {
const wordSeparators = getMapForWordSeparators(config.wordSeparators);
if (insertOpenCharacter && position.column > 1 && wordSeparators.get(lineText.charCodeAt(position.column - 2)) === WordCharacterClass.Regular) {
return null;
}
if (!insertOpenCharacter && position.column > 2 && wordSeparators.get(lineText.charCodeAt(position.column - 3)) === WordCharacterClass.Regular) {
return null;
if (lineBefore.length > 0) {
const characterBefore = lineBefore.charCodeAt(lineBefore.length - 1);
if (wordSeparators.get(characterBefore) === WordCharacterClass.Regular) {
return null;
}
}
}
model.forceTokenization(position.lineNumber);
const lineTokens = model.getLineTokens(position.lineNumber);
model.forceTokenization(lineNumber);
const lineTokens = model.getLineTokens(lineNumber);
let shouldAutoClosePair = false;
try {
shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(pair, lineTokens, insertOpenCharacter ? position.column : position.column - 1);
shouldAutoClosePair = LanguageConfigurationRegistry.shouldAutoClosePair(pair, lineTokens, beforeColumn);
} catch (e) {
onUnexpectedError(e);
}
@ -646,11 +666,11 @@ export class TypeOperations {
}
}
private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, insertOpenCharacter: boolean, autoClosingPairClose: string): EditOperationResult {
private static _runAutoClosingOpenCharType(prevEditOperationType: EditOperationType, config: CursorConfiguration, model: ITextModel, selections: Selection[], ch: string, chIsAlreadyTyped: boolean, autoClosingPairClose: string): EditOperationResult {
let commands: ICommand[] = [];
for (let i = 0, len = selections.length; i < len; i++) {
const selection = selections[i];
commands[i] = new TypeWithAutoClosingCommand(selection, ch, insertOpenCharacter, autoClosingPairClose);
commands[i] = new TypeWithAutoClosingCommand(selection, ch, !chIsAlreadyTyped, autoClosingPairClose);
}
return new EditOperationResult(EditOperationType.TypingOther, commands, {
shouldPushStackElementBefore: true,
@ -825,9 +845,9 @@ export class TypeOperations {
});
}
const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, false);
const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, true);
if (autoClosingPairClose !== null) {
return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairClose);
return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairClose);
}
return null;
@ -869,9 +889,9 @@ export class TypeOperations {
}
if (!isDoingComposition) {
const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, true);
const autoClosingPairClose = this._getAutoClosingPairClose(config, model, selections, ch, false);
if (autoClosingPairClose) {
return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, true, autoClosingPairClose);
return this._runAutoClosingOpenCharType(prevEditOperationType, config, model, selections, ch, false, autoClosingPairClose);
}
}