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

Fixes #1974: U command #2081

Merged
merged 16 commits into from
Nov 13, 2017
Merged

Fixes #1974: U command #2081

merged 16 commits into from
Nov 13, 2017

Conversation

westim
Copy link
Contributor

@westim westim commented Oct 19, 2017

This PR adds the U command, which performs an undo on all changes which occurred on the line of the most recent change.

This feature was relatively complicated to implement. The first challenge is that, unlike the u command, the U command is itself a change. This means that you can undo and redo a U command. Therefore, U performs the following steps:

  1. Identify & collect all consecutive changes to undo by looping through all steps & changes in reverse.
  2. Run undo() on all the changes in the list.
  3. Reverse the undone changes (additions to deletions and vice versa).
  4. Remove the undone history steps from HistoryTracker.historySteps.
  5. Re-add the undone changes as a single history step.

The second challenge was the presence of newlines in some changes. For example, if a change has the text '\nhello, world!', we want to undo only the text 'hello, world!' and not the newline character, so the newline characters must be removed. This must also account for how newline is '\n' on MacOS/Linux and '\r\n' on Windows. This special case also has a starting cursor position offset that must be compensated.

I chose to implement workarounds for these challenges rather than modify the architecture elsewhere. The benefit is that any bugs introduced are isolated to this command. The downside is that the code is not as elegant as it could be.

I have added 7 unit tests to cover the major functionality of this command. I have also updated the ROADMAP accordingly.

@Chillee
Copy link
Member

Chillee commented Oct 19, 2017

Oh wow! To be honest, I didn't think anybody would fix this issue: undo is a pretty tricky thing to deal with.

This deserves a more thorough review, so I'll do it over the weekend.

Copy link
Member

@Chillee Chillee left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of things I didn't understand the motivation before.

Otherwise, it all looks great! Tests look comprehensive enough, and the logic mostly makes sense to me.

Thanks a lot!

@@ -100,7 +100,7 @@ class HistoryStep {
cursorEnd?: Position[] | undefined;
marks?: IMark[];
}) {
this.changes = init.changes = [];
this.changes = init.changes || [];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this just a bug? Could this be the cause for the undo issues we've been experiencing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this was a bug that was preventing me from creating the new HistoryStep near the end of the function. Any list of changes passed in would always be overwritten as an empty array. I'm not aware of the existing undo issues, but it's possible.

return undefined;
if (this.currentHistoryStepIndex === 0) {
return undefined;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reasoning behind putting this in the if statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not completely necessary, I simply thought it looked odd to have the exact same if statement twice in the same block. Nesting this statement makes the relationship between the second and third if statements more obvious (to me). I did the same change in the new function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I didn't see the if statement above.

@jpoon
Copy link
Member

jpoon commented Oct 31, 2017

Is this good to merge @Chillee?

Edit: Actually, I see that you approved the review. I'm going to update the branch and merge if/when the new build passes.

@jpoon
Copy link
Member

jpoon commented Oct 31, 2017

Argh, build on master is green but this PR is failing two UTs which don't seem relevant.

@westim
Copy link
Contributor Author

westim commented Nov 1, 2017

Looks like a small bug with the g; and g, commands. I tweaked these commands in PR #2040 and added the unit tests in PR #2088.

In Vim, when the jumplist is at the most recent state, the first invocation of g; moves the cursor to the most recent change. In this PR, we currently move the cursor to the second most recent change in this scenario.

This was previously working, as indicated by the passing tests in the master branch. From my investigation, appears to be a random side-effect of fixing this line, which currently contains a bug:

this.changes = init.changes = [];

See my discussion with @Chillee about this change above for the bug in this line.

I can work on a fix for this bug. For this PR, should we simply disable these tests?

@Chillee
Copy link
Member

Chillee commented Nov 8, 2017

@westim Yeah that sounds fine to me.

@westim
Copy link
Contributor Author

westim commented Nov 9, 2017

I have decided to revert the small change which caused regressions & failing tests for the g; and g, commands. I have left a comment to indicate that the line may be a bug.

@westim
Copy link
Contributor Author

westim commented Nov 13, 2017

@jpoon this PR is ready for review.

@Chillee Chillee merged commit 6809064 into VSCodeVim:master Nov 13, 2017
@Chillee
Copy link
Member

Chillee commented Nov 13, 2017

I reviewed this earlier and it all looks good to me! Thanks for the contribution, awesome work!

@westim westim deleted the command-U branch November 14, 2017 00:19
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants