-
Notifications
You must be signed in to change notification settings - Fork 319
[vi-mode] Correctly closing unbalanced start edit undo groups #2012
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
Changes from all commits
3787afa
298bdc2
60f069f
d84cc88
58fe511
fb73486
1e66817
b7e64a2
84e16f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,24 +3,36 @@ | |
| --********************************************************************/ | ||
|
|
||
| using System; | ||
| using System.Collections.Generic; | ||
|
|
||
| namespace Microsoft.PowerShell | ||
| { | ||
| public partial class PSConsoleReadLine | ||
| { | ||
| private class GroupUndoHelper | ||
| /// <summary> | ||
| /// Encapsulates state and behaviour for an edit group. | ||
| /// | ||
| /// An edit group is a sequence of distinct internal changes | ||
| /// to the buffer that are considered to be a single user-facing | ||
| /// change for the purpose of the undo/redo actions. | ||
| /// </summary> | ||
| internal struct GroupUndoHelper | ||
| { | ||
| public Action<ConsoleKeyInfo?, object> _instigator; | ||
| public object _instigatorArg; | ||
|
|
||
| public GroupUndoHelper() | ||
| { | ||
| _instigator = null; | ||
| _instigatorArg = null; | ||
| } | ||
|
|
||
| public void StartGroup(Action<ConsoleKeyInfo?, object> instigator, object instigatorArg) | ||
| { | ||
| if (_singleton._editGroupStart != -1) | ||
| { | ||
| // A nested "start" of a group is being made, so we | ||
| // need to record the state of the preceding start. | ||
| _singleton._groupUndoStates.Push( | ||
| new GroupUndoState(_singleton._groupUndoHelper, _singleton._editGroupStart)); | ||
|
|
||
| _singleton._editGroupStart = -1; | ||
| } | ||
|
|
||
| _instigator = instigator; | ||
| _instigatorArg = instigatorArg; | ||
|
Comment on lines
36
to
37
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It doesn't seem to be right to override the existing |
||
| _singleton.StartEditGroup(); | ||
|
|
@@ -34,14 +46,60 @@ public void Clear() | |
|
|
||
| public void EndGroup() | ||
| { | ||
| if (_singleton._editGroupStart >= 0) | ||
| { | ||
| _singleton.EndEditGroup(_instigator, _instigatorArg); | ||
| if (_singleton._editGroupStart >= 0) | ||
| { | ||
| _singleton.EndEditGroup( | ||
| _singleton._groupUndoHelper._instigator, | ||
| _singleton._groupUndoHelper._instigatorArg); | ||
|
|
||
| while (_singleton._groupUndoStates.Count > 0) | ||
| { | ||
| var groupUndoState = _singleton._groupUndoStates.Pop(); | ||
| _singleton._editGroupStart = groupUndoState.EditGroupStart; | ||
| _singleton._groupUndoHelper = groupUndoState.GroupUndoHelper; | ||
|
|
||
| _singleton.EndEditGroup( | ||
| _singleton._groupUndoHelper._instigator, | ||
| _singleton._groupUndoHelper._instigatorArg); | ||
| } | ||
| } | ||
| Clear(); | ||
|
|
||
| _singleton._groupUndoHelper.Clear(); | ||
| } | ||
| } | ||
|
|
||
| internal GroupUndoHelper _groupUndoHelper = new(); | ||
|
|
||
| /// <summary> | ||
| /// Records states of changes made as part of an edit group. | ||
| /// | ||
| /// In an ideal situation, edit groups would be started and ended | ||
| /// in balanced pairs of calls. However, we expose public methods | ||
| /// that may start an edit group and rely on future actions to | ||
| /// properly end the group. | ||
| /// | ||
| /// To improve robustness of the code, we allow starting "nested" | ||
| /// edit groups and end the whole sequence of groups once. That is, | ||
| /// a single call to _singleton.EndEditGroup() will properly record the | ||
| /// changes made up to that point from calls to _singleton.StartEditGroup() | ||
| /// that have been made at different points in the overall sequence of changes. | ||
| /// </summary> | ||
| internal class GroupUndoState | ||
| { | ||
| public GroupUndoState(GroupUndoHelper undoHelper, int editGroupStart) | ||
| { | ||
| GroupUndoHelper = undoHelper; | ||
| EditGroupStart = editGroupStart; | ||
| } | ||
|
|
||
| public GroupUndoHelper GroupUndoHelper { get; } | ||
| public int EditGroupStart { get; } | ||
| } | ||
| private readonly GroupUndoHelper _groupUndoHelper = new GroupUndoHelper(); | ||
|
|
||
| /// <summary> | ||
| /// Records the sequence of "nested" starts of a edit group. | ||
| /// </summary> | ||
| private readonly Stack<GroupUndoState> _groupUndoStates = new(); | ||
|
|
||
| /// <summary> | ||
| /// Undo all previous edits for line. | ||
|
|
@@ -63,4 +121,23 @@ public static void UndoAll(ConsoleKeyInfo? key = null, object arg = null) | |
| } | ||
| } | ||
| } | ||
|
|
||
| internal static class StackExtensions | ||
| { | ||
| /// <summary> | ||
| /// This helper method copies the contents of the target stack | ||
| /// with items from the supplied stack. | ||
| /// </summary> | ||
| /// <typeparam name="T"></typeparam> | ||
| /// <param name="origin"></param> | ||
| /// <param name="target"></param> | ||
| public static void CopyTo<T>(this Stack<T> origin, Stack<T> target) | ||
| { | ||
| target.Clear(); | ||
| foreach (var item in origin) | ||
| { | ||
| target.Push(item); | ||
| } | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.