提交 2ddfa2ec 编写于 作者: R rebornix

Refactor and test cases

上级 4527fd7c
......@@ -404,7 +404,7 @@ const editorConfiguration: IConfigurationNode = {
'editor.autoIndent': {
'type': 'boolean',
'default': EDITOR_DEFAULTS.autoIndent,
'description': nls.localize('autoIndent', "Controls if the editor should automatically adjust the indenation when users type. Indentation Rules of the language must be available. ")
'description': nls.localize('autoIndent', "Controls if the editor should automatically adjust the indenation when users type, paste or move lines. Indentation Rules of the language must be available. ")
},
'editor.suggestOnTriggerCharacters': {
'type': 'boolean',
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export function getSpaceCnt(str, tabSize) {
let spacesCnt = 0;
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === '\t') {
spacesCnt += tabSize;
} else {
spacesCnt++;
}
}
return spacesCnt;
}
export function generateIndent(spacesCnt: number, tabSize, insertSpaces) {
spacesCnt = spacesCnt < 0 ? 0 : spacesCnt;
let result = '';
if (!insertSpaces) {
let tabsCnt = Math.floor(spacesCnt / tabSize);
spacesCnt = spacesCnt % tabSize;
for (let i = 0; i < tabsCnt; i++) {
result += '\t';
}
}
for (let i = 0; i < spacesCnt; i++) {
result += ' ';
}
return result;
}
\ No newline at end of file
......@@ -19,6 +19,7 @@ import { TextModel } from 'vs/editor/common/model/textModel';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand';
import { TextEdit } from 'vs/editor/common/modes';
import * as IndentUtil from './indentUtils';
export function shiftIndent(tabSize: number, indentation: string, count?: number): string {
count = count || 1;
......@@ -402,7 +403,7 @@ export class AutoIndentOnPaste implements IEditorContribution {
this.callOnModel = dispose(this.callOnModel);
// we are disabled
if (!this.editor.getConfiguration().autoIndent) {
if (!this.editor.getConfiguration().autoIndent || this.editor.getConfiguration().contribInfo.formatOnPaste) {
return;
}
......@@ -451,11 +452,11 @@ export class AutoIndentOnPaste implements IEditorContribution {
if (indentOfFirstLine !== null) {
let firstLineText = model.getLineContent(range.startLineNumber);
let oldIndentation = strings.getLeadingWhitespace(firstLineText);
let newSpaceCnt = this.getSpaceCnt(indentOfFirstLine, tabSize);
let oldSpaceCnt = this.getSpaceCnt(oldIndentation, tabSize);
let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfFirstLine, tabSize);
let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
let newIndent = this.generateIndent(newSpaceCnt, tabSize, insertSpaces);
let newIndent = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces);
textEdits.push({
range: new Range(range.startLineNumber, 1, range.startLineNumber, oldIndentation.length + 1),
text: newIndent
......@@ -483,17 +484,17 @@ export class AutoIndentOnPaste implements IEditorContribution {
}
};
let indentOfSecondLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdentifier().id, range.startLineNumber + 1, indentConverter);
let newSpaceCntOfSecondLine = this.getSpaceCnt(indentOfSecondLine, tabSize);
let oldSpaceCntOfSecondLine = this.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(range.startLineNumber + 1)), tabSize);
let newSpaceCntOfSecondLine = IndentUtil.getSpaceCnt(indentOfSecondLine, tabSize);
let oldSpaceCntOfSecondLine = IndentUtil.getSpaceCnt(strings.getLeadingWhitespace(model.getLineContent(range.startLineNumber + 1)), tabSize);
if (newSpaceCntOfSecondLine !== oldSpaceCntOfSecondLine) {
let spaceCntOffset = newSpaceCntOfSecondLine - oldSpaceCntOfSecondLine;
for (let i = range.startLineNumber + 1; i <= range.endLineNumber; i++) {
let lineContent = model.getLineContent(i);
let originalIndent = strings.getLeadingWhitespace(lineContent);
let originalSpacesCnt = this.getSpaceCnt(originalIndent, tabSize);
let originalSpacesCnt = IndentUtil.getSpaceCnt(originalIndent, tabSize);
let newSpacesCnt = originalSpacesCnt + spaceCntOffset;
let newIndent = this.generateIndent(newSpacesCnt, tabSize, insertSpaces);
let newIndent = IndentUtil.generateIndent(newSpacesCnt, tabSize, insertSpaces);
if (newIndent !== originalIndent) {
textEdits.push({
......@@ -519,39 +520,6 @@ export class AutoIndentOnPaste implements IEditorContribution {
this.callOnDispose = dispose(this.callOnDispose);
this.callOnModel = dispose(this.callOnModel);
}
private getSpaceCnt(str, tabSize) {
let spacesCnt = 0;
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === '\t') {
spacesCnt += tabSize;
} else {
spacesCnt++;
}
}
return spacesCnt;
}
private generateIndent(spacesCnt: number, tabSize, insertSpaces) {
spacesCnt = spacesCnt < 0 ? 0 : spacesCnt;
let result = '';
if (!insertSpaces) {
let tabsCnt = Math.floor(spacesCnt / tabSize);
spacesCnt = spacesCnt % tabSize;
for (let i = 0; i < tabsCnt; i++) {
result += '\t';
}
}
for (let i = 0; i < spacesCnt; i++) {
result += ' ';
}
return result;
}
}
function getIndentationEditOperations(model: ITokenizedModel, builder: IEditOperationBuilder, tabSize: number, tabsToSpaces: boolean): void {
......
......@@ -10,6 +10,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { ICommand, ICursorStateComputerData, IEditOperationBuilder, ITokenizedModel } from 'vs/editor/common/editorCommon';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
import { ShiftCommand } from 'vs/editor/common/commands/shiftCommand';
import * as IndentUtil from 'vs/editor/contrib/indentation/common/indentUtils';
export class MoveLinesCommand implements ICommand {
......@@ -113,7 +114,7 @@ export class MoveLinesCommand implements ICommand {
let insertingText = movingLineText;
// Insert line that needs to be moved before
if (this._autoIndent) {
if (this.isAutoIndent(model, s)) {
virtualModel.getLineContent = (lineNumber) => {
if (lineNumber === s.startLineNumber) {
return model.getLineContent(movingLineNumber);
......@@ -121,14 +122,14 @@ export class MoveLinesCommand implements ICommand {
return model.getLineContent(lineNumber);
}
};
let newIndentation = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(
let indentOfMovingLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(
movingLineNumber, 1), s.startLineNumber, indentConverter);
if (newIndentation !== null) {
if (indentOfMovingLine !== null) {
let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(movingLineNumber));
let newSpaceCnt = this.getSpaceCnt(newIndentation, tabSize);
let oldSpaceCnt = this.getSpaceCnt(oldIndentation, tabSize);
let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfMovingLine, tabSize);
let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
let newIndentation = this.generateIndent(newSpaceCnt, tabSize, insertSpaces);
let newIndentation = IndentUtil.generateIndent(newSpaceCnt, tabSize, insertSpaces);
insertingText = newIndentation + strings.ltrim(strings.ltrim(movingLineText), '\t');
}
}
......@@ -147,27 +148,17 @@ export class MoveLinesCommand implements ICommand {
}
};
let newIndentationForMovingBlock = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(
let newIndentatOfMovingBlock = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(
movingLineNumber, 1), s.startLineNumber + 1, indentConverter);
if (newIndentationForMovingBlock !== null) {
let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber));
let newSpaceCnt = this.getSpaceCnt(newIndentationForMovingBlock, tabSize);
let oldSpaceCnt = this.getSpaceCnt(oldIndentation, tabSize);
if (newIndentatOfMovingBlock !== null) {
const oldIndentation = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber));
const newSpaceCnt = IndentUtil.getSpaceCnt(newIndentatOfMovingBlock, tabSize);
const oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndentation, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
let spaceCntOffset = newSpaceCnt - oldSpaceCnt;
const spaceCntOffset = newSpaceCnt - oldSpaceCnt;
for (let i = s.startLineNumber; i <= s.endLineNumber; i++) {
let lineContent = model.getLineContent(i);
let originalIndent = strings.getLeadingWhitespace(lineContent);
let originalSpacesCnt = this.getSpaceCnt(originalIndent, tabSize);
let newSpacesCnt = originalSpacesCnt + spaceCntOffset;
let newIndent = this.generateIndent(newSpacesCnt, tabSize, insertSpaces);
if (newIndent !== originalIndent) {
builder.addEditOperation(new Range(i, 1, i, originalIndent.length + 1), newIndent);
}
}
this.getIndentEditsOfMovingBlock(model, builder, s.startLineNumber, s.endLineNumber, tabSize, insertSpaces, spaceCntOffset);
}
}
} else {
......@@ -183,7 +174,7 @@ export class MoveLinesCommand implements ICommand {
// Insert line that needs to be moved after
builder.addEditOperation(new Range(s.endLineNumber, model.getLineMaxColumn(s.endLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), '\n' + movingLineText);
if (this._autoIndent && (model.getLanguageIdAtPosition(s.startLineNumber, 1) === model.getLanguageIdAtPosition(s.endLineNumber, 1))) {
if (this.isAutoIndent(model, s)) {
virtualModel.getLineContent = (lineNumber: number) => {
if (lineNumber === movingLineNumber) {
return model.getLineContent(s.startLineNumber);
......@@ -192,26 +183,16 @@ export class MoveLinesCommand implements ICommand {
}
};
let newIndentation = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(s.startLineNumber, 1), movingLineNumber, indentConverter);
if (newIndentation !== null) {
let indentOfFirstLine = LanguageConfigurationRegistry.getGoodIndentForLine(virtualModel, model.getLanguageIdAtPosition(s.startLineNumber, 1), movingLineNumber, indentConverter);
if (indentOfFirstLine !== null) {
// adjust the indentation of the moving block
let oldIndentation = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber));
let newSpaceCnt = this.getSpaceCnt(newIndentation, tabSize);
let oldSpaceCnt = this.getSpaceCnt(oldIndentation, tabSize);
let oldIndent = strings.getLeadingWhitespace(model.getLineContent(s.startLineNumber));
let newSpaceCnt = IndentUtil.getSpaceCnt(indentOfFirstLine, tabSize);
let oldSpaceCnt = IndentUtil.getSpaceCnt(oldIndent, tabSize);
if (newSpaceCnt !== oldSpaceCnt) {
let spaceCntOffset = newSpaceCnt - oldSpaceCnt;
for (let i = s.startLineNumber; i <= s.endLineNumber; i++) {
let lineContent = model.getLineContent(i);
let originalIndent = strings.getLeadingWhitespace(lineContent);
let originalSpacesCnt = this.getSpaceCnt(originalIndent, tabSize);
let newSpacesCnt = originalSpacesCnt + spaceCntOffset;
let newIndent = this.generateIndent(newSpacesCnt, tabSize, insertSpaces);
if (newIndent !== originalIndent) {
builder.addEditOperation(new Range(i, 1, i, originalIndent.length + 1), newIndent);
}
}
this.getIndentEditsOfMovingBlock(model, builder, s.startLineNumber, s.endLineNumber, tabSize, insertSpaces, spaceCntOffset);
}
}
}
......@@ -221,37 +202,22 @@ export class MoveLinesCommand implements ICommand {
this._selectionId = builder.trackSelection(s);
}
private getSpaceCnt(str, tabSize) {
let spacesCnt = 0;
for (let i = 0; i < str.length; i++) {
if (str.charAt(i) === '\t') {
spacesCnt += tabSize;
} else {
spacesCnt++;
}
}
return spacesCnt;
private isAutoIndent(model: ITokenizedModel, selection: Selection) {
return this._autoIndent && (model.getLanguageIdAtPosition(selection.startLineNumber, 1) === model.getLanguageIdAtPosition(selection.endLineNumber, 1));
}
private generateIndent(spacesCnt: number, tabSize, insertSpaces) {
spacesCnt = spacesCnt < 0 ? 0 : spacesCnt;
private getIndentEditsOfMovingBlock(model: ITokenizedModel, builder: IEditOperationBuilder, startLineNumber: number, endLineNumber: number, tabSize: number, insertSpaces: boolean, offset: number) {
for (let i = startLineNumber; i <= endLineNumber; i++) {
let lineContent = model.getLineContent(i);
let originalIndent = strings.getLeadingWhitespace(lineContent);
let originalSpacesCnt = IndentUtil.getSpaceCnt(originalIndent, tabSize);
let newSpacesCnt = originalSpacesCnt + offset;
let newIndent = IndentUtil.generateIndent(newSpacesCnt, tabSize, insertSpaces);
let result = '';
if (!insertSpaces) {
let tabsCnt = Math.floor(spacesCnt / tabSize);
spacesCnt = spacesCnt % tabSize;
for (let i = 0; i < tabsCnt; i++) {
result += '\t';
if (newIndent !== originalIndent) {
builder.addEditOperation(new Range(i, 1, i, originalIndent.length + 1), newIndent);
}
}
for (let i = 0; i < spacesCnt; i++) {
result += ' ';
}
return result;
}
public computeCursorState(model: ITokenizedModel, helper: ICursorStateComputerData): Selection {
......
......@@ -3,10 +3,13 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Selection } from 'vs/editor/common/core/selection';
import { MoveLinesCommand } from 'vs/editor/contrib/linesOperations/common/moveLinesCommand';
import { testCommand } from 'vs/editor/test/common/commands/commandTestUtils';
import { MockMode } from 'vs/editor/test/common/mocks/mockMode';
import { LanguageIdentifier } from 'vs/editor/common/modes';
import { IndentationRule } from 'vs/editor/common/modes/languageConfiguration';
import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageConfigurationRegistry';
function testMoveLinesDownCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
testCommand(lines, null, selection, (sel) => new MoveLinesCommand(sel, true, false), expectedLines, expectedSelection);
......@@ -16,6 +19,14 @@ function testMoveLinesUpCommand(lines: string[], selection: Selection, expectedL
testCommand(lines, null, selection, (sel) => new MoveLinesCommand(sel, false, false), expectedLines, expectedSelection);
}
function testMoveLinesDownWithIndentCommand(languageId: LanguageIdentifier, lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
testCommand(lines, languageId, selection, (sel) => new MoveLinesCommand(sel, true, true), expectedLines, expectedSelection);
}
function testMoveLinesUpWithIndentCommand(languageId: LanguageIdentifier, lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void {
testCommand(lines, languageId, selection, (sel) => new MoveLinesCommand(sel, false, true), expectedLines, expectedSelection);
}
suite('Editor Contrib - Move Lines Command', () => {
test('move first up / last down disabled', function () {
......@@ -248,3 +259,73 @@ suite('Editor Contrib - Move Lines Command', () => {
});
});
class IndentRulesMode extends MockMode {
private static _id = new LanguageIdentifier('moveLinesIndentMode', 7);
constructor(indentationRules: IndentationRule) {
super(IndentRulesMode._id);
this._register(LanguageConfigurationRegistry.register(this.getLanguageIdentifier(), {
indentationRules: indentationRules
}));
}
}
suite('Editor contrib - Move Lines Command honors Indentation Rules', () => {
let indentRules = {
decreaseIndentPattern: /^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$/,
increaseIndentPattern: /(\{[^}"'`]*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$/,
indentNextLinePattern: /^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$)/,
unIndentedLinePattern: /^(?!.*([;{}]|\S:)\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!.*(\{[^}"']*|\([^)"']*|\[[^\]"']*|^\s*(\{\}|\(\)|\[\]|(case\b.*|default):))\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*((?!\S.*\/[*]).*[*]\/\s*)?[})\]]|^\s*(case\b.*|default):\s*(\/\/.*|\/[*].*[*]\/\s*)?$)(?!^\s*(for|while|if|else)\b(?!.*[;{}]\s*(\/\/.*|\/[*].*[*]\/\s*)?$))/
};
// https://github.com/Microsoft/vscode/issues/28552#issuecomment-307862797
test('first line indentation adjust to 0', () => {
let mode = new IndentRulesMode(indentRules);
testMoveLinesUpWithIndentCommand(
mode.getLanguageIdentifier(),
[
'class X {',
'\tz = 2',
'}'
],
new Selection(2, 1, 2, 1),
[
'z = 2',
'class X {',
'}'
],
new Selection(1, 1, 1, 1)
);
mode.dispose();
});
// https://github.com/Microsoft/vscode/issues/28552#issuecomment-307867717
test('move lines across block', () => {
let mode = new IndentRulesMode(indentRules);
testMoveLinesDownWithIndentCommand(
mode.getLanguageIdentifier(),
[
'const value = 2;',
'const standardLanguageDescriptions = [',
' {',
' diagnosticSource: \'js\',',
' }',
'];'
],
new Selection(1, 1, 1, 1),
[
'const standardLanguageDescriptions = [',
' const value = 2;',
' {',
' diagnosticSource: \'js\',',
' }',
'];'
],
new Selection(2, 5, 2, 5)
);
mode.dispose();
});
});
\ No newline at end of file
......@@ -2340,6 +2340,37 @@ suite('Editor Controller - Indentation Rules', () => {
});
});
test('Enter honors indentNextLinePattern 2', () => {
let model = Model.createFromString(
[
'if (true)',
'\tif (true)'
].join('\n'),
{
defaultEOL: DefaultEndOfLine.LF,
detectIndentation: false,
insertSpaces: false,
tabSize: 4,
trimAutoWhitespace: true
},
mode.getLanguageIdentifier()
);
withMockCodeEditor(null, { model: model, autoIndent: true }, (editor, cursor) => {
moveTo(cursor, 2, 11, false);
assertCursor(cursor, new Selection(2, 11, 2, 11));
cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard');
assertCursor(cursor, new Selection(3, 3, 3, 3));
cursorCommand(cursor, H.Type, { text: 'console.log();' }, 'keyboard');
cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard');
assertCursor(cursor, new Selection(4, 1, 4, 1));
});
model.dispose();
});
test('Enter adjusts indentation of current line 1', () => {
usingCursor({
text: [
......@@ -2380,6 +2411,26 @@ suite('Editor Controller - Indentation Rules', () => {
});
});
test('Enter honors intential indent', () => {
usingCursor({
text: [
'if (true) {',
'\tif (true) {',
'return true;',
'}}'
],
languageIdentifier: mode.getLanguageIdentifier(),
modelOpts: { insertSpaces: false, tabSize: 4, detectIndentation: false, defaultEOL: DefaultEndOfLine.LF, trimAutoWhitespace: true }
}, (model, cursor) => {
moveTo(cursor, 3, 13, false);
assertCursor(cursor, new Selection(3, 13, 3, 13));
cursorCommand(cursor, H.Type, { text: '\n' }, 'keyboard');
assertCursor(cursor, new Selection(4, 1, 4, 1));
assert.equal(model.getLineContent(3), 'return true;', '001');
});
});
test('Enter supports selection 1', () => {
usingCursor({
text: [
......@@ -2765,6 +2816,40 @@ suite('Editor Controller - Indentation Rules', () => {
model.dispose();
});
test('type honors indentation rules: ruby keywords', () => {
let rubyMode = new IndentRulesMode({
increaseIndentPattern: /^\s*((begin|class|def|else|elsif|ensure|for|if|module|rescue|unless|until|when|while)|(.*\sdo\b))\b[^\{;]*$/,
decreaseIndentPattern: /^\s*([}\]]([,)]?\s*(#|$)|\.[a-zA-Z_]\w*\b)|(end|rescue|ensure|else|elsif|when)\b)/
});
let model = Model.createFromString(
[
'class Greeter',
' def initialize(name)',
' @name = name',
' en'
].join('\n'),
{
defaultEOL: DefaultEndOfLine.LF,
detectIndentation: false,
insertSpaces: true,
tabSize: 2,
trimAutoWhitespace: true
},
rubyMode.getLanguageIdentifier()
);
withMockCodeEditor(null, { model: model, autoIndent: true }, (editor, cursor) => {
moveTo(cursor, 4, 7, false);
assertCursor(cursor, new Selection(4, 7, 4, 7));
cursorCommand(cursor, H.Type, { text: 'd' }, 'keyboard');
assert.equal(model.getLineContent(4), ' end');
});
rubyMode.dispose();
model.dispose();
});
});
interface ICursorOpts {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册