From 9758b24c38e2c0e65d37e983070d16417a6d54ed Mon Sep 17 00:00:00 2001 From: tshino Date: Wed, 30 Mar 2022 00:40:13 +0900 Subject: [PATCH 1/5] Use replacePreviousChar command instead of edit and move --- test/suite/playback_typing.test.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/test/suite/playback_typing.test.js b/test/suite/playback_typing.test.js index 82c6ce19..fb41ddcc 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(), [ From 3ed104c486bde64865915fa7db79864dd8d7040c Mon Sep 17 00:00:00 2001 From: tshino Date: Wed, 30 Mar 2022 19:49:10 +0900 Subject: [PATCH 2/5] Use replacePreviousChar command instead of edit and move --- test/suite/playback_typing.test.js | 56 +++++++++++------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/test/suite/playback_typing.test.js b/test/suite/playback_typing.test.js index fb41ddcc..f3a7ee04 100644 --- a/test/suite/playback_typing.test.js +++ b/test/suite/playback_typing.test.js @@ -425,10 +425,10 @@ 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 vscode.commands.executeCommand('type', { text: 'msg' }); await setSelections([[2, 17]]); // end of the line (moved by hitting TAB key) @@ -450,19 +450,16 @@ 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 vscode.commands.executeCommand('type', { text: 'a' }); - await setSelections([[5, 7]]); await setSelections([[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 vscode.commands.executeCommand('type', { text: '5' }); - await setSelections([[5, 21]]); await setSelections([[5, 23]]); keyboardMacro.finishRecording(); assert.deepStrictEqual(getSequence(), [ @@ -486,19 +483,17 @@ 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 vscode.commands.executeCommand('type', { text: 'say' }); - await setSelections([[4, 12]]); await setSelections([[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 setSelections([[5, 16]]); // bracket completion await vscode.commands.executeCommand('type', { text: 'name' }); await setSelections([[5, 21]]); await vscode.commands.executeCommand('type', { text: ';' }); @@ -531,27 +526,22 @@ 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([ [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 vscode.commands.executeCommand('type', { text: 'ary' }); - await setSelections([[4, 27], [5, 23]]); await setSelections([[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 keyboardMacro.finishRecording(); assert.deepStrictEqual(getSequence(), [ @@ -594,18 +584,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('漢字') From 7b317c3246f9cb8421f2d03d588a3ab37281bd03 Mon Sep 17 00:00:00 2001 From: tshino Date: Wed, 30 Mar 2022 20:09:45 +0900 Subject: [PATCH 3/5] Use real bracket completion in test instead of edit and move --- test/suite/playback_typing.test.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/suite/playback_typing.test.js b/test/suite/playback_typing.test.js index f3a7ee04..05b1276d 100644 --- a/test/suite/playback_typing.test.js +++ b/test/suite/playback_typing.test.js @@ -492,10 +492,9 @@ describe('Recording and Playback: Typing', () => { await setSelections([[4, 13, 4, 19]]); // placeholder 'params' await vscode.commands.executeCommand('type', { text: 'name' }); await setSelections([[5, 4]]); // inside the function block - await vscode.commands.executeCommand('type', { text: 'console.log(' }); - await setSelections([[5, 16]]); // bracket completion + 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(), [ @@ -508,8 +507,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);'); From 687d1634af5a8bee404ffc37c5ee5f1ce60e5619 Mon Sep 17 00:00:00 2001 From: tshino Date: Thu, 31 Mar 2022 02:07:53 +0900 Subject: [PATCH 4/5] Use wrap command to simulate cursor movement on snippet insertion --- test/suite/playback_typing.test.js | 39 ++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/test/suite/playback_typing.test.js b/test/suite/playback_typing.test.js index 05b1276d..92c878ec 100644 --- a/test/suite/playback_typing.test.js +++ b/test/suite/playback_typing.test.js @@ -414,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) + @@ -429,9 +442,9 @@ describe('Recording and Playback: Typing', () => { text: 'console.log();', replaceCharCnt: 3 }); - 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();'), @@ -454,13 +467,13 @@ describe('Recording and Playback: Typing', () => { text: 'const name = new type(arguments);', replaceCharCnt: 3 }); - 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, 14, 5, 18]]); // placeholder 'type' + await jumpToPlaceholder([[5, 14, 5, 18]]); // placeholder 'type' await vscode.commands.executeCommand('type', { text: 'Array' }); - 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, 23]]); + await jumpToPlaceholder([[5, 23]]); keyboardMacro.finishRecording(); assert.deepStrictEqual(getSequence(), [ Type('const name = new type(arguments);'), @@ -487,11 +500,11 @@ describe('Recording and Playback: Typing', () => { text: 'function name(params) {\n \n}', replaceCharCnt: 3 }); - 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, 13, 4, 19]]); // placeholder 'params' + await jumpToPlaceholder([[4, 13, 4, 19]]); // placeholder 'params' await vscode.commands.executeCommand('type', { text: 'name' }); - await setSelections([[5, 4]]); // inside the function block + 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 vscode.commands.executeCommand('type', { text: ')' }); // overwrite closing bracket @@ -532,15 +545,15 @@ describe('Recording and Playback: Typing', () => { '}', replaceCharCnt: 3 }); - 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, 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([[5, 10, 5, 17]]); // placeholder 'element' + await jumpToPlaceholder([[5, 10, 5, 17]]); // placeholder 'element' await vscode.commands.executeCommand('type', { text: 'el' }); - 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' + From 461f0e6bd4f26770da68447d2340f6a734af6219 Mon Sep 17 00:00:00 2001 From: tshino Date: Thu, 31 Mar 2022 21:55:24 +0900 Subject: [PATCH 5/5] Add tests for cursor motion detection --- test/suite/cursor_motion_detector.test.js | 71 ++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) 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) ],