private void CheckIfTagsChanged(SnapshotRegions oldSnapshotRegions, SnapshotRegions newSnapshotRegions)
        {
            ITextSnapshot          oldSnapshot = oldSnapshotRegions.Snapshot;
            IReadOnlyList <Region> oldRegions  = oldSnapshotRegions.Regions;
            ITextSnapshot          newSnapshot = newSnapshotRegions.Snapshot;
            IReadOnlyList <Region> newRegions  = newSnapshotRegions.Regions;

            // Determine the changed spans and send a changed event with the new spans.
            List <Span> oldSpans = new List <Span>(
                oldRegions.Select(r => AsSnapshotSpan(r, oldSnapshot).TranslateTo(newSnapshot, SpanTrackingMode.EdgeExclusive).Span));
            List <Span> newSpans = new List <Span>(newRegions.Select(r => AsSnapshotSpan(r, newSnapshot).Span));

            NormalizedSpanCollection oldSpanCollection = new NormalizedSpanCollection(oldSpans);
            NormalizedSpanCollection newSpanCollection = new NormalizedSpanCollection(newSpans);

            // The changed regions are regions that appear in one set or the other, but not both.
            NormalizedSpanCollection removed = NormalizedSpanCollection.Difference(oldSpanCollection, newSpanCollection);

            int changeStart = int.MaxValue;
            int changeEnd   = -1;

            if (removed.Count > 0)
            {
                changeStart = removed[0].Start;
                changeEnd   = removed[removed.Count - 1].End;
            }

            if (newSpans.Count > 0)
            {
                changeStart = Math.Min(changeStart, newSpans[0].Start);
                changeEnd   = Math.Max(changeEnd, newSpans[newSpans.Count - 1].End);
            }

            if (changeStart <= changeEnd)
            {
                var handler = this.TagsChanged;
                if (handler != null)
                {
                    var args = new SnapshotSpanEventArgs(new SnapshotSpan(newSnapshot, Span.FromBounds(changeStart, changeEnd)));
                    handler(this, args);
                }
            }
        }
        public OutliningTagger(ITextBuffer buffer, ScanInfo scanInfo)
        {
            this.buffer = buffer;

            if (scanInfo != null)
            {
                // Our RegionHandler's GetRegionBeginRegex only looks for single line comment tokens
                // when doing Collapse/ExpandAllRegions, so we'll use the same restriction here.
                this.startExpressions = scanInfo.GetTokenRegexes(StartToken, true).ToList();
                this.endExpressions   = scanInfo.GetTokenRegexes(EndToken, true).Concat(scanInfo.GetTokenRegexes(AltEndToken, true)).ToList();
            }
            else
            {
                // If we don't have scan info, then we can't provide tags.
                this.startExpressions = CollectionUtility.EmptyArray <Regex>();
                this.endExpressions   = CollectionUtility.EmptyArray <Regex>();
            }

            this.snapshotRegions = new SnapshotRegions(buffer.CurrentSnapshot, CollectionUtility.EmptyArray <Region>());
            this.buffer.Changed += this.BufferChanged;
            this.BackgroundReparse();
        }
        private void Reparse()
        {
            ITextSnapshot newSnapshot = this.buffer.CurrentSnapshot;
            List <Region> newRegions  = new List <Region>();

            // Keep the current (deepest) partial region, which will have references to any parent partial regions.
            PartialRegion currentRegion = null;

            foreach (var line in newSnapshot.Lines)
            {
                string text = line.GetText();

                // Lines that match a start regex denote the start of a new region.
                Match startMatch = this.startExpressions.Select(regex => regex.Match(text)).FirstOrDefault(m => m.Success);
                if (startMatch != null)
                {
                    int matchStartIndex = startMatch.Index;
                    int currentLevel    = (currentRegion != null) ? currentRegion.Level : 1;
                    int newLevel        = currentLevel + 1;

                    // Levels are the same, and we have an existing region;
                    // End the current region and start the next.
                    if (currentLevel == newLevel && currentRegion != null)
                    {
                        newRegions.Add(new Region()
                        {
                            Level       = currentRegion.Level,
                            StartLine   = currentRegion.StartLine,
                            StartOffset = currentRegion.StartOffset,
                            EndLine     = line.LineNumber
                        });

                        currentRegion = new PartialRegion()
                        {
                            Level         = newLevel,
                            StartLine     = line.LineNumber,
                            StartOffset   = matchStartIndex,
                            PartialParent = currentRegion.PartialParent
                        };
                    }
                    else
                    {
                        // This is a new (sub)region
                        currentRegion = new PartialRegion()
                        {
                            Level         = newLevel,
                            StartLine     = line.LineNumber,
                            StartOffset   = matchStartIndex,
                            PartialParent = currentRegion
                        };
                    }
                }
                else
                {
                    // Lines that match an end regex denote the end of a region
                    Match endMatch = this.endExpressions.Select(regex => regex.Match(text)).FirstOrDefault(m => m.Success);
                    if (endMatch != null)
                    {
                        int currentLevel = (currentRegion != null) ? currentRegion.Level : 1;
                        int closingLevel = currentLevel;

                        // The regions match
                        if (currentRegion != null && currentLevel == closingLevel)
                        {
                            newRegions.Add(new Region()
                            {
                                Level       = currentLevel,
                                StartLine   = currentRegion.StartLine,
                                StartOffset = currentRegion.StartOffset,
                                EndLine     = line.LineNumber
                            });

                            currentRegion = currentRegion.PartialParent;
                        }
                    }
                }
            }

            SnapshotRegions newSnapshotRegions = new SnapshotRegions(newSnapshot, newRegions);
            SnapshotRegions oldSnapshotRegions;

            lock (this.resourceLock)
            {
                oldSnapshotRegions   = this.snapshotRegions;
                this.snapshotRegions = newSnapshotRegions;
            }

            this.CheckIfTagsChanged(oldSnapshotRegions, newSnapshotRegions);
        }