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

[WIP] Improve foldFix further by making more motion prefixed actions fold-aware #2887

Closed
wants to merge 20 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a949f81
Return `IMovement` instead of `Position` from foldFix calls of `Move…
xmbhasin Jul 24, 2018
dfda430
Fix bugs with executing actions prefixed with up motion
xmbhasin Jul 24, 2018
5a07f4e
Add comments and rename a few symbols in `BaseMovement`
xmbhasin Jul 24, 2018
8adf56a
Apply foldFix adjustments to `DeleteOperator` and `YankOperator` for …
xmbhasin Jul 24, 2018
4771a11
Merge branch 'master' into improve-foldfix
xmbhasin Jul 24, 2018
8d97462
Return `IMovement` from `Move*FoldFix` when at first line or last vis…
xmbhasin Jul 24, 2018
91f1c3e
Merge branch 'master' into improve-foldfix
xmbhasin Jul 24, 2018
90bcfaf
Remove dead commented out code
xmbhasin Jul 24, 2018
2156fe0
fix motion suffixed actions in normal mode by returning `IMovement` …
xmbhasin Jul 25, 2018
74eabb0
Fix detection of dd and yy commands
xmbhasin Jul 25, 2018
4762316
Add foldfix tests
xmbhasin Jul 25, 2018
212adfa
Merge branch 'master' into improve-foldfix
xmbhasin Jul 25, 2018
aefdc20
Merge branch 'master' into improve-foldfix
xmbhasin Sep 9, 2018
f2b9408
Refactor foldfix movement code
xmbhasin Sep 9, 2018
0ad85c9
Merge branch 'master' into improve-foldfix
xmbhasin Oct 10, 2018
ba88bed
Fix move up foldfix with count and action
xmbhasin Oct 10, 2018
e420dfe
Fix jump tracking by adding back `isJump` field in `motion.ts`
xmbhasin Oct 10, 2018
a63397a
Fix MoveDownFoldFix inheritance
xmbhasin Oct 10, 2018
94ba1cc
Abstract foldfix adjustment for dd and yy operators
xmbhasin Oct 10, 2018
c571089
Refactor foldfix tests and remove some dead code
xmbhasin Oct 11, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 112 additions & 25 deletions src/actions/motion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,13 @@ export abstract class BaseMovement extends BaseAction {
for (let i = 0; i < count; i++) {
const firstIteration = i === 0;
const lastIteration = i === count - 1;
result = await this.createMovementResult(position, vimState, recordedState, lastIteration);
result = await this.createMovementResult(
position,
vimState,
recordedState,
firstIteration,
lastIteration
);

if (result instanceof Position) {
position = result;
Expand Down Expand Up @@ -171,6 +177,7 @@ export abstract class BaseMovement extends BaseAction {
position: Position,
vimState: VimState,
recordedState: RecordedState,
firstIteration: boolean,
lastIteration: boolean
): Promise<Position | IMovement> {
const result =
Expand All @@ -179,6 +186,7 @@ export abstract class BaseMovement extends BaseAction {
: await this.execAction(position, vimState);
return result;
}

protected adjustPosition(position: Position, result: IMovement, lastIteration: boolean) {
if (!lastIteration) {
position = result.stop.getRightThroughLineBreaks();
Expand Down Expand Up @@ -295,56 +303,118 @@ class MoveDownByScreenLineMaintainDesiredColumn extends MoveByScreenLineMaintain
value = 1;
}

class MoveDownFoldFix extends MoveByScreenLineMaintainDesiredColumn {
export abstract class FoldFixMovement extends BaseMovement {
protected async createMovementResult(
position: Position,
vimState: VimState,
recordedState: RecordedState,
firstIteration: boolean,
lastIteration: boolean
): Promise<Position | IMovement> {
const isFirstUp = firstIteration && this instanceof MoveUp;
const includeFoldedRegionBelowBeforeUp =
configuration.foldfix && recordedState.operator && isFirstUp;

let result = includeFoldedRegionBelowBeforeUp
? <IMovement>await new MoveDownFoldFix().execAction(position, vimState)
: <IMovement>(
await super.createMovementResult(
position,
vimState,
recordedState,
firstIteration,
lastIteration
)
);

// exclude line just below folded region added before first up
if (includeFoldedRegionBelowBeforeUp) {
result.start = result.stop.getUp(vimState.desiredColumn);
result.stop = (<IMovement>await new MoveUpFoldFix().execAction(position, vimState)).stop;
}

const lastDownBeforeAction =
lastIteration && configuration.foldfix && recordedState.operator && this instanceof MoveDown;
if (lastDownBeforeAction) {
// include folded region of last line after action prefixed down motion
result.stop = (<IMovement>(
await new MoveDownFoldFix().execAction(position, vimState)
)).stop.getUp(vimState.desiredColumn);
}
return result;
}
}
export class MoveDownFoldFix extends MoveByScreenLineMaintainDesiredColumn {
movementType: CursorMovePosition = 'down';
by: CursorMoveByUnit = 'line';
value = 1;

public async execAction(position: Position, vimState: VimState): Promise<Position | IMovement> {
if (position.line >= TextEditor.getLineCount() - 1) {
return position;
return { start: position, stop: position };
}
let t: Position | IMovement;
let tStop: Position;
let prevLine: number = position.line;
let prevChar: number = position.character;
const prevDesiredColumn = vimState.desiredColumn;
const moveDownByScreenLine = new MoveDownByScreenLine();
do {
t = <Position | IMovement>await moveDownByScreenLine.execAction(position, vimState);
t = t instanceof Position ? t : t.stop;
const lineChanged = prevLine !== t.line;
tStop = t instanceof Position ? t : t.stop;
const lineChanged = prevLine !== tStop.line;
// wrappedLine movement goes to eol character only when at the last line
// thus a column change on wrappedLine movement represents a visual last line
const colChanged = prevChar !== t.character;
const colChanged = prevChar !== tStop.character;
if (lineChanged || !colChanged) {
break;
}
prevChar = t.character;
prevLine = t.line;
} while (t.line === position.line);
prevChar = tStop.character;
prevLine = tStop.line;
} while (tStop.line === position.line);

// fix column change at last line caused by wrappedLine movement
// causes cursor lag and flicker if a large repeat prefix is given to movement
if (t.character !== prevDesiredColumn) {
t = new Position(t.line, prevDesiredColumn);
if (tStop.character !== prevDesiredColumn) {
tStop = new Position(tStop.line, prevDesiredColumn);
}
return t;

// create and return an `IMovement` to support `execActionForOperator`
const tMovement: IMovement = { start: position, stop: tStop };
return tMovement;
}
}

@RegisterAction
class MoveDown extends BaseMovement {
class MoveDown extends FoldFixMovement {
keys = ['j'];
doesntChangeDesiredColumn = true;

public async execAction(position: Position, vimState: VimState): Promise<Position | IMovement> {
if (configuration.foldfix && vimState.currentMode !== ModeName.VisualBlock) {
return new MoveDownFoldFix().execAction(position, vimState);
if (vimState.currentMode === ModeName.Normal) {
return await new MoveDownFoldFix().execAction(position, vimState);
} else {
// insert and visual modes break given an IMovement
return (<IMovement>await new MoveDownFoldFix().execAction(position, vimState)).stop;
}
}
return position.getDown(vimState.desiredColumn);
}

public async execActionForOperator(position: Position, vimState: VimState): Promise<Position> {
public async execActionForOperator(
position: Position,
vimState: VimState
): Promise<Position | IMovement> {
vimState.currentRegisterMode = RegisterMode.LineWise;

if (configuration.foldfix && vimState.currentMode !== ModeName.VisualBlock) {
let actionRange = <IMovement>await new MoveDownFoldFix().execAction(position, vimState);
// adjust range to exclude line after wrapped and/or folded block
actionRange.stop = actionRange.stop.getUp(vimState.desiredColumn);
return actionRange;
}

return position.getDown(position.getLineEnd().character);
}
}
Expand All @@ -361,46 +431,63 @@ class MoveUpByScreenLineMaintainDesiredColumn extends MoveByScreenLineMaintainDe
}

@RegisterAction
class MoveUp extends BaseMovement {
class MoveUp extends FoldFixMovement {
keys = ['k'];
doesntChangeDesiredColumn = true;

public async execAction(position: Position, vimState: VimState): Promise<Position | IMovement> {
if (configuration.foldfix && vimState.currentMode !== ModeName.VisualBlock) {
return new MoveUpFoldFix().execAction(position, vimState);
if (vimState.currentMode === ModeName.Normal) {
return await new MoveUpFoldFix().execAction(position, vimState);
} else {
return (<IMovement>await new MoveUpFoldFix().execAction(position, vimState)).stop;
}
}
return position.getUp(vimState.desiredColumn);
}

public async execActionForOperator(position: Position, vimState: VimState): Promise<Position> {
public async execActionForOperator(
position: Position,
vimState: VimState
): Promise<Position | IMovement> {
vimState.currentRegisterMode = RegisterMode.LineWise;
if (configuration.foldfix && vimState.currentMode !== ModeName.VisualBlock) {
const stop = (<IMovement>await new MoveUpFoldFix().execAction(position, vimState)).stop;
const actionRange: IMovement = { start: position, stop };
return actionRange;
}
return position.getUp(position.getLineEnd().character);
}
}

@RegisterAction
class MoveUpFoldFix extends MoveByScreenLineMaintainDesiredColumn {
export class MoveUpFoldFix extends MoveByScreenLineMaintainDesiredColumn {
movementType: CursorMovePosition = 'up';
by: CursorMoveByUnit = 'line';
value = 1;

public async execAction(position: Position, vimState: VimState): Promise<Position | IMovement> {
if (position.line === 0) {
return position;
return { start: position, stop: position };
}
let t: Position | IMovement;
let tStop: Position;
const prevDesiredColumn = vimState.desiredColumn;
const moveUpByScreenLine = new MoveUpByScreenLine();
do {
t = <Position | IMovement>await moveUpByScreenLine.execAction(position, vimState);
t = t instanceof Position ? t : t.stop;
} while (t.line === position.line);
tStop = t instanceof Position ? t : t.stop;
} while (tStop.line === position.line);

// fix column change at last line caused by wrappedLine movement
// causes cursor lag and flicker if a large repeat prefix is given to movement
if (t.character !== prevDesiredColumn) {
t = new Position(t.line, prevDesiredColumn);
if (tStop.character !== prevDesiredColumn) {
tStop = new Position(tStop.line, prevDesiredColumn);
}
return t;

// create and return an `IMovement` to support `execActionForOperator`
const tMovement: IMovement = { start: position, stop: tStop };
return tMovement;
}
}

Expand Down
29 changes: 28 additions & 1 deletion src/actions/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { TextEditor } from './../textEditor';
import { BaseAction, RegisterAction } from './base';
import { CommandNumber } from './commands/actions';
import { TextObjectMovement } from './textobject';
import { IMovement, MoveDownFoldFix, MoveUpFoldFix } from './motion';

export class BaseOperator extends BaseAction {
constructor(multicursorIndex?: number) {
Expand Down Expand Up @@ -121,8 +122,11 @@ export class DeleteOperator extends BaseOperator {
if (registerMode === RegisterMode.LineWise) {
start = start.getLineBegin();
end = end.getLineEnd();
}

if (configuration.foldfix) {
end = await includeFoldedLines(start, end, vimState);
}
}
end = new Position(end.line, end.character + 1);

const isOnLastLine = end.line === TextEditor.getLineCount() - 1;
Expand Down Expand Up @@ -263,6 +267,10 @@ export class YankOperator extends BaseOperator {
if (vimState.currentRegisterMode === RegisterMode.LineWise) {
start = start.getLineBegin();
end = end.getLineEnd();

if (configuration.foldfix) {
end = await includeFoldedLines(start, end, vimState);
}
}

let text = TextEditor.getText(new vscode.Range(start, end));
Expand Down Expand Up @@ -300,6 +308,25 @@ export class YankOperator extends BaseOperator {
}
}

async function includeFoldedLines(
start: Position,
end: Position,
vimState: VimState
): Promise<Position> {
const isddCommand = vimState.recordedState.commandWithoutCountPrefix === 'dd';
const isyyCommand = vimState.recordedState.commandWithoutCountPrefix === 'yy';

if (isddCommand || isyyCommand) {
const startEndLineDistance = Math.abs(end.line - start.line);
end = new Position(start.line, end.character);
for (let len = startEndLineDistance + 1; len > 0; len--) {
end = (<IMovement>await new MoveDownFoldFix().execAction(end, vimState)).stop;
}
end = end.getUp(0).getLineEnd();
}
return end;
}

@RegisterAction
export class ShiftYankOperatorVisual extends BaseOperator {
public keys = ['Y'];
Expand Down
Loading