Welcome to mirror list, hosted at ThFree Co, Russian Federation.

AfterTextBufferChangeUndoPrimitive.cs « EditorOperations « Impl « Text « src - github.com/microsoft/vs-editor-api.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: d19da5974e0d6ad42091c4349714355004deee31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//  Licensed under the MIT License. See License.txt in the project root for license information.
//
// This file contain implementations details that are subject to change without notice.
// Use at your own risk.
//
namespace Microsoft.VisualStudio.Text.Operations.Implementation
{
    using System;
    using System.Diagnostics;
    using Microsoft.VisualStudio.Text.Editor;

    /// <summary>
    /// The UndoPrimitive to take place on the Undo stack after a text buffer change. This is the simpler
    /// version of the primitive that handles most common cases.
    /// </summary>
    internal class AfterTextBufferChangeUndoPrimitive : TextUndoPrimitive
    {
        // Think twice before adding any fields here! These objects are long-lived and consume considerable space.
        // Unusual cases should be handled by the GeneralAfterTextBufferChangedUndoPrimitive class below.
        private readonly ITextUndoHistory _undoHistory;
        public readonly SelectionState State;
        private bool _canUndo;

        /// <summary>
        /// Constructs a AfterTextBufferChangeUndoPrimitive.
        /// </summary>
        /// <param name="textView">
        /// The text view that was responsible for causing this change.
        /// </param>
        /// <param name="undoHistory">
        /// The <see cref="ITextUndoHistory" /> this primitive will be added to.
        /// </param>
        /// <exception cref="ArgumentNullException"><paramref name="textView"/> is null.</exception>
        /// <exception cref="ArgumentNullException"><paramref name="undoHistory"/> is null.</exception>
        public static AfterTextBufferChangeUndoPrimitive Create(ITextView textView, ITextUndoHistory undoHistory)
        {
            if (textView == null)
            {
                throw new ArgumentNullException(nameof(textView));
            }
            if (undoHistory == null)
            {
                throw new ArgumentNullException(nameof(undoHistory));
            }

            return new AfterTextBufferChangeUndoPrimitive(textView, undoHistory);

        }

        protected AfterTextBufferChangeUndoPrimitive(ITextView textView, ITextUndoHistory undoHistory)
        {
            _undoHistory = undoHistory;
            this.State = new SelectionState(textView);
            _canUndo = true;
        }

        // Internal empty constructor for unit testing.
        internal AfterTextBufferChangeUndoPrimitive() { }

        /// <summary>
        /// The <see cref="ITextView"/> that this <see cref="AfterTextBufferChangeUndoPrimitive"/> is bound to.
        /// </summary>
        internal ITextView GetTextView()
        {
                ITextView view = null;
                _undoHistory.Properties.TryGetProperty(typeof(ITextView), out view);
                return view;
        }


        #region ITextUndoPrimitive Members

        /// <summary>
        /// Returns true if operation can be undone, false otherwise.
        /// </summary>
        public override bool CanUndo
        {
            get { return _canUndo; }
        }

        /// <summary>
        /// Returns true if operation can be redone, false otherwise.
        /// </summary>
        public override bool CanRedo
        {
            get { return !_canUndo; }
        }

        /// <summary>
        /// Do the action.
        /// </summary>
        /// <exception cref="InvalidOperationException">Operation cannot be redone.</exception>
        public override void Do()
        {
            // Validate, we shouldn't be allowed to undo
            if (!CanRedo)
            {
                throw new InvalidOperationException(Strings.CannotRedo);
            }

            // Set the new caret position and active selection
            var view = this.GetTextView();
            Debug.Assert(view == null || !view.IsClosed, "Attempt to undo/redo on a closed view?  This shouldn't happen.");
            if (view != null && !view.IsClosed)
            {
                this.State.Restore(view);
                view.Caret.EnsureVisible();
            }

            _canUndo = true;
        }

        /// <summary>
        /// Undo the action.
        /// </summary>
        /// <exception cref="InvalidOperationException">Operation cannot be undone.</exception>
        public override void Undo()
        {
            // Validate that we can undo this change
            if (!CanUndo)
            {
                throw new InvalidOperationException(Strings.CannotUndo);
            }

            // Currently, no action is done on the Undo.  To restore the caret and selection after a text buffer undo, there is the BeforeTextBufferChangeUndoPrimitive.
            // This undo should not do anything with the caret and selection, because we only want to reset them after the TextBuffer change has occurred.
            // Therefore, we need to add this UndoPrimitive to the undo stack before the UndoPrimitive for the TextBuffer change.  On an redo, the TextBuffer changed UndoPrimitive
            // will fire it's Redo first, and than the Redo for this UndoPrimitive will fire.
            // However, on an undo, the undo for this UndoPrimitive will be fired, and then the undo for the TextBuffer change UndoPrimitive.  If we had set any caret placement/selection here (ie the old caret placement/selection), 
            // we may crash because the TextBuffer change has not occurred yet (ie you try to set the caret to be at CharacterIndex 1 when the TextBuffer is still empty).

            _canUndo = false;
        }

        public override bool CanMerge(ITextUndoPrimitive older)
        {
            return false;
        }
        #endregion
    }
}