diff --git a/src/history/historyTracker.ts b/src/history/historyTracker.ts index 1fea94527..61b1fd2f7 100644 --- a/src/history/historyTracker.ts +++ b/src/history/historyTracker.ts @@ -526,20 +526,42 @@ export class HistoryTracker { * Adds a mark. */ public addMark(document: vscode.TextDocument, position: Position, markName: string): void { - // Sets previous context mark (adds current position to jump list). - if (markName === "'" || markName === '`') { - return globalState.jumpTracker.recordJump(Jump.fromStateNow(this.vimState)); + globalState.jumpTracker.recordJump(Jump.fromStateNow(this.vimState)); + } else if (markName === '<') { + if (this.vimState.lastVisualSelection) { + this.vimState.lastVisualSelection.start = position; + } else { + // TODO: Not quite right: only "< should be set. + this.vimState.lastVisualSelection = { + mode: Mode.Visual, + start: position, + end: position.getRight(), + }; + } + // TODO: Ensure "> is always after "< + } else if (markName === '>') { + if (this.vimState.lastVisualSelection) { + this.vimState.lastVisualSelection.end = position.getRight(); + } else { + // TODO: Not quite right: only "> should be set. + this.vimState.lastVisualSelection = { + mode: Mode.Visual, + start: position, + end: position.getRight(), + }; + } + // TODO: Ensure "< is always before "> + } else { + const isUppercaseMark = markName.toUpperCase() === markName; + const newMark: IMark = { + position, + name: markName, + isUppercaseMark, + document: isUppercaseMark ? document : undefined, + }; + this.putMarkInList(newMark); } - - const isUppercaseMark = markName.toUpperCase() === markName; - const newMark: IMark = { - position, - name: markName, - isUppercaseMark, - document: isUppercaseMark ? document : undefined, - }; - this.putMarkInList(newMark); } /** diff --git a/test/marks.test.ts b/test/marks.test.ts new file mode 100644 index 000000000..44fe66ee1 --- /dev/null +++ b/test/marks.test.ts @@ -0,0 +1,107 @@ +import * as vscode from 'vscode'; +import { strict as assert } from 'assert'; +import { getAndUpdateModeHandler } from '../extensionBase'; +import { Mode } from '../src/mode/mode'; +import { Configuration } from './testConfiguration'; +import { newTest, newTestSkip } from './testSimplifier'; +import { cleanUpWorkspace, setupWorkspace } from './testUtils'; +import { ModeHandler } from '../src/mode/modeHandler'; + +suite('Marks', async () => { + let modeHandler: ModeHandler; + + suiteSetup(async () => { + await setupWorkspace(); + modeHandler = (await getAndUpdateModeHandler())!; + }); + suiteTeardown(cleanUpWorkspace); + + const jumpToNewFile = async () => { + const configuration = new Configuration(); + configuration.tabstop = 4; + configuration.expandtab = false; + await setupWorkspace(configuration); + return (await getAndUpdateModeHandler())!; + }; + + test(`Capital marks can change the editor's active document`, async () => { + const firstDocumentName = vscode.window.activeTextEditor!.document.fileName; + await modeHandler.handleMultipleKeyEvents('mA'.split('')); + + const otherModeHandler = await jumpToNewFile(); + const otherDocumentName = vscode.window.activeTextEditor!.document.fileName; + assert.notStrictEqual(firstDocumentName, otherDocumentName); + + await otherModeHandler.handleMultipleKeyEvents(`'A`.split('')); + assert.strictEqual(vscode.window.activeTextEditor!.document.fileName, firstDocumentName); + }); + + newTest({ + title: 'Can jump to lowercase mark', + start: ['|hello world and mars'], + keysPressed: 'wma2w`a', + end: ['hello |world and mars'], + endMode: Mode.Normal, + }); + + suite('"< and ">', () => { + newTest({ + title: '"< set by Visual mode', + start: ['one', 't|wo', 'three'], + keysPressed: 'vjl' + 'gg' + '`<', + end: ['one', 't|wo', 'three'], + }); + newTest({ + title: '"> set by Visual mode', + start: ['one', 't|wo', 'three'], + keysPressed: 'vjl' + 'gg' + '`>', + end: ['one', 'two', 'th|ree'], + }); + + newTest({ + title: '"< set by m<', + start: ['one', 't|wo', 'three'], + keysPressed: 'm<' + 'gg' + '`<', + end: ['one', 't|wo', 'three'], + }); + newTest({ + title: '"> set by m>', + start: ['one', 't|wo', 'three'], + keysPressed: 'm>' + 'gg' + '`>', + end: ['one', 't|wo', 'three'], + }); + + newTestSkip({ + // TODO + title: 'gv fails if "< is not set', + start: ['one', 't|wo', 'three'], + keysPressed: 'm>' + 'gg' + 'gv', + end: ['|one', 'two', 'three'], + endMode: Mode.Normal, + }); + newTestSkip({ + // TODO + title: 'gv fails if "> is not set', + start: ['one', 't|wo', 'three'], + keysPressed: 'm<' + 'gg' + 'gv', + end: ['|one', 'two', 'three'], + endMode: Mode.Normal, + }); + + newTest({ + title: 'gv is affected by m<', + start: ['one', 't|wo', 'three'], + keysPressed: 'v' + 'k' + 'm<' + 'G' + 'gvd', + end: ['o|o', 'three'], + }); + newTest({ + title: 'gv is affected by m>', + start: ['one', 't|wo', 'three'], + keysPressed: 'v' + 'j' + 'm>' + 'G' + 'gvd', + end: ['one', 't|ree'], + }); + + // TODO: If m> AFTER "<: "< set to "> and gv fails + // TODO: If m< AFTER ">: "> set to "< and gv fails + }); +}); diff --git a/test/mode/modeNormal.test.ts b/test/mode/modeNormal.test.ts index db5bd694d..486b159f1 100755 --- a/test/mode/modeNormal.test.ts +++ b/test/mode/modeNormal.test.ts @@ -1,4 +1,3 @@ -import * as vscode from 'vscode'; import * as assert from 'assert'; import { getAndUpdateModeHandler } from '../../extension'; import { Mode } from '../../src/mode/mode'; @@ -2759,36 +2758,6 @@ suite('Mode Normal', () => { end: ['\t |hello world', 'hello', 'hi hello', 'very long line at the bottom'], }); - suite('marks', async () => { - const jumpToNewFile = async () => { - const configuration = new Configuration(); - configuration.tabstop = 4; - configuration.expandtab = false; - await setupWorkspace(configuration); - return (await getAndUpdateModeHandler())!; - }; - - test('capital marks can change the editors active document', async () => { - const firstDocumentName = vscode.window.activeTextEditor!.document.fileName; - await modeHandler.handleMultipleKeyEvents('mA'.split('')); - - const otherModeHandler = await jumpToNewFile(); - const otherDocumentName = vscode.window.activeTextEditor!.document.fileName; - assert.notStrictEqual(firstDocumentName, otherDocumentName); - - await otherModeHandler.handleMultipleKeyEvents(`'A`.split('')); - assert.strictEqual(vscode.window.activeTextEditor!.document.fileName, firstDocumentName); - }); - - newTest({ - title: `can jump to lowercase mark`, - start: ['|hello world and mars'], - keysPressed: 'wma2w`a', - end: ['hello |world and mars'], - endMode: Mode.Normal, - }); - }); - suite('', () => { // TODO: test with untitled file // TODO: test [count]