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: 79f205d72145ca5f0c44ee373b33bfbab02791d9 (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
//
//  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 Microsoft.VisualStudio.Text;
    using Microsoft.VisualStudio.Utilities;
    using System;
    using System.Diagnostics;

    internal sealed class PersistentSpan : IPersistentSpan
    {
        #region members
        private PersistentSpanFactory _factory;

        private ITrackingSpan _span;            //null for spans on closed documents or disposed spans
        private ITextDocument _document;        //null for spans on closed documents or disposed spans
        private string _filePath;               //null for spans on opened 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 Span _nonTrackingSpan;
        private bool _useLineIndex;

        private readonly SpanTrackingMode _trackingMode;
        #endregion

        internal PersistentSpan(ITextDocument document, SnapshotSpan span, SpanTrackingMode trackingMode, PersistentSpanFactory factory)
        {
            //Arguments verified in factory
            _document = document;

            _span = span.Snapshot.CreateTrackingSpan(span, trackingMode);
            _trackingMode = trackingMode;

            _factory = factory;
        }

        internal PersistentSpan(string filePath, int startLine, int startIndex, int endLine, int endIndex, SpanTrackingMode trackingMode, PersistentSpanFactory factory)
        {
            //Arguments verified in factory
            _filePath = filePath;

            _useLineIndex = true;
            _startLine = startLine;
            _startIndex = startIndex;
            _endLine = endLine;
            _endIndex = endIndex;

            _trackingMode = trackingMode;

            _factory = factory;
        }

        internal PersistentSpan(string filePath, Span span, SpanTrackingMode trackingMode, PersistentSpanFactory factory)
        {
            //Arguments verified in factory
            _filePath = filePath;

            _useLineIndex = false;
            _nonTrackingSpan = span;

            _trackingMode = trackingMode;

            _factory = factory;
        }

        #region IPersistentSpan members
        public bool IsDocumentOpen { get { return _document != null; } }

        public ITextDocument Document { get { return _document; } }

        public ITrackingSpan Span { get { return _span; } }

        public string FilePath
        {
            get
            {
                return (_document != null) ? _document.FilePath : _filePath;
            }
        }

        public bool TryGetStartLineIndex(out int startLine, out int startIndex)
        {
            if ((_document == null) && (_filePath == null))
                throw new ObjectDisposedException("PersistentSpan");

            if (_span != null)
                this.UpdateStartEnd();

            startLine = _startLine;
            startIndex = _startIndex;

            return ((_span != null) || _useLineIndex);
        }

        public bool TryGetEndLineIndex(out int endLine, out int endIndex)
        {
            if ((_document == null) && (_filePath == null))
                throw new ObjectDisposedException("PersistentSpan");

            if (_span != null)
                this.UpdateStartEnd();

            endLine = _endLine;
            endIndex = _endIndex;
            return ((_span != null) || _useLineIndex);
        }

        public bool TryGetSpan(out Span span)
        {
            if ((_document == null) && (_filePath == null))
                throw new ObjectDisposedException("PersistentSpan");

            if (_span != null)
                this.UpdateStartEnd();

            span = _nonTrackingSpan;
            return ((_span != null) || !_useLineIndex);
        }
        #endregion

        #region IDisposable members
        public void Dispose()
        {
            if ((_document != null) || (_filePath != null))
            {
                _factory.Delete(this);

                _span = null;
                _document = null;
                _filePath = null;
            }
        }
        #endregion

        #region private helpers
        internal void DocumentClosed()
        {
            this.UpdateStartEnd();

            //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;
            _nonTrackingSpan = new Span(0, 0);

            _filePath = _document.FilePath;
            _document = null;
            _span = null;
        }

        internal void DocumentReopened(ITextDocument document)
        {
            _document = document;

            ITextSnapshot snapshot = 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(_nonTrackingSpan.Start, snapshot.Length));
                end = new SnapshotPoint(snapshot, Math.Min(_nonTrackingSpan.End, snapshot.Length));
            }

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

            _filePath = null;
        }

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

            _nonTrackingSpan = span;

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

        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)
        {
            ITextSnapshotLine l = snapshot.GetLineFromLineNumber(Math.Min(line, snapshot.LineCount - 1));

            return l.Start + Math.Min(index, l.Length);
        }
        #endregion
    }
}