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

PersistentSpan.cs « TextModel « Impl « Text « src - github.com/microsoft/vs-editor-api.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 6ec563a637e9a779cf4b0bc40f5212e392b17223 (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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
//
//  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;

    internal sealed class PersistentSpan : IPersistentSpan
    {
        #region members
        public PersistentSpanSet SpanSet;

        private ITrackingSpan _span;            //null for spans on closed documents or disposed spans
        private int _startLine;                 //these parameters are valid whether or not the document is open (but _start*,_end* may be stale).
        private int _startIndex;
        private int _endLine;
        private int _endIndex;
        private ITextVersion _originalVersion = null;
        private Span _originalSpan;             // This is either the span when this was created or when the document was reopened.
                                                // It is default(Span) if either we were created (on an unopened document) with line/column indices or after the document was closed.
        private bool _useLineIndex;

        private readonly SpanTrackingMode _trackingMode;
        #endregion

        internal PersistentSpan(SnapshotSpan span, SpanTrackingMode trackingMode, PersistentSpanSet spanSet)
        {
            _span = span.Snapshot.CreateTrackingSpan(span, trackingMode);

            _originalVersion = span.Snapshot.Version;
            _originalSpan = span;

            PersistentSpan.SnapshotPointToLineIndex(span.Start, out _startLine, out _startIndex);
            PersistentSpan.SnapshotPointToLineIndex(span.End, out _endLine, out _endIndex);

            _trackingMode = trackingMode;
            this.SpanSet = spanSet;
        }

        internal PersistentSpan(int startLine, int startIndex, int endLine, int endIndex, SpanTrackingMode trackingMode, PersistentSpanSet spanSet)
        {
            _useLineIndex = true;
            _startLine = startLine;
            _startIndex = startIndex;
            _endLine = endLine;
            _endIndex = endIndex;

            _trackingMode = trackingMode;
            this.SpanSet = spanSet;
        }

        internal PersistentSpan(Span span, SpanTrackingMode trackingMode, PersistentSpanSet spanSet)
        {
            _useLineIndex = false;
            _originalSpan = span;

            _trackingMode = trackingMode;
            this.SpanSet = spanSet;
        }

        #region IPersistentSpan members
        public bool IsDocumentOpen { get { return this.SpanSet.Document != null; } }

        public ITextDocument Document { get { return this.SpanSet.Document; } }

        public ITrackingSpan Span { get { return _span; } }

        public string FilePath
        {
            get
            {
                if (this.SpanSet == null)
                    throw new ObjectDisposedException("PersistentSpan");

                return (this.SpanSet.Document != null) ? this.SpanSet.Document.FilePath : this.SpanSet.FileKey.ToString();
            }
        }

        public bool TryGetStartLineIndex(out int startLine, out int startIndex)
        {
            if (this.SpanSet == null)
                throw new ObjectDisposedException("PersistentSpan");

            if (_span != null)
            {
                SnapshotSpan span = _span.GetSpan(_span.TextBuffer.CurrentSnapshot);
                PersistentSpan.SnapshotPointToLineIndex(span.Start, out startLine, out startIndex);
                return true;
            }
            else if (_useLineIndex)
            {
                startLine = _startLine;
                startIndex = _startIndex;
                return true;
            }

            startLine = startIndex = 0;
            return false;
        }

        public bool TryGetEndLineIndex(out int endLine, out int endIndex)
        {
            if (this.SpanSet == null)
                throw new ObjectDisposedException("PersistentSpan");

            if (_span != null)
            {
                SnapshotSpan span = _span.GetSpan(_span.TextBuffer.CurrentSnapshot);
                PersistentSpan.SnapshotPointToLineIndex(span.End, out endLine, out endIndex);
                return true;
            }
            else if (_useLineIndex)
            {
                endLine = _endLine;
                endIndex = _endIndex;
                return true;
            }

            endLine = endIndex = 0;
            return false;
        }

        public bool TryGetSpan(out Span span)
        {
            if (this.SpanSet == null)
                throw new ObjectDisposedException("PersistentSpan");

            if (_span != null)
            {
                span = _span.GetSpan(_span.TextBuffer.CurrentSnapshot);
                return true;
            }
            else if (!_useLineIndex)
            {
                span = _originalSpan;
                return true;
            }

            span = new Span();
            return false;
        }
        #endregion

        #region IDisposable members
        public void Dispose()
        {
            if (this.SpanSet != null)
            {
                this.SpanSet.Delete(this);
                this.SpanSet = null;
                _originalVersion = null;
                _span = null;
            }
        }
        #endregion

        internal void SetSpanSet(PersistentSpanSet spanSet)
        {
            if (this.SpanSet == null)
                throw new ObjectDisposedException("PersistentSpan");

            this.SpanSet = spanSet;
        }

        internal void DocumentClosed(ITextSnapshot savedSnapshot)
        {
            Assumes.NotNull(_originalVersion);

            if ((savedSnapshot != null) && (savedSnapshot.Version.VersionNumber > _originalVersion.VersionNumber))
            {
                // The document was saved and we want to line/column indices in the saved snapshot (& not the current snapshot)
                var savedSpan = new SnapshotSpan(savedSnapshot, Tracking.TrackSpanForwardInTime(_trackingMode, _originalSpan, _originalVersion, savedSnapshot.Version));

                PersistentSpan.SnapshotPointToLineIndex(savedSpan.Start, out _startLine, out _startIndex);
                PersistentSpan.SnapshotPointToLineIndex(savedSpan.End, out _endLine, out _endIndex);
            }
            else
            {
                // The document was never saved (or was saved before we created) so continue to use the old line/column indices.
                // Since those are set when either the span is created (against an open document) or when the document is reopened,
                // they don't need to be changed.
            }

            //We set this to false when the document is closed because we have an accurate line/index and that is more stable
            //than a simple offset.
            _useLineIndex = true;
            _originalSpan = default(Span);
            _originalVersion = null;
            _span = null;
        }

        internal void DocumentReopened()
        {
            ITextSnapshot snapshot = this.SpanSet.Document.TextBuffer.CurrentSnapshot;

            SnapshotPoint start;
            SnapshotPoint end;
            if (_useLineIndex)
            {
                start = PersistentSpan.LineIndexToSnapshotPoint(_startLine, _startIndex, snapshot);
                end = PersistentSpan.LineIndexToSnapshotPoint(_endLine, _endIndex, snapshot);

                if (end < start)
                {
                    //Guard against the case where _start & _end are something like (100,2) & (101, 1).
                    //Those points would pass the argument validation (since _endLine > _startLine) but
                    //would cause problems if the document has only 5 lines since they would map to
                    //(5, 2) & (5, 1).
                    end = start;
                }
            }
            else
            {
                start = new SnapshotPoint(snapshot, Math.Min(_originalSpan.Start, snapshot.Length));
                end = new SnapshotPoint(snapshot, Math.Min(_originalSpan.End, snapshot.Length));
            }

            var snapshotSpan = new SnapshotSpan(start, end);
            _span = snapshot.CreateTrackingSpan(snapshotSpan, _trackingMode);
            _originalSpan = snapshotSpan;

            _originalVersion = snapshot.Version;
            PersistentSpan.SnapshotPointToLineIndex(snapshotSpan.Start, out _startLine, out _startIndex);
            PersistentSpan.SnapshotPointToLineIndex(snapshotSpan.End, out _endLine, out _endIndex);
        }

        private SnapshotSpan UpdateStartEnd()
        {
            SnapshotSpan span = _span.GetSpan(_span.TextBuffer.CurrentSnapshot);

            PersistentSpan.SnapshotPointToLineIndex(span.Start, out _startLine, out _startIndex);
            PersistentSpan.SnapshotPointToLineIndex(span.End, out _endLine, out _endIndex);

            return span;
        }

        private static void SnapshotPointToLineIndex(SnapshotPoint p, out int line, out int index)
        {
            ITextSnapshotLine l = p.GetContainingLine();

            line = l.LineNumber;
            index = Math.Min(l.Length, p - l.Start);
        }

        internal static SnapshotPoint LineIndexToSnapshotPoint(int line, int index, ITextSnapshot snapshot)
        {
            if (line >= snapshot.LineCount)
            {
                return new SnapshotPoint(snapshot, snapshot.Length);
            }

            ITextSnapshotLine l = snapshot.GetLineFromLineNumber(line);
            return l.Start + Math.Min(index, l.Length);
        }
    }
}