diff --git a/test/suite/cursor_motion_detector.test.js b/test/suite/cursor_motion_detector.test.js index 6faee8bb..318c7990 100644 --- a/test/suite/cursor_motion_detector.test.js +++ b/test/suite/cursor_motion_detector.test.js @@ -194,9 +194,78 @@ describe('CursorMotionDetector', () => { expectedLogs: [ MoveRight(1) ] }); }); + + it('should detect implicit motion (split into multi-cursor)', async () => { + testDetection({ + init: [ new vscode.Selection(3, 7, 3, 7) ], + inputs: [ + { predicted: [ new vscode.Selection(3, 7, 3, 7) ] }, + { changed: [ new vscode.Selection(3, 10, 3, 10), new vscode.Selection(3, 12, 3, 12) ] } + ], + expectedLogs: [ Split([ 3, 5 ]) ] + }); + }); + it('should detect implicit motion (split multi to multi)', async () => { + testDetection({ + init: [ + new vscode.Selection(3, 7, 3, 7), + new vscode.Selection(6, 7, 6, 7) + ], + inputs: [ + { predicted: [ + new vscode.Selection(3, 7, 3, 7), + new vscode.Selection(6, 7, 6, 7) + ] }, + { changed: [ + new vscode.Selection(3, 10, 3, 10), new vscode.Selection(3, 12, 3, 12), + new vscode.Selection(6, 10, 6, 10), new vscode.Selection(6, 12, 6, 12) + ] } + ], + expectedLogs: [ Split([ 3, 5 ]) ] + }); + }); + it('should detect implicit motion (split into multi-cursor on different lines) (1)', async () => { + testDetection({ + init: [ new vscode.Selection(3, 7, 3, 7) ], + inputs: [ + { predicted: [ new vscode.Selection(3, 7, 3, 7) ] }, + { changed: [ new vscode.Selection(3, 10, 3, 10), new vscode.Selection(5, 2, 5, 2) ] } + ], + expectedLogs: [ Split2([ 3, 2 ], [0, 2]) ] + }); + }); + it('should detect implicit motion (split into multi-cursor on different lines) (2)', async () => { + testDetection({ + init: [ + new vscode.Selection(3, 7, 3, 7), + new vscode.Selection(6, 7, 6, 7) + ], + inputs: [ + { predicted: [ + new vscode.Selection(3, 7, 3, 7), + new vscode.Selection(6, 7, 6, 7) + ] }, + { changed: [ + new vscode.Selection(4, 2, 4, 2), new vscode.Selection(5, 7, 5, 7), + new vscode.Selection(7, 2, 7, 2), new vscode.Selection(8, 7, 8, 7), + ] } + ], + expectedLogs: [ Split2([ 2, 7 ], [ 1, 2 ]) ] + }); + }); + it('should detect implicit motion (split into multi-cursor with selection)', async () => { + testDetection({ + init: [ new vscode.Selection(3, 7, 3, 7) ], + inputs: [ + { predicted: [ new vscode.Selection(3, 7, 3, 7) ] }, + { changed: [ new vscode.Selection(3, 10, 3, 13), new vscode.Selection(3, 12, 3, 15) ] } + ], + expectedLogs: [ SplitSelect([ 3, 5 ], 3) ] + }); + }); }); - describe('implicit motion', () => { + describe('implicit motion without prediction', () => { it('should detect the unexpected motion of cursor (move to left)', async () => { testDetection({ init: [ new vscode.Selection(3, 6, 3, 6) ], diff --git a/test/suite/playback_typing.test.js b/test/suite/playback_typing.test.js index 82c6ce19..92c878ec 100644 --- a/test/suite/playback_typing.test.js +++ b/test/suite/playback_typing.test.js @@ -398,10 +398,7 @@ describe('Recording and Playback: Typing', () => { keyboardMacro.startRecording(); await vscode.commands.executeCommand('type', { text: 'a' }); await vscode.commands.executeCommand('type', { text: 'b' }); - await textEditor.edit(edit => { - edit.replace(new vscode.Selection(1, 0, 1, 2), 'Abcde'); - }); - await setSelections([[1, 5]]); + await vscode.commands.executeCommand('replacePreviousChar', { text: 'Abcde', replaceCharCnt: 2 }); await vscode.commands.executeCommand('type', { text: '.' }); keyboardMacro.finishRecording(); assert.deepStrictEqual(getSequence(), [ @@ -417,6 +414,19 @@ describe('Recording and Playback: Typing', () => { }); describe('snippet insertion', () => { + keyboardMacro.registerInternalCommand('internal:setSelections', async function(args) { + if (args && args.selections) { + textEditor.selections = TestUtil.arrayToSelections(args.selections); + } + }); + const jumpToPlaceholder = async function(selections) { + await keyboardMacro.wrapSync({ + command: 'internal:setSelections', + args: { selections }, + 'await': 'selection', + record: 'side-effect' + }); + }; beforeEach(async () => { await TestUtil.resetDocument(textEditor, ( '\n'.repeat(10) + @@ -428,13 +438,13 @@ describe('Recording and Playback: Typing', () => { await setSelections([[2, 0]]); keyboardMacro.startRecording(); await vscode.commands.executeCommand('type', { text: 'log' }); - await textEditor.edit(edit => { - edit.replace(new vscode.Selection(2, 0, 2, 3), 'console.log();'); + await vscode.commands.executeCommand('replacePreviousChar', { + text: 'console.log();', + replaceCharCnt: 3 }); - await setSelections([[2, 14]]); // end of the line - await setSelections([[2, 12]]); // in the parentheses + await jumpToPlaceholder([[2, 12]]); // inside the parentheses await vscode.commands.executeCommand('type', { text: 'msg' }); - await setSelections([[2, 17]]); // end of the line (moved by hitting TAB key) + await jumpToPlaceholder([[2, 17]]); // end of the line (moved by hitting TAB key) keyboardMacro.finishRecording(); assert.deepStrictEqual(getSequence(), [ Type('console.log();'), @@ -453,20 +463,17 @@ describe('Recording and Playback: Typing', () => { await setSelections([[5, 0]]); keyboardMacro.startRecording(); await vscode.commands.executeCommand('type', { text: 'new' }); - await textEditor.edit(edit => { - edit.replace(new vscode.Selection(5, 0, 5, 3), 'const name = new type(arguments);'); + await vscode.commands.executeCommand('replacePreviousChar', { + text: 'const name = new type(arguments);', + replaceCharCnt: 3 }); - await setSelections([[5, 33]]); // end of the line - await setSelections([[5, 6, 5, 10]]); // placeholder 'name' + await jumpToPlaceholder([[5, 6, 5, 10]]); // placeholder 'name' await vscode.commands.executeCommand('type', { text: 'a' }); - await setSelections([[5, 7]]); - await setSelections([[5, 14, 5, 18]]); // placeholder 'type' + await jumpToPlaceholder([[5, 14, 5, 18]]); // placeholder 'type' await vscode.commands.executeCommand('type', { text: 'Array' }); - await setSelections([[5, 19]]); - await setSelections([[5, 20, 5, 29]]); // placeholder 'arguments' + await jumpToPlaceholder([[5, 20, 5, 29]]); // placeholder 'arguments' await vscode.commands.executeCommand('type', { text: '5' }); - await setSelections([[5, 21]]); - await setSelections([[5, 23]]); + await jumpToPlaceholder([[5, 23]]); keyboardMacro.finishRecording(); assert.deepStrictEqual(getSequence(), [ Type('const name = new type(arguments);'), @@ -489,21 +496,18 @@ describe('Recording and Playback: Typing', () => { await setSelections([[4, 0]]); keyboardMacro.startRecording(); await vscode.commands.executeCommand('type', { text: 'fun' }); - await textEditor.edit(edit => { - edit.replace(new vscode.Selection(4, 0, 4, 3), 'function name(params) {\n \n}'); + await vscode.commands.executeCommand('replacePreviousChar', { + text: 'function name(params) {\n \n}', + replaceCharCnt: 3 }); - await setSelections([[6, 1]]); // end of the snippet - await setSelections([[4, 9, 4, 13]]); // placeholder 'name' + await jumpToPlaceholder([[4, 9, 4, 13]]); // placeholder 'name' await vscode.commands.executeCommand('type', { text: 'say' }); - await setSelections([[4, 12]]); - await setSelections([[4, 13, 4, 19]]); // placeholder 'params' + await jumpToPlaceholder([[4, 13, 4, 19]]); // placeholder 'params' await vscode.commands.executeCommand('type', { text: 'name' }); - await setSelections([[4, 17]]); - await setSelections([[5, 4]]); // inside the function block - await vscode.commands.executeCommand('type', { text: 'console.log(' }); - await setSelections([[5, 16]]); + await jumpToPlaceholder([[5, 4]]); // inside the function block + await vscode.commands.executeCommand('type', { text: 'console.log(' }); // triggers bracket completion await vscode.commands.executeCommand('type', { text: 'name' }); - await setSelections([[5, 21]]); + await vscode.commands.executeCommand('type', { text: ')' }); // overwrite closing bracket await vscode.commands.executeCommand('type', { text: ';' }); keyboardMacro.finishRecording(); assert.deepStrictEqual(getSequence(), [ @@ -516,8 +520,7 @@ describe('Recording and Playback: Typing', () => { Type('console.log()'), MoveLeft(1), Type('name'), - MoveRight(1), - Type(';') + ReplaceRight(1, ');') ]); assert.strictEqual(textEditor.document.lineAt(4).text, 'function say(name) {'); assert.strictEqual(textEditor.document.lineAt(5).text, ' console.log(name);'); @@ -534,28 +537,23 @@ describe('Recording and Playback: Typing', () => { await setSelections([[4, 0]]); keyboardMacro.startRecording(); await vscode.commands.executeCommand('type', { text: 'for' }); - await textEditor.edit(edit => { - edit.replace( - new vscode.Selection(4, 0, 4, 3), + await vscode.commands.executeCommand('replacePreviousChar', { + text: 'for (let index = 0; index < array.length; index++) {\n' + ' const element = array[index];\n' + ' \n' + - '}' - ); + '}', + replaceCharCnt: 3 }); - await setSelections([[7, 1]]); // end of the snippet - await setSelections([ + await jumpToPlaceholder([ [4, 9, 4, 14], [4, 20, 4, 25], [4, 42, 4, 47], [5, 26, 5, 31] ]); // placeholder 'index' await vscode.commands.executeCommand('type', { text: 'idx' }); - await setSelections([[4, 12], [4, 21], [4, 41], [5, 29]]); - await setSelections([[4, 24, 4, 29], [5, 20, 5, 25]]); // placeholder 'array' + await jumpToPlaceholder([[4, 24, 4, 29], [5, 20, 5, 25]]); // placeholder 'array' await vscode.commands.executeCommand('type', { text: 'ary' }); - await setSelections([[4, 27], [5, 23]]); - await setSelections([[5, 10, 5, 17]]); // placeholder 'element' + await jumpToPlaceholder([[5, 10, 5, 17]]); // placeholder 'element' await vscode.commands.executeCommand('type', { text: 'el' }); - await setSelections([[5, 12]]); - await setSelections([[6, 4]]); // the blank line in the block + await jumpToPlaceholder([[6, 4]]); // the blank line in the block keyboardMacro.finishRecording(); assert.deepStrictEqual(getSequence(), [ Type('for (let index = 0; index < array.length; index++) {\n' + @@ -597,18 +595,12 @@ describe('Recording and Playback: Typing', () => { await setSelections([[1, 0]]); keyboardMacro.startRecording(); await vscode.commands.executeCommand('type', { text: 'k' }); - await textEditor.edit(edit => edit.replace(new vscode.Selection(1, 0, 1, 1), 'か')); - await setSelections([[1, 1]]); - await textEditor.edit(edit => edit.replace(new vscode.Selection(1, 0, 1, 1), 'かn')); - await setSelections([[1, 2]]); - await textEditor.edit(edit => edit.replace(new vscode.Selection(1, 0, 1, 2), 'かんj')); - await setSelections([[1, 3]]); - await textEditor.edit(edit => edit.replace(new vscode.Selection(1, 0, 1, 3), 'かんじ')); - await setSelections([[1, 3]]); - await textEditor.edit(edit => edit.replace(new vscode.Selection(1, 0, 1, 3), '感じ')); - await setSelections([[1, 2]]); - await textEditor.edit(edit => edit.replace(new vscode.Selection(1, 0, 1, 2), '漢字')); - await setSelections([[1, 2]]); + await vscode.commands.executeCommand('replacePreviousChar', { text: 'か', replaceCharCnt: 1 }); + await vscode.commands.executeCommand('replacePreviousChar', { text: 'かn', replaceCharCnt: 1 }); + await vscode.commands.executeCommand('replacePreviousChar', { text: 'かんj', replaceCharCnt: 3 }); + await vscode.commands.executeCommand('replacePreviousChar', { text: 'かんじ', replaceCharCnt: 3 }); + await vscode.commands.executeCommand('replacePreviousChar', { text: '感じ', replaceCharCnt: 3 }); + await vscode.commands.executeCommand('replacePreviousChar', { text: '漢字', replaceCharCnt: 2 }); keyboardMacro.finishRecording(); assert.deepStrictEqual(getSequence(), [ Type('漢字')