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

HighFidelityTrackingSpan.cs « TextModel « Impl « Text « src - github.com/microsoft/vs-editor-api.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: e889804b3fcab163d970313574a83d50e4cd0baf (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
//
//  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.Implementation
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;

    /// <summary>
    /// Implementation of ITrackingSpan for the UndoRedo and Backward TrackingFidelityMode.
    /// Records noninvertible transition and consults these records as appropriate.
    /// </summary>
    internal class HighFidelityTrackingSpan : TrackingSpan
    {
        #region State and Construction
        private class VersionSpanHistory
        {
            private readonly ITextVersion version;
            private readonly Span span;
            private readonly List<VersionNumberPosition> noninvertibleStartHistory;
            private readonly List<VersionNumberPosition> noninvertibleEndHistory;

            public VersionSpanHistory(ITextVersion version,
                                      Span span,
                                      List<VersionNumberPosition> noninvertibleStartHistory,
                                      List<VersionNumberPosition> noninvertibleEndHistory)
            {
                this.version = version;
                this.span = span;
                this.noninvertibleStartHistory = noninvertibleStartHistory;
                this.noninvertibleEndHistory = noninvertibleEndHistory;
            }

            public ITextVersion Version { get { return this.version; } }
            public Span Span { get { return this.span; } }
            public List<VersionNumberPosition> NoninvertibleStartHistory { get { return this.noninvertibleStartHistory; } }
            public List<VersionNumberPosition> NoninvertibleEndHistory { get { return this.noninvertibleEndHistory; } }
        }

        private VersionSpanHistory cachedSpan;
        private TrackingFidelityMode fidelity;

        internal HighFidelityTrackingSpan(ITextVersion version, Span span, SpanTrackingMode spanTrackingMode, TrackingFidelityMode fidelity)
            : base(version, span, spanTrackingMode)
        {
            if (fidelity != TrackingFidelityMode.UndoRedo && fidelity != TrackingFidelityMode.Backward)
            {
                throw new ArgumentOutOfRangeException(nameof(fidelity));
            }
            List<VersionNumberPosition> startHistory = null;
            List<VersionNumberPosition> endHistory = null;
            if (fidelity == TrackingFidelityMode.UndoRedo && version.VersionNumber > 0)
            {
                // The system may perform undo operations that reach prior to the initial version; if any of
                // those transitions are noninvertible, then redoing back to the initial version will give the
                // wrong answer. Thus we save the state of the span for the initial version, unless
                // the initial version happens to be version zero (in which case we could not undo past it).

                startHistory = new List<VersionNumberPosition>();
                endHistory = new List<VersionNumberPosition>();

                if (version.VersionNumber != version.ReiteratedVersionNumber)
                {
                    Debug.Assert(version.ReiteratedVersionNumber < version.VersionNumber);
                    // If the current version and reiterated version differ, also remember the position
                    // using the reiterated version number, since when undoing back to this point it
                    // will be the key that is used.
                    startHistory.Add(new VersionNumberPosition(version.ReiteratedVersionNumber, span.Start));
                    endHistory.Add(new VersionNumberPosition(version.ReiteratedVersionNumber, span.End));
                }

                startHistory.Add(new VersionNumberPosition(version.VersionNumber, span.Start));
                endHistory.Add(new VersionNumberPosition(version.VersionNumber, span.End));
            }
            this.cachedSpan = new VersionSpanHistory(version, span, startHistory, endHistory);
            this.fidelity = fidelity;
        }
        #endregion

        #region Overrides
        public override ITextBuffer TextBuffer
        {
            get { return this.cachedSpan.Version.TextBuffer; }
        }

        public override TrackingFidelityMode TrackingFidelity
        {
            get { return this.fidelity; }
        }

        protected override Span TrackSpan(ITextVersion targetVersion)
        {
            // Compute the span on the requested snapshot.
            // This object caches the most recently requested version and the span in that version.
            //
            // We are relying on the atomicity of pointer copies (this.cachedSpan might change after we've
            // fetched it below but we will always get a self-consistent VersionSpan). This ensures we
            // have proper behavior when called from multiple threads (multiple threads may all track and update the
            // cached value if called at inconvenient times, but they will return consistent results).
            //
            // In most cases, one can track backwards from the cached version to a previously computed
            // version and get the same result, but this is not always the case: in particular, when one or both
            // ends of the span lie in a deleted region, simulating reinsertion of that region will not cause
            // the previous value of the span to be recovered. Such transitions are called noninvertible.
            // This class explicitly tracks the positions of span endpoints for versions for which the subsequent
            // transition is noninvertible; this allows the value to be computed properly when tracking backwards
            // or in undo/redo situations.

            VersionSpanHistory cached = this.cachedSpan;        // must fetch just once
            if (targetVersion == cached.Version)
            {
                // easy!
                return cached.Span;
            }

            PointTrackingMode startMode =
                (this.trackingMode == SpanTrackingMode.EdgeExclusive || this.trackingMode == SpanTrackingMode.EdgePositive)
                    ? PointTrackingMode.Positive
                    : PointTrackingMode.Negative;

            PointTrackingMode endMode =
                (this.trackingMode == SpanTrackingMode.EdgeExclusive || this.trackingMode == SpanTrackingMode.EdgeNegative)
                    ? PointTrackingMode.Negative
                    : PointTrackingMode.Positive;

            List<VersionNumberPosition> noninvertibleStartHistory = cached.NoninvertibleStartHistory;
            List<VersionNumberPosition> noninvertibleEndHistory = cached.NoninvertibleEndHistory;

            Span targetSpan;
            if (targetVersion.VersionNumber > cached.Version.VersionNumber)
            {
                // Compute the target span by going forward from the cached version
                int start = HighFidelityTrackingPoint.TrackPositionForwardInTime
                                (startMode, this.fidelity, ref noninvertibleStartHistory, cached.Span.Start, cached.Version, targetVersion);
                int end = HighFidelityTrackingPoint.TrackPositionForwardInTime
                                (endMode, this.fidelity, ref noninvertibleEndHistory, cached.Span.End, cached.Version, targetVersion);
                targetSpan = Span.FromBounds(start, System.Math.Max(start, end));

                // Cache the new span
                this.cachedSpan = new VersionSpanHistory(targetVersion, targetSpan, noninvertibleStartHistory, noninvertibleEndHistory);
            }
            else
            {
                // we are looking for a version prior to the cached version.
                int start = HighFidelityTrackingPoint.TrackPositionBackwardInTime
                    (startMode, this.fidelity == TrackingFidelityMode.Backward ? noninvertibleStartHistory : null, cached.Span.Start, cached.Version, targetVersion);
                int end = HighFidelityTrackingPoint.TrackPositionBackwardInTime
                    (endMode, this.fidelity == TrackingFidelityMode.Backward ? noninvertibleEndHistory : null, cached.Span.End, cached.Version, targetVersion);

                targetSpan = Span.FromBounds(start, System.Math.Max(start, end));
            }
            return targetSpan;
        }
        #endregion

        #region Diagnostic Support

        public override string ToString()
        {
            VersionSpanHistory c = this.cachedSpan;
            System.Text.StringBuilder sb = new System.Text.StringBuilder("*");
            sb.Append(ToString(c.Version, c.Span, this.trackingMode));
            if (c.NoninvertibleStartHistory != null)
            {
                sb.Append("[Start");
                foreach (VersionNumberPosition vp in c.NoninvertibleStartHistory)
                {
                    sb.Append(string.Format(System.Globalization.CultureInfo.CurrentCulture, "V{0}@{1}", vp.VersionNumber, vp.Position));
                }
                sb.Append("]");
            }
            if (c.NoninvertibleEndHistory != null)
            {
                sb.Append("[End");
                foreach (VersionNumberPosition vp in c.NoninvertibleEndHistory)
                {
                    sb.Append(string.Format(System.Globalization.CultureInfo.CurrentCulture, "V{0}@{1}", vp.VersionNumber, vp.Position));
                }
                sb.Append("]");
            }
            return sb.ToString();
        }
        #endregion
    }
}