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

CollapsedMoveUndoPrimitive.cs « EditorOperations « Impl « Text « src - github.com/microsoft/vs-editor-api.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 44fa09e4ee447bed81948c19722711de50de0d7a (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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
//
//  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 System.Collections.Generic;
    using Microsoft.VisualStudio.Text.Editor;
    using Microsoft.VisualStudio.Text.Outlining;
    using Microsoft.VisualStudio.Text.Tagging;
    using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods;

    /// <summary>
    /// BeforeCollapsedMoveUndoPrimitive is added to the Undo stack before a collapsed region is moved.
    /// Undo operations will cause this primitive to collapse the given regions, returning them
    /// to their original pre-move state. Redo is ignored here and handled in the AfterCollapsedMoveUndoPrimitive.
    /// </summary>
    internal class BeforeCollapsedMoveUndoPrimitive : CollapsedMoveUndoPrimitive
    {
        public BeforeCollapsedMoveUndoPrimitive(IOutliningManager outliningManager, ITextView textView, IEnumerable<Tuple<Span, IOutliningRegionTag>> collapsedSpans)
            : base(outliningManager, textView, collapsedSpans)
        {

        }

        /// <summary>
        /// Redo the action
        /// </summary>
        /// <exception cref="InvalidOperationException">Operation cannot be undone.</exception>
        public override void Do()
        {
            if (!CanRedo)
            {
                throw new InvalidOperationException(Strings.CannotRedo);
            }

            // Redo is handled by AfterCollapsedMoveUndoPrimitive and there is nothing for the before to Do here.
            _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);
            }

            CollapseRegions();

            _canUndo = false;
        }
    }


    /// <summary>
    /// AfterCollapsedMoveUndoPrimitive is added to the Undo stack after a collapsed region has been moved.
    /// When a Redo occurs this primitive will collapse the regions to return them to their correct state.
    /// Undo operations are ignored here as they are handled in the BeforeCollapsedMoveUndoPrimitive.
    /// </summary>
    internal class AfterCollapsedMoveUndoPrimitive : CollapsedMoveUndoPrimitive
    {
        public AfterCollapsedMoveUndoPrimitive(IOutliningManager outliningManager, ITextView textView, IEnumerable<Tuple<Span, IOutliningRegionTag>> collapsedSpans)
            : base(outliningManager, textView, collapsedSpans)
        {

        }

        /// <summary>
        /// Redo the action
        /// </summary>
        /// <exception cref="InvalidOperationException">Operation cannot be undone.</exception>
        public override void Do()
        {
            if (!CanRedo)
            {
                throw new InvalidOperationException(Strings.CannotRedo);
            }

            CollapseRegions();

            _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);
            }

            // Undo is handled by the BeforeCollapsedMoveUndoPrimitive and so there is nothing to do here.
            _canUndo = false;
        }
    }

    /// <summary>
    /// CollapsedMoveUndoPrimitive handles re-tagging of regions and collapsing them during moves.
    /// OutliningUndoManager does not handle collapsing moved regions due to the way it listens to collapse/expands
    /// events and then records an Expand undo operation for the collapse. For Move Line operations we need to always collapse. 
    /// Re-tagging of the outlined regions is needed since the tagging it not present on the newly inserted text
    /// in the middle of the undo/redo operation, and without it the region cannot be collapsed.
    /// </summary>
    internal abstract class CollapsedMoveUndoPrimitive : TextUndoPrimitive
    {
        readonly IOutliningManager _outliningManager;
        readonly ITextView _textView;
        readonly IEnumerable<Tuple<Span, IOutliningRegionTag>> _collapsedSpans;
        protected bool _canUndo;

        public CollapsedMoveUndoPrimitive(IOutliningManager outliningManager, ITextView textView, IEnumerable<Tuple<Span, IOutliningRegionTag>> collaspedSpans)
        {
            if (textView == null)
            {
                throw new ArgumentNullException("textView");
            }

            if (outliningManager == null)
            {
                throw new ArgumentNullException("outliningManager");
            }

            if (collaspedSpans == null)
            {
                throw new ArgumentNullException("collaspedSpans");
            }

            _outliningManager = outliningManager;
            _textView = textView;
            _collapsedSpans = collaspedSpans;

            _canUndo = true;
        }

        // Re-collapse the spans
        public void CollapseRegions()
        {
            if (_outliningManager == null || _textView == null || _collapsedSpans == null)
                return;

            ITextSnapshot snapshot = _textView.TextBuffer.CurrentSnapshot;

            // Get a span that includes all collapsed regions
            int min = Int32.MinValue;
            int max = Int32.MinValue;

            foreach (Tuple<Span, IOutliningRegionTag> span in _collapsedSpans)
            {
                if (min == Int32.MinValue || span.Item1.Start < min)
                {
                    min = span.Item1.Start;
                }

                if (max == Int32.MinValue || span.Item1.End > max)
                {
                    max = span.Item1.End;
                }
            }

            // avoid running if there were no spans
            if (min == Int32.MinValue)
            {
                Debug.Fail("No spans");
                return;
            }

            // span containing all collapsed regions
            SnapshotSpan entireSpan = new SnapshotSpan(snapshot, min, max - min);

            // regions have not yet been tagged by the language service during the undo/redo and 
            // so we need to tag them again in order to do the collapse
            SimpleTagger<IOutliningRegionTag> simpleTagger =
                        _textView.TextBuffer.Properties.GetOrCreateSingletonProperty<SimpleTagger<IOutliningRegionTag>>(() => new SimpleTagger<IOutliningRegionTag>(_textView.TextBuffer));

            Debug.Assert(!simpleTagger.GetTaggedSpans(entireSpan).GetEnumerator().MoveNext(),
                "The code is not expecting the regions to be tagged already. Verify that redundant tagging is not occurring.");

            List<Span> toCollapse = new List<Span>();

            // tag the regions and add them to the list to be bulk collapsed
            foreach (Tuple<Span, IOutliningRegionTag> span in _collapsedSpans)
            {
                ITrackingSpan tspan = snapshot.CreateTrackingSpan(span.Item1, SpanTrackingMode.EdgeExclusive);
                simpleTagger.CreateTagSpan(tspan, span.Item2);

                toCollapse.Add(span.Item1);
            }

            // Disable the OutliningUndoManager to avoid it adding our collapse to the undo stack as an expand
            bool disableOutliningUndo = _textView.Options.IsOutliningUndoEnabled();

            try
            {
                if (disableOutliningUndo)
                {
                    _textView.Options.SetOptionValue(DefaultTextViewOptions.OutliningUndoOptionId, false);
                }

                // Do the collapse
                _outliningManager.CollapseAll(entireSpan, colSpan => (!colSpan.IsCollapsed && toCollapse.Contains(colSpan.Extent.GetSpan(snapshot))));
            }
            finally
            {
                if (disableOutliningUndo)
                {
                    _textView.Options.SetOptionValue(DefaultTextViewOptions.OutliningUndoOptionId, true);
                }
            }
        }

        public override bool CanUndo
        {
            get { return _canUndo; }
        }

        public override bool CanRedo
        {
            get { return !_canUndo; }
        }
    }
}