From b35ea7e5a0d74e9809ac96039dbc0fe2b33c7d04 Mon Sep 17 00:00:00 2001 From: rebornix Date: Wed, 4 Nov 2020 11:17:25 -0800 Subject: [PATCH] fix #54829 --- .../linesOperations/moveLinesCommand.ts | 97 +++++++++++++------ .../test/moveLinesCommand.test.ts | 53 ++++++++++ 2 files changed, 123 insertions(+), 27 deletions(-) diff --git a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts index d9600938cc6..681c8f02732 100644 --- a/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/moveLinesCommand.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from 'vs/editor/common/editorCommon'; import { ITextModel } from 'vs/editor/common/model'; -import { IndentAction } from 'vs/editor/common/modes/languageConfiguration'; +import { CompleteEnterAction, IndentAction } from 'vs/editor/common/modes/languageConfiguration'; import { IIndentConverter, LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry'; import { IndentConsts } from 'vs/editor/common/modes/supports/indentRules'; import * as indentUtils from 'vs/editor/contrib/indentation/indentUtils'; @@ -135,7 +135,8 @@ export class MoveLinesCommand implements ICommand { // to s.startLineNumber builder.addEditOperation(new Range(s.startLineNumber, 1, s.startLineNumber, 1), insertingText + '\n'); - let ret = this.matchEnterRule(model, indentConverter, tabSize, s.startLineNumber, s.startLineNumber, insertingText); + let ret = this.matchEnterRuleMovingDown(model, indentConverter, tabSize, s.startLineNumber, movingLineNumber, insertingText); + // check if the line being moved before matches onEnter rules, if so let's adjust the indentation by onEnter rules. if (ret !== null) { if (ret !== 0) { @@ -229,31 +230,7 @@ export class MoveLinesCommand implements ICommand { }; } - private matchEnterRule(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, oneLineAboveText?: string) { - let validPrecedingLine = oneLineAbove; - while (validPrecedingLine >= 1) { - // ship empty lines as empty lines just inherit indentation - let lineContent; - if (validPrecedingLine === oneLineAbove && oneLineAboveText !== undefined) { - lineContent = oneLineAboveText; - } else { - lineContent = model.getLineContent(validPrecedingLine); - } - - let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineContent); - if (nonWhitespaceIdx >= 0) { - break; - } - validPrecedingLine--; - } - - if (validPrecedingLine < 1 || line > model.getLineCount()) { - return null; - } - - let maxColumn = model.getLineMaxColumn(validPrecedingLine); - let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); - + private parseEnterResult(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, enter: CompleteEnterAction | null) { if (enter) { let enterPrefix = enter.indentation; @@ -283,6 +260,72 @@ export class MoveLinesCommand implements ICommand { return null; } + /** + * + * @param model + * @param indentConverter + * @param tabSize + * @param line the line moving down + * @param futureAboveLineNumber the line which will be at the `line` position + * @param futureAboveLineText + */ + private matchEnterRuleMovingDown(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, futureAboveLineNumber: number, futureAboveLineText: string) { + if (strings.lastNonWhitespaceIndex(futureAboveLineText) >= 0) { + // break + let maxColumn = model.getLineMaxColumn(futureAboveLineNumber); + let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(futureAboveLineNumber, maxColumn, futureAboveLineNumber, maxColumn)); + return this.parseEnterResult(model, indentConverter, tabSize, line, enter); + } else { + // go upwards, starting from `line - 1` + let validPrecedingLine = line - 1; + while (validPrecedingLine >= 1) { + let lineContent = model.getLineContent(validPrecedingLine); + let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineContent); + + if (nonWhitespaceIdx >= 0) { + break; + } + + validPrecedingLine--; + } + + if (validPrecedingLine < 1 || line > model.getLineCount()) { + return null; + } + + let maxColumn = model.getLineMaxColumn(validPrecedingLine); + let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); + return this.parseEnterResult(model, indentConverter, tabSize, line, enter); + } + } + + private matchEnterRule(model: ITextModel, indentConverter: IIndentConverter, tabSize: number, line: number, oneLineAbove: number, oneLineAboveText?: string) { + let validPrecedingLine = oneLineAbove; + while (validPrecedingLine >= 1) { + // ship empty lines as empty lines just inherit indentation + let lineContent; + if (validPrecedingLine === oneLineAbove && oneLineAboveText !== undefined) { + lineContent = oneLineAboveText; + } else { + lineContent = model.getLineContent(validPrecedingLine); + } + + let nonWhitespaceIdx = strings.lastNonWhitespaceIndex(lineContent); + if (nonWhitespaceIdx >= 0) { + break; + } + validPrecedingLine--; + } + + if (validPrecedingLine < 1 || line > model.getLineCount()) { + return null; + } + + let maxColumn = model.getLineMaxColumn(validPrecedingLine); + let enter = LanguageConfigurationRegistry.getEnterAction(this._autoIndent, model, new Range(validPrecedingLine, maxColumn, validPrecedingLine, maxColumn)); + return this.parseEnterResult(model, indentConverter, tabSize, line, enter); + } + private trimLeft(str: string) { return str.replace(/^\s+/, ''); } diff --git a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts index dd0a86ac066..e8bdc29c8eb 100644 --- a/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/moveLinesCommand.test.ts @@ -329,6 +329,7 @@ suite('Editor contrib - Move Lines Command honors Indentation Rules', () => { mode.dispose(); }); + test('move line should still work as before if there is no indentation rules', () => { testMoveLinesUpWithIndentCommand( null!, @@ -351,3 +352,55 @@ suite('Editor contrib - Move Lines Command honors Indentation Rules', () => { ); }); }); + +class EnterRulesMode extends MockMode { + private static readonly _id = new LanguageIdentifier('moveLinesEnterMode', 8); + constructor() { + super(EnterRulesMode._id); + this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), { + indentationRules: { + decreaseIndentPattern: /^\s*\[$/, + increaseIndentPattern: /^\s*\]$/, + }, + brackets: [ + ['{', '}'] + ] + })); + } +} + +suite('Editor - contrib - Move Lines Command honors onEnter Rules', () => { + + test('issue #54829. move block across block', () => { + let mode = new EnterRulesMode(); + + testMoveLinesDownWithIndentCommand( + mode.getLanguageIdentifier(), + + [ + 'if (true) {', + ' if (false) {', + ' if (1) {', + ' console.log(\'b\');', + ' }', + ' console.log(\'a\');', + ' }', + '}' + ], + new Selection(3, 9, 5, 10), + [ + 'if (true) {', + ' if (false) {', + ' console.log(\'a\');', + ' if (1) {', + ' console.log(\'b\');', + ' }', + ' }', + '}' + ], + new Selection(4, 9, 6, 10), + ); + + mode.dispose(); + }); +}); -- GitLab