Skip to content

Commit

Permalink
Support [ and ] commands to go to start / end of previously operate…
Browse files Browse the repository at this point in the history
…d text (#4147)

Fixes #2004
  • Loading branch information
haifengkao authored and J-Fields committed Oct 26, 2019
1 parent e7f1cd7 commit bce62a7
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 12 deletions.
24 changes: 13 additions & 11 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,25 +132,27 @@ Now follows an exhaustive list of every known Vim command that we could find.

## Marks and motions

| Status | Command | Description |
| ------------------ | ----------------------------------------------------------- | -------------------------------------------------- |
| :white_check_mark: | m{a-zA-Z} | mark current position with mark {a-zA-Z} |
| Status | Command | Description |
| ------------------ | ----------------------------------------------------------- | ------------------------------------------------------ |
| :white_check_mark: | m{a-zA-Z} | mark current position with mark {a-zA-Z} |
| :white_check_mark: | `{a-z} | go to mark {a-z} within current file |
| :white_check_mark: | `{A-Z} | go to mark {A-Z} in any file |
| :white_check_mark: | `{0-9} | go to the position where Vim was previously exited |
| :white_check_mark: | `` | go to the position before the last jump |
| :arrow_down: | `" | go to the position when last editing this file |
| :arrow_down: | `[ | go to the start of the previously operated or put text |
| :arrow_down: | `] | go to the end of the previously operated or put text |
| :white_check_mark: | `[ | go to the start of the previously operated or put text |
| :white_check_mark: | '[ | go to the start of the previously operated or put text |
| :white_check_mark: | `] | go to the end of the previously operated or put text |
| :white_check_mark: | '] | go to the end of the previously operated or put text |
| :arrow_down: | `< | go to the start of the (previous) Visual area |
| :arrow_down: | `> | go to the end of the (previous) Visual area |
| :white_check_mark: | `. | go to the position of the last change in this file |
| :white_check_mark: | '. | go to the position of the last change in this file |
| :arrow_down: | '{a-zA-Z0-9[]'"<>.} | same as `, but on the first non-blank in the line |
| :arrow_down: | :marks | print the active marks |
| :white_check_mark: | :1234: CTRL-O | go to Nth older position in jump list |
| :white_check_mark: | :1234: CTRL-I | go to Nth newer position in jump list |
| :arrow_down: | :ju[mps] | print the jump list |
| :white_check_mark: | '. | go to the position of the last change in this file |
| :arrow_down: | '{a-zA-Z0-9[]'"<>.} | same as `, but on the first non-blank in the line |
| :arrow_down: | :marks | print the active marks |
| :white_check_mark: | :1234: CTRL-O | go to Nth older position in jump list |
| :white_check_mark: | :1234: CTRL-I | go to Nth newer position in jump list |
| :arrow_down: | :ju[mps] | print the jump list |

## Various motions

Expand Down
32 changes: 32 additions & 0 deletions src/actions/commands/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3008,6 +3008,38 @@ class CommandGoForwardInChangelist extends BaseCommand {
}
}

@RegisterAction
class CommandGoStartPrevOperatedText extends BaseCommand {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock];
keys = [['`', '['], ["'", '[']];
isJump = true;

public async exec(position: Position, vimState: VimState): Promise<VimState> {
const lastPos = vimState.historyTracker.getLastChangeStartPosition();
if (lastPos !== undefined) {
vimState.cursorStopPosition = lastPos;
}

return vimState;
}
}

@RegisterAction
class CommandGoEndPrevOperatedText extends BaseCommand {
modes = [ModeName.Normal, ModeName.Visual, ModeName.VisualLine, ModeName.VisualBlock];
keys = [['`', ']'], ["'", ']']];
isJump = true;

public async exec(position: Position, vimState: VimState): Promise<VimState> {
const lastPos = vimState.historyTracker.getLastChangeEndPosition();
if (lastPos !== undefined) {
vimState.cursorStopPosition = lastPos;
}

return vimState;
}
}

@RegisterAction
class CommandGoLastChange extends BaseCommand {
modes = [ModeName.Normal];
Expand Down
22 changes: 21 additions & 1 deletion src/history/historyTracker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -667,11 +667,18 @@ export class HistoryTracker {
if (this.currentHistoryStepIndex === 0) {
return undefined;
}

const lastChangeIndex = this.historySteps[this.currentHistoryStepIndex].changes.length;
if (lastChangeIndex === 0) {
return undefined;
}
return this.historySteps[this.currentHistoryStepIndex].changes[lastChangeIndex - 1].end();

const lastChange = this.historySteps[this.currentHistoryStepIndex].changes[lastChangeIndex - 1];
if (lastChange.isAdd) {
return lastChange.end();
}

return lastChange.start;
}

getLastHistoryStartPosition(): Position[] | undefined {
Expand All @@ -682,6 +689,19 @@ export class HistoryTracker {
return this.historySteps[this.currentHistoryStepIndex].cursorStart;
}

getLastChangeStartPosition(): Position | undefined {
if (this.currentHistoryStepIndex === 0) {
return undefined;
}

const lastChangeIndex = this.historySteps[this.currentHistoryStepIndex].changes.length;
if (lastChangeIndex === 0) {
return undefined;
}

return this.historySteps[this.currentHistoryStepIndex].changes[lastChangeIndex - 1].start;
}

setLastHistoryEndPosition(pos: Position[]) {
this.historySteps[this.currentHistoryStepIndex].cursorEnd = pos;
}
Expand Down
28 changes: 28 additions & 0 deletions test/mode/modeNormal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2071,6 +2071,34 @@ suite('Mode Normal', () => {
endMode: ModeName.Normal,
});

newTest({
title: '`] go to the end of the previously operated or put text',
start: ['hello|'],
keysPressed: 'a world<Esc>`]',
end: ['hello worl|d'],
});

newTest({
title: "'] go to the end of the previously operated or put text",
start: ['hello|'],
keysPressed: "a world<Esc>']",
end: ['hello worl|d'],
});

newTest({
title: '`[ go to the start of the previously operated or put text',
start: ['hello|'],
keysPressed: 'a world<Esc>`[',
end: ['hello| world'],
});

newTest({
title: "'[ go to the start of the previously operated or put text",
start: ['hello|'],
keysPressed: "a world<Esc>'[",
end: ['hello| world'],
});

suite('can handle gn', () => {
test(`gn selects the next match text`, async () => {
await modeHandler.handleMultipleKeyEvents('ifoo\nhello world\nhello\nhello'.split(''));
Expand Down

0 comments on commit bce62a7

Please sign in to comment.