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

Add tests and improve the implementation

上级 ab4cab8a
...@@ -438,7 +438,7 @@ export class WordOperations { ...@@ -438,7 +438,7 @@ export class WordOperations {
return new Range(lineNumber, column, position.lineNumber, position.column); 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()) { if (!selection.isEmpty()) {
return selection; return selection;
} }
...@@ -453,42 +453,105 @@ export class WordOperations { ...@@ -453,42 +453,105 @@ export class WordOperations {
return this._deleteInsideWordDetermineDeleteRange(wordSeparators, model, position); 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 { private static _deleteInsideWordWhitespace(model: ICursorSimpleModel, position: Position): Range | null {
const lineContent = model.getLineContent(position.lineNumber); const lineContent = model.getLineContent(position.lineNumber);
const startIndex1 = position.column - 1; // deleteRight const lineContentLength = lineContent.length;
const startIndex2 = position.column - 2; // deleteLeft
const firstNonWhitespace = this._findFirstNonWhitespaceChar(lineContent, startIndex1); if (lineContentLength === 0) {
const lastNonWhitespace = strings.lastNonWhitespaceIndex(lineContent, startIndex2); // empty line
if ((startIndex1 + 1 < firstNonWhitespace && lastNonWhitespace + 1 < startIndex2) || (startIndex1 + 1 >= firstNonWhitespace && lastNonWhitespace + 1 < startIndex2) || (startIndex1 + 1 <= firstNonWhitespace && lastNonWhitespace + 1 === startIndex2)) { return null;
return new Range(position.lineNumber, lastNonWhitespace + 2, position.lineNumber, firstNonWhitespace + 1);
} }
let leftIndex = Math.max(position.column - 2, 0);
if (!this._charAtIsWhitespace(lineContent, leftIndex)) {
// touches a non-whitespace character to the left
return null; return null;
} }
private static _deleteInsideWordDetermineDeleteRange(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position): Range { let rightIndex = Math.min(position.column - 1, lineContentLength - 1);
let lineNumber = position.lineNumber; if (!this._charAtIsWhitespace(lineContent, rightIndex)) {
let column = position.column; // touches a non-whitespace character to the right
let columnEnd = position.column; return null;
let lineContent = model.getLineContent(position.lineNumber); }
let prevWordOnLine = WordOperations._findPreviousWordOnLine(wordSeparators, model, position); // walk over whitespace to the left
while (leftIndex > 0 && this._charAtIsWhitespace(lineContent, leftIndex - 1)) {
leftIndex--;
}
if (prevWordOnLine) { // walk over whitespace to the right
column = prevWordOnLine.start + 1; while (rightIndex + 1 < lineContentLength && this._charAtIsWhitespace(lineContent, rightIndex + 1)) {
columnEnd = prevWordOnLine.end + 1; rightIndex++;
const chCode = lineContent.charCodeAt(columnEnd - 1); }
if (chCode === CharCode.Space || chCode === CharCode.Tab) {
columnEnd++; return new Range(position.lineNumber, leftIndex + 1, position.lineNumber, rightIndex + 2);
} }
private static _deleteInsideWordDetermineDeleteRange(wordSeparators: WordCharacterClassifier, model: ICursorSimpleModel, position: Position): Range {
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 { } else {
if (column > 1) { if (position.lineNumber < model.getLineCount()) {
column = 1; return new Range(position.lineNumber, 1, position.lineNumber + 1, 1);
} else { } else {
lineNumber--; // empty model
column = model.getLineMaxColumn(lineNumber); return new Range(position.lineNumber, 1, position.lineNumber, 1);
}
}
}
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 new Range(lineNumber, column, position.lineNumber, columnEnd); 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) {
return createRangeWithPosition(prevWordOnLine.start + 1, prevWordOnLine.end + 1);
}
if (nextWordOnLine) {
return createRangeWithPosition(nextWordOnLine.start + 1, nextWordOnLine.end + 1);
}
return createRangeWithPosition(1, lineLength + 1);
} }
public static _deleteWordPartLeft(model: ICursorSimpleModel, selection: Selection): Range { public static _deleteWordPartLeft(model: ICursorSimpleModel, selection: Selection): Range {
......
...@@ -9,7 +9,7 @@ import { EditorCommand } from 'vs/editor/browser/editorExtensions'; ...@@ -9,7 +9,7 @@ import { EditorCommand } from 'vs/editor/browser/editorExtensions';
import { Position } from 'vs/editor/common/core/position'; import { Position } from 'vs/editor/common/core/position';
import { Selection } from 'vs/editor/common/core/selection'; import { Selection } from 'vs/editor/common/core/selection';
import { deserializePipePositions, serializePipePositions, testRepeatedActionAndExtractPositions } from 'vs/editor/contrib/wordOperations/test/wordTestUtils'; 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 { withTestCodeEditor } from 'vs/editor/test/browser/testCodeEditor';
import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands';
import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl'; import { ViewModel } from 'vs/editor/common/viewModel/viewModelImpl';
...@@ -42,6 +42,7 @@ suite('WordOperations', () => { ...@@ -42,6 +42,7 @@ suite('WordOperations', () => {
const _deleteWordRight = new DeleteWordRight(); const _deleteWordRight = new DeleteWordRight();
const _deleteWordStartRight = new DeleteWordStartRight(); const _deleteWordStartRight = new DeleteWordStartRight();
const _deleteWordEndRight = new DeleteWordEndRight(); const _deleteWordEndRight = new DeleteWordEndRight();
const _deleteInsideWord = new DeleteInsideWord();
function runEditorCommand(editor: ICodeEditor, command: EditorCommand): void { function runEditorCommand(editor: ICodeEditor, command: EditorCommand): void {
command.runEditorCommand(null, editor, null); command.runEditorCommand(null, editor, null);
...@@ -88,6 +89,9 @@ suite('WordOperations', () => { ...@@ -88,6 +89,9 @@ suite('WordOperations', () => {
function deleteWordEndRight(editor: ICodeEditor): void { function deleteWordEndRight(editor: ICodeEditor): void {
runEditorCommand(editor, _deleteWordEndRight); runEditorCommand(editor, _deleteWordEndRight);
} }
function deleteInsideWord(editor: ICodeEditor): void {
runEditorCommand(editor, _deleteInsideWord);
}
test('cursorWordLeft - simple', () => { test('cursorWordLeft - simple', () => {
const EXPECTED = [ const EXPECTED = [
...@@ -750,4 +754,119 @@ suite('WordOperations', () => { ...@@ -750,4 +754,119 @@ suite('WordOperations', () => {
model.dispose(); model.dispose();
mode.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 { ...@@ -498,7 +498,7 @@ export class DeleteInsideWord extends EditorCommand {
const selections = editor.getSelections(); const selections = editor.getSelections();
const commands = selections.map((sel) => { const commands = selections.map((sel) => {
const deleteRange = this._delete(wordSeparators, model, sel); const deleteRange = WordOperations.deleteInsideWord(wordSeparators, model, sel);
return new ReplaceCommand(deleteRange, ''); return new ReplaceCommand(deleteRange, '');
}); });
...@@ -506,16 +506,6 @@ export class DeleteInsideWord extends EditorCommand { ...@@ -506,16 +506,6 @@ export class DeleteInsideWord extends EditorCommand {
editor.executeCommands(this.id, commands); editor.executeCommands(this.id, commands);
editor.pushUndoStop(); 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()); registerEditorCommand(new CursorWordStartLeft());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册