-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGenericBlockTagger.cs
148 lines (126 loc) · 4.59 KB
/
GenericBlockTagger.cs
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
using System;
using Microsoft.VisualStudio.Text;
using Microsoft.VisualStudio.Text.Tagging;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
namespace BraceProvider
{
internal sealed class GenericBlockTagger : ITagger<IBlockTag>
{
private ITextBuffer _buffer;
private BraceParser _parser;
private BackgroundScan _scan;
private BraceBlockTag _root;
private int _refCount;
public void AddRef()
{
if (++_refCount == 1)
{
_buffer.Changed += OnChanged;
this.ScanBuffer(_buffer.CurrentSnapshot);
}
}
public void Release()
{
if (--_refCount == 0)
{
_buffer.Changed -= OnChanged;
if (_scan != null)
{
_scan.Cancel();
_scan = null;
}
_root = null;
}
}
private void OnChanged(object sender, TextContentChangedEventArgs e)
{
if (AnyTextChanges(e.BeforeVersion, e.After.Version))
this.ScanBuffer(e.After);
}
private static bool AnyTextChanges(ITextVersion oldVersion, ITextVersion currentVersion)
{
while (oldVersion != currentVersion)
{
if (oldVersion.Changes.Count > 0)
return true;
oldVersion = oldVersion.Next;
}
return false;
}
private void ScanBuffer(ITextSnapshot snapshot)
{
if (_scan != null)
{
_scan.Cancel();
_scan = null;
}
_scan = new BackgroundScan(snapshot, _parser,
delegate (BraceBlockTag newRoot)
{
_root = newRoot;
this.TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, 0, snapshot.Length)));
});
}
public GenericBlockTagger(ITextBuffer buffer, BraceParser parser)
{
_buffer = buffer;
_parser = parser;
}
public event EventHandler<SnapshotSpanEventArgs> TagsChanged;
public IEnumerable<ITagSpan<IBlockTag>> GetTags(NormalizedSnapshotSpanCollection spans)
{
BraceBlockTag root = _root;
if (root != null)
{
if (root.Span.Snapshot != spans[0].Snapshot)
{
IList<SnapshotSpan> translatedSpans = new List<SnapshotSpan>(spans.Count);
foreach (var span in spans)
translatedSpans.Add(span.TranslateTo(root.Span.Snapshot, SpanTrackingMode.EdgeExclusive));
spans = new NormalizedSnapshotSpanCollection(translatedSpans);
}
foreach (var child in root.Children)
{
foreach (var tag in GetTags(child, spans))
yield return tag;
}
}
}
private static IEnumerable<ITagSpan<IBlockTag>> GetTags(BraceBlockTag block, NormalizedSnapshotSpanCollection spans)
{
if (spans.IntersectsWith(new NormalizedSnapshotSpanCollection(block.Span)))
{
yield return new TagSpan<IBlockTag>(block.Span, block);
foreach (var child in block.Children)
{
foreach (var tag in GetTags(child, spans))
yield return tag;
}
}
}
}
internal class BackgroundScan
{
public CancellationTokenSource CancellationSource = new CancellationTokenSource();
public delegate void CompletionCallback(BraceBlockTag root);
public BackgroundScan(ITextSnapshot snapshot, BraceParser parser, CompletionCallback completionCallback)
{
Task.Run(async delegate
{
BraceBlockTag newRoot = await parser.ParseAsync(snapshot, this.CancellationSource.Token);
if ((newRoot != null) && !this.CancellationSource.Token.IsCancellationRequested)
completionCallback(newRoot);
});
}
public void Cancel()
{
if (this.CancellationSource != null)
{
this.CancellationSource.Cancel();
this.CancellationSource.Dispose();
}
}
}
}