Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement basic separator targets from targets.vim #9127

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,11 @@
"markdownDescription": "Enable last/next movements for bracket objects.",
"default": true
},
"vim.targets.separatorObjects.enable": {
"type": "boolean",
"markdownDescription": "Enable separator text objects.",
"default": true
},
"vim.targets.smartQuotes.enable": {
"type": "boolean",
"markdownDescription": "Enable the smart quotes movements from [targets.vim](https://github.com/wellle/targets.vim#quote-text-objects).",
Expand Down
335 changes: 335 additions & 0 deletions src/actions/plugins/targets/separators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,335 @@
import { TextObject } from '../../../textobject/textobject';
import { RegisterAction } from '../../base';
import { VimState } from '../../../state/vimState';
import { failedMovement, IMovement } from '../../baseMotion';
import { Position } from 'vscode';
import { isVisualMode } from '../../../mode/mode';
import { separatorObjectsEnabled } from './targetsConfig';

abstract class SeparatorTextObjectMovement extends TextObject {
protected abstract readonly separator: string;
protected abstract includeLeadingSeparator: boolean;

public override doesActionApply(vimState: VimState, keysPressed: string[]) {
return super.doesActionApply(vimState, keysPressed) && separatorObjectsEnabled();
}

public override couldActionApply(vimState: VimState, keysPressed: string[]) {
return super.couldActionApply(vimState, keysPressed) && separatorObjectsEnabled();
}

public async execAction(position: Position, vimState: VimState): Promise<IMovement> {
const res = this.matchSeparators(position, vimState);
if (res === undefined) {
return failedMovement(vimState);
}

let { start, stop } = res;

stop = stop.translate({ characterDelta: -1 });
if (!this.includeLeadingSeparator) {
start = start.getRightThroughLineBreaks(false);
}

if (!isVisualMode(vimState.currentMode) && position.isBefore(start)) {
vimState.recordedState.operatorPositionDiff = start.subtract(position);
} else if (!isVisualMode(vimState.currentMode) && position.isAfter(stop)) {
if (position.line === stop.line) {
vimState.recordedState.operatorPositionDiff = stop.getRight().subtract(position);
} else {
vimState.recordedState.operatorPositionDiff = start.subtract(position);
}
}

vimState.cursorStartPosition = start;
return {
start,
stop,
};
}

private matchSeparators(
position: Position,
vimState: VimState,
): { start: Position; stop: Position } | undefined {
let start = this.getPrevTarget(position, vimState);
let stop = this.getNextTarget(position, vimState);

if (start === undefined && stop !== undefined) {
start = stop;
stop = this.getNextTarget(start, vimState);
} else if (start !== undefined && stop === undefined) {
stop = start;
start = this.getPrevTarget(stop, vimState);
}
if (start === undefined || stop === undefined) {
return undefined;
}
return {
start,
stop,
};
}

private getPrevTarget(position: Position, vimState: VimState): Position | undefined {
let lineText = vimState.document.lineAt(position).text;
for (let i = position.character - 1; i >= 0; i--) {
if (lineText[i] === this.separator) {
return position.with({ character: i });
}
}

// If opening character not found, search backwards across lines
for (let line = position.line - 1; line >= 0; line--) {
lineText = vimState.document.lineAt(line).text;
const matchIndex = lineText.lastIndexOf(this.separator);
if (matchIndex !== -1) {
return position.with({ line, character: matchIndex });
}
}
return undefined;
}

private getNextTarget(position: Position, vimState: VimState): Position | undefined {
let lineText = vimState.document.lineAt(position).text;
for (let i = position.character + 1; i < lineText.length; i++) {
if (lineText[i] === this.separator) {
return position.with({ character: i });
}
}

// If closing character not found, search forwards across lines
for (let line = position.line + 1; line < vimState.document.lineCount; line++) {
lineText = vimState.document.lineAt(line).text;
const matchIndex = lineText.indexOf(this.separator);
if (matchIndex !== -1) {
return position.with({ line, character: matchIndex });
}
}
return undefined;
}
}

@RegisterAction
class SelectInsideComma extends SeparatorTextObjectMovement {
keys = ['i', ','];
readonly separator = ',';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundComma extends SeparatorTextObjectMovement {
keys = ['a', ','];
readonly separator = ',';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsidePeriod extends SeparatorTextObjectMovement {
keys = ['i', '.'];
readonly separator = '.';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundPeriod extends SeparatorTextObjectMovement {
keys = ['a', '.'];
readonly separator = '.';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideSemicolon extends SeparatorTextObjectMovement {
keys = ['i', ';'];
readonly separator = ';';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundSemicolon extends SeparatorTextObjectMovement {
keys = ['a', ';'];
readonly separator = ';';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideColon extends SeparatorTextObjectMovement {
keys = ['i', ':'];
readonly separator = ':';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundColon extends SeparatorTextObjectMovement {
keys = ['a', ':'];
readonly separator = ':';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsidePlus extends SeparatorTextObjectMovement {
keys = ['i', '+'];
readonly separator = '+';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundPlus extends SeparatorTextObjectMovement {
keys = ['a', '+'];
readonly separator = '+';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideMinus extends SeparatorTextObjectMovement {
keys = ['i', '-'];
readonly separator = '-';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundMinus extends SeparatorTextObjectMovement {
keys = ['a', '-'];
readonly separator = '-';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideEquals extends SeparatorTextObjectMovement {
keys = ['i', '='];
readonly separator = '=';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundEquals extends SeparatorTextObjectMovement {
keys = ['a', '='];
readonly separator = '=';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideTilde extends SeparatorTextObjectMovement {
keys = ['i', '~'];
readonly separator = '~';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundTilde extends SeparatorTextObjectMovement {
keys = ['a', '~'];
readonly separator = '~';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideUnderscore extends SeparatorTextObjectMovement {
keys = ['i', '_'];
readonly separator = '_';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundUnderscore extends SeparatorTextObjectMovement {
keys = ['a', '_'];
readonly separator = '_';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideAsterisk extends SeparatorTextObjectMovement {
keys = ['i', '*'];
readonly separator = '*';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundAsterisk extends SeparatorTextObjectMovement {
keys = ['a', '*'];
readonly separator = '*';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideHash extends SeparatorTextObjectMovement {
keys = ['i', '#'];
readonly separator = '#';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundHash extends SeparatorTextObjectMovement {
keys = ['a', '#'];
readonly separator = '#';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideSlash extends SeparatorTextObjectMovement {
keys = ['i', '/'];
readonly separator = '/';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundSlash extends SeparatorTextObjectMovement {
keys = ['a', '/'];
readonly separator = '/';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsidePipe extends SeparatorTextObjectMovement {
keys = ['i', '|'];
readonly separator = '|';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundPipe extends SeparatorTextObjectMovement {
keys = ['a', '|'];
readonly separator = '|';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideBackslash extends SeparatorTextObjectMovement {
keys = ['i', '\\'];
readonly separator = '\\';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundBackslash extends SeparatorTextObjectMovement {
keys = ['a', '\\'];
readonly separator = '\\';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideAmpersand extends SeparatorTextObjectMovement {
keys = ['i', '&'];
readonly separator = '&';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundAmpersand extends SeparatorTextObjectMovement {
keys = ['a', '&'];
readonly separator = '&';
readonly includeLeadingSeparator = true;
}

@RegisterAction
class SelectInsideDollar extends SeparatorTextObjectMovement {
keys = ['i', '$'];
readonly separator = '$';
readonly includeLeadingSeparator = false;
}

@RegisterAction
class SelectAroundDollar extends SeparatorTextObjectMovement {
keys = ['a', '$'];
readonly separator = '$';
readonly includeLeadingSeparator = true;
}
1 change: 1 addition & 0 deletions src/actions/plugins/targets/targets.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// targets sub-plugins
import './smartQuotes';
import './lastNextObjects';
import './separators';
9 changes: 9 additions & 0 deletions src/actions/plugins/targets/targetsConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,12 @@ export function bracketObjectsEnabled(): boolean {
configuration.targets.bracketObjects.enable === true)
);
}

export function separatorObjectsEnabled(): boolean {
return (
(configuration.targets.enable === true &&
configuration.targets.separatorObjects.enable !== false) ||
(configuration.targets.enable === undefined &&
configuration.targets.separatorObjects.enable === true)
);
}
4 changes: 4 additions & 0 deletions src/configuration/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,10 @@ class Configuration implements IConfiguration {
enable: true,
},

separatorObjects: {
enable: true,
},

smartQuotes: {
enable: false,
breakThroughLines: false,
Expand Down
1 change: 1 addition & 0 deletions src/configuration/iconfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export interface ITargetsConfiguration {
*/
enable: boolean;
bracketObjects: { enable: boolean };
separatorObjects: { enable: boolean };
smartQuotes: ISmartQuotesConfiguration;
}

Expand Down
Loading