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

Add tests and improve the implementation

上级 ab4cab8a
......@@ -438,7 +438,7 @@ export class WordOperations {
return new Range(lineNumber, column, position.lineNumber, position.column);
}
public static deleteInsideWord(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection): Range | null {
public static deleteInsideWord(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection): Range {
if (!selection.isEmpty()) {
return selection;
}
......@@ -453,42 +453,105 @@ export class WordOperations {
return this._deleteInsideWordDetermineDeleteRange(wordSeparators, model, position);
}
private static _charAtIsWhitespace(str: string, index: number): boolean {
const charCode = str.charCodeAt(index);
return (charCode === CharCode.Space || charCode === CharCode.Tab);
}
private static _deleteInsideWordWhitespace(model: ICursorSimpleModel, position: Position): Range | null {
const lineContent = model.getLineContent(position.lineNumber);
const startIndex1 = position.column - 1; // deleteRight
const startIndex2 = position.column - 2; // deleteLeft
const firstNonWhitespace = this._findFirstNonWhitespaceChar(lineContent, startIndex1);
const lastNonWhitespace = strings.lastNonWhitespaceIndex(lineContent, startIndex2);
if ((startIndex1 + 1 < firstNonWhitespace && lastNonWhitespace + 1 < startIndex2) || (startIndex1 + 1 >= firstNonWhitespace && lastNonWhitespace + 1 < startIndex2) || (startIndex1 + 1 <= firstNonWhitespace && lastNonWhitespace + 1 === startIndex2)) {
return new Range(position.lineNumber, lastNonWhitespace + 2, position.lineNumber, firstNonWhitespace + 1);
const lineContentLength = lineContent.length;
if (lineContentLength === 0) {
// empty line
return null;
}
return null;
let leftIndex = Math.max(position.column - 2, 0);
if (!this._charAtIsWhitespace(lineContent, leftIndex)) {
// touches a non-whitespace character to the left
return null;
}
let rightIndex = Math.min(position.column - 1, lineContentLength - 1);
if (!this._charAtIsWhitespace(lineContent, rightIndex)) {
// touches a non-whitespace character to the right
return null;
}
// walk over whitespace to the left
while (leftIndex > 0 && this._charAtIsWhitespace(lineContent, leftIndex - 1)) {
leftIndex--;
}
// walk over whitespace to the right
while (rightIndex + 1 < lineContentLength && this._charAtIsWhitespace(lineContent, rightIndex + 1)) {
rightIndex++;
}
return new Range(position.lineNumber, leftIndex + 1, position.lineNumber, rightIndex + 2);
}
private static _deleteInsideWordDetermineDeleteRange(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position): Range {
let lineNumber = position.lineNumber;
let column = position.column;
let columnEnd = position.column;
let lineContent = model.getLineContent(position.lineNumber);
const lineContent = model.getLineContent(position.lineNumber);
const lineLength = lineContent.length;
if (lineLength === 0) {
// empty line
if (position.lineNumber > 1) {
return new Range(position.lineNumber - 1, model.getLineMaxColumn(position.lineNumber - 1), position.lineNumber, 1);
} else {
if (position.lineNumber < model.getLineCount()) {
return new Range(position.lineNumber, 1, position.lineNumber + 1, 1);
} else {
// empty model
return new Range(position.lineNumber, 1, position.lineNumber, 1);
}
}
}
let prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, position);
const touchesWord = (word: IFindWordResult) => {
return (word.start + 1 <= position.column && position.column <= word.end + 1);
};
const createRangeWithPosition = (startColumn: number, endColumn: number) => {
startColumn = Math.min(startColumn, position.column);
endColumn = Math.max(endColumn, position.column);
return new Range(position.lineNumber, startColumn, position.lineNumber, endColumn);
};
const deleteWordAndAdjacentWhitespace = (word: IFindWordResult) => {
let startColumn = word.start + 1;
let endColumn = word.end + 1;
let expandedToTheRight = false;
while (endColumn - 1 < lineLength && this._charAtIsWhitespace(lineContent, endColumn - 1)) {
expandedToTheRight = true;
endColumn++;
}
if (!expandedToTheRight) {
while (startColumn > 1 && this._charAtIsWhitespace(lineContent, startColumn - 2)) {
startColumn--;
}
}
return createRangeWithPosition(startColumn, endColumn);
};
const prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, position);
if (prevWordOnLine && touchesWord(prevWordOnLine)) {
return deleteWordAndAdjacentWhitespace(prevWordOnLine);
}
const nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, position);
if (nextWordOnLine && touchesWord(nextWordOnLine)) {
return deleteWordAndAdjacentWhitespace(nextWordOnLine);
}
if (prevWordOnLine && nextWordOnLine) {
return createRangeWithPosition(prevWordOnLine.end + 1, nextWordOnLine.start + 1);
}
if (prevWordOnLine) {
column = prevWordOnLine.start + 1;
columnEnd = prevWordOnLine.end + 1;
const chCode = lineContent.charCodeAt(columnEnd - 1);
if (chCode === CharCode.Space || chCode === CharCode.Tab) {
columnEnd++;
}
} else {
if (column > 1) {
column = 1;
} else {
lineNumber--;
column = model.getLineMaxColumn(lineNumber);
}
return createRangeWithPosition(prevWordOnLine.start + 1, prevWordOnLine.end + 1);
}
return new Range(lineNumber, column, position.lineNumber, columnEnd);
if (nextWordOnLine) {
return createRangeWithPosition(nextWordOnLine.start + 1, nextWordOnLine.end + 1);
}
return createRangeWithPosition(1, lineLength + 1);
}
public static _deleteWordPartLeft(model: ICursorSimpleModel, selection: Selection): Range {
......
......@@ -9,7 +9,7 @@ import { EditorCommand } from 'vs/editor/browser/editorExtensions';
import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection';
import { deserializePipePositions, serializePipePositions, testRepeatedActionAndExtractPositions } from 'vs/editor/contrib/wordOperations/test/wordTestUtils';
import { CursorWordEndLeft, CursorWordEndLeftSelect, CursorWordEndRight, CursorWordEndRightSelect, CursorWordLeft, CursorWordLeftSelect, CursorWordRight, CursorWordRightSelect, CursorWordStartLeft, CursorWordStartLeftSelect, CursorWordStartRight, CursorWordStartRightSelect, DeleteWordEndLeft, DeleteWordEndRight, DeleteWordLeft, DeleteWordRight, DeleteWordStartLeft, DeleteWordStartRight, CursorWordAccessibilityLeft, CursorWordAccessibilityLeftSelect, CursorWordAccessibilityRight, CursorWordAccessibilityRightSelect } from 'vs/editor/contrib/wordOperations/wordOperations';
import { CursorWordEndLeft, CursorWordEndLeftSelect, CursorWordEndRight, CursorWordEndRightSelect, CursorWordLeft, CursorWordLeftSelect, CursorWordRight, CursorWordRightSelect, CursorWordStartLeft, CursorWordStartLeftSelect, CursorWordStartRight, CursorWordStartRightSelect, DeleteWordEndLeft, DeleteWordEndRight, DeleteWordLeft, DeleteWordRight, DeleteWordStartLeft, DeleteWordStartRight, CursorWordAccessibilityLeft, CursorWordAccessibilityLeftSelect, CursorWordAccessibilityRight, CursorWordAccessibilityRightSelect, DeleteInsideWord } from 'vs/editor/contrib/wordOperations/wordOperations';
import { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
......@@ -42,6 +42,7 @@ suite('WordOperations', () => {
const _deleteWordRight = new DeleteWordRight();
const _deleteWordStartRight = new DeleteWordStartRight();
const _deleteWordEndRight = new DeleteWordEndRight();
const _deleteInsideWord = new DeleteInsideWord();
function runEditorCommand(editor: ICodeEditor, command: EditorCommand): void {
command.runEditorCommand(null, editor, null);
......@@ -88,6 +89,9 @@ suite('WordOperations', () => {
function deleteWordEndRight(editor: ICodeEditor): void {
runEditorCommand(editor, _deleteWordEndRight);
}
function deleteInsideWord(editor: ICodeEditor): void {
runEditorCommand(editor, _deleteInsideWord);
}
test('cursorWordLeft - simple', () => {
const EXPECTED = [
......@@ -750,4 +754,119 @@ suite('WordOperations', () => {
model.dispose();
mode.dispose();
});
test('deleteInsideWord - empty line', () => {
withTestCodeEditor([
'Line1',
'',
'Line2'
], {}, (editor, _) => {
const model = editor.getModel()!;
editor.setPosition(new Position(2, 1));
deleteInsideWord(editor);
assert.equal(model.getValue(), 'Line1\nLine2');
});
});
test('deleteInsideWord - in whitespace 1', () => {
withTestCodeEditor([
'Just some text.'
], {}, (editor, _) => {
const model = editor.getModel()!;
editor.setPosition(new Position(1, 6));
deleteInsideWord(editor);
assert.equal(model.getValue(), 'Justsome text.');
});
});
test('deleteInsideWord - in whitespace 2', () => {
withTestCodeEditor([
'Just some text.'
], {}, (editor, _) => {
const model = editor.getModel()!;
editor.setPosition(new Position(1, 6));
deleteInsideWord(editor);
assert.equal(model.getValue(), 'Justsome text.');
});
});
test('deleteInsideWord - in whitespace 3', () => {
withTestCodeEditor([
'Just "some text.'
], {}, (editor, _) => {
const model = editor.getModel()!;
editor.setPosition(new Position(1, 6));
deleteInsideWord(editor);
assert.equal(model.getValue(), 'Just"some text.');
deleteInsideWord(editor);
assert.equal(model.getValue(), '"some text.');
deleteInsideWord(editor);
assert.equal(model.getValue(), 'some text.');
deleteInsideWord(editor);
assert.equal(model.getValue(), 'text.');
deleteInsideWord(editor);
assert.equal(model.getValue(), '.');
deleteInsideWord(editor);
assert.equal(model.getValue(), '');
deleteInsideWord(editor);
assert.equal(model.getValue(), '');
});
});
test('deleteInsideWord - in non-words', () => {
withTestCodeEditor([
'x=3+4+5+6'
], {}, (editor, _) => {
const model = editor.getModel()!;
editor.setPosition(new Position(1, 7));
deleteInsideWord(editor);
assert.equal(model.getValue(), 'x=3+45+6');
deleteInsideWord(editor);
assert.equal(model.getValue(), 'x=3++6');
deleteInsideWord(editor);
assert.equal(model.getValue(), 'x=36');
deleteInsideWord(editor);
assert.equal(model.getValue(), 'x=');
deleteInsideWord(editor);
assert.equal(model.getValue(), 'x');
deleteInsideWord(editor);
assert.equal(model.getValue(), '');
deleteInsideWord(editor);
assert.equal(model.getValue(), '');
});
});
test('deleteInsideWord - in words 1', () => {
withTestCodeEditor([
'This is interesting'
], {}, (editor, _) => {
const model = editor.getModel()!;
editor.setPosition(new Position(1, 7));
deleteInsideWord(editor);
assert.equal(model.getValue(), 'This interesting');
deleteInsideWord(editor);
assert.equal(model.getValue(), 'This');
deleteInsideWord(editor);
assert.equal(model.getValue(), '');
deleteInsideWord(editor);
assert.equal(model.getValue(), '');
});
});
test('deleteInsideWord - in words 2', () => {
withTestCodeEditor([
'This is interesting'
], {}, (editor, _) => {
const model = editor.getModel()!;
editor.setPosition(new Position(1, 7));
deleteInsideWord(editor);
assert.equal(model.getValue(), 'This interesting');
deleteInsideWord(editor);
assert.equal(model.getValue(), 'This');
deleteInsideWord(editor);
assert.equal(model.getValue(), '');
deleteInsideWord(editor);
assert.equal(model.getValue(), '');
});
});
});
......@@ -498,7 +498,7 @@ export class DeleteInsideWord extends EditorCommand {
const selections = editor.getSelections();
const commands = selections.map((sel) => {
const deleteRange = this._delete(wordSeparators, model, sel);
const deleteRange = WordOperations.deleteInsideWord(wordSeparators, model, sel);
return new ReplaceCommand(deleteRange, '');
});
......@@ -506,16 +506,6 @@ export class DeleteInsideWord extends EditorCommand {
editor.executeCommands(this.id, commands);
editor.pushUndoStop();
}
private _delete(wordSeparators: WordCharacterClassifier, model: ITextModel, selection: Selection): Range {
let r = WordOperations.deleteInsideWord(wordSeparators, model, selection);
if (r) {
return r;
}
const lineCount = model.getLineCount();
const maxColumn = model.getLineMaxColumn(lineCount);
return new Range(lineCount, maxColumn, lineCount, maxColumn);
}
}
registerEditorCommand(new CursorWordStartLeft());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册