Make gv affected by m< and m>. Fixes #8977.

Plus some general test cases for this feature.
There's still a few bugs here - I've added some TODOs.
This commit is contained in:
Jason Fields 2024-04-07 12:28:48 -04:00
parent 8fba985b5f
commit 8d9b2b9b2f
3 changed files with 141 additions and 43 deletions

View File

@ -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);
}
/**

107
test/marks.test.ts Normal file
View File

@ -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<Esc>' + 'gg' + '`<',
end: ['one', 't|wo', 'three'],
});
newTest({
title: '"> set by Visual mode',
start: ['one', 't|wo', 'three'],
keysPressed: 'vjl<Esc>' + '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<Esc>' + 'k' + 'm<' + 'G' + 'gvd',
end: ['o|o', 'three'],
});
newTest({
title: 'gv is affected by m>',
start: ['one', 't|wo', 'three'],
keysPressed: 'v<Esc>' + '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
});
});

View File

@ -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('<C-g>', () => {
// TODO: test with untitled file
// TODO: test [count]<C-g>